请使用openjdk21
其他java版本需要单独打包
Addresstool简介
作者邮箱addresstool@163.com
Addresstool功能清单 1
一、 部署要求 1
二、 效率测试 2
三、 地址分词 2
四、 地址补全 3
五、 单条标准地址导入 3
六、 标准地址规范化 6
七、 地址关联算法 6
八、 标准批量地址导入 7
九、 标准地址导出 8
十、 大数据地址关联 8
十一、 API服务地址关联 12
十二、 地址质量评估 13
1.服务器要求 由于算法很多数据都是直接加载到内存,服务器配置取决于楼盘表地址数据。大致对应关系如下:
服务器内存 楼盘表地址数量级
16G 100万以下
32G 200万以下
2.软件环境 场景 软件要求 其他
单服务器 openjdk21
3.数据依赖
数据项 现状 提供方
全国5级行政区划 已提前集成到算法 国家统计局爬取
全国道路部分名称 已提前集成到算法 网上下载
标准地址楼盘表 需初始化 自有/客户提供
对核心功能进行速度测试(仅共参考,真实速度取决于硬件资源+楼盘表数据量+地址质量)
功能 配置 楼盘表 次数 耗时 速度
地址分词 I7cpu16G内存笔记本 / 50万 6秒 8.3万/秒
地址关联算法 I7cpu16G内存笔记本 100万 10万 32秒 5000/秒
通过nlp分词识别出地址中的省市区县、乡镇、街道、aoi、楼栋、单元和户室。并且可以对三级行政区进行写法修正操作。 示例代码:
1.import org.address.AddressTool;
2.import org.address.entity.Word;
3.
4.import java.util.List;
5.
6.public class AddressCutTest {
7. public static void main(String[] args) {
AddressSplit ss = new AddressSplit();
ss.setUserDefineDic( "新平村","community",100);
ss.setUserDefineDic( "委会","n",1);
List<String> words;
//正常地址
words = ss.split("四川省自贡市贡井区成佳镇新平村委会4组-1-4");
System.out.println(words);
//城市名修正
words = ss.split("湖北省武汉汉阳区汉阳大道10号花果山5栋1单元101户");
System.out.println(words);
// 省份修正 城市修正
words = ss.split("湖北武汉汉阳区汉阳大道10号花果山5号楼1单元101室");
System.out.println(words);
// 城市修正
words = ss.split("江苏省镇江市学府路花果小区");
System.out.println(words);
// 省份补全 城市补全
words = ss.split("佛祖岭社区汉阳大道10号花果山5号楼1单元101室");
System.out.println(words);
22. }
23.}
效果:
[四川省, 自贡市, 贡井区, 成佳镇, 新平村, 委会, 4组, -, 1-4]
[湖北省, 武汉市, 汉阳区, 汉阳大道, 10号, 花果山, 5栋, 1单元, 101户]
[湖北省, 武汉市, 汉阳区, 汉阳大道, 10号, 花果山, 5号楼, 1单元, 101室]
[江苏省, 镇江市, 学府路, 花果小区]
[佛祖岭社区, 汉阳大道, 10号, 花果山, 5号楼, 1单元, 101室]
对于有些地址三级行政区缺失的,可以进行向上补全。例如地址中写江夏区,可以补充湖北省武汉市,如果地址中写武汉市,可以补充湖北省。 示例代码:
1.public class AddressCutTest {
2. public static void main(String[] args) {
3. AddressTool ss = new AddressTool();
4. System.out.println(ss.complete(ss.cutAddress("武汉市 汉阳永丰路 永丰乡政府小区")));
5. System.out.println(ss.complete(ss.cutAddress("上海市长宁区金钟路658弄5号楼5楼")));
6. }
7.}
效果:
1.[{province:湖北省}, {city:武汉市}, {county:汉阳区}, {road:永丰路}, {aoi:永丰乡政府小区}]
2.[{province:上海市}, {county:长宁区}, {road:金钟路}, {road_no:658}, {building:5}, {floor:5}]
将已经治理好的楼盘表数据逐条写入到内存,通过简单规范化治理,就可以进行关联操作。 代码示例:
1. public static void main(String[] args) throws Exception {
2.
3.// AddressTool ss = new AddressTool("localhost",6379); // 配置数据写入redis
4. AddressTool ss = new AddressTool(); // 配置数据写入内存
5. DataTable data = new DataTable();
6.
7. HashMap<String,String> address5 = new HashMap<>();
8. address5.put("province","江苏省");;
9. address5.put("city","南京市");
10. address5.put("county","江宁区");
11. address5.put("town","荡山街道");
12. address5.put("community","中前社区");
13. address5.put("aoi","秦淮绿洲");
14. address5.put("other_name","南京乾程塑胶模具");
15. address5.put("sub_aoi","北苑");
16. address5.put("road","宏运大道");
17. address5.put("road_no","2299");
18. address5.put("near_roads","天地大道#金山大道#花果山大道:99"); // 道路别名
19. address5.put("building","9");
20. address5.put("unit","1");
21. address5.put("room","1001");
22. address5.put("id","5");
23. data.addAddressDic(address5);
24. HashMap<String,String> address6 = new HashMap<>();
25. address6.put("province","江苏省");
26. address6.put("city","南京市");
27. address6.put("county","江宁区");
28. address6.put("town","荡山街道");
29. address6.put("community","中前社区");
30. address6.put("aoi","秦淮绿洲");
31. address6.put("other_name","南京乾程塑胶模具");
32. address6.put("sub_aoi","北苑");
33. address6.put("road","宏运大道");
34. address6.put("road_no","2299");
35. address6.put("near_roads","天地大道#金山大道#花果山大道:99"); // 道路别名
36. address6.put("building","9");
37. address6.put("unit","2");
38. address6.put("room","1001");
39. address6.put("id","6");
40. data.addAddressDic(address6);
41.
42. // 标准地址库修复,比如用户只有户室级地址,此方法为用户补充楼栋级和aoi级地址,当然,如果用户有自己已经标准化好的地址库,可以省略此方法
43. data.addressFix();
44. // 将加工好的地址库写入到addresstool中
45. data.initData(ss);
46.
47.
48.
49. // 万事俱备,我们可以进行地址关联啦
50. System.out.println(ss.getStdAddress("江苏省南京市江宁区中前社区宏运大道2299号秦淮绿洲"));
51. System.out.println(ss.getStdAddress("江苏省南京市江宁区中前社区金山大道2299号秦淮绿洲"));
52. System.out.println(ss.getStdAddress("江苏省南京市江宁区中前社区金山大道9号秦淮绿洲"));
53. System.out.println(ss.getStdAddress("花果山大道99号秦淮绿洲北苑9-1-1001"));
54. System.out.println(ss.getStdAddress("花果山大道秦淮绿洲北苑9-1-1001"));
55. System.out.println(ss.getStdAddress("花果山大道秦淮绿洲9-1-1001"));
56. System.out.println(ss.getStdAddress("金山大道秦淮绿洲"));
57.
58. ss.destroy();
59. }
效果:
1.{linkLevel=aoi, list=[{near_roads=天地大道#金山大道#花果山大道:99, address=江苏省南京市江宁区荡山街道中前社区宏运大道2299号秦淮绿洲, town=荡山街道, city=南京市, county=江宁区, community=中前社区, type=aoi, aoi_id=6_unit_sub_aoi, province=江苏省, road=宏运大道, road_no=2299, alias_aois=南京乾程塑胶模具, aoi=秦淮绿洲, id=6_unit_sub_aoi}], addressLevel=aoi}
2.{linkLevel=aoi, list=[{near_roads=天地大道#金山大道#花果山大道:99, address=江苏省南京市江宁区荡山街道中前社区宏运大道2299号秦淮绿洲, town=荡山街道, city=南京市, county=江宁区, community=中前社区, type=aoi, aoi_id=6_unit_sub_aoi, province=江苏省, road=宏运大道, road_no=2299, alias_aois=南京乾程塑胶模具, aoi=秦淮绿洲, id=6_unit_sub_aoi}], addressLevel=aoi}
3.{linkLevel=aoi, list=[{near_roads=天地大道#金山大道#花果山大道:99, address=江苏省南京市江宁区荡山街道中前社区宏运大道2299号秦淮绿洲, town=荡山街道, city=南京市, county=江宁区, community=中前社区, type=aoi, aoi_id=6_unit_sub_aoi, province=江苏省, road=宏运大道, road_no=2299, alias_aois=南京乾程塑胶模具, aoi=秦淮绿洲, id=6_unit_sub_aoi}], addressLevel=aoi}
4.{linkLevel=room, list=[{room_id=5, near_roads=天地大道#金山大道#花果山大道:99, building_id=5_bld, subaoi_id=6_unit_sub, address=江苏省南京市江宁区荡山街道中前社区宏运大道2299号秦淮绿洲北苑9栋1单元1001室, town=荡山街道, city=南京市, county=江宁区, community=中前社区, type=room, aoi_id=6_unit_sub_aoi, building=9, room=1001, unit=1, province=江苏省, road=宏运大道, road_no=2299, alias_aois=南京乾程塑胶模具, sub_aoi=北苑, aoi=秦淮绿洲, id=5, unit_id=5_unit}], addressLevel=room}
5.{linkLevel=room, list=[{room_id=5, near_roads=天地大道#金山大道#花果山大道:99, building_id=5_bld, subaoi_id=6_unit_sub, address=江苏省南京市江宁区荡山街道中前社区宏运大道2299号秦淮绿洲北苑9栋1单元1001室, town=荡山街道, city=南京市, county=江宁区, community=中前社区, type=room, aoi_id=6_unit_sub_aoi, building=9, room=1001, unit=1, province=江苏省, road=宏运大道, road_no=2299, alias_aois=南京乾程塑胶模具, sub_aoi=北苑, aoi=秦淮绿洲, id=5, unit_id=5_unit}], addressLevel=room}
6.{linkLevel=room, list=[{room_id=5, near_roads=天地大道#金山大道#花果山大道:99, building_id=5_bld, subaoi_id=6_unit_sub, address=江苏省南京市江宁区荡山街道中前社区宏运大道2299号秦淮绿洲北苑9栋1单元1001室, town=荡山街道, city=南京市, county=江宁区, community=中前社区, type=room, aoi_id=6_unit_sub_aoi, building=9, room=1001, unit=1, province=江苏省, road=宏运大道, road_no=2299, alias_aois=南京乾程塑胶模具, sub_aoi=北苑, aoi=秦淮绿洲, id=5, unit_id=5_unit}], addressLevel=aoi}
7.{linkLevel=aoi, list=[{near_roads=天地大道#金山大道#花果山大道:99, address=江苏省南京市江宁区荡山街道中前社区宏运大道2299号秦淮绿洲, town=荡山街道, city=南京市, county=江宁区, community=中前社区, type=aoi, aoi_id=6_unit_sub_aoi, province=江苏省, road=宏运大道, road_no=2299, alias_aois=南京乾程塑胶模具, aoi=秦淮绿洲, id=6_unit_sub_aoi}], addressLevel=aoi}
对于已经有的标准地址,为了进一步提高地址质量,并且达到addresstool的可识别要求,需要进行简单的规范化处理。 示例代码:
1. AddressTool ss = new AddressTool(); // 配置数据写入内存
2. DataTable data = new DataTable();
3.
4. // 此处省略原始标准地址导入操作(单条导入/批量导入)
5. // 。。。导入代码
6.
7. // 补全行3级政区 对于行政完整的地址无需此操作
8. data.completion();
9. // 标准地址库修复,比如用户只有户室级地址,此方法为用户补充楼栋级和aoi级地址,当然,如果用户有自己已经标准化好的地址库,可以省略此方法
10. data.addressFix();
11. // 将加工好的地址库写入到addresstool中
12. data.initData(ss);
通过业务地址,关联到楼盘表中标准地址,有通用算法和带参数算法2中模式 通用算法,代码示例:
1. AddressTool ss = new AddressTool(); // 配置数据写入内存
2. DataTable data = new DataTable();
3.
4. // 此处省略标准地址加载过程
5. //。。。。
6. // getStdAddress为地址关联方法
7. StandardAddress res = ss.getStdAddress("江苏南京市汤山街道中前社区宏运大道2299号秦淮绿洲北苑9栋1单元1001室");
8. System.out.println(res);
带参算法,ignore参数代表要忽略的地理要素,如加上town,community的话,城镇和社区将不会参与地址判断。 代码示例:
1. Map<String,String> mp = new HashMap<>();
2. mp.put("ignore","town,community");
3. System.out.println(ss.getStdAddress("武汉市锦绣龙城"));
4. System.out.println(ss.getStdAddress("湖北省武汉市江夏高新四路1号万科魅力之城"));
5. System.out.println(ss.getStdAddress("湖北省武汉市江夏高新四路万科魅力之城"));
6. System.out.println(ss.getStdAddress("武汉市万科魅力之城"));
通过csv导入标准地址,代码示例:
1. public static void main(String[] args) throws Exception {
2. AddressTool ss = new AddressTool();
3. DataTable as = new DataTable();
4. as.loadFromCsv("D:\\","武汉.csv");
5. as.addressFix();
6. System.out.println("用户地址 读取完毕!!! ");
7. as.initData(ss);
8. System.out.println(ss.getStdAddress("武汉市锦绣龙城"));
9. System.out.println(ss.getStdAddress("湖北省武汉市江夏高新四路1号万科魅力之城"));
10. System.out.println(ss.getStdAddress("湖北省武汉市江夏高新四路万科魅力之城"));
11. System.out.println(ss.getStdAddress("武汉市万科魅力之城"));
12. // close方法销毁DataTable,释放部分内存
13. as.close();
14. }
通过json文件导入标准地址,代码示例:
1. public static void main(String[] args) throws Exception {
2. AddressTool ss = new AddressTool();
3. DataTable as = new DataTable();
4.
5. as.loadFromJson("D:\\","wuhan.json");
6. System.out.println("用户地址 读取完毕!!! ");
7. as.initData(ss);
8.
9. System.out.println(ss.getStdAddress("武汉市锦绣龙城"));
10. System.out.println(ss.getStdAddress("湖北省武汉市江夏高新四路1号万科魅力之城"));
11. System.out.println(ss.getStdAddress("湖北省武汉市江夏高新四路万科魅力之城"));
12. System.out.println(ss.getStdAddress("武汉市万科魅力之城"));
13. as.close();
14. }
对于已经规范化后的地址,可以保存为json文件,下次使用时就不需要再次做规范化操作了。
1. public static void main(String[] args) throws Exception {
2. AddressTool ss = new AddressTool();
3. DataTable as = new DataTable();
4. as.loadFromCsv("D:\\","武汉.csv");
5. as.addressFix();
6. // 将标准地址保存为json文件
7. as.saveAsJson("D:\\","wuhan.json");
8. as.close();
9. }
将关联算法相关的(标准地址加载-数据初始化-地址关联)流程全部封装到大数据环境,赋予大数据地址关联的能力。 代码示例:
1.public class AddressLink extends GenericUDF {
2. private PrimitiveObjectInspector addressIO;
3. private static AddressTool addressTool;
4.
5. private String bld(String building){
6. if(building!=null&&!building.isEmpty() ){
7. if(building.endsWith("栋")||building.endsWith("幢")){
8. return building.substring(0,building.length()-1);
9. }else if(building.endsWith("号楼")){
10. return building.substring(0,building.length()-2);
11. }
12. }
13.
14. return building;
15. }
16.
17. private String unit(String unit){
18. if(unit!=null&&!unit.isEmpty() ){
19. if(unit.endsWith("单元")){
20. return unit.substring(0,unit.length()-2);
21. }
22. }
23.
24. return unit;
25. }
26.
27. private String room(String room){
28. if(room!=null&&!room.isEmpty() ){
29. if(room.endsWith("室")||room.endsWith("户")){
30. return room.substring(0,room.length()-1);
31. }
32. }
33.
34. return room;
35. }
36.
37. @Override
38. public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
39. if (arguments[0] instanceof ObjectInspector) {
40. addressIO = (PrimitiveObjectInspector) arguments[0];
41. }else{
42. throw new UDFArgumentLengthException("The function GetMapValue accepts 1 argument. simple: GetSqName(sq_name)");
43. }
44. addressTool = new AddressTool();
45. DataTable data = new DataTable();
46. try{
47. // 数据源无限制,可以使用任何数据库作为标准地址存储工具,本项目以postgres为例
48. //注册Driver
49. String driver = "org.postgresql.Driver";//prop.getProperty("driver");
50. String url = "jdbc:postgresql://*****:5432/postgres";//prop.getProperty("url");
51. String username = "******";//prop.getProperty("user");
52. String password = "******";//prop.getProperty("password");
53. Class.forName(driver);
54. Connection connection = DriverManager.getConnection(url, username, password);
55. Statement statement = connection.createStatement();
56.
57. // 数据初始化
58. ResultSet res = statement.executeQuery("select id,province,city,county,town,community,road,road_no,aoi,sub_aoi,building,unit,room,address from st_address order by aoi,road,road_no");
59. int cnt = 0;
60. while (res.next()) {
61. HashMap<String,String> mp = new HashMap<>();
62. if(res.getString("id")!=null&& !Objects.equals(res.getString("id"), "")){mp.put("id",res.getString("id"));}
63. if(res.getString("province")!=null&& !Objects.equals(res.getString("province"), "")){mp.put("province",res.getString("province"));}
64. if(res.getString("city")!=null&& !Objects.equals(res.getString("city"), "")){mp.put("city",res.getString("city"));}
65. if(res.getString("county")!=null&& !Objects.equals(res.getString("county"), "")){mp.put("county",res.getString("county"));}
66. if(res.getString("town")!=null&& !Objects.equals(res.getString("town"), "")){mp.put("town",res.getString("town"));}
67. if(res.getString("community")!=null&& !Objects.equals(res.getString("community"), "")){mp.put("community",res.getString("community"));}
68. if(res.getString("road")!=null&& !Objects.equals(res.getString("road"), "")){mp.put("road",res.getString("road"));}
69. if(res.getString("road_no")!=null&& !Objects.equals(res.getString("road_no"), "")){mp.put("road_no",res.getString("road_no"));}
70. if(res.getString("aoi")!=null&& !Objects.equals(res.getString("aoi"), "")){mp.put("aoi",res.getString("aoi"));}
71. if(res.getString("sub_aoi")!=null&& !Objects.equals(res.getString("sub_aoi"), "")){mp.put("sub_aoi",res.getString("sub_aoi"));}
72. if(res.getString("building")!=null&& !Objects.equals(res.getString("building"), "")){mp.put("building",bld(res.getString("building")));}
73. if(res.getString("unit")!=null&& !Objects.equals(res.getString("unit"), "")){mp.put("unit",unit(res.getString("unit")));}
74. if(res.getString("room")!=null&& !Objects.equals(res.getString("room"), "")){mp.put("room",room(res.getString("room")));}
75. if(res.getString("com/address")!=null&& !Objects.equals(res.getString("com/address"), "")){mp.put("com/address",res.getString("com/address"));}
76. data.addAddressDic(mp);
77. cnt = cnt + 1;
78. }
79.
80. //标准数据地址数据加载到addresstool
81. data.initData(addressTool);
82. data = null;
83. statement.close();
84. connection.close();
85.
86. } catch (Exception throwables) {
87. throwables.printStackTrace();
88. }
89.
90. return ObjectInspectorFactory.getStandardMapObjectInspector(
91. PrimitiveObjectInspectorFactory.javaStringObjectInspector,
92. PrimitiveObjectInspectorFactory.javaStringObjectInspector);
93. }
94.
95. @Override
96. public Object evaluate(DeferredObject[] arguments) throws HiveException {
97. if(arguments[0].get()==null){
98. return null;
99. }
100. String address = PrimitiveObjectInspectorUtils.getString(arguments[0].get(), this.addressIO);
101. // 中文地址中的异常字符预处理
102. while(address.contains(" ")){address = address.replace(" ","");}
103. while(address.contains("--")){address = address.replace("--","-");}
104. while(address.contains("——")){address = address.replace("——","-");}
105. while(address.contains("- ")){address = address.replace("- ","-");}
106. while(address.contains(" -")){address = address.replace(" -","-");}
107. while(address.contains("— ")){address = address.replace("— ","-");}
108. while(address.contains(" —")){address = address.replace(" —","-");}
109.
110. // 地址关联
111. StandardAddress stdAddress = addressTool.getStdAddress(address);
112. Map<String,String> result = stdAddress.getStdAddress();
113. // 地址级别判断
114. if(stdAddress.addressLevel!=null&& !stdAddress.addressLevel.equals("")){
115. result.put("addressLevel",stdAddress.addressLevel);
116. }else{
117. result.put("addressLevel","未知");
118. }
119.
120. // 地址关联级别判断
121. if(stdAddress.linkLevel!=null&& !stdAddress.linkLevel.equals("")){
122. result.put("linkLevel",stdAddress.linkLevel);
123. }else{
124. result.put("linkLevel","未关联");
125. }
126. return result;
127. }
128.
129. @Override
130. public String getDisplayString(String[] children) {
131. return "Address(" + children[0] + ")";
132. }
133.
134.}
将地址关联以api服务的形式发布 代码示例:
1.public class AddressServer {
2. static AddressTool ss = new AddressTool();
3.// static AddressTool ss = new AddressTool("localhost",6379);
4. static DataTable as = new DataTable();
5. static Gson gson = new Gson();
6.
7. public static void main(String[] args) throws Exception {
8. as.loadFromJson("D:\\","address2.json");
9.
10. System.out.println("用户地址 读取完毕!!! ");
11. as.initData(ss);
12.
13. HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
14. server.createContext("/standard", new MyHandler());
15. server.createContext("/test", new MyHandler2());
16. server.setExecutor(null);
17. System.out.println("Starting server on port: 8000");
18. server.start();
19. }
20.
21. static class MyHandler implements HttpHandler {
22. public void handle(HttpExchange t) throws IOException {
23. String response = "{\"response\":\"Hello World\"}";
24.
25. String parmsStr = t.getRequestURI().getQuery();
26. if(!parmsStr.isEmpty()){
27. String[] parms = parmsStr.split("&");
28. if(parms.length>0){
29. HashMap<String,String> keys = new HashMap<>();
30. for (String urlKey:parms){
31. if(urlKey.contains("=")){
32. String[] kv = urlKey.split("=");
33. keys.put(kv[0].toLowerCase(),kv[1]);
34. }
35. }
36. if(keys.containsKey("address")){
37. StandardAddress resp = ss.getStdAddress(keys.get("address"));
38.
39. if(resp.getAddress()!=null&& !resp.getAddress().isEmpty()){
40. String addressJson;
41. ArrayList<String> addrs = new ArrayList<>();
42. int i = 1;
43. for(Map.Entry<String,Map<String,String>> mp:resp.getAddress().entrySet()){
44. addressJson = gson.toJson(mp.getValue());
45. addrs.add(addressJson);
46. }
47. String mapJson = gson.toJson(addrs);
48. response = mapJson;
49. }
50.
51. }
52. }
53. }
54.
55.// System.out.println(t.getRequestURI().getQuery());
56.
57. t.getResponseHeaders().set("Content-Type", "application/json");
58.// t.getResponseHeaders().set("Content-Type", "text/html;charset=utf-8");
59. t.sendResponseHeaders(200, 0);
60. OutputStream os = t.getResponseBody();
61. byte[] b = response.getBytes();
62. for (int i = 0; i < b.length; i++) {
63. os.write(b[i]);
64. }
65. os.close();
66. }
67. }
68.}
暂时未开发。
运行license函数,查看license文件有效期
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。