AddressQueryEngine.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. package com.skyversation.poiaddr.addquery;
  2. import com.alibaba.fastjson.JSONArray;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.skyversation.poiaddr.bean.AddressResult;
  5. import com.skyversation.poiaddr.bean.GeoJsonBean;
  6. import com.skyversation.poiaddr.bean.WDToken;
  7. import com.skyversation.poiaddr.service.AreaService;
  8. import com.skyversation.poiaddr.util.net.AddressNetTools;
  9. import com.skyversation.poiaddr.util.status.AddressLevel;
  10. import com.skyversation.poiaddr.util.status.AddressResultEnum;
  11. import org.springframework.http.HttpMethod;
  12. import org.springframework.http.ResponseEntity;
  13. import org.springframework.util.LinkedMultiValueMap;
  14. import org.springframework.util.MultiValueMap;
  15. import org.springframework.util.StringUtils;
  16. import org.apache.commons.codec.binary.Base64;
  17. import javax.crypto.Mac;
  18. import javax.crypto.spec.SecretKeySpec;
  19. import java.util.*;
  20. public class AddressQueryEngine {
  21. private static AddressQueryEngine instance = new AddressQueryEngine();
  22. private AddressQueryEngine() {
  23. }
  24. public synchronized static AddressQueryEngine getInstance() {
  25. if (instance == null) {
  26. instance = new AddressQueryEngine();
  27. }
  28. return instance;
  29. }
  30. private WDToken wdToken = null;
  31. /***
  32. * 单条地名搜索通用方法,搜索顺序 1. 武大吉奥; 2. 市四中心; 3. 高德搜索; 4. 高德高级搜索;
  33. * @param addrs
  34. * @return
  35. */
  36. public AddressResult commonSearchByName(List<String> addrs) {
  37. if (addrs == null || addrs.size() < 1) {
  38. return null;
  39. }
  40. AddressResult addressResult = new AddressResult();
  41. if (addressResult.getData() == null || addressResult.getData().size() == 0) {
  42. for (String addr : addrs) {
  43. // 创建请求
  44. addressResult = sj_szxSearchByName(addr);
  45. if(addressResult != null){
  46. addressResult.setCode(AddressResultEnum.SZX_SUCCESS);
  47. addressResult.setMessage("成功");
  48. return getCjWgWgwByLoc(addressResult);
  49. }
  50. }
  51. }
  52. addressResult.setCode(AddressResultEnum.RESULT_NULL);
  53. addressResult.setMessage("失败");
  54. return addressResult;
  55. }
  56. /***
  57. * 武大吉奥单条地名搜索
  58. * @param address
  59. */
  60. public AddressResult wdjaSearchByName(String address) {
  61. System.out.println("<<<<<<<<<<----------开始武大吉奥地名地址搜索------------>>>>>>>>>>>>>");
  62. if (wdToken == null || System.currentTimeMillis() - wdToken.getTime() > 36000000) {
  63. wdToken = AddressTools.getInstance().getWDToken(Constant.WD_USER_NAME, Constant.WD_USER_PWD);
  64. if (wdToken == null) {
  65. return null;
  66. }
  67. wdToken.setTime(System.currentTimeMillis());
  68. }
  69. if (wdToken == null)
  70. return AddressTools.getInstance().faildQuery(AddressResultEnum.NO_TOKEN, "token获取失败");
  71. String token = wdToken.getToken();
  72. String url = Constant.GET_ADDRESS_MEG_URL + "?token=" + token + "&addr=" + address;
  73. String body = null;
  74. try {
  75. body = AddressNetTools.getInstance().wdSendGetRequest(url);
  76. } catch (Exception e) {
  77. e.toString();
  78. }
  79. if (body == null || body.equals("null") || !StringUtils.hasText(body))
  80. return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
  81. try {
  82. JSONObject json = JSONObject.parseObject(body);
  83. return TransfromDataTool.wdResultToResult(json);
  84. } catch (Exception e) {
  85. return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
  86. }
  87. }
  88. /***
  89. * 市四中心地名搜索
  90. * @param address
  91. * @return
  92. */
  93. public AddressResult szxSearchByName(String address) {
  94. ResponseEntity response = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.GET, Constant.SZX_URL + "&region=" + Constant.getAMAP_CITY_CODE() + "&query=" + address, null, null, 10);
  95. if (response != null) {
  96. String body = response.getBody() + "";
  97. if (!StringUtils.hasText(body))
  98. return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
  99. try {
  100. return TransfromDataTool.szxResultToResult(JSONObject.parseObject(body));
  101. } catch (Exception e) {
  102. System.err.println(e);
  103. return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
  104. }
  105. } else {
  106. System.out.println("地址[" + address + "]未查询到数据!");
  107. return null;
  108. }
  109. }
  110. public String getSigns(String timestamp, String appCode, String UUid, String appSecret) {
  111. // 先拼接
  112. String signData = timestamp + appCode + UUid;
  113. // 生成sign
  114. String sign = "";
  115. try {
  116. Mac sha256 = Mac.getInstance("HmacSHA256");
  117. sha256.init(new SecretKeySpec(appSecret.getBytes(), "HmacSHA256"));
  118. byte[] result = sha256.doFinal(signData.getBytes());
  119. sign = Base64.encodeBase64String(result);
  120. } catch (Exception e) {
  121. System.err.println("sign 生成异常:" + e);
  122. }
  123. return sign;
  124. }
  125. public AddressResult sj_szxSearchByName(String address) {
  126. // 鉴权
  127. Map<String, String> headerMap = new HashMap<>();
  128. String timestamp = System.currentTimeMillis() + "";
  129. String appCode = "7unv4vbwqxnq4a7m9h";
  130. String UUid = UUID.randomUUID().toString();
  131. String appSecret = "w6tawvf4k3ck4ikij9";
  132. headerMap.put("x-timestamp", timestamp);
  133. headerMap.put("x-appcode", appCode);
  134. headerMap.put("x-uuid", UUid);
  135. // 签名
  136. headerMap.put("x-sign", getSigns(timestamp, appCode, UUid, appSecret));
  137. JSONObject params = new JSONObject();
  138. JSONArray requestJson = new JSONArray();
  139. JSONObject paramObject = new JSONObject();
  140. paramObject.put("name", "query");
  141. paramObject.put("position", "QUERY");
  142. paramObject.put("value", address);
  143. requestJson.add(paramObject);
  144. params.put("requestJson", requestJson);
  145. // 发起请求
  146. ResponseEntity response = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.POST, Constant.SJ_SZX_SEARCH_BY_NAME, params, headerMap, 1);
  147. if (response != null) {
  148. String body = response.getBody() + "";
  149. if (!StringUtils.hasText(body))
  150. return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
  151. try {
  152. if(body.indexOf("[") != 0){
  153. System.out.println("请求地址:" + address + ";返回结果错误:" + body);
  154. return null;
  155. }else{
  156. // 匹配得到得分最高的结果
  157. JSONObject res = com.skyversation.poiaddr.util.AddressTools.getInstance().findBestMatch(address,JSONArray.parseArray(body),"address");
  158. // 将得分最高的结果映射到实体类
  159. return TransfromDataTool.szxResultToResult2(res);
  160. }
  161. } catch (Exception e) {
  162. System.err.println("请求地址:" + address + ";返回结果:" + body + ";处理异常:" + e);
  163. return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
  164. }
  165. } else {
  166. System.out.println("地址[" + address + "]未查询到数据!");
  167. return null;
  168. }
  169. }
  170. /***
  171. * 高德普通地名搜索
  172. * @param address
  173. * @return
  174. */
  175. public AddressResult gdSearchByName(String address) {
  176. String geoUrl = Constant.AMAP_GEO_URL + "?key=" + Constant.AMAP_KEY + "&address=" + address + "&city=" + Constant.getAMAP_CITY_CODE() + "&output=JSON";
  177. ResponseEntity response = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.GET, geoUrl, null, null, 0);
  178. if (response != null && response.getBody() != null) {
  179. String body = response.getBody() + "";
  180. if (!StringUtils.hasText(body))
  181. return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
  182. try {
  183. JSONObject json = JSONObject.parseObject(body);
  184. return TransfromDataTool.gdResultToResult(json);
  185. } catch (Exception e) {
  186. System.err.println(e);
  187. return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
  188. }
  189. } else {
  190. return null;
  191. }
  192. }
  193. /***
  194. * 高德高级地名搜索
  195. * @param address
  196. * @return
  197. */
  198. public AddressResult gdV3SearchByName(String address) {
  199. String geoUrl =
  200. Constant.AMAP_SEARCH_NAME_V3 + "?key=" + Constant.AMAP_KEY + "&types=" + Constant.AMAP_SEARCH_TYPES +
  201. "&keywords=" + address + "&city=" + Constant.getAMAP_CITY_CODE() + "&offset=20";
  202. ResponseEntity responseEntity = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.GET, geoUrl, null, null, 0);
  203. if (responseEntity.hasBody()) {
  204. String body = responseEntity.getBody() + "";
  205. if (!StringUtils.hasText(body)) {
  206. return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
  207. }
  208. JSONArray pois = new JSONArray();
  209. JSONObject json = JSONObject.parseObject(body);
  210. if (json.containsKey("pois")) {
  211. pois.addAll(json.getJSONArray("pois"));
  212. }
  213. if (json.containsKey("count")) {
  214. int count = Integer.parseInt(json.get("count") + "");
  215. if (count > 20) {
  216. for (int i = 2; i < (count % 20 + 1); i++) {
  217. geoUrl =
  218. Constant.AMAP_SEARCH_NAME_V3 + "?key=" + Constant.AMAP_KEY + "&types=" + Constant.AMAP_SEARCH_TYPES +
  219. "&keywords=" + address + "&city=" + Constant.getAMAP_CITY_CODE() + "&offset=20&page=" + i;
  220. ResponseEntity responseEntity2 = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.GET, geoUrl, null, null, 0);
  221. if (responseEntity2.hasBody()) {
  222. body = responseEntity2.getBody() + "";
  223. }
  224. if (!StringUtils.hasText(body)) {
  225. return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_AGAIN_NULL, "多次搜索无结果");
  226. }
  227. JSONObject json2 = JSONObject.parseObject(body);
  228. if (json2.containsKey(pois)) {
  229. pois.addAll(JSONObject.parseObject(body).getJSONArray("pois"));
  230. }
  231. }
  232. }
  233. }
  234. try {
  235. return TransfromDataTool.gdV3ResultToResult(pois);
  236. } catch (Exception e) {
  237. System.err.println(e);
  238. return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
  239. }
  240. } else {
  241. return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_AGAIN_NULL, "高德V3分页查询无结果");
  242. }
  243. }
  244. /***
  245. * 根据搜索地址,赋值村居、网格、微格网信息
  246. * @param result
  247. * @return
  248. */
  249. public AddressResult getCjWgWgwByLoc(AddressResult result) {
  250. if (result == null || result.getData() == null || result.getData().size() < 1) {
  251. return result;
  252. } else {
  253. AreaService areaService = AreaService.getInstance();
  254. for (AddressResult.ContentBean content : result.getData()) {
  255. if ((content.getAdname() == null || content.getCityname() == null) && content.getLat() != null && content.getLon() != null) {
  256. GeoJsonBean adBean = areaService.isInadPolygon(content.getLon(), content.getLat());
  257. content.setAdJson(adBean == null ? new JSONObject() : adBean.getProperties());
  258. GeoJsonBean townBean = areaService.isInTownPolygon(content.getLon(), content.getLat());
  259. content.setTownJson(townBean == null ? new JSONObject() : townBean.getProperties());
  260. }
  261. }
  262. return result;
  263. }
  264. }
  265. //
  266. public static boolean isNotEmptyOrBlank(String str) {
  267. return str != null && !str.trim().isEmpty();
  268. }
  269. /**
  270. * 1. 搜索到结果就返回第一个;
  271. * 2. 搜索到结果去除行政区划后,存在包含关系,则使用;
  272. * 3. 搜索到结果,数字进行分词,数字匹配则使用;
  273. * 4. 搜索到结果,数字与文本均匹配,则使用;
  274. * 5. 结果与基准数据完全一致,则使用。
  275. *
  276. * @param result
  277. * @param level
  278. * @return
  279. */
  280. public AddressResult.ContentBean verificaData(AddressResult result, AddressLevel level, String addr) {
  281. switch (level) {
  282. case LEVEL_1: {
  283. if (result != null && result.getData() != null && result.getData().size() > 0) {
  284. return result.getData().get(0);
  285. } else {
  286. return null;
  287. }
  288. }
  289. // 2. 搜索到结果去除行政区划后,存在包含关系,则使用;
  290. case LEVEL_CONTAINS_2: {
  291. if (result != null && result.getData() != null && result.getData().size() > 0) {
  292. List<AddressResult.ContentBean> contentBean = result.getData();
  293. for (AddressResult.ContentBean contentBean1 : contentBean) {
  294. if (contentBean1.getAddress() != null) {
  295. String address = townReplaceAll(addressReplaceAll(contentBean1.getAddress()));
  296. String address2 = townReplaceAll(addressReplaceAll(addr));
  297. if (isNotEmptyOrBlank(address) && address.contains(address2)) {
  298. return contentBean1;
  299. }
  300. }
  301. }
  302. } else {
  303. return null;
  304. }
  305. }
  306. // 3. 搜索到结果,数字进行分词,数字匹配则使用;
  307. case LEVLE_NUMBER_3: {
  308. if (result != null && result.getData() != null && result.getData().size() > 0) {
  309. List<AddressResult.ContentBean> contentBean = result.getData();
  310. for (AddressResult.ContentBean contentBean1 : contentBean) {
  311. if (contentBean1.getAddress() != null) {
  312. Set<String> address = tokenizeString(contentBean1.getAddress()).get(1);
  313. if (address != null && address.size() > 1) {
  314. Set<String> address2 = tokenizeString(addr).get(1);
  315. int addressSize = address.size();
  316. for (String addr2 : address2) {
  317. if (address.contains(addr2)) {
  318. addressSize--;
  319. if (addressSize == 0) {
  320. return contentBean1;
  321. }
  322. }
  323. }
  324. }
  325. }
  326. }
  327. } else {
  328. return null;
  329. }
  330. }
  331. // 4. 搜索到结果,数字与文本均匹配,则使用;
  332. case LEVEL_NUMBER_TEXT_4: {
  333. if (result != null && result.getData() != null && result.getData().size() > 0) {
  334. List<AddressResult.ContentBean> contentBean = result.getData();
  335. for (AddressResult.ContentBean contentBean1 : contentBean) {
  336. if (contentBean1.getAddress() != null) {
  337. Set<String> addressString = tokenizeString(contentBean1.getAddress()).get(0);
  338. Set<String> addressNumber = tokenizeString(contentBean1.getAddress()).get(1);
  339. Set<String> address2String = tokenizeString(addr).get(0);
  340. Set<String> address2Number = tokenizeString(addr).get(1);
  341. if (addressString != null && addressString.size() > 1) {
  342. int addressStrSize = addressString.size();
  343. for (String addr2str : address2String) {
  344. if (addressString.contains(addr2str)) {
  345. addressStrSize--;
  346. if (addressStrSize == 0) {
  347. int addressNumSize = addressNumber.size();
  348. for (String addr2Num : address2Number) {
  349. if (addressNumber.contains(addr2Num)) {
  350. addressNumSize--;
  351. if (addressNumSize == 0) {
  352. return contentBean1;
  353. }
  354. }
  355. }
  356. }
  357. }
  358. }
  359. }
  360. }
  361. }
  362. } else {
  363. return null;
  364. }
  365. }
  366. // 结果与基准数据完全一致,则使用。
  367. case LEVEL_TOTAL_CONTAINS_5: {
  368. if (result != null && result.getData() != null && result.getData().size() > 0) {
  369. List<AddressResult.ContentBean> contentBean = result.getData();
  370. for (AddressResult.ContentBean contentBean1 : contentBean) {
  371. if (contentBean1.getAddress() != null) {
  372. String address = addressReplaceAll(contentBean1.getAddress());
  373. String address2 = addressReplaceAll(addr);
  374. if (isNotEmptyOrBlank(address) && address.equals(address2)) {
  375. return contentBean1;
  376. }
  377. }
  378. }
  379. } else {
  380. return null;
  381. }
  382. }
  383. default: {
  384. return null;
  385. }
  386. }
  387. }
  388. public static String addressReplaceAll(String address) {
  389. return address.replaceAll("上海市", "").replaceAll(Constant.getArea() + "区", "").replaceAll("-", "").replaceAll("_", "").replaceAll("/", "").replaceAll(" ", "").replaceAll(",", "").replaceAll("\\.", "").replaceAll(",", "").replaceAll("。", "").replaceAll("\\+", "").replaceAll("\\*", "");
  390. }
  391. public static String townReplaceAll(String address) {
  392. String[] towns = Constant.getTowns();
  393. for (String town : towns) {
  394. address = address.replaceAll(town, "");
  395. }
  396. return address.replaceAll("号", "0").replaceAll("弄", "0").replaceAll("室", "0").replaceAll("户", "0").replaceAll("单元", "0").replaceAll("幢", "0");
  397. }
  398. /**
  399. * 数字和文字分词返回List<Set<String>>
  400. *
  401. * @param input
  402. * @return
  403. */
  404. public static List<Set<String>> tokenizeString(String input) {
  405. input = townReplaceAll(input);
  406. // 初始化两个集合,一个用于存储非数字字符串,一个用于存储数字字符串
  407. Set<String> nonNumberSet = new HashSet<>();
  408. Set<String> numberSet = new HashSet<>();
  409. StringBuilder currentToken = new StringBuilder();
  410. for (int i = 0; i < input.length(); i++) {
  411. char c = input.charAt(i);
  412. if (Character.isDigit(c)) {
  413. // 如果当前字符是数字
  414. if (currentToken.length() > 0 && !Character.isDigit(currentToken.charAt(0))) {
  415. // 如果之前的 token 是非数字,将其添加到非数字集合中
  416. nonNumberSet.add(currentToken.toString());
  417. currentToken.setLength(0);
  418. }
  419. currentToken.append(c);
  420. } else {
  421. // 如果当前字符不是数字
  422. if (currentToken.length() > 0 && Character.isDigit(currentToken.charAt(0))) {
  423. // 如果之前的 token 是数字,将其添加到数字集合中
  424. numberSet.add(currentToken.toString());
  425. currentToken.setLength(0);
  426. }
  427. if (!Character.isWhitespace(c)) {
  428. currentToken.append(c);
  429. }
  430. }
  431. }
  432. // 处理最后一个 token
  433. if (currentToken.length() > 0) {
  434. if (Character.isDigit(currentToken.charAt(0))) {
  435. numberSet.add(currentToken.toString());
  436. } else {
  437. nonNumberSet.add(currentToken.toString());
  438. }
  439. }
  440. // 将两个集合添加到列表中
  441. List<Set<String>> result = new ArrayList<>();
  442. result.add(nonNumberSet);
  443. result.add(numberSet);
  444. return result;
  445. }
  446. }