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. }

2021年8月17日 星期二

tips on installation of xrdp for remote desktop connection to linux machines

大家都知道,Linux桌面跑的是X Windows視窗系統。微軟Windows如果想要連上Linux桌面,一種作法是本身主機消耗硬碟,裝上X Windows伺服軟體VcXsrv,甚至加上微軟應用商店的免費Linux作業系統,例如Ubuntu,此作法步驟可詳Lainme's Blog。另一種作法是遠方Linux主機安裝tightvnc server及xrdp套件,微軟Windows只要沿用原來的遠端桌面連線(mstsc.exe),即可使用rdp協定,連上Linux桌面。後者作法,剪貼簿可用,聲音則待個別測試。以下將依據Linux作業系統為 Ubuntu 或 CentOS,分別介紹其安裝訣竅。

A. Ubuntu 安裝 xrdp 作法

-- 先去除舊套件干擾 sudo apt-get remove xrdp tightvncserver # 只刪執行檔 sudo apt-get purge xrdp tightvncserver # 刪除執行檔及相關組態檔 sudo dpkg --purge xrdp tightvncserver # 刪除執行檔及相關組態檔 sudo dpkg -S /etc/xrdp/startwm.sh # 查檔案所屬套件 -- 安裝tightvncserver及xrdp套件 sudo apt-get install tightvncserver # 一定要先安裝此視窗套件 sudo apt-get install xrdp # 然後安裝此傳輸套件,才會順利偵測運用視窗套件 sudo vi /etc/xrdp/startwm.sh # 加入管理視窗指令 xfce4-session # 若未安裝,可使用apt-get install xfce4-session -- 啟動服務 sudo service xrdp status # 詢問xrdp服務狀態 sudo service xrdp start # 啟動xrdp服務 註: 若連線過程,顯示詢息,缺rsakeys.ini金鑰,可如下產生金鑰 cd /etc/xrdp sudo xrdp-keygen xrdp # generate /etc/xrdp/rsakeys.ini -- 測試連線 sudo netstat -tulpn # 觀看是否3389及3350連接埠有監聽服務 -- rdp連線過程,適用於 Ubuntu 16.04.6 LTS (1) xrdp (3389) # 選擇 sesman-Xvnc 模式 (2) xrdp-sesman (3350,root) (3) xrdp-chansrv # 執行 /etc/xrdp/startwm.sh 及 xfce4-session (4) Xvnc (59xx) -- rdp連線過程,適用於 Ubuntu 18.04.5 LTS (1) xrdp (3389) # 選擇 Xorg 模式 (2) xrdp-sesman (3350,root) (3) xrdp-chansrv # 執行 /etc/xrdp/startwm.sh 及 xfce4-session (4) Xorg 註: xrdp安裝apt-get內建版本即可,不須安裝 https://github.com/neutrinolabs/xrdp.git

B. CentOS 安裝 xrdp 作法

-- 安裝tightvnc-server及xrdp套件 sudo yum erase xrdp tightvnc tightvnc-server sudo yum install tightvnc-server # 要先裝tightvnc-server sudo yum install xrdp # 再裝xrdp -- sudo repoquery -l xrdp # 查套件安裝哪些檔案 sudo rpm -ql /usr/bin/Xvnc # 查檔案所屬套件 -- cat /etc/xrdp/startwm.sh # 確認系統裝有如下任一種視窗管理程式 SESSIONS="gnome-session blackbox fluxbox startxfce4 startkde xterm" -- 啟動服務 sudo service xrdp status # 詢問xrdp服務狀態 sudo service xrdp start # 啟動xrdp服務 -- 測試連線 sudo netstat -antup | grep xrdp # 觀看是否3389及3350連接埠有監聽服務 註: 若連線過程,顯示訊息,缺權限,可如下給予selinux權限 sudo chcon --type=bin_t /usr/sbin/xrdp sudo chcon --type=bin_t /usr/sbin/xrdp-sesman -- rdp連線過程,適用於 CentOS 6.10 (1) xrdp (3389) # 選擇 sesman-Xvnc 模式 (2) xrdp-sesman (3350,root) (3) xrdp-chansrv # 執行 /etc/xrdp/startwm.sh 及 gnome-session (4) Xvnc (59xx) 註: 若為舊 CentOS-6,因自2020/11/30已不再提供維護更新,故須更新repo來源,如下: sudo rm CentOS-SCLo-scl* sudo curl https://www.getpagespeed.com/files/centos6-eol.repo --output /etc/yum.repos.d/CentOS-Base.repo sudo curl https://www.getpagespeed.com/files/centos6-epel-eol.repo --output /etc/yum.repos.d/epel.repocurl \ https://www.getpagespeed.com/files/centos6-epel-eol.repo --output /etc/yum.repos.d/epel.repo sudo yum update # 更新到6.10版 參考: 阿吉的部落格: 使用 xrdp 遠端登入 Linux (Gentoo)