qwc2 2025.3.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (528) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +68 -0
  3. package/actions/display.js +7 -0
  4. package/actions/editing.js +7 -0
  5. package/actions/layerinfo.js +7 -0
  6. package/actions/layers.js +8 -0
  7. package/actions/localConfig.js +7 -0
  8. package/actions/locale.js +10 -0
  9. package/actions/locate.js +8 -0
  10. package/actions/logging.js +7 -0
  11. package/actions/map.js +15 -0
  12. package/actions/measurement.js +8 -0
  13. package/actions/processNotifications.js +7 -0
  14. package/actions/redlining.js +7 -0
  15. package/actions/redliningPick.js +7 -0
  16. package/actions/serviceinfo.js +7 -0
  17. package/actions/task.js +9 -0
  18. package/actions/theme.js +24 -0
  19. package/actions/windows.js +10 -0
  20. package/components/AppMenu.js +8 -0
  21. package/components/AttributeForm.js +34 -0
  22. package/components/AutoEditForm.js +8 -0
  23. package/components/CoordinateDisplayer.js +7 -0
  24. package/components/EditComboField.js +10 -0
  25. package/components/EditUploadField.js +8 -0
  26. package/components/FullscreenSwitcher.js +8 -0
  27. package/components/Icon.js +7 -0
  28. package/components/IdentifyViewer.js +16 -0
  29. package/components/ImportLayer.js +25 -0
  30. package/components/LayerInfoWindow.js +7 -0
  31. package/components/LinkFeatureForm.js +10 -0
  32. package/components/MapSelection.js +13 -0
  33. package/components/NumericInputWindow.js +7 -0
  34. package/components/PickFeature.js +7 -0
  35. package/components/PluginsContainer.js +8 -0
  36. package/components/PrintSelection.js +54 -0
  37. package/components/QtDesignerForm.js +30 -0
  38. package/components/ResizeableWindow.js +12 -0
  39. package/components/SearchBox.js +26 -0
  40. package/components/ServiceInfoWindow.js +7 -0
  41. package/components/SideBar.js +9 -0
  42. package/components/StandardApp.js +35 -0
  43. package/components/Swipeable.js +1 -0
  44. package/components/TaskBar.js +7 -0
  45. package/components/ThemeLayersListWindow.js +9 -0
  46. package/components/ThemeList.js +12 -0
  47. package/components/Toolbar.js +7 -0
  48. package/components/WindowManager.js +7 -0
  49. package/components/map/OlLayer.js +12 -0
  50. package/components/map/OlLocate.css +56 -0
  51. package/components/map/OlLocate.js +17 -0
  52. package/components/map/OlMap.js +11 -0
  53. package/components/map/layers/BingLayer.js +8 -0
  54. package/components/map/layers/GoogleLayer.js +27 -0
  55. package/components/map/layers/GraticuleLayer.js +8 -0
  56. package/components/map/layers/ImageLayer.js +8 -0
  57. package/components/map/layers/MVTLayer.js +7 -0
  58. package/components/map/layers/OSMLayer.js +8 -0
  59. package/components/map/layers/OverlayLayer.js +10 -0
  60. package/components/map/layers/VectorLayer.js +14 -0
  61. package/components/map/layers/WFSLayer.js +11 -0
  62. package/components/map/layers/WMSLayer.js +11 -0
  63. package/components/map/layers/WMTSLayer.js +9 -0
  64. package/components/map/layers/XYZLayer.js +7 -0
  65. package/components/map/layers/index.js +8 -0
  66. package/components/map3d/BottomBar3D.js +7 -0
  67. package/components/map3d/Compare3D.js +12 -0
  68. package/components/map3d/Draw3D.js +10 -0
  69. package/components/map3d/ExportObjects3D.js +17 -0
  70. package/components/map3d/HeightProfile3D.js +8 -0
  71. package/components/map3d/HideObjects3D.js +12 -0
  72. package/components/map3d/Identify3D.js +13 -0
  73. package/components/map3d/LayerTree3D.js +8 -0
  74. package/components/map3d/Map3D.js +29 -0
  75. package/components/map3d/Map3DLight.js +10 -0
  76. package/components/map3d/MapControls3D.js +20 -0
  77. package/components/map3d/MapExport3D.js +11 -0
  78. package/components/map3d/Measure3D.js +23 -0
  79. package/components/map3d/OverviewMap3D.js +9 -0
  80. package/components/map3d/SearchField3D.js +16 -0
  81. package/components/map3d/TopBar3D.js +7 -0
  82. package/components/map3d/View3DSwitcher.js +7 -0
  83. package/components/map3d/drawtool/CreateTool3D.js +9 -0
  84. package/components/map3d/drawtool/EditTool3D.js +9 -0
  85. package/components/map3d/drawtool/NumericInput3D.js +7 -0
  86. package/components/map3d/img/viewcone.svg +124 -0
  87. package/components/map3d/layers/VectorLayer3D.js +7 -0
  88. package/components/map3d/layers/WFSLayer3D.js +8 -0
  89. package/components/map3d/layers/WMSLayer3D.js +7 -0
  90. package/components/map3d/layers/WMTSLayer3D.js +9 -0
  91. package/components/map3d/layers/index.js +8 -0
  92. package/components/map3d/models/arrow.glb +0 -0
  93. package/components/map3d/models/pin.glb +0 -0
  94. package/components/map3d/slices/map3d.js +1 -0
  95. package/components/map3d/style/BottomBar3D.css +67 -0
  96. package/components/map3d/style/Compare3D.css +57 -0
  97. package/components/map3d/style/Draw3D.css +7 -0
  98. package/components/map3d/style/HideObjects3D.css +29 -0
  99. package/components/map3d/style/LayerTree3D.css +126 -0
  100. package/components/map3d/style/Map3D.css +42 -0
  101. package/components/map3d/style/Map3DLight.css +72 -0
  102. package/components/map3d/style/MapControls3D.css +56 -0
  103. package/components/map3d/style/MapExport3D.css +25 -0
  104. package/components/map3d/style/OverviewMap3D.css +37 -0
  105. package/components/map3d/style/SearchField3D.css +7 -0
  106. package/components/map3d/style/View3DSwitcher.css +27 -0
  107. package/components/map3d/utils/Tiles3DStyle.js +1 -0
  108. package/components/share/ShareLink.js +7 -0
  109. package/components/share/ShareQRCode.js +8 -0
  110. package/components/share/ShareSocials.js +9 -0
  111. package/components/share/style/ShareLink.css +13 -0
  112. package/components/share/style/ShareSocials.css +9 -0
  113. package/components/style/App.css +229 -0
  114. package/components/style/AppMenu.css +162 -0
  115. package/components/style/AttributeForm.css +57 -0
  116. package/components/style/AutoEditForm.css +64 -0
  117. package/components/style/DefaultColorScheme.css +60 -0
  118. package/components/style/EditUploadField.css +103 -0
  119. package/components/style/FullscreenSwitcher.css +25 -0
  120. package/components/style/Icon.css +26 -0
  121. package/components/style/IdentifyViewer.css +249 -0
  122. package/components/style/ImportLayer.css +28 -0
  123. package/components/style/LayerInfoWindow.css +46 -0
  124. package/components/style/LinkFeatureForm.css +48 -0
  125. package/components/style/NumericInputWindow.css +11 -0
  126. package/components/style/PickFeature.css +25 -0
  127. package/components/style/PluginsContainer.css +4 -0
  128. package/components/style/QtDesignerForm.css +225 -0
  129. package/components/style/ResizeableWindow.css +178 -0
  130. package/components/style/SearchBox.css +159 -0
  131. package/components/style/ServiceInfoWindow.css +34 -0
  132. package/components/style/SideBar.css +100 -0
  133. package/components/style/TaskBar.css +35 -0
  134. package/components/style/ThemeLayersListWindow.css +47 -0
  135. package/components/style/ThemeList.css +200 -0
  136. package/components/style/Toolbar.css +20 -0
  137. package/components/style/WindowManager.css +50 -0
  138. package/components/timeline/FixedTimeline.js +10 -0
  139. package/components/timeline/InfiniteTimeline.js +13 -0
  140. package/components/timeline/TimelineFeaturesSlider.js +10 -0
  141. package/components/timeline/style/FixedTimeline.css +76 -0
  142. package/components/timeline/style/InfiniteTimeline.css +94 -0
  143. package/components/timeline/style/TimelineFeaturesSlider.css +131 -0
  144. package/components/widgets/AccordeonWidget.js +7 -0
  145. package/components/widgets/ButtonBar.js +7 -0
  146. package/components/widgets/ColorButton.js +8 -0
  147. package/components/widgets/ComboBox.js +7 -0
  148. package/components/widgets/CopyButton.js +7 -0
  149. package/components/widgets/DateTimeInput.js +8 -0
  150. package/components/widgets/EditableSelect.js +8 -0
  151. package/components/widgets/FileSelector.js +9 -0
  152. package/components/widgets/Input.js +7 -0
  153. package/components/widgets/InputContainer.js +7 -0
  154. package/components/widgets/LayerCatalogWidget.js +3 -0
  155. package/components/widgets/MenuButton.js +1 -0
  156. package/components/widgets/ModalDialog.js +7 -0
  157. package/components/widgets/NavBar.js +7 -0
  158. package/components/widgets/NumberInput.js +9 -0
  159. package/components/widgets/PopupMenu.js +1 -0
  160. package/components/widgets/Primitives.js +7 -0
  161. package/components/widgets/ReCaptchaWidget.js +1 -0
  162. package/components/widgets/SearchWidget.js +7 -0
  163. package/components/widgets/Spinner.js +6 -0
  164. package/components/widgets/SuggestionInput.js +7 -0
  165. package/components/widgets/TextInput.js +7 -0
  166. package/components/widgets/ToggleSwitch.js +7 -0
  167. package/components/widgets/VectorLayerPicker.js +8 -0
  168. package/components/widgets/style/AccordeonWidget.css +22 -0
  169. package/components/widgets/style/ButtonBar.css +88 -0
  170. package/components/widgets/style/ColorButton.css +88 -0
  171. package/components/widgets/style/ComboBox.css +83 -0
  172. package/components/widgets/style/CopyButton.css +21 -0
  173. package/components/widgets/style/EditableSelect.css +47 -0
  174. package/components/widgets/style/FileSelector.css +14 -0
  175. package/components/widgets/style/InputContainer.css +34 -0
  176. package/components/widgets/style/LayerCatalogWidget.css +59 -0
  177. package/components/widgets/style/MenuButton.css +122 -0
  178. package/components/widgets/style/ModalDialog.css +50 -0
  179. package/components/widgets/style/NavBar.css +12 -0
  180. package/components/widgets/style/NumberInput.css +76 -0
  181. package/components/widgets/style/SearchWidget.css +43 -0
  182. package/components/widgets/style/Spinner.css +32 -0
  183. package/components/widgets/style/SuggestionInput.css +8 -0
  184. package/components/widgets/style/TextInput.css +94 -0
  185. package/components/widgets/style/ToggleSwitch.css +61 -0
  186. package/components/widgets/style/VectorLayerPicker.css +3 -0
  187. package/icons/2d.svg +70 -0
  188. package/icons/3d.svg +70 -0
  189. package/icons/3d2d.svg +92 -0
  190. package/icons/after.svg +76 -0
  191. package/icons/arrow-down.svg +60 -0
  192. package/icons/arrow-up.svg +60 -0
  193. package/icons/before.svg +76 -0
  194. package/icons/bglayer.svg +71 -0
  195. package/icons/bookmark.svg +170 -0
  196. package/icons/box.svg +63 -0
  197. package/icons/buffer.svg +67 -0
  198. package/icons/camera.svg +118 -0
  199. package/icons/catalog.svg +62 -0
  200. package/icons/checked.svg +80 -0
  201. package/icons/chevron-down.svg +60 -0
  202. package/icons/chevron-left.svg +59 -0
  203. package/icons/chevron-right.svg +60 -0
  204. package/icons/chevron-up.svg +60 -0
  205. package/icons/circle.svg +70 -0
  206. package/icons/clear.svg +62 -0
  207. package/icons/clock.svg +71 -0
  208. package/icons/cog.svg +60 -0
  209. package/icons/collapse.svg +73 -0
  210. package/icons/compare.svg +72 -0
  211. package/icons/cone.svg +58 -0
  212. package/icons/confirm.svg +62 -0
  213. package/icons/copy.svg +60 -0
  214. package/icons/cuboid.svg +58 -0
  215. package/icons/cyclomedia.svg +47 -0
  216. package/icons/cylinder.svg +58 -0
  217. package/icons/dataimport.svg +116 -0
  218. package/icons/detach.svg +79 -0
  219. package/icons/dock.svg +82 -0
  220. package/icons/dock_bottom.svg +82 -0
  221. package/icons/dock_left.svg +84 -0
  222. package/icons/dock_right.svg +84 -0
  223. package/icons/dock_top.svg +84 -0
  224. package/icons/draw.svg +78 -0
  225. package/icons/editdraw.svg +84 -0
  226. package/icons/edited.svg +78 -0
  227. package/icons/editing.svg +118 -0
  228. package/icons/ellipse.svg +72 -0
  229. package/icons/embed.svg +88 -0
  230. package/icons/expand.svg +73 -0
  231. package/icons/export.svg +126 -0
  232. package/icons/eye.svg +127 -0
  233. package/icons/featureform.svg +115 -0
  234. package/icons/fill.svg +62 -0
  235. package/icons/filter.svg +69 -0
  236. package/icons/folder-open.svg +60 -0
  237. package/icons/freehand.svg +82 -0
  238. package/icons/group.svg +76 -0
  239. package/icons/home.svg +60 -0
  240. package/icons/identify_radius.svg +71 -0
  241. package/icons/identify_region.svg +71 -0
  242. package/icons/import.svg +62 -0
  243. package/icons/info-sign.svg +60 -0
  244. package/icons/info.svg +80 -0
  245. package/icons/layers.svg +71 -0
  246. package/icons/light.svg +77 -0
  247. package/icons/line.svg +61 -0
  248. package/icons/line_buffer.svg +65 -0
  249. package/icons/link.svg +75 -0
  250. package/icons/list-alt.svg +111 -0
  251. package/icons/lock.svg +54 -0
  252. package/icons/login.svg +56 -0
  253. package/icons/logout.svg +56 -0
  254. package/icons/map3d.svg +51 -0
  255. package/icons/maximize.svg +84 -0
  256. package/icons/measure.svg +82 -0
  257. package/icons/measure_line.svg +87 -0
  258. package/icons/measure_polygon.svg +88 -0
  259. package/icons/menu-hamburger.svg +60 -0
  260. package/icons/minimize.svg +76 -0
  261. package/icons/minus-sign.svg +60 -0
  262. package/icons/minus.svg +61 -0
  263. package/icons/nav-end.svg +74 -0
  264. package/icons/nav-left.svg +74 -0
  265. package/icons/nav-right.svg +74 -0
  266. package/icons/nav-start.svg +73 -0
  267. package/icons/new.svg +84 -0
  268. package/icons/nodetool.svg +68 -0
  269. package/icons/numericinput.svg +86 -0
  270. package/icons/ok.svg +60 -0
  271. package/icons/open.svg +79 -0
  272. package/icons/open_link.svg +74 -0
  273. package/icons/paint.svg +75 -0
  274. package/icons/pen.svg +41 -0
  275. package/icons/person.svg +44 -0
  276. package/icons/pick.svg +67 -0
  277. package/icons/pick_point.svg +74 -0
  278. package/icons/pick_region.svg +71 -0
  279. package/icons/plus.svg +60 -0
  280. package/icons/point.svg +67 -0
  281. package/icons/point_buffer.svg +74 -0
  282. package/icons/polygon.svg +61 -0
  283. package/icons/print.svg +60 -0
  284. package/icons/pyramid.svg +58 -0
  285. package/icons/radio_checked.svg +78 -0
  286. package/icons/radio_tristate.svg +75 -0
  287. package/icons/radio_unchecked.svg +72 -0
  288. package/icons/rasterexport.svg +124 -0
  289. package/icons/rect.svg +63 -0
  290. package/icons/refresh.svg +44 -0
  291. package/icons/remove.svg +60 -0
  292. package/icons/report.svg +84 -0
  293. package/icons/routing-bicycle.svg +40 -0
  294. package/icons/routing-bus.svg +40 -0
  295. package/icons/routing-cablecar.svg +40 -0
  296. package/icons/routing-car.svg +40 -0
  297. package/icons/routing-ship.svg +40 -0
  298. package/icons/routing-train.svg +40 -0
  299. package/icons/routing-tram.svg +40 -0
  300. package/icons/routing-truck.svg +40 -0
  301. package/icons/routing-walking.svg +40 -0
  302. package/icons/routing.svg +68 -0
  303. package/icons/save.svg +190 -0
  304. package/icons/scale.svg +62 -0
  305. package/icons/screenshot.svg +60 -0
  306. package/icons/search.svg +60 -0
  307. package/icons/selectbox.svg +79 -0
  308. package/icons/share.svg +64 -0
  309. package/icons/snap_edge.svg +79 -0
  310. package/icons/snap_vertex.svg +79 -0
  311. package/icons/sphere.svg +58 -0
  312. package/icons/square.svg +68 -0
  313. package/icons/sync.svg +72 -0
  314. package/icons/text.svg +68 -0
  315. package/icons/themes.svg +81 -0
  316. package/icons/tilt-down.svg +67 -0
  317. package/icons/tilt-left.svg +48 -0
  318. package/icons/tilt-right.svg +48 -0
  319. package/icons/tilt-up.svg +67 -0
  320. package/icons/today.svg +45 -0
  321. package/icons/tools.svg +77 -0
  322. package/icons/transformtool.svg +74 -0
  323. package/icons/transparency.svg +132 -0
  324. package/icons/trash.svg +60 -0
  325. package/icons/tree_minus.svg +81 -0
  326. package/icons/tree_plus.svg +81 -0
  327. package/icons/triangle-down.svg +59 -0
  328. package/icons/triangle-left.svg +60 -0
  329. package/icons/triangle-right.svg +60 -0
  330. package/icons/triangle-up.svg +59 -0
  331. package/icons/tristate.svg +79 -0
  332. package/icons/unchecked.svg +74 -0
  333. package/icons/undock.svg +82 -0
  334. package/icons/undock_bottom.svg +82 -0
  335. package/icons/undock_left.svg +82 -0
  336. package/icons/undock_right.svg +84 -0
  337. package/icons/undock_top.svg +84 -0
  338. package/icons/unmaximize.svg +84 -0
  339. package/icons/unminimize.svg +76 -0
  340. package/icons/up-down-arrow.svg +66 -0
  341. package/icons/warning.svg +72 -0
  342. package/icons/wedge.svg +58 -0
  343. package/icons/zoom.svg +60 -0
  344. package/icons/zoomin.svg +71 -0
  345. package/icons/zoomout.svg +80 -0
  346. package/libs/openlayers.js +11 -0
  347. package/package.json +135 -0
  348. package/plugins/API.js +114 -0
  349. package/plugins/AttributeTable.js +25 -0
  350. package/plugins/Authentication.js +13 -0
  351. package/plugins/BackgroundSwitcher.js +10 -0
  352. package/plugins/Bookmark.js +13 -0
  353. package/plugins/BottomBar.js +10 -0
  354. package/plugins/CookiePopup.js +9 -0
  355. package/plugins/Cyclomedia.js +11 -0
  356. package/plugins/Editing.js +21 -0
  357. package/plugins/FeatureForm.js +21 -0
  358. package/plugins/FeatureSearch.js +11 -0
  359. package/plugins/GeometryDigitizer.js +43 -0
  360. package/plugins/HeightProfile.js +26 -0
  361. package/plugins/Help.js +11 -0
  362. package/plugins/HomeButton.js +9 -0
  363. package/plugins/Identify.js +18 -0
  364. package/plugins/LayerCatalog.js +45 -0
  365. package/plugins/LayerTree.js +15 -0
  366. package/plugins/LocateButton.js +10 -0
  367. package/plugins/Map.js +22 -0
  368. package/plugins/MapCompare.js +11 -0
  369. package/plugins/MapCopyright.js +11 -0
  370. package/plugins/MapExport.js +23 -0
  371. package/plugins/MapFilter.js +20 -0
  372. package/plugins/MapInfoTooltip.js +32 -0
  373. package/plugins/MapLegend.js +14 -0
  374. package/plugins/MapTip.js +15 -0
  375. package/plugins/Measure.js +10 -0
  376. package/plugins/NewsPopup.js +12 -0
  377. package/plugins/Panoramax.js +10 -0
  378. package/plugins/Portal.js +10 -0
  379. package/plugins/Print.js +22 -0
  380. package/plugins/ProcessNotifications.js +11 -0
  381. package/plugins/Redlining.js +12 -0
  382. package/plugins/Reports.js +15 -0
  383. package/plugins/Routing.js +23 -0
  384. package/plugins/ScratchDrawing.js +15 -0
  385. package/plugins/Settings.js +12 -0
  386. package/plugins/Share.js +11 -0
  387. package/plugins/StartupMarker.js +12 -0
  388. package/plugins/TaskButton.js +9 -0
  389. package/plugins/ThemeSwitcher.js +10 -0
  390. package/plugins/TimeManager.js +21 -0
  391. package/plugins/TopBar.js +13 -0
  392. package/plugins/ValueTool.js +11 -0
  393. package/plugins/View3D.js +34 -0
  394. package/plugins/ZoomButtons.js +11 -0
  395. package/plugins/map/EditingSupport.js +13 -0
  396. package/plugins/map/LocateSupport.js +14 -0
  397. package/plugins/map/MeasurementSupport.js +15 -0
  398. package/plugins/map/OverviewSupport.js +14 -0
  399. package/plugins/map/RedliningPickSupport.js +12 -0
  400. package/plugins/map/RedliningSupport.js +28 -0
  401. package/plugins/map/ScaleBarSupport.js +10 -0
  402. package/plugins/map/SnapInteraction.js +1 -0
  403. package/plugins/map/SnapSupport.js +7 -0
  404. package/plugins/map/SnappingSupport.js +23 -0
  405. package/plugins/map/style/OverviewSupport.css +42 -0
  406. package/plugins/map/style/ScaleBarSupport.css +18 -0
  407. package/plugins/map/style/SnappingSupport.css +33 -0
  408. package/plugins/redlining/RedliningBufferSupport.js +8 -0
  409. package/plugins/style/AttributeTable.css +220 -0
  410. package/plugins/style/Authentication.css +16 -0
  411. package/plugins/style/BackgroundSwitcher.css +99 -0
  412. package/plugins/style/Bookmark.css +62 -0
  413. package/plugins/style/BottomBar.css +53 -0
  414. package/plugins/style/Buttons.css +68 -0
  415. package/plugins/style/CookiePopup.css +24 -0
  416. package/plugins/style/Cyclomedia.css +66 -0
  417. package/plugins/style/Editing.css +40 -0
  418. package/plugins/style/FeatureForm.css +21 -0
  419. package/plugins/style/FeatureSearch.css +71 -0
  420. package/plugins/style/GeometryDigitizer.css +28 -0
  421. package/plugins/style/HeightProfile.css +26 -0
  422. package/plugins/style/Identify.css +4 -0
  423. package/plugins/style/LayerCatalog.css +8 -0
  424. package/plugins/style/LayerTree.css +316 -0
  425. package/plugins/style/Map.css +43 -0
  426. package/plugins/style/MapCompare.css +27 -0
  427. package/plugins/style/MapCopyright.css +17 -0
  428. package/plugins/style/MapExport.css +55 -0
  429. package/plugins/style/MapFilter.css +101 -0
  430. package/plugins/style/MapInfoTooltip.css +78 -0
  431. package/plugins/style/MapLegend.css +36 -0
  432. package/plugins/style/MapTip.css +22 -0
  433. package/plugins/style/Measure.css +23 -0
  434. package/plugins/style/NewsPopup.css +87 -0
  435. package/plugins/style/Panoramax.css +15 -0
  436. package/plugins/style/Portal.css +115 -0
  437. package/plugins/style/Print.css +116 -0
  438. package/plugins/style/ProcessNotifications.css +55 -0
  439. package/plugins/style/Redlining.css +101 -0
  440. package/plugins/style/Reports.css +11 -0
  441. package/plugins/style/Routing.css +243 -0
  442. package/plugins/style/ScratchDrawing.css +8 -0
  443. package/plugins/style/Settings.css +11 -0
  444. package/plugins/style/Share.css +59 -0
  445. package/plugins/style/ThemeSwitcher.css +7 -0
  446. package/plugins/style/TimeManager.css +91 -0
  447. package/plugins/style/TopBar.css +50 -0
  448. package/plugins/style/ValueTool.css +60 -0
  449. package/reducers/display.js +7 -0
  450. package/reducers/editing.js +8 -0
  451. package/reducers/index.js +7 -0
  452. package/reducers/layerinfo.js +7 -0
  453. package/reducers/layers.js +14 -0
  454. package/reducers/localConfig.js +8 -0
  455. package/reducers/locale.js +8 -0
  456. package/reducers/locate.js +8 -0
  457. package/reducers/map.js +11 -0
  458. package/reducers/measurement.js +8 -0
  459. package/reducers/processNotifications.js +7 -0
  460. package/reducers/redlining.js +7 -0
  461. package/reducers/redliningPick.js +7 -0
  462. package/reducers/serviceinfo.js +7 -0
  463. package/reducers/task.js +7 -0
  464. package/reducers/theme.js +7 -0
  465. package/reducers/windows.js +7 -0
  466. package/scripts/dist.sh +16 -0
  467. package/scripts/gen-plugin-docs.js +9 -0
  468. package/scripts/makeIconkit.js +7 -0
  469. package/scripts/package-commands.bat +8 -0
  470. package/scripts/package-commands.sh +22 -0
  471. package/scripts/themesConfig.js +48 -0
  472. package/scripts/themesConfig.py +659 -0
  473. package/scripts/translateui.sh +51 -0
  474. package/scripts/updateTranslations.js +17 -0
  475. package/scripts/wmts_config_generator.py +169 -0
  476. package/selectors/searchproviders.js +12 -0
  477. package/static/translations/bg-BG.json +709 -0
  478. package/static/translations/ca-ES.json +709 -0
  479. package/static/translations/cs-CZ.json +709 -0
  480. package/static/translations/de-CH.json +709 -0
  481. package/static/translations/de-DE.json +709 -0
  482. package/static/translations/en-US.json +709 -0
  483. package/static/translations/es-ES.json +709 -0
  484. package/static/translations/fi-FI.json +709 -0
  485. package/static/translations/fr-FR.json +709 -0
  486. package/static/translations/hu-HU.json +709 -0
  487. package/static/translations/it-IT.json +709 -0
  488. package/static/translations/ja-JP.json +709 -0
  489. package/static/translations/nl-NL.json +709 -0
  490. package/static/translations/no-NO.json +709 -0
  491. package/static/translations/pl-PL.json +709 -0
  492. package/static/translations/pt-BR.json +709 -0
  493. package/static/translations/pt-PT.json +709 -0
  494. package/static/translations/ro-RO.json +709 -0
  495. package/static/translations/ru-RU.json +709 -0
  496. package/static/translations/sv-SE.json +709 -0
  497. package/static/translations/tr-TR.json +709 -0
  498. package/static/translations/tsconfig.json +606 -0
  499. package/stores/StandardStore.js +8 -0
  500. package/utils/ConfigUtils.js +9 -0
  501. package/utils/CoordinatesUtils.js +29 -0
  502. package/utils/DxfUtils.js +10 -0
  503. package/utils/EditingInterface.js +67 -0
  504. package/utils/EditingUtils.js +11 -0
  505. package/utils/FeatureStyles.js +10 -0
  506. package/utils/IdentifyUtils.js +12 -0
  507. package/utils/ImageEditor.js +15 -0
  508. package/utils/LayerUtils.js +57 -0
  509. package/utils/LocaleUtils.js +9 -0
  510. package/utils/MapUtils.js +66 -0
  511. package/utils/MeasureUtils.js +9 -0
  512. package/utils/MiscUtils.js +15 -0
  513. package/utils/PermaLinkUtils.js +11 -0
  514. package/utils/ResourceRegistry.js +7 -0
  515. package/utils/RoutingInterface.js +12 -0
  516. package/utils/SearchProviders.js +19 -0
  517. package/utils/ServiceLayerUtils.js +20 -0
  518. package/utils/Signal.js +2 -0
  519. package/utils/ThemeUtils.js +12 -0
  520. package/utils/VectorLayerUtils.js +20 -0
  521. package/utils/expr_grammar/README.md +3 -0
  522. package/utils/expr_grammar/grammar.js +3 -0
  523. package/utils/expr_grammar/grammar.ne +118 -0
  524. package/utils/expr_grammar/test.js +13 -0
  525. package/utils/img/README.markers +1 -0
  526. package/utils/img/arrowhead.svg +4 -0
  527. package/utils/img/marker-icon.png +0 -0
  528. package/utils/img/measurehead.svg +4 -0
@@ -0,0 +1,659 @@
1
+ #!/bin/python
2
+
3
+ # Copyright 2017-2024 Sourcepole AG
4
+ # All rights reserved.
5
+ #
6
+ # This source code is licensed under the BSD-style license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+
9
+ import os
10
+ try:
11
+ from urllib import request
12
+ except:
13
+ import urllib2 as request
14
+ try:
15
+ from urllib.parse import quote, urljoin, urlparse, parse_qsl, urlencode, urlunparse
16
+ except:
17
+ from urllib import quote
18
+ from urlparse import urljoin
19
+ from xml.dom.minidom import parseString
20
+ import json
21
+ import traceback
22
+ import socket
23
+ import re
24
+ import uuid
25
+
26
+ baseUrl = "http://" + socket.getfqdn()
27
+ qwc2_path = "."
28
+ themesConfig = os.environ.get("QWC2_THEMES_CONFIG", "static/themesConfig.json")
29
+
30
+ usedThemeIds = []
31
+ autogenExternalLayers = []
32
+
33
+ def uniqueThemeId(themeName):
34
+ if not themeName:
35
+ return str(uuid.uuid1())
36
+ if themeName in usedThemeIds:
37
+ i = 1
38
+ while ("%s%d") % (themeName, i) in usedThemeIds:
39
+ i += 1
40
+ usedThemeIds.append(("%s%d") % (themeName, i))
41
+ return usedThemeIds[-1]
42
+ else:
43
+ usedThemeIds.append(themeName)
44
+ return usedThemeIds[-1]
45
+
46
+
47
+ def getUrlOpener(configItem):
48
+ auth = configItem.get('wmsBasicAuth')
49
+ if auth:
50
+ manager = request.HTTPPasswordMgrWithDefaultRealm()
51
+ manager.add_password(None, configItem["url"], auth['username'], auth['password'])
52
+ auth_handler = request.HTTPBasicAuthHandler(manager)
53
+ opener = request.build_opener(auth_handler).open
54
+ else:
55
+ opener = request.urlopen
56
+ return opener
57
+
58
+ def update_params(url,params):
59
+ url_parse = urlparse(url)
60
+ query = url_parse.query
61
+ url_dict = dict(parse_qsl(query))
62
+ url_dict.update(params)
63
+ url_new_query = urlencode(url_dict)
64
+ new_url = url_parse._replace(query=url_new_query).geturl()
65
+ return new_url
66
+
67
+ # load thumbnail from file or GetMap
68
+ def getThumbnail(configItem, resultItem, layers, crs, extent):
69
+ if "thumbnail" in configItem:
70
+ if os.path.exists(qwc2_path + "/static/assets/img/mapthumbs/" + configItem["thumbnail"]):
71
+ resultItem["thumbnail"] = "img/mapthumbs/" + configItem["thumbnail"]
72
+ return
73
+
74
+ print("Using WMS GetMap to generate thumbnail for " + configItem["url"])
75
+
76
+ # WMS GetMap request
77
+ url = update_params(urljoin(baseUrl, configItem["url"]), {'SERVICE': 'WMS', 'VERSION': '1.3.0', 'REQUEST': 'GetMap', 'FORMAT': 'image/png', 'STYLES': '', 'WIDTH': '200', 'HEIGHT': '100', 'CRS': crs})
78
+ bboxw = extent[2] - extent[0]
79
+ bboxh = extent[3] - extent[1]
80
+ bboxcx = 0.5 * (extent[0] + extent[2])
81
+ bboxcy = 0.5 * (extent[1] + extent[3])
82
+ imgratio = 200. / 100.
83
+ if bboxw > bboxh:
84
+ bboxratio = bboxw / bboxh
85
+ if bboxratio > imgratio:
86
+ bboxh = bboxw / imgratio
87
+ else:
88
+ bboxw = bboxh * imgratio
89
+ else:
90
+ bboxw = bboxh * imgratio
91
+ adjustedExtent = [bboxcx - 0.5 * bboxw, bboxcy - 0.5 * bboxh,
92
+ bboxcx + 0.5 * bboxw, bboxcy + 0.5 * bboxh]
93
+ url += "&BBOX=" + (",".join(map(str, adjustedExtent)))
94
+ url += "&LAYERS=" + quote(",".join(layers).encode('utf-8'))
95
+
96
+ try:
97
+ opener = getUrlOpener(configItem)
98
+ reply = opener(url).read()
99
+ basename = configItem["url"].rsplit("/")[-1].rstrip("?") + ".png"
100
+ try:
101
+ os.makedirs(qwc2_path + "/static/assets/img/genmapthumbs/")
102
+ except Exception as e:
103
+ if not isinstance(e, FileExistsError): raise e
104
+ thumbnail = qwc2_path + "/static/assets/img/genmapthumbs/" + basename
105
+ with open(thumbnail, "wb") as fh:
106
+ fh.write(reply)
107
+ resultItem["thumbnail"] = "img/genmapthumbs/" + basename
108
+ except Exception as e:
109
+ print("ERROR generating thumbnail for WMS " + configItem["url"] + ":\n" + str(e))
110
+ resultItem["thumbnail"] = "img/mapthumbs/default.jpg"
111
+ traceback.print_exc()
112
+
113
+ def getEditConfig(editConfig):
114
+ if not editConfig:
115
+ return None
116
+ elif isinstance(editConfig, dict):
117
+ return editConfig
118
+ elif os.path.isabs(editConfig) and os.path.exists(editConfig):
119
+ with open(editConfig, encoding='utf-8') as fh:
120
+ config = json.load(fh)
121
+ return config
122
+ else:
123
+ dirname = os.path.dirname(themesConfig)
124
+ if not dirname:
125
+ dirname = "."
126
+ filename = os.path.join(dirname, editConfig)
127
+ if os.path.exists(filename):
128
+ with open(filename, encoding='utf-8') as fh:
129
+ config = json.load(fh)
130
+ return config
131
+ return None
132
+
133
+ def getDirectChildElements(parent, tagname):
134
+ return [node for node in parent.childNodes if node.nodeName.split(':')[-1] == tagname]
135
+
136
+
137
+ def getChildElement(parent, path):
138
+ for part in path.split("/"):
139
+ for node in parent.childNodes:
140
+ if node.nodeName.split(':')[-1] == part:
141
+ parent = node
142
+ break
143
+ else:
144
+ return None
145
+ return parent
146
+
147
+
148
+ def getElementValue(element):
149
+ return element.firstChild.nodeValue if element and element.firstChild else ""
150
+
151
+
152
+ def getChildElementValue(parent, path):
153
+ return getElementValue(getChildElement(parent, path))
154
+
155
+
156
+ # recursively get layer tree
157
+ def getLayerTree(layer, resultLayers, visibleLayers, printLayers, level, collapseBelowLevel, titleNameMap, featureReports, externalLayers):
158
+ name = getChildElementValue(layer, "Name")
159
+ title = getChildElementValue(layer, "Title")
160
+ layers = getDirectChildElements(layer, "Layer")
161
+ treeName = getChildElementValue(layer, "TreeName")
162
+
163
+
164
+ # skip print layers
165
+ for printLayer in printLayers:
166
+ if type(printLayer) is list:
167
+ for entry in printLayer:
168
+ if entry["name"] == name:
169
+ return
170
+ elif printLayer == name:
171
+ return
172
+
173
+ layerEntry = {"name": name, "title": title}
174
+
175
+ if not layers:
176
+ if layer.getAttribute("geometryType") == "WKBNoGeometry" or layer.getAttribute("geometryType") == "NoGeometry":
177
+ # skip layers without geometry
178
+ return
179
+
180
+ # layer
181
+ layerEntry["geometryType"] = layer.getAttribute("geometryType")
182
+ if layer.getAttribute("visibilityChecked") != "":
183
+ layerEntry["visibility"] = layer.getAttribute("visibilityChecked") == "1"
184
+ else:
185
+ layerEntry["visibility"] = layer.getAttribute("visible") == "1"
186
+ if layerEntry["visibility"]:
187
+ # collect visible layers
188
+ visibleLayers.append(name)
189
+
190
+ layerEntry["queryable"] = layer.getAttribute("queryable") == "1"
191
+ if layerEntry["queryable"] and layer.getAttribute("displayField"):
192
+ layerEntry["displayField"] = layer.getAttribute("displayField")
193
+
194
+ try:
195
+ onlineResource = getChildElement(layer, "Attribution/OnlineResource")
196
+ layerEntry["attribution"] = {
197
+ "Title": getChildElementValue(layer, "Attribution/Title"),
198
+ "OnlineResource": onlineResource.getAttribute("xlink:href") if onlineResource else ""
199
+ }
200
+ except:
201
+ pass
202
+ try:
203
+ layerEntry["abstract"] = getChildElementValue(layer, "Abstract")
204
+ except:
205
+ pass
206
+ try:
207
+ onlineResource = getChildElement(layer, "DataURL/OnlineResource")
208
+ layerEntry["dataUrl"] = onlineResource.getAttribute("xlink:href")
209
+ if layerEntry["dataUrl"].startswith("wms:"):
210
+ externalLayers.append({"internalLayer": name, "name": layerEntry["dataUrl"]})
211
+ layerEntry["dataUrl"] = ""
212
+ except:
213
+ pass
214
+ try:
215
+ onlineResource = getChildElement(layer, "MetadataURL/OnlineResource")
216
+ layerEntry["metadataUrl"] = onlineResource.getAttribute("xlink:href")
217
+ except:
218
+ pass
219
+ try:
220
+ keywords = []
221
+ for keyword in getChildElement(layer, "KeywordList").getElementsByTagName("Keyword"):
222
+ keywords.append(getElementValue(keyword))
223
+ layerEntry["keywords"] = ", ".join(keywords)
224
+ except:
225
+ pass
226
+
227
+ styles = {}
228
+ for style in layer.getElementsByTagName("Style"):
229
+ name = getChildElementValue(style, "Name")
230
+ title = getChildElementValue(style, "Title")
231
+ styles[name] = title
232
+ layerEntry["styles"] = styles
233
+ layerEntry['style'] = 'default' if 'default' in styles else (list(styles)[0] if len(styles) > 0 else '')
234
+
235
+ if layer.getAttribute("transparency"):
236
+ layerEntry["opacity"] = 255 - int(float(layer.getAttribute("transparency")) * 255)
237
+ elif layer.getAttribute("opacity"):
238
+ layerEntry["opacity"] = int(float(layer.getAttribute("opacity")) * 255)
239
+ else:
240
+ layerEntry["opacity"] = 255
241
+ minScale = getChildElementValue(layer, "MinScaleDenominator")
242
+ maxScale = getChildElementValue(layer, "MaxScaleDenominator")
243
+ if minScale and maxScale:
244
+ layerEntry["minScale"] = int(float(minScale))
245
+ layerEntry["maxScale"] = int(float(maxScale))
246
+ # use geographic bounding box, as default CRS may have inverted axis order with WMS 1.3.0
247
+ geoBBox = getChildElement(layer, "EX_GeographicBoundingBox")
248
+ if geoBBox:
249
+ layerEntry["bbox"] = {
250
+ "crs": "EPSG:4326",
251
+ "bounds": [
252
+ float(getChildElementValue(geoBBox, "westBoundLongitude")),
253
+ float(getChildElementValue(geoBBox, "southBoundLatitude")),
254
+ float(getChildElementValue(geoBBox, "eastBoundLongitude")),
255
+ float(getChildElementValue(geoBBox, "northBoundLatitude"))
256
+ ]
257
+ }
258
+ if name in featureReports:
259
+ layerEntry["featureReport"] = featureReports[name]
260
+
261
+ layerEntry["dimensions"] = []
262
+ for dim in getDirectChildElements(layer, "Dimension"):
263
+ layerEntry["dimensions"].append({
264
+ "units": dim.getAttribute("units"),
265
+ "name": dim.getAttribute("name"),
266
+ "multiple": dim.getAttribute("multipleValues") == "1",
267
+ "value": getElementValue(dim),
268
+ "fieldName": dim.getAttribute("fieldName"),
269
+ "endFieldName": dim.getAttribute("endFieldName")
270
+ })
271
+
272
+ else:
273
+ # group
274
+ layerEntry["mutuallyExclusive"] = layer.getAttribute("mutuallyExclusive") == "1"
275
+ if layer.getAttribute("visibilityChecked") != "":
276
+ layerEntry["visibility"] = layer.getAttribute("visibilityChecked") == "1"
277
+ else:
278
+ layerEntry["visibility"] = layer.getAttribute("visible") == "1"
279
+ layerEntry["sublayers"] = []
280
+ if layer.getAttribute("expanded") == "0":
281
+ layerEntry["expanded"] = False
282
+ else:
283
+ layerEntry["expanded"] = False if collapseBelowLevel >= 0 and level >= collapseBelowLevel else True
284
+ for sublayer in layers:
285
+ getLayerTree(sublayer, layerEntry["sublayers"], visibleLayers, printLayers, level + 1, collapseBelowLevel, titleNameMap, featureReports, externalLayers)
286
+
287
+ if not layerEntry["sublayers"]:
288
+ # skip empty groups
289
+ return
290
+
291
+ resultLayers.append(layerEntry)
292
+ titleNameMap[treeName] = name
293
+
294
+ # parse GetCapabilities for theme
295
+ def getTheme(config, configItem, result, resultItem):
296
+ global autogenExternalLayers
297
+
298
+ url = update_params(urljoin(baseUrl, configItem["url"]), {'SERVICE': 'WMS', 'VERSION': '1.3.0', 'REQUEST': 'GetProjectSettings'})
299
+
300
+ try:
301
+ opener = getUrlOpener(configItem)
302
+ reply = opener(url).read()
303
+ capabilities = parseString(reply)
304
+ capabilities = capabilities.getElementsByTagName("WMS_Capabilities")[0]
305
+ print("Parsing WMS GetProjectSettings of " + configItem["url"])
306
+
307
+ topLayer = getChildElement(getChildElement(capabilities, "Capability"), "Layer")
308
+ wmsName = re.sub(r".*/", "", configItem["url"]).rstrip("?")
309
+
310
+ # use name from config or fallback to WMS title
311
+ wmsTitle = configItem.get("title") or getChildElementValue(capabilities, "Service/Title") or getChildElementValue(topLayer, "Title") or wmsName
312
+
313
+
314
+ # keywords
315
+ keywords = []
316
+ keywordList = getChildElement(capabilities, "Service/KeywordList")
317
+ if keywordList:
318
+ for keyword in keywordList.getElementsByTagName("Keyword"):
319
+ value = getElementValue(keyword)
320
+ if value != "infoMapAccessService":
321
+ keywords.append(value)
322
+
323
+ # collect WMS layers for printing
324
+ printLayers = configItem["extraPrintLayers"] if "extraPrintLayers" in configItem else []
325
+ if "backgroundLayers" in configItem:
326
+ printLayers = [entry["printLayer"] for entry in configItem["backgroundLayers"] if "printLayer" in entry]
327
+
328
+ # layer tree and visible layers
329
+ collapseLayerGroupsBelowLevel = -1
330
+ if "collapseLayerGroupsBelowLevel" in configItem:
331
+ collapseLayerGroupsBelowLevel = configItem["collapseLayerGroupsBelowLevel"]
332
+
333
+ layerTree = []
334
+ visibleLayers = []
335
+ titleNameMap = {}
336
+ featureReports = configItem["featureReport"] if "featureReport" in configItem else {}
337
+ externalLayers = []
338
+ getLayerTree(topLayer, layerTree, visibleLayers, printLayers, 1, collapseLayerGroupsBelowLevel, titleNameMap, featureReports, externalLayers)
339
+ autogenExternalLayers += list(map(lambda entry: entry["name"], externalLayers))
340
+ if "externalLayers" in configItem:
341
+ externalLayers += configItem["externalLayers"]
342
+ visibleLayers.reverse()
343
+
344
+ # print templates
345
+ printTemplates = []
346
+ composerTemplates = getChildElement(capabilities, "Capability/ComposerTemplates")
347
+ if composerTemplates:
348
+
349
+ composerTemplateMap = {}
350
+ for composerTemplate in composerTemplates.getElementsByTagName("ComposerTemplate"):
351
+ composerMap = getChildElement(composerTemplate, "ComposerMap")
352
+ if composerMap:
353
+ composerTemplateMap[composerTemplate.getAttribute("name")] = composerTemplate;
354
+
355
+
356
+ for composerTemplate in composerTemplateMap.values():
357
+ templateName = composerTemplate.getAttribute("name")
358
+ if templateName.endswith("_legend") and templateName[:-7] in composerTemplateMap:
359
+ continue
360
+
361
+ composerMap = getChildElement(composerTemplate, "ComposerMap")
362
+ printTemplate = {
363
+ "name": templateName,
364
+ "map": {
365
+ "name": composerMap.getAttribute("name"),
366
+ "width": float(composerMap.getAttribute("width")),
367
+ "height": float(composerMap.getAttribute("height"))
368
+ }
369
+ }
370
+ if printTemplate["name"] + "_legend" in composerTemplateMap:
371
+ printTemplate["legendLayout"] = printTemplate["name"] + "_legend";
372
+
373
+ composerLabels = composerTemplate.getElementsByTagName("ComposerLabel")
374
+ labels = [composerLabel.getAttribute("name") for composerLabel in composerLabels]
375
+ if "printLabelBlacklist" in configItem:
376
+ labels = list(filter(lambda label: label not in configItem["printLabelBlacklist"], labels))
377
+ printTemplate['default'] = printTemplate['name'] == configItem.get('defaultPrintLayout')
378
+
379
+ if labels:
380
+ printTemplate["labels"] = labels
381
+ if composerTemplate.getAttribute('atlasEnabled') == '1':
382
+ atlasLayer = composerTemplate.getAttribute('atlasCoverageLayer')
383
+ try:
384
+ layers = capabilities.getElementsByTagName("Layer")
385
+ pk = getChildElementValue(list(filter(lambda l: getChildElementValue(l, "Name") == atlasLayer, layers))[0], "PrimaryKey/PrimaryKeyAttribute")
386
+ printTemplate["atlasCoverageLayer"] = atlasLayer
387
+ printTemplate["atlas_pk"] = pk
388
+ except:
389
+ print("Failed to determine primary key for atlas layer " + atlasLayer)
390
+
391
+ printTemplates.append(printTemplate)
392
+
393
+ # drawing order
394
+ drawingOrder = getChildElementValue(capabilities, "Capability/LayerDrawingOrder").split(",")
395
+ drawingOrder = list(map(lambda title: titleNameMap[title] if title in titleNameMap else title, drawingOrder))
396
+
397
+ # getmap formats
398
+ availableFormats = []
399
+ for format in getChildElement(capabilities, "Capability/Request/GetMap").getElementsByTagName("Format"):
400
+ availableFormats.append(getElementValue(format))
401
+
402
+ # update theme config
403
+ resultItem["url"] = configItem["url"]
404
+ resultItem["id"] = uniqueThemeId(wmsName)
405
+ resultItem["name"] = getChildElementValue(topLayer, "Name")
406
+ resultItem["title"] = wmsTitle
407
+ resultItem["description"] = configItem["description"] if "description" in configItem else ""
408
+ resultItem["attribution"] = {
409
+ "Title": configItem["attribution"] if "attribution" in configItem else "",
410
+ "OnlineResource": configItem["attributionUrl"] if "attributionUrl" in configItem else ""
411
+ }
412
+ # service info
413
+ resultItem["abstract"] = getChildElementValue(capabilities, "Service/Abstract")
414
+ resultItem["keywords"] = ", ".join(keywords)
415
+ resultItem["onlineResource"] = getChildElement(capabilities, "Service/OnlineResource").getAttribute("xlink:href")
416
+ resultItem["contact"] = {
417
+ "person": getChildElementValue(capabilities, "Service/ContactInformation/ContactPersonPrimary/ContactPerson"),
418
+ "organization": getChildElementValue(capabilities, "Service/ContactInformation/ContactPersonPrimary/ContactOrganization"),
419
+ "position": getChildElementValue(capabilities, "Service/ContactInformation/ContactPosition"),
420
+ "phone": getChildElementValue(capabilities, "Service/ContactInformation/ContactVoiceTelephone"),
421
+ "email": getChildElementValue(capabilities, "Service/ContactInformation/ContactElectronicMailAddress")
422
+ }
423
+
424
+ if "format" in configItem:
425
+ resultItem["format"] = configItem["format"]
426
+ resultItem["availableFormats"] = availableFormats
427
+ if "tiled" in configItem:
428
+ resultItem["tiled"] = configItem["tiled"]
429
+ if "tileSize" in configItem:
430
+ resultItem["tileSize"] = configItem["tileSize"]
431
+ if "version" in configItem:
432
+ resultItem["version"] = configItem["version"]
433
+ elif "defaultWMSVersion" in config:
434
+ resultItem["version"] = config["defaultWMSVersion"]
435
+ resultItem["infoFormats"] = [getElementValue(format) for format in getChildElement(capabilities, "Capability/Request/GetFeatureInfo").getElementsByTagName("Format")]
436
+ # use geographic bounding box for theme, as default CRS may have inverted axis order with WMS 1.3.0
437
+ bounds = [
438
+ float(getChildElementValue(topLayer, "EX_GeographicBoundingBox/westBoundLongitude")),
439
+ float(getChildElementValue(topLayer, "EX_GeographicBoundingBox/southBoundLatitude")),
440
+ float(getChildElementValue(topLayer, "EX_GeographicBoundingBox/eastBoundLongitude")),
441
+ float(getChildElementValue(topLayer, "EX_GeographicBoundingBox/northBoundLatitude"))
442
+ ]
443
+ resultItem["bbox"] = {
444
+ "crs": "EPSG:4326",
445
+ "bounds": bounds
446
+ }
447
+ if "extent" in configItem:
448
+ resultItem["initialBbox"] = {
449
+ "crs": configItem["mapCrs"] if "mapCrs" in configItem else result["themes"]["defaultMapCrs"],
450
+ "bounds": configItem["extent"]
451
+ }
452
+ else:
453
+ resultItem["initialBbox"] = resultItem["bbox"]
454
+ if "scales" in configItem:
455
+ resultItem["scales"] = configItem["scales"]
456
+ if "printScales" in configItem:
457
+ resultItem["printScales"] = configItem["printScales"]
458
+ if "printResolutions" in configItem:
459
+ resultItem["printResolutions"] = configItem["printResolutions"]
460
+ if "printGrid" in configItem:
461
+ resultItem["printGrid"] = configItem["printGrid"]
462
+ # NOTE: skip root WMS layer
463
+ resultItem["sublayers"] = layerTree[0]["sublayers"] if len(layerTree) > 0 and "sublayers" in layerTree[0] else []
464
+ resultItem["expanded"] = True
465
+ if "backgroundLayers" in configItem:
466
+ resultItem["backgroundLayers"] = configItem["backgroundLayers"]
467
+ resultItem["externalLayers"] = externalLayers
468
+ if "pluginData" in configItem:
469
+ resultItem["pluginData"] = configItem["pluginData"]
470
+ if "predefinedFilters" in configItem:
471
+ resultItem["predefinedFilters"] = configItem["predefinedFilters"]
472
+ if "snapping" in configItem:
473
+ resultItem["snapping"] = configItem["snapping"]
474
+ if "minSearchScaleDenom" in configItem:
475
+ resultItem["minSearchScaleDenom"] = configItem["minSearchScaleDenom"]
476
+ elif "minSearchScale" in configItem: # Legacy name
477
+ resultItem["minSearchScaleDenom"] = configItem["minSearchScale"]
478
+ if "themeInfoLinks" in configItem:
479
+ resultItem["themeInfoLinks"] = configItem["themeInfoLinks"]
480
+ if "layerTreeHiddenSublayers" in configItem:
481
+ resultItem["layerTreeHiddenSublayers"] = configItem["layerTreeHiddenSublayers"]
482
+ resultItem["searchProviders"] = configItem["searchProviders"] if "searchProviders" in configItem else []
483
+ if "additionalMouseCrs" in configItem:
484
+ resultItem["additionalMouseCrs"] = configItem["additionalMouseCrs"]
485
+ if "mapCrs" in configItem:
486
+ resultItem["mapCrs"] = configItem["mapCrs"]
487
+ else:
488
+ resultItem["mapCrs"] = result["themes"]["defaultMapCrs"]
489
+ if printTemplates:
490
+ resultItem["print"] = printTemplates
491
+ resultItem["drawingOrder"] = drawingOrder
492
+ if "extraDxfParameters" in configItem:
493
+ resultItem["extraDxfParameters"] = configItem["extraDxfParameters"]
494
+ if "extraPrintParameters" in configItem:
495
+ resultItem["extraPrintParameters"] = configItem["extraPrintParameters"]
496
+ extraLegenParams = configItem["extraLegendParameters"] if "extraLegendParameters" in configItem else ""
497
+ if "legendUrl" in configItem:
498
+ resultItem["legendUrl"] = configItem["legendUrl"]
499
+ else:
500
+ resultItem["legendUrl"] = getChildElement(capabilities, "Capability/Request/GetLegendGraphic/DCPType/HTTP/Get/OnlineResource").getAttribute("xlink:href").rstrip("?") + "?" + extraLegenParams
501
+ if "featureInfoUrl" in configItem:
502
+ resultItem["featureInfoUrl"] = configItem["featureInfoUrl"]
503
+ else:
504
+ resultItem["featureInfoUrl"] = getChildElement(capabilities, "Capability/Request/GetFeatureInfo/DCPType/HTTP/Get/OnlineResource").getAttribute("xlink:href").rstrip("?") + "?"
505
+ if "printUrl" in configItem:
506
+ resultItem["printUrl"] = configItem["printUrl"]
507
+ else:
508
+ resultItem["printUrl"] = getChildElement(capabilities, "Capability/Request/GetPrint/DCPType/HTTP/Get/OnlineResource").getAttribute("xlink:href").rstrip("?") + "?"
509
+ if "printLabelForSearchResult" in configItem:
510
+ resultItem["printLabelForSearchResult"] = configItem["printLabelForSearchResult"]
511
+ if "printLabelForAttribution" in configItem:
512
+ resultItem["printLabelForAttribution"] = configItem["printLabelForAttribution"]
513
+ if "printLabelConfig" in configItem:
514
+ resultItem["printLabelConfig"] = configItem["printLabelConfig"]
515
+
516
+ if "watermark" in configItem:
517
+ resultItem["watermark"] = configItem["watermark"]
518
+
519
+ if "skipEmptyFeatureAttributes" in configItem:
520
+ resultItem["skipEmptyFeatureAttributes"] = configItem["skipEmptyFeatureAttributes"]
521
+
522
+ if "config" in configItem:
523
+ resultItem["config"] = configItem["config"]
524
+
525
+ if "flags" in configItem:
526
+ resultItem["flags"] = configItem["flags"]
527
+
528
+ if "mapTips" in configItem:
529
+ resultItem["mapTips"] = configItem["mapTips"]
530
+
531
+ if "userMap" in configItem:
532
+ resultItem["userMap"] = configItem["userMap"]
533
+
534
+ if "map3d" in configItem:
535
+ resultItem["map3d"] = configItem["map3d"]
536
+
537
+ resultItem["editConfig"] = getEditConfig(configItem["editConfig"] if "editConfig" in configItem else None)
538
+
539
+ # set default theme
540
+ if configItem.get("default", False) or not result["themes"]["defaultTheme"]:
541
+ result["themes"]["defaultTheme"] = resultItem["id"]
542
+
543
+ # use first CRS for thumbnail request which is not CRS:84
544
+ for item in topLayer.getElementsByTagName("CRS"):
545
+ crs = getElementValue(item)
546
+ if crs != "CRS:84":
547
+ break
548
+ extent = None
549
+ for bbox in topLayer.getElementsByTagName("BoundingBox"):
550
+ if bbox.getAttribute("CRS") == crs:
551
+ extent = [
552
+ float(bbox.getAttribute("minx")),
553
+ float(bbox.getAttribute("miny")),
554
+ float(bbox.getAttribute("maxx")),
555
+ float(bbox.getAttribute("maxy"))
556
+ ]
557
+ break
558
+ if extent:
559
+ getThumbnail(configItem, resultItem, visibleLayers, crs, extent)
560
+
561
+ except Exception as e:
562
+ print("ERROR reading WMS GetProjectSettings of " + configItem["url"] + ":\n" + str(e))
563
+ resultItem["error"] = "Could not read GetProjectSettings"
564
+ resultItem["title"] = "Error"
565
+ traceback.print_exc()
566
+
567
+
568
+ # recursively get themes for groups
569
+ def getGroupThemes(config, configGroup, result, resultGroup, groupCounter):
570
+ for item in configGroup["items"]:
571
+ itemEntry = {}
572
+ getTheme(config, item, result, itemEntry)
573
+ if itemEntry:
574
+ resultGroup["items"].append(itemEntry)
575
+
576
+ if "groups" in configGroup:
577
+ for group in configGroup["groups"]:
578
+ groupCounter += 1
579
+ groupEntry = {
580
+ "id": "g%d" % groupCounter,
581
+ "title": group["title"],
582
+ "items": [],
583
+ "subdirs": []
584
+ }
585
+ getGroupThemes(config, group, result, groupEntry, groupCounter)
586
+ resultGroup["subdirs"].append(groupEntry)
587
+
588
+
589
+ def reformatAttribution(entry):
590
+ entry["attribution"] = {
591
+ "Title": entry["attribution"] if "attribution" in entry else None,
592
+ "OnlineResource": entry["attributionUrl"] if "attributionUrl" in entry else None
593
+ }
594
+ entry.pop("attributionUrl", None)
595
+ return entry
596
+
597
+
598
+ def genThemes(themesConfig):
599
+ # load themesConfig.json
600
+ try:
601
+ with open(themesConfig, encoding='utf-8') as fh:
602
+ config = json.load(fh)
603
+ except:
604
+ return {"error": "Failed to read themesConfig.json"}
605
+
606
+ result = {
607
+ "themes": {
608
+ "title": "root",
609
+ "subdirs": [],
610
+ "items": [],
611
+ "defaultTheme": config["defaultTheme"] if "defaultTheme" in config else None,
612
+ "defaultMapCrs": config["defaultMapCrs"] if "defaultMapCrs" in config else "EPSG:3857",
613
+ "defaultScales": config["defaultScales"],
614
+ "defaultPrintScales": config["defaultPrintScales"] if "defaultPrintScales" in config else None,
615
+ "defaultPrintResolutions": config["defaultPrintResolutions"] if "defaultPrintResolutions" in config else None,
616
+ "defaultPrintGrid": config["defaultPrintGrid"] if "defaultPrintGrid" in config else None,
617
+ "defaultSearchProviders": config["defaultSearchProviders"] if "defaultSearchProviders" in config else None,
618
+ "defaultBackgroundLayers": config["defaultBackgroundLayers"] if "defaultBackgroundLayers" in config else [],
619
+ "pluginData": config["themes"]["pluginData"] if "pluginData" in config["themes"] else [],
620
+ "themeInfoLinks": config["themes"]["themeInfoLinks"] if "themeInfoLinks" in config["themes"] else [],
621
+ "externalLayers": config["themes"]["externalLayers"] if "externalLayers" in config["themes"] else [],
622
+ "backgroundLayers": list(map(reformatAttribution, config["themes"]["backgroundLayers"])),
623
+ "defaultWMSVersion": config["defaultWMSVersion"] if "defaultWMSVersion" in config else None
624
+ }
625
+ }
626
+ groupCounter = 0
627
+ getGroupThemes(config, config["themes"], result, result["themes"], groupCounter)
628
+
629
+ for entry in autogenExternalLayers:
630
+ cpos = entry.find(":")
631
+ hpos = entry.rfind('#')
632
+ type = entry[0:cpos]
633
+ url = entry[cpos+1:hpos]
634
+ layername = entry[hpos+1:]
635
+ result["themes"]["externalLayers"].append({
636
+ "name": entry,
637
+ "type": type,
638
+ "url": url,
639
+ "params": {"LAYERS": layername},
640
+ "infoFormats": ["text/plain"]
641
+ })
642
+
643
+ if "backgroundLayers" in result["themes"]:
644
+ # get thumbnails for background layers
645
+ for backgroundLayer in result["themes"]["backgroundLayers"]:
646
+ imgPath = "img/mapthumbs/" + backgroundLayer.get("thumbnail", "default.jpg")
647
+ if not os.path.isfile(qwc2_path + "/static/assets/" + imgPath):
648
+ imgPath = "img/mapthumbs/default.jpg"
649
+ backgroundLayer["thumbnail"] = imgPath
650
+
651
+ return result
652
+
653
+
654
+ if __name__ == '__main__':
655
+ print("Reading " + themesConfig)
656
+ themes = genThemes(themesConfig)
657
+ # write config file
658
+ with open("./static/themes.json", "w") as fh:
659
+ json.dump(themes, fh, indent=2, separators=(',', ': '), sort_keys=True)