2021年8月27日 星期五

how to query OSM data in Java?

開放街圖OSM的公共圖資可以透過overpass-turbo介面查詢獲得。
若想利用Java程式碼查詢,可參考以下查詢範例。
其中,查詢指令可參考overpass-turbo範例指令,測試成功再代入程式。

  1.  
  2. /*
  3. QueryOSM.java
  4. 展示如何用retrofit套件,同步及非同步(適用於Android),存取如下OSM圖資服務
  5. http://overpass-api.de/api/interpreter?data=xxx
  6.  
  7. // 建立服務連線客戶端及請求內容
  8. OverpassService requestClient = OverpassServiceProvider.get();
  9. String request = composeRequest();
  10. // 非同步查詢圖資,適用於手機平板Android平台
  11. asyncRequest(requestClient, request);
  12. // 同步查詢圖資,適用於桌機Application應用
  13. OverpassQueryResult result = syncRequest(requestClient, request);
  14. postProcess(result);
  15. 執行步驟:
  16. javac QueryOSM.java
  17. java QueryOSM
  18. run:
  19. [out:json][timeout:25];
  20. (
  21. node[tourism](25.1735, 121.446, 25.1775, 121.455);
  22. relation[!highway][type!='route'][!boundary](25.1735, 121.446, 25.1775, 121.455);
  23. );
  24. out center;
  25. end of asyncRequest()
  26. 21 elements...
  27. 1. id:4326372453, type:node, lat:25.1761808, lon:121.4490491, name:五虎碑, wheelchair:limited
  28. 2. id:4384988658, type:node, lat:25.1750071, lon:121.4522078, name:文錙藝術中心, wheelchair:yes
  29. 3. id:4492221396, type:node, lat:25.1736267, lon:121.4473809, name:三化牆
  30. 4. id:4492221397, type:node, lat:25.1744069, lon:121.4474037, name:地球村雕像, wheelchair:limited
  31. 5. id:4492221399, type:node, lat:25.1735175, lon:121.4484658, name:淡江大學花牆
  32. 6. id:4492221425, type:node, lat:25.1749949, lon:121.450678, name:閱讀的少女, wheelchair:yes
  33. 7. id:4502075211, type:node, lat:25.1751384, lon:121.4523232, name:旅者
  34. 8. id:4502075212, type:node, lat:25.1739938, lon:121.4505047, name:李雙澤紀念碑, wheelchair:yes
  35. 9. id:4502075213, type:node, lat:25.1761919, lon:121.4499374, name:福園金鷹銅雕, wheelchair:no
  36. 10. id:4502075222, type:node, lat:25.1738907, lon:121.4475716, name:驚聲銅像, wheelchair:limited
  37. 11. id:4507662408, type:node, lat:25.1741082, lon:121.4474671, name:溫馨, wheelchair:yes
  38. 12. id:5012978611, type:node, lat:25.1741784, lon:121.4507282, name:黃河母親, wheelchair:no
  39. 13. id:5072580167, type:node, lat:25.1769149, lon:121.4495309
  40. 14. id:5130535622, type:node, lat:25.1757202, lon:121.4496844, name:會文館, wheelchair:yes
  41. 15. id:5132288341, type:node, lat:25.1741586, lon:121.4508061
  42. 16. id:5132288342, type:node, lat:25.174208, lon:121.4475417
  43. 17. id:6050843218, type:node, lat:25.1770813, lon:121.449821
  44. 18. id:8991981256, type:node, lat:25.1750049, lon:121.4480033, name:淡江願景牆, wheelchair:yes
  45. 19. id:3974590, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:操場
  46. 20. id:3983402, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:松濤廣場
  47. 21. id:7530081, type:relation, lat:0.0, lon:0.0, type:multipolygon
  48.  
  49. Response{protocol=http/1.1, code=200, message=OK, url=http://overpass-api.de/api/interpreter?data=%5Bout%3Ajson%5D%5Btimeout%3A25%5D%3B%0A%28%0Anode%5Btourism%5D%2825.1735%2C%20121.446%2C%2025.1775%2C%20121.455%29%3B%0Arelation%5B%21highway%5D%5Btype%21%3D%27route%27%5D%5B%21boundary%5D%2825.1735%2C%20121.446%2C%2025.1775%2C%20121.455%29%3B%0A%29%3B%0Aout%20center%3B}
  50. end of syncRequest()
  51. 21 elements...
  52. 1. id:4326372453, type:node, lat:25.1761808, lon:121.4490491, name:五虎碑, wheelchair:limited
  53. 2. id:4384988658, type:node, lat:25.1750071, lon:121.4522078, name:文錙藝術中心, wheelchair:yes
  54. 3. id:4492221396, type:node, lat:25.1736267, lon:121.4473809, name:三化牆
  55. 4. id:4492221397, type:node, lat:25.1744069, lon:121.4474037, name:地球村雕像, wheelchair:limited
  56. 5. id:4492221399, type:node, lat:25.1735175, lon:121.4484658, name:淡江大學花牆
  57. 6. id:4492221425, type:node, lat:25.1749949, lon:121.450678, name:閱讀的少女, wheelchair:yes
  58. 7. id:4502075211, type:node, lat:25.1751384, lon:121.4523232, name:旅者
  59. 8. id:4502075212, type:node, lat:25.1739938, lon:121.4505047, name:李雙澤紀念碑, wheelchair:yes
  60. 9. id:4502075213, type:node, lat:25.1761919, lon:121.4499374, name:福園金鷹銅雕, wheelchair:no
  61. 10. id:4502075222, type:node, lat:25.1738907, lon:121.4475716, name:驚聲銅像, wheelchair:limited
  62. 11. id:4507662408, type:node, lat:25.1741082, lon:121.4474671, name:溫馨, wheelchair:yes
  63. 12. id:5012978611, type:node, lat:25.1741784, lon:121.4507282, name:黃河母親, wheelchair:no
  64. 13. id:5072580167, type:node, lat:25.1769149, lon:121.4495309
  65. 14. id:5130535622, type:node, lat:25.1757202, lon:121.4496844, name:會文館, wheelchair:yes
  66. 15. id:5132288341, type:node, lat:25.1741586, lon:121.4508061
  67. 16. id:5132288342, type:node, lat:25.174208, lon:121.4475417
  68. 17. id:6050843218, type:node, lat:25.1770813, lon:121.449821
  69. 18. id:8991981256, type:node, lat:25.1750049, lon:121.4480033, name:淡江願景牆, wheelchair:yes
  70. 19. id:3974590, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:操場
  71. 20. id:3983402, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:松濤廣場
  72. 21. id:7530081, type:relation, lat:0.0, lon:0.0, type:multipolygon
  73.  
  74. end of main()
  75. BUILD SUCCESSFUL (total time: 1 minute 2 seconds)
  76.  
  77. 參考:
  78. a. Retrofit 2 – Synchronous and asynchronous call example
  79. https://howtodoinjava.com/retrofit2/retrofit-sync-async-calls/
  80. b. https://github.com/zsoltk/overpasser
  81. hu.supercluster.overpasser.adapter
  82. OverpassQueryResult
  83. OverpassQueryResult.Element
  84. OverpassQueryResult.Element.Tags
  85. OverpassService
  86. c. 引用函數庫
  87. compile/run:
  88. okhttp-3.14.9.jar
  89. okio-1.17.2.jar
  90. converter-gson-2.10.2.jar
  91. gson-2.8.5.jar
  92. retrofit-2.10.2.jar
  93. compile/run tests:
  94. byte-buddy-1.11.3.jar
  95. byte-buddy-agent-1.11.3.jar
  96. mockito-core-3.11.2.jar
  97. objenesis-3.2.jar
  98. */
  99. import com.google.gson.Gson;
  100. import com.google.gson.GsonBuilder;
  101.  
  102. import retrofit2.Call;
  103. import retrofit2.Callback;
  104. import retrofit2.Response;
  105. import retrofit2.Retrofit;
  106. import retrofit2.converter.gson.GsonConverterFactory;
  107.  
  108. import hu.supercluster.overpasser.library.output.OutputFormat;
  109. import hu.supercluster.overpasser.library.output.OutputModificator;
  110. import hu.supercluster.overpasser.library.output.OutputOrder;
  111. import hu.supercluster.overpasser.library.output.OutputVerbosity;
  112. import hu.supercluster.overpasser.library.query.OverpassQuery;
  113. import hu.supercluster.overpasser.adapter.OverpassQueryResult;
  114. import hu.supercluster.overpasser.adapter.OverpassQueryResult.Element;
  115. import hu.supercluster.overpasser.adapter.OverpassQueryResult.Element.Tags;
  116. import hu.supercluster.overpasser.adapter.OverpassService;
  117. import hu.supercluster.overpasser.adapter.OverpassServiceProvider;
  118.  
  119. import java.lang.reflect.Field;
  120. import java.util.List;
  121. /**
  122. *
  123. * @author seke
  124. */
  125.  
  126. public class QueryOSM {
  127. // 可利用下址測試查詢指令
  128. // http://overpass-turbo.eu/
  129. public static String composeRequest()
  130. {
  131. /* 查詢範例1:
  132. 在(47.48047027491862,19.039797484874725,47.51331674014172,19.07404761761427)範圍內
  133. 列出所有非私人停車場
  134. A. 查詢指令
  135. ["out":"json"]["timeout":"30"];
  136. (
  137. node
  138. ["amenity"="parking"]
  139. ["access"!="private"]
  140. (47.48047027491862,19.039797484874725,47.51331674014172,19.07404761761427);
  141. <;
  142. );
  143. out body center qt 100;
  144.  
  145. B. 組合查詢指令方法
  146. String query = new OverpassQuery()
  147. .format(OutputFormat.JSON)
  148. .timeout(30)
  149. .filterQuery()
  150. .node()
  151. .amenity("parking")
  152. .tagNot("access", "private")
  153. .boundingBox(
  154. 47.48047027491862, 19.039797484874725,
  155. 47.51331674014172, 19.07404761761427
  156. )
  157. .end()
  158. .output(OutputVerbosity.BODY, OutputModificator.CENTER, OutputOrder.QT, 100)
  159. .build()
  160. ;
  161. */
  162.  
  163. /* 查詢範例2:
  164. 在(25.1735, 121.446, 25.1775, 121.455)範圍內,撈取
  165. 非座椅設施,商店,觀光點,辦公室,繄急設施之節點
  166. 屬於大學設施之線條,關係
  167. 不屬於公路、路線、森林、邊界之線條,關係
  168. 列出其中心位置及相關屬性(標籤)
  169.  
  170. A. 查詢指令
  171. [out:json][timeout:25];
  172. // gather results
  173. (
  174. node[amenity][amenity!=bench](25.1735, 121.446, 25.1775, 121.455);
  175. node[shop](25.1735, 121.446, 25.1775, 121.455);
  176. node[tourism](25.1735, 121.446, 25.1775, 121.455);
  177. node[office](25.1735, 121.446, 25.1775, 121.455);
  178. node[emergency](25.1735, 121.446, 25.1775, 121.455);
  179. //way[amenity="university"](25.1735, 121.446, 25.1775, 121.455);
  180. relation[amenity="university"](25.1735, 121.446, 25.1775, 121.455);
  181. way[!highway][type!="route"][landuse!="forest"][!boundary](25.1735, 121.446, 25.1775, 121.455);
  182. relation[!highway][type!="route"][!boundary](25.1735, 121.446, 25.1775, 121.455);
  183. );
  184. // print results
  185. out center;
  186. ---
  187. 註: 淡江大學之範圍為 25.1735, 121.446, 25.1775, 121.455
  188. */
  189. String query = String.join("\n",
  190. "[out:json][timeout:25];",
  191. "(",
  192. // "node[amenity][amenity!=bench](25.1735, 121.446, 25.1775, 121.455);",
  193. // "node[shop](25.1735, 121.446, 25.1775, 121.455);",
  194. "node[tourism](25.1735, 121.446, 25.1775, 121.455);",
  195. // "node[office](25.1735, 121.446, 25.1775, 121.455);",
  196. // "node[emergency](25.1735, 121.446, 25.1775, 121.455);",
  197. // "relation[amenity='university'](25.1735, 121.446, 25.1775, 121.455);",
  198. // "way[!highway][type!='route'][landuse!='forest'][!boundary](25.1735, 121.446, 25.1775, 121.455);",
  199. "relation[!highway][type!='route'][!boundary](25.1735, 121.446, 25.1775, 121.455);",
  200. ");",
  201. "out center;");
  202.  
  203. System.out.println(query);
  204. return query;
  205. }
  206. public static void asyncRequest(OverpassService apiClient, String request)
  207. {
  208. //Call call = service.interpreter(query);
  209. apiClient.interpreter(request).enqueue(new Callback()
  210. {
  211. @Override
  212. public void onResponse(Call call, Response response)
  213. {
  214. if(response.isSuccessful()==false)
  215. {
  216. System.out.println("asyncRequest: response.isSuccessful(): false");
  217. System.out.println(response.errorBody());
  218. return;
  219. }
  220. OverpassQueryResult result = response.body();
  221. postProcess(result);
  222. }
  223. public void onFailure(Call call, Throwable t) {
  224. // DO failure handling
  225. System.out.println("onFailure");
  226. System.out.println(t.getLocalizedMessage());
  227. }
  228. });
  229. System.out.println("end of asyncRequest()");
  230. }
  231. public static OverpassQueryResult syncRequest(OverpassService apiClient, String request)
  232. {
  233. OverpassQueryResult result = null;
  234. Call callSync = apiClient.interpreter(request);
  235. try
  236. {
  237. Response response = callSync.execute();
  238. //OverpassQueryResult apiResponse = response.body();
  239. //API response
  240. System.out.println(response);
  241. result = response.body();
  242. }
  243. catch (Exception ex)
  244. {
  245. ex.printStackTrace();
  246. }
  247. System.out.println("end of syncRequest()");
  248. return result;
  249. }
  250. public static void postProcess(OverpassQueryResult result)
  251. {
  252. if(result==null) return;
  253. // DO success handling
  254. StringBuilder sb = new StringBuilder();
  255. System.out.println(result.elements.size() + " elements...");
  256. int count = 1;
  257. for (Element p : result.elements)
  258. {
  259. sb.append(count); count++;
  260. sb.append(String.format(". id:%s, type:%s, lat:%s, lon:%s", p.id, p.type, p.lat, p.lon));
  261. Tags tags = p.tags;
  262. for (Field f : tags.getClass().getFields()) {
  263. f.setAccessible(true);
  264. try
  265. {
  266. if (f.get(tags) != null) {
  267. sb.append(String.format(", %s:%s", f.getName(), f.get(tags)));
  268. }
  269. }
  270. catch (IllegalAccessException e)
  271. { // shouldn't happen because I used setAccessible
  272. }
  273. }
  274. //if(p.tags. != null)
  275. // sb.append(String.format("tags:%s", p.tags.name));
  276. sb.append("\n");
  277. }
  278. System.out.println(sb.toString());
  279. }
  280. public static void main(String args[])
  281. {
  282. // 建立服務連線客戶端及請求內容
  283. OverpassService requestClient = OverpassServiceProvider.get();
  284. String request = composeRequest();
  285. // 非同步請求
  286. asyncRequest(requestClient, request);
  287. // 同步請求
  288. OverpassQueryResult result = syncRequest(requestClient, request);
  289. postProcess(result);
  290. System.out.println("end of main()");
  291. }
  292. }

1 則留言:

Richard Alwin 提到...

This post gave me a lot of information on this topic. Keep it up and keep sharing this type of information with us. Try to explore our services towards digital transformation.

Data Analytics Solutions

Data Engineering Solutions

Artificial Intelligence (AI) Solutions