PoiAddressController.java 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. package com.skyversation.poiaddr.controller;
  2. import com.skyversation.poiaddr.addquery.AddressQueryEngine;
  3. import com.skyversation.poiaddr.bean.AddressResult;
  4. import com.skyversation.poiaddr.entity.FileDataDto;
  5. import com.skyversation.poiaddr.service.impl.TestDataServiceImpl;
  6. import com.skyversation.poiaddr.util.*;
  7. import com.skyversation.poiaddr.util.dms.DmsTools;
  8. import com.skyversation.poiaddr.util.fileTools.ReadFileData;
  9. import com.skyversation.poiaddr.util.geotools.GeoJsonIntersector;
  10. import com.skyversation.poiaddr.util.geotools.GeoJsonPointInRegion;
  11. import com.skyversation.poiaddr.util.status.AddressLevel;
  12. import com.skyversation.poiaddr.util.zipunit.DataExportUtil;
  13. import lombok.extern.slf4j.Slf4j;
  14. import org.json.simple.JSONObject;
  15. import org.springframework.http.MediaType;
  16. import org.springframework.util.StringUtils;
  17. import org.springframework.validation.annotation.Validated;
  18. import org.springframework.web.bind.annotation.*;
  19. import org.springframework.web.multipart.MultipartFile;
  20. import javax.annotation.Resource;
  21. import javax.servlet.http.HttpServletRequest;
  22. import javax.servlet.http.HttpServletResponse;
  23. import java.io.*;
  24. import java.util.*;
  25. import java.util.concurrent.ExecutionException;
  26. import java.util.concurrent.ExecutorService;
  27. import java.util.concurrent.Executors;
  28. import java.util.concurrent.Future;
  29. @Slf4j
  30. @Validated
  31. @RestController
  32. @RequestMapping("/poiAddress")
  33. public class PoiAddressController {
  34. @Resource
  35. private TestDataServiceImpl testDataService;
  36. @Resource
  37. private AddressQueryEngine addressQueryEngine;
  38. // 地址查询
  39. @GetMapping(value = "/selectAddressInfo/{address}")
  40. public Object selectAddressInfo(@PathVariable("address") String address) {
  41. return RequestUtils.request(address);
  42. }
  43. @RequestMapping("/reverseGeocoding")
  44. public Object reverseGeocoding(HttpServletRequest request){
  45. String lonStr = request.getParameter("lon");
  46. String latStr = request.getParameter("lat");
  47. double lon = 0;
  48. double lat = 0;
  49. try {
  50. lon = Double.valueOf(lonStr);
  51. lat = Double.valueOf(latStr);
  52. } catch (Exception e){
  53. return MessageManage.getInstance().getResultContent(Constant.PARAM_ERROR, "参数错误", "参数错误");
  54. }
  55. if(lon == 0 || lat == 0){
  56. return MessageManage.getInstance().getResultContent(Constant.PARAM_ERROR, "参数错误", "参数错误");
  57. }
  58. AddressResult addressResult = AddressQueryEngine.getInstance().reverseGeocoding(lon, lat);
  59. if (addressResult == null || addressResult.getData() == null || addressResult.getData().size() < 1 ){
  60. return MessageManage.getInstance().getResultContent(Constant.NO_DATA,"无数据","未找到");
  61. }else {
  62. return MessageManage.getInstance().getAddressResultContent(Constant.SUCCESS,addressResult,"成功");
  63. }
  64. }
  65. @RequestMapping("/selectPoi")
  66. public Object selectPoi(HttpServletRequest request){
  67. String address = request.getParameter("address");
  68. if(!StringUtils.hasText(address)){
  69. return MessageManage.getInstance().getResultContent(Constant.PARAM_ERROR, "参数错误", "参数错误");
  70. }
  71. AddressResult addressResult = addressQueryEngine.commonSearchByName(address,AddressLevel.LEVEL_1);
  72. if (addressResult == null || addressResult.getData() == null || addressResult.getData().size() < 1 || addressResult.getMessage().equals("失败")){
  73. return MessageManage.getInstance().getResultContent(Constant.NO_DATA,"无数据","未找到");
  74. }else {
  75. return MessageManage.getInstance().getAddressResultContent(Constant.SUCCESS,addressResult,"成功");
  76. }
  77. }
  78. // 根据街镇名称和村居名称返回符合条件的geojson集合文件
  79. @PostMapping(value = "/selectGeojsonByNames", produces = MediaType.MULTIPART_FORM_DATA_VALUE)
  80. public Object selectGeojsonByNames(HttpServletResponse response, @RequestParam("jdNameStr") String jdNameStr, @RequestParam(name = "czNameStr", required = false) String czNameStr) throws Exception {
  81. String fileName = UUID.randomUUID().toString();
  82. GeoJsonIntersector.run(jdNameStr, czNameStr, fileName);
  83. ReadFileData.outputFile("output/result_" + fileName + ".geojson", response);
  84. return "成功";
  85. }
  86. /**
  87. * (开发中)
  88. * 定时器:每天都全量更新法人库的地名地址数据
  89. * 每次分页查询每批次处理1W条数据
  90. * 1、获取数据库连接
  91. * 2、查询数据
  92. * 3、得到地名地址字段和值(清洗数据,并地名格式调整)
  93. * 4、批量多任务处理查询结果并判断网格区划
  94. * 5、将结果保存为ser文件
  95. */
  96. @PostMapping(value = "/test", produces = MediaType.APPLICATION_JSON_VALUE)
  97. public Object updateDataBaseData(@RequestParam(name = "pageSize") Integer pageSize, @RequestParam(name = "level") Integer level, @RequestParam(name = "init") Integer init) {
  98. // 记录程序开始时间
  99. long startTime = System.currentTimeMillis();
  100. // 批量处理数据
  101. if (level == null) {
  102. level = 1;
  103. }
  104. testDataService.iterativeProcessing(pageSize, level, init);// 记录程序结束时间
  105. long endTime = System.currentTimeMillis();
  106. return "处理完成!用时" + (endTime - startTime) / 1000 + "秒!";
  107. }
  108. /**
  109. * (测试中)
  110. * 地名查询任务接口
  111. * * 参数:
  112. * ** 必填:
  113. * *** file: xlsx、csv、geojson、shape
  114. * *** addrColNames: 地名地址字段名称数组,依次查询
  115. * ** 非必填:
  116. * *** inCoordinate: 输入坐标系(默认为wgs84)
  117. * *** latLonColName: 经纬度字段名称,如果长度为一,经纬度字段分隔符为必填
  118. * *** latLonSplitStr: 经纬度字段分隔符
  119. * *** matchingDistance: 匹配距离,如果不为空,经纬度字段名称不能为空
  120. * *** outCoordinate: 输出坐标系(默认为wgs84)
  121. * *** matchingLevel: 匹配等级,详情见xmind文件【1、2、3、4、5】
  122. * *** regionalJudgment: 区域判断,geojson、shape文件[前端可以内置一些常用的区域文件,通过下拉框的方式进行选择]
  123. * <p>
  124. * JDK版本要规定为11(已完成)回退为8(已完成)
  125. * 需要使用request获取参数,参数校验不能太严格。(通过拦截器的方式也实现了参数乱斗并对必传参数的非空判断以及错误提示)
  126. * 接口调用要使用oauth认证,添加拦截器验证headers里面的token。(已完成,暂时使用的是121的Oauth系统,用户名:sj_test)
  127. * 任务开始和结束要操作DMS(开始时间-创建时间,结束时间-修改时间,文件名,任务状态,备注)。根据token调用接口就可以(待测试)
  128. *
  129. * @return Object
  130. */
  131. @RequestMapping(value = "/nameQueryTaskInterface", produces = MediaType.MULTIPART_FORM_DATA_VALUE)
  132. public Object GeoCoordinate(HttpServletResponse response,
  133. @RequestHeader(value = "token") String token,
  134. @RequestParam(name = "file") MultipartFile file,
  135. @RequestParam(name = "addrColNames") String addrColNames,
  136. @RequestParam(name = "inCoordinate", required = false) String inCoordinate,
  137. @RequestParam(name = "latLonColName", required = false) String latLonColName,
  138. @RequestParam(name = "latLonSplitStr", required = false) String latLonSplitStr,
  139. @RequestParam(name = "matchingDistance", required = false) Long matchingDistance,
  140. @RequestParam(name = "outCoordinate", required = false) String outCoordinate,
  141. @RequestParam(name = "matchingLevel", required = false) Integer matchingLevel,
  142. @RequestParam(name = "regionalJudgment", required = false) MultipartFile regionalJudgment,
  143. @RequestParam(name = "outputFileName", required = false) String outputFileName) {
  144. // 参数合法性判断
  145. if ((file != null && !file.isEmpty()) && (addrColNames != null && !addrColNames.isEmpty())) {
  146. // 先更新一下token
  147. DmsTools.DmsToken = token;
  148. if (outputFileName == null) {
  149. outputFileName = "";
  150. }
  151. outputFileName += UUID.randomUUID();
  152. org.json.JSONObject params = new org.json.JSONObject();
  153. params.put("columnId", "1542");
  154. params.put("modelId", "1445");
  155. org.json.JSONObject paramsContent = new org.json.JSONObject();
  156. paramsContent.put("title", outputFileName);
  157. paramsContent.put("content", new Date().toString());
  158. paramsContent.put("c_content", "任务初始化");
  159. // 0:任务初始化;1:开始处理;2:异常中断;3:任务完成
  160. paramsContent.put("c_task_status", 0);
  161. params.put("content", paramsContent);
  162. JSONObject initDmsData = DmsTools.addContent(params);
  163. if (initDmsData.get("message") == null || !"200".equals(initDmsData.get("code").toString())) {
  164. return "任务初始化失败!DMS数据插入异常,请联系管理员!";
  165. }
  166. paramsContent.put("id", initDmsData.get("content"));
  167. System.out.println("任务初始化:" + initDmsData.get("message"));
  168. try {
  169. // 搜索地址key
  170. String addr1Key = "";
  171. String addr2Key = "";
  172. // 参考经纬度key
  173. String latKey = "";
  174. String lonKey = "";
  175. String latLonKey = "";
  176. String SplitStr = "";
  177. paramsContent.put("c_task_status", 1);
  178. paramsContent.put("c_content", "开始处理");
  179. params.put("content", paramsContent);
  180. DmsTools.updateContent(params);
  181. // 多地址的形式
  182. if (addrColNames.contains(",")) {
  183. String[] addrs = addrColNames.split(",");
  184. if (addrs.length > 2) {
  185. return "最多支持两个地址列查询,请检查addrColNames参数!";
  186. }
  187. addr1Key = addrs[0];
  188. addr2Key = addrs[1];
  189. } else {
  190. addr1Key = addrColNames;
  191. }
  192. if (matchingLevel == null) {
  193. matchingLevel = 1;
  194. }
  195. // 匹配距离参数合法性判断
  196. if (matchingDistance != null) {
  197. if (latLonColName == null || latLonColName.isEmpty()) {
  198. return "当匹配距离参数不为空时,必须传入经纬度参数!";
  199. }
  200. }
  201. // 经纬度合法性判断
  202. if (latLonColName != null) {
  203. // 两个key
  204. if (latLonColName.contains(",")) {
  205. String[] latLons = latLonColName.split(",");
  206. latKey = latLons[0];
  207. lonKey = latLons[1];
  208. } else if (latLonSplitStr != null && !latLonSplitStr.isEmpty()) {
  209. // 单个key分割,必须要分割符参数
  210. latLonKey = latLonColName;
  211. SplitStr = latLonSplitStr;
  212. } else {
  213. return "经纬度列名解析失败,请传入经度和纬度的列名以逗号分割或传入经纬度列名和分隔符参数!";
  214. }
  215. }
  216. // 封装解析文件的参数
  217. // TODO 文件数据解析
  218. List<FileDataDto> fileDataDtoList = ReadFileData.ReadMultipartFile(file);
  219. List<FileDataDto> regionalDataList = new ArrayList<>();
  220. if (regionalJudgment != null && !regionalJudgment.isEmpty()) {
  221. // 解析geojson文件得到区域
  222. regionalDataList = ReadFileData.ReadMultipartFile(regionalJudgment);
  223. }
  224. // TODO 补充FileDataDto中的搜索条件参数
  225. for (FileDataDto fileDataDto : fileDataDtoList) {
  226. // 搜索等级
  227. fileDataDto.setMatchingLevel(matchingLevel);
  228. // TODO 数据过滤
  229. // set地名地址搜索字段
  230. Map<String, Object> properties = fileDataDto.getProperties();
  231. try {
  232. if (properties != null && addr1Key != null && properties.get(addr1Key) != null && properties.get(addr1Key).toString().length() > 2 && ExcelReaderUtils.isOtherDistrictThanQingpu(properties.get(addr1Key).toString())) {
  233. String address = "上海市青浦区" + AddressQueryEngine.addressReplaceAll(properties.getOrDefault(addr1Key, "").toString());
  234. fileDataDto.setAddr1(address);
  235. }
  236. if (properties != null && addr2Key != null && properties.get(addr2Key) != null && properties.get(addr2Key).toString().length() > 2 && ExcelReaderUtils.isOtherDistrictThanQingpu(properties.get(addr2Key).toString())) {
  237. String address = "上海市青浦区" + AddressQueryEngine.addressReplaceAll(properties.getOrDefault(addr2Key, "").toString());
  238. fileDataDto.setAddr2(address);
  239. }
  240. } catch (Exception e) {
  241. System.err.println(e);
  242. }
  243. // 判断是否有参考经纬度字段
  244. if (properties != null && latKey != null && !latKey.isEmpty() && lonKey != null && !lonKey.isEmpty() && properties.get(latKey) != null && properties.get(lonKey) != null) {
  245. String latStr = properties.get(latKey).toString();
  246. String lonStr = properties.get(lonKey).toString();
  247. fileDataDto.setLat(Double.valueOf(latStr));
  248. fileDataDto.setLon(Double.valueOf(lonStr));
  249. } else if (!latLonKey.isEmpty()) {
  250. String[] latLonKeys = latLonKey.split(SplitStr);
  251. if (properties != null && properties.get(latLonKeys[0]) != null && properties.get(latLonKeys[1]) != null) {
  252. String latStr = properties.get(latLonKeys[0]).toString();
  253. String lonStr = properties.get(latLonKeys[1]).toString();
  254. fileDataDto.setLat(Double.valueOf(latStr));
  255. fileDataDto.setLon(Double.valueOf(lonStr));
  256. }
  257. }
  258. }
  259. // TODO 地名查询
  260. // 创建线程池
  261. int threadCount = Runtime.getRuntime().availableProcessors();
  262. ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
  263. List<Future<?>> futures = new ArrayList<>();
  264. // 为每个元素提交任务
  265. for (FileDataDto fileDataDto : fileDataDtoList) {
  266. futures.add(executorService.submit(() -> {
  267. List<String> addrList = new ArrayList<>();
  268. if (fileDataDto.getAddr1() != null) {
  269. addrList.add(fileDataDto.getAddr1());
  270. }
  271. if (fileDataDto.getAddr2() != null) {
  272. addrList.add(fileDataDto.getAddr2());
  273. }
  274. if (addrList.size() > 0) {
  275. AddressResult addressResult = addressQueryEngine.commonSearchByName(addrList, AddressLevel.values()[fileDataDto.getMatchingLevel() - 1]);
  276. if (addressResult != null) {
  277. if (addressResult.getData() == null || addressResult.getData().size() < 1) {
  278. fileDataDto.getProperties().put("所属街道", null);
  279. fileDataDto.getProperties().put("所属居委", null);
  280. fileDataDto.getProperties().put("纬度", null);
  281. fileDataDto.getProperties().put("经度", null);
  282. fileDataDto.getProperties().put("搜索结果地址", null);
  283. } else {
  284. try {
  285. for (AddressResult.ContentBean contentBean : addressResult.getData()) {
  286. String resultAddrKey = contentBean.getAddress();
  287. if (resultAddrKey != null && contentBean.getLon() != null && contentBean.getLat() != null && contentBean.getCjJson() != null) {
  288. String lng = contentBean.getLat() + "";
  289. String lat = contentBean.getLon() + "";
  290. fileDataDto.getProperties().put("所属街道", contentBean.getCjJson().getString("所属街道"));
  291. fileDataDto.getProperties().put("所属居委", contentBean.getCjJson().getString("所属居委"));
  292. fileDataDto.getProperties().put("纬度", lng);
  293. fileDataDto.getProperties().put("经度", lat);
  294. fileDataDto.getProperties().put("搜索结果地址", resultAddrKey);
  295. System.out.println("成功:" + lat + "," + lng);
  296. break;
  297. } else if (resultAddrKey != null && contentBean.getLon() != null && contentBean.getLat() != null) {
  298. fileDataDto.getProperties().put("所属街道", null);
  299. fileDataDto.getProperties().put("所属居委", null);
  300. fileDataDto.getProperties().put("纬度", null);
  301. fileDataDto.getProperties().put("经度", null);
  302. fileDataDto.getProperties().put("搜索结果地址", null);
  303. }
  304. }
  305. } catch (Exception e) {
  306. fileDataDto.getProperties().put("所属街道", null);
  307. fileDataDto.getProperties().put("所属居委", null);
  308. fileDataDto.getProperties().put("纬度", null);
  309. fileDataDto.getProperties().put("经度", null);
  310. fileDataDto.getProperties().put("搜索结果地址", null);
  311. System.err.println(e);
  312. }
  313. }
  314. }
  315. } else {
  316. fileDataDto.getProperties().put("所属街道", null);
  317. fileDataDto.getProperties().put("所属居委", null);
  318. fileDataDto.getProperties().put("纬度", null);
  319. fileDataDto.getProperties().put("经度", null);
  320. fileDataDto.getProperties().put("搜索结果地址", null);
  321. }
  322. }));
  323. }
  324. // 等待所有任务完成
  325. for (Future<?> future : futures) {
  326. try {
  327. future.get();
  328. } catch (InterruptedException | ExecutionException e) {
  329. e.printStackTrace();
  330. }
  331. }
  332. // 关闭线程池
  333. executorService.shutdown();
  334. // TODO 转换坐标(根据输入坐标系将参考坐标统一转换成wgs84)
  335. ArrayList<String> selectOptions = new ArrayList<>();
  336. // {"WGS84(国际通用)", "GCJ02(高德、QQ地图)", "BD09(百度地图)", "上海2000坐标系"}
  337. selectOptions.add("WGS84");
  338. selectOptions.add("GCJ02");
  339. selectOptions.add("BD09");
  340. selectOptions.add("SH2000");
  341. if (inCoordinate != null && !inCoordinate.isEmpty()) {
  342. if (!selectOptions.contains(inCoordinate)) {
  343. return "请传入正确的坐标系名称!可选坐标系名称:WGS84,GCJ02,BD09,SH2000";
  344. }
  345. } else {
  346. inCoordinate = "WGS84";
  347. }
  348. List<Map<String, Object>> dataList = new ArrayList<>();
  349. // TODO 距离计算和区域判断和转换坐标
  350. for (FileDataDto fileDataDto : fileDataDtoList) {
  351. if (fileDataDto.getLat() != null && fileDataDto.getLon() != null) {
  352. double[] lonLat = com.skyversation.poiaddr.util.Coordinate.transformationCoordinateByCoordinate(fileDataDto.getLat(), fileDataDto.getLon(), inCoordinate, outCoordinate);
  353. fileDataDto.setLat(lonLat[1]);
  354. fileDataDto.setLon(lonLat[0]);
  355. }
  356. // TODO 距离计算
  357. if (matchingDistance != null) {
  358. if (fileDataDto.getResultLat() != null && fileDataDto.getResultLon() != null && fileDataDto.getLat() != null && fileDataDto.getLon() != null) {
  359. double distance = com.skyversation.poiaddr.util.Coordinate.calculateDistance(fileDataDto.getResultLat(), fileDataDto.getResultLon(), fileDataDto.getLat(), fileDataDto.getLon());
  360. fileDataDto.setMatchingDistance(distance);
  361. double matchingDistanceDb = Double.parseDouble(matchingDistance.toString());
  362. if (distance < matchingDistanceDb) {
  363. fileDataDto.setLtMatchingDistance("是");
  364. } else {
  365. fileDataDto.setLtMatchingDistance("否");
  366. }
  367. }
  368. }
  369. // TODO 区域判断(得到经纬度后,要判断得到的点是否在传入的geojson面内)
  370. if (regionalDataList != null && regionalDataList.size() > 0) {
  371. // 解析geojson文件得到区域
  372. boolean isArea = false;
  373. for (FileDataDto regionlItem : regionalDataList) {
  374. if (GeoJsonPointInRegion.isPointInGeoJsonRegion(regionlItem.getGeometry().toString(), fileDataDto.getLat(), fileDataDto.getLon())) {
  375. isArea = true;
  376. break;
  377. }
  378. }
  379. fileDataDto.getProperties().put("是否在区域内", isArea ? "是" : "否");
  380. }
  381. dataList.add(fileDataDto.getProperties());
  382. }
  383. // TODO 结果输出 result[_匹配等级][_匹配距离][_区域名称][_坐标系].[文件类型]
  384. // 保存实体类到序列化文件
  385. SerializationUtils.serialize(fileDataDtoList, "output/output" + outputFileName + ".ser");
  386. System.out.println("处理完成!");
  387. paramsContent.put("c_task_status", 3);
  388. paramsContent.put("c_content", "处理完成!");
  389. paramsContent.put("c_file_name", "output/output" + outputFileName + ".ser");
  390. params.put("content", paramsContent);
  391. DmsTools.updateContent(params);
  392. return "文件解析完成,共" + fileDataDtoList.size() + "条数据!";
  393. } catch (Exception e) {
  394. paramsContent.put("c_task_status", 2);
  395. paramsContent.put("c_content", "异常中断:" + e);
  396. params.put("content", paramsContent);
  397. DmsTools.updateContent(params);
  398. return "异常中断:" + e;
  399. }
  400. } else {
  401. return "file或addrColName不能为空!";
  402. }
  403. }
  404. /**
  405. * (开发中)
  406. * 根据文件名和文件类型,下载对应文件类型的数据文件
  407. *
  408. * @param response
  409. * @param outFileType: 输出文件类型【xlsx、csv、geojson(暂不支持)、shape(暂不支持)】
  410. * @param fileName
  411. * @throws IOException
  412. */
  413. @RequestMapping(value = "/getFileByParams", produces = MediaType.MULTIPART_FORM_DATA_VALUE)
  414. public void getFileByParams(HttpServletResponse response, @RequestParam(name = "outFileType", required = false) String outFileType, @RequestParam(name = "fileName", required = false) String fileName) throws IOException {
  415. // 从文件中反序列化读取数据
  416. List<FileDataDto> deserializedList = SerializationUtils.deserialize(fileName);
  417. List<Map<String, Object>> dataList = new ArrayList<>();
  418. if (deserializedList != null) {
  419. for (FileDataDto person : deserializedList) {
  420. dataList.add(person.getProperties());
  421. }
  422. }
  423. if (outFileType != null) {
  424. if (outFileType.contains("csv")) {
  425. outFileType = "csv";
  426. }
  427. if (outFileType.contains("xlsx")) {
  428. outFileType = "xlsx";
  429. }
  430. } else {
  431. outFileType = "csv";
  432. }
  433. DataExportUtil.exportDataToZip(dataList, fileName.replace(".ser", ""), response, outFileType);
  434. }
  435. }