@defra/interactive-map 0.0.3-alpha → 0.0.5-alpha

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 (407) hide show
  1. package/README.md +16 -57
  2. package/dist/css/index.css +1 -1
  3. package/dist/esm/im-core.js +1 -1
  4. package/dist/esm/index.js +1 -1
  5. package/dist/umd/im-core.js +1 -1
  6. package/dist/umd/index.js +1 -1
  7. package/docs/api/button-definition.md +176 -0
  8. package/docs/api/context.md +124 -0
  9. package/docs/api/control-definition.md +72 -0
  10. package/docs/api/icon-definition.md +28 -0
  11. package/docs/api/map-style-config.md +76 -0
  12. package/docs/api/marker-config.md +51 -0
  13. package/docs/api/panel-definition.md +117 -0
  14. package/docs/api/slots.md +25 -0
  15. package/docs/api.md +721 -0
  16. package/docs/architecture.md +139 -0
  17. package/docs/building-a-plugin.md +149 -0
  18. package/docs/getting-started.md +46 -0
  19. package/docs/govuk-prototype.md +0 -0
  20. package/docs/plugins/interact.md +205 -0
  21. package/docs/plugins/map-styles.md +84 -0
  22. package/docs/plugins/plugin-context.md +67 -0
  23. package/docs/plugins/plugin-descriptor.md +71 -0
  24. package/docs/plugins/plugin-manifest.md +118 -0
  25. package/docs/plugins/scale-bar.md +62 -0
  26. package/docs/plugins/search.md +162 -0
  27. package/docs/plugins.md +116 -0
  28. package/govuk-prototype-kit.config.json +7 -7
  29. package/package.json +16 -2
  30. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +2 -0
  31. package/plugins/beta/datasets/dist/esm/index.js +2 -0
  32. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +2 -0
  33. package/plugins/beta/datasets/dist/umd/index.js +2 -0
  34. package/plugins/beta/datasets/src/datasets.js +113 -0
  35. package/plugins/beta/datasets/src/fetch/createDynamicSource.js +206 -0
  36. package/plugins/beta/datasets/src/fetch/fetchGeoJSON.js +38 -0
  37. package/plugins/{datasets → beta/datasets}/src/handleSetMapStyle.js +8 -1
  38. package/plugins/{datasets → beta/datasets}/src/manifest.js +1 -1
  39. package/plugins/{datasets → beta/datasets}/src/mapLayers.js +42 -4
  40. package/plugins/{datasets → beta/datasets}/src/panels/Key.jsx +1 -1
  41. package/plugins/beta/datasets/src/utils/bbox.js +115 -0
  42. package/plugins/beta/draw-ml/dist/css/index.css +1 -0
  43. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -0
  44. package/plugins/beta/draw-ml/dist/esm/index.js +2 -0
  45. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -0
  46. package/plugins/beta/draw-ml/dist/umd/index.js +2 -0
  47. package/plugins/beta/draw-ml/src/api/addFeature.js +27 -0
  48. package/plugins/{draw-ml → beta/draw-ml}/src/api/editFeature.js +17 -6
  49. package/plugins/beta/draw-ml/src/api/merge.js +9 -0
  50. package/plugins/beta/draw-ml/src/api/newLine.js +68 -0
  51. package/plugins/beta/draw-ml/src/api/newPolygon.js +68 -0
  52. package/plugins/beta/draw-ml/src/api/split.js +81 -0
  53. package/plugins/{draw-ml → beta/draw-ml}/src/defaults.js +3 -0
  54. package/plugins/beta/draw-ml/src/draw.scss +32 -0
  55. package/plugins/beta/draw-ml/src/events.js +171 -0
  56. package/plugins/beta/draw-ml/src/manifest.js +132 -0
  57. package/plugins/{draw-ml → beta/draw-ml}/src/mapboxDraw.js +51 -2
  58. package/plugins/{draw-ml → beta/draw-ml}/src/mapboxSnap.js +18 -12
  59. package/plugins/beta/draw-ml/src/modes/createDrawMode.js +497 -0
  60. package/plugins/beta/draw-ml/src/modes/drawLineMode.js +18 -0
  61. package/plugins/beta/draw-ml/src/modes/drawPolygonMode.js +16 -0
  62. package/plugins/beta/draw-ml/src/modes/editVertex/geometryHelpers.js +135 -0
  63. package/plugins/beta/draw-ml/src/modes/editVertex/helpers.js +2 -0
  64. package/plugins/beta/draw-ml/src/modes/editVertex/touchHandlers.js +134 -0
  65. package/plugins/beta/draw-ml/src/modes/editVertex/undoHandlers.js +133 -0
  66. package/plugins/beta/draw-ml/src/modes/editVertex/vertexOperations.js +141 -0
  67. package/plugins/beta/draw-ml/src/modes/editVertex/vertexQueries.js +121 -0
  68. package/plugins/beta/draw-ml/src/modes/editVertexMode.js +437 -0
  69. package/plugins/{draw-ml → beta/draw-ml}/src/reducer.js +30 -2
  70. package/plugins/{draw-ml → beta/draw-ml}/src/styles.js +66 -36
  71. package/plugins/beta/draw-ml/src/undoStack.js +50 -0
  72. package/plugins/beta/draw-ml/src/utils/debounce.js +16 -0
  73. package/plugins/beta/draw-ml/src/utils/flattenStyleProperties.js +49 -0
  74. package/plugins/beta/draw-ml/src/utils/spatial.js +258 -0
  75. package/plugins/beta/frame/dist/esm/im-frame-plugin.js +1 -0
  76. package/plugins/beta/frame/dist/esm/index.js +2 -0
  77. package/plugins/beta/frame/dist/umd/im-frame-plugin.js +1 -0
  78. package/plugins/beta/frame/dist/umd/index.js +2 -0
  79. package/plugins/{frame → beta/frame}/src/Frame.jsx +1 -1
  80. package/plugins/{map-styles → beta/map-styles}/dist/css/index.css +1 -1
  81. package/plugins/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -0
  82. package/plugins/beta/map-styles/dist/esm/index.js +2 -0
  83. package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -0
  84. package/plugins/beta/map-styles/dist/umd/index.js +2 -0
  85. package/plugins/{map-styles → beta/map-styles}/src/MapStyles.jsx +1 -1
  86. package/plugins/{map-styles → beta/map-styles}/src/manifest.js +8 -10
  87. package/plugins/{map-styles → beta/map-styles}/src/mapStyles.scss +4 -1
  88. package/plugins/beta/scale-bar/dist/esm/im-scale-bar-plugin.js +1 -0
  89. package/plugins/beta/scale-bar/dist/esm/index.js +2 -0
  90. package/plugins/beta/scale-bar/dist/umd/im-scale-bar-plugin.js +1 -0
  91. package/plugins/beta/scale-bar/dist/umd/index.js +2 -0
  92. package/plugins/beta/use-location/dist/esm/im-use-location-plugin.js +1 -0
  93. package/plugins/beta/use-location/dist/esm/index.js +2 -0
  94. package/plugins/beta/use-location/dist/umd/im-use-location-plugin.js +1 -0
  95. package/plugins/beta/use-location/dist/umd/index.js +2 -0
  96. package/plugins/interact/dist/css/index.css +1 -1
  97. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  98. package/plugins/interact/dist/esm/index.js +1 -1
  99. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  100. package/plugins/interact/dist/umd/index.js +1 -1
  101. package/plugins/interact/src/InteractInit.jsx +1 -1
  102. package/plugins/interact/src/InteractInit.test.js +97 -0
  103. package/plugins/interact/src/api/clear.test.js +18 -0
  104. package/plugins/interact/src/api/disable.test.js +12 -0
  105. package/plugins/interact/src/api/enable.test.js +48 -0
  106. package/plugins/interact/src/api/selectFeature.test.js +33 -0
  107. package/plugins/interact/src/api/unselectFeature.test.js +33 -0
  108. package/plugins/interact/src/defaults.js +2 -0
  109. package/plugins/interact/src/events.js +37 -52
  110. package/plugins/interact/src/events.test.js +186 -0
  111. package/plugins/interact/src/hooks/useHighlightSync.js +1 -1
  112. package/plugins/interact/src/hooks/useHighlightSync.test.js +212 -0
  113. package/plugins/interact/src/hooks/useInteractionHandlers.js +80 -41
  114. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +316 -0
  115. package/plugins/interact/src/index.test.js +22 -0
  116. package/plugins/interact/src/interact.scss +6 -0
  117. package/plugins/interact/src/manifest.js +1 -1
  118. package/plugins/interact/src/manifest.test.js +94 -0
  119. package/plugins/interact/src/reducer.js +28 -21
  120. package/plugins/interact/src/reducer.test.js +155 -0
  121. package/plugins/interact/src/utils/buildStylesMap.test.js +57 -0
  122. package/plugins/interact/src/utils/featureQueries.js +2 -2
  123. package/plugins/interact/src/utils/featureQueries.test.js +56 -0
  124. package/plugins/interact/src/utils/spatial.js +95 -0
  125. package/plugins/interact/src/utils/spatial.test.js +93 -0
  126. package/plugins/search/dist/css/index.css +1 -1
  127. package/plugins/search/dist/esm/im-search-plugin.js +1 -1
  128. package/plugins/search/dist/esm/index.js +1 -1
  129. package/plugins/search/dist/umd/im-search-plugin.js +1 -1
  130. package/plugins/search/dist/umd/index.js +1 -1
  131. package/plugins/search/src/Search.jsx +2 -2
  132. package/plugins/search/src/components/Form/Form.jsx +1 -1
  133. package/plugins/search/src/components/Suggestions/Suggestions.jsx +5 -5
  134. package/plugins/search/src/datasets.js +1 -1
  135. package/plugins/search/src/defaults.js +3 -0
  136. package/plugins/search/src/events/fetchSuggestions.js +48 -53
  137. package/plugins/search/src/events/formHandlers.js +3 -2
  138. package/plugins/search/src/events/index.js +1 -1
  139. package/plugins/search/src/events/inputHandlers.js +3 -1
  140. package/plugins/search/src/events/suggestionHandlers.js +12 -3
  141. package/plugins/search/src/search.scss +4 -1
  142. package/plugins/search/src/utils/parseOsNamesResults.js +15 -19
  143. package/providers/{esri → beta/esri}/src/esriProvider.js +1 -1
  144. package/providers/{esri → beta/esri}/src/index.js +1 -1
  145. package/providers/{esri → beta/esri}/src/mapEvents.js +2 -2
  146. package/providers/beta/open-names/dist/esm/im-reverse-geocode.js +2 -0
  147. package/providers/beta/open-names/dist/esm/index.js +2 -0
  148. package/providers/beta/open-names/dist/umd/im-reverse-geocode.js +2 -0
  149. package/providers/beta/open-names/dist/umd/im-reverse-geocode.js.LICENSE.txt +1 -0
  150. package/providers/beta/open-names/dist/umd/index.js +2 -0
  151. package/providers/beta/open-names/dist/umd/index.js.LICENSE.txt +1 -0
  152. package/providers/maplibre/dist/esm/im-maplibre-framework.js +1 -1
  153. package/providers/maplibre/dist/esm/im-maplibre-framework.js.LICENSE.txt +1 -1
  154. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  155. package/providers/maplibre/dist/esm/index.js +1 -1
  156. package/providers/maplibre/dist/umd/im-maplibre-framework.js +1 -1
  157. package/providers/maplibre/dist/umd/im-maplibre-framework.js.LICENSE.txt +1 -1
  158. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  159. package/providers/maplibre/dist/umd/index.js +1 -1
  160. package/providers/maplibre/src/defaults.js +3 -2
  161. package/providers/maplibre/src/index.js +36 -20
  162. package/providers/maplibre/src/maplibreProvider.js +140 -11
  163. package/providers/maplibre/src/utils/detectWebgl.js +1 -2
  164. package/providers/maplibre/src/utils/highlightFeatures.js +78 -67
  165. package/providers/maplibre/src/utils/labels.js +174 -120
  166. package/providers/maplibre/src/utils/maplibreFixes.js +2 -2
  167. package/providers/maplibre/src/utils/queryFeatures.js +146 -0
  168. package/providers/maplibre/src/utils/spatial.js +14 -17
  169. package/sonar-project.properties +40 -3
  170. package/src/App/components/Actions/Actions.jsx +2 -2
  171. package/src/App/components/Actions/Actions.module.scss +2 -2
  172. package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +3 -1
  173. package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +9 -0
  174. package/src/App/components/Logo/Logo.jsx +1 -1
  175. package/src/App/components/MapButton/MapButton.jsx +217 -41
  176. package/src/App/components/MapButton/MapButton.module.scss +32 -21
  177. package/src/App/components/MapButton/MapButton.test.jsx +128 -84
  178. package/src/App/components/Markers/Markers.jsx +1 -1
  179. package/src/App/components/Markers/Markers.module.scss +0 -5
  180. package/src/App/components/Panel/Panel.jsx +75 -45
  181. package/src/App/components/Panel/Panel.module.scss +18 -24
  182. package/src/App/components/Panel/Panel.test.jsx +18 -0
  183. package/src/App/components/PopupMenu/PopupMenu.jsx +246 -0
  184. package/src/App/components/PopupMenu/PopupMenu.module.scss +70 -0
  185. package/src/App/components/PopupMenu/PopupMenu.test.jsx +304 -0
  186. package/src/App/components/Tooltip/Tooltip.jsx +4 -2
  187. package/src/App/components/Tooltip/getTooltipPosition.js +1 -1
  188. package/src/App/components/Viewport/MapController.jsx +1 -1
  189. package/src/App/components/Viewport/MapStatus.jsx +1 -1
  190. package/src/App/components/Viewport/Viewport.jsx +2 -2
  191. package/src/App/components/Viewport/Viewport.module.scss +2 -2
  192. package/src/App/controls/keyboardActions.js +3 -2
  193. package/src/App/controls/keyboardShortcuts.js +4 -2
  194. package/src/App/hooks/useButtonStateEvaluator.js +58 -45
  195. package/src/App/hooks/useButtonStateEvaluator.test.js +36 -3
  196. package/src/App/hooks/useCrossHairAPI.js +49 -47
  197. package/src/App/hooks/useFocusVisible.js +2 -2
  198. package/src/App/hooks/useInterfaceAPI.js +36 -1
  199. package/src/App/hooks/useKeyboardHint.js +1 -1
  200. package/src/App/hooks/useKeyboardShortcuts.js +4 -2
  201. package/src/App/hooks/useMapAnnouncements.js +34 -32
  202. package/src/App/hooks/useMapEvents.js +12 -1
  203. package/src/App/hooks/useMapProviderOverrides.js +3 -3
  204. package/src/App/hooks/useMapStateSync.js +7 -1
  205. package/src/App/hooks/useMapURLSync.js +6 -1
  206. package/src/App/hooks/useMarkersAPI.js +88 -50
  207. package/src/App/hooks/useMediaQueryDispatch.test.js +48 -0
  208. package/src/App/hooks/useModalPanelBehaviour.js +46 -40
  209. package/src/App/hooks/useResizeObserver.js +2 -2
  210. package/src/App/initialiseApp.js +45 -38
  211. package/src/App/layout/Layout.jsx +6 -4
  212. package/src/App/layout/layout.module.scss +13 -17
  213. package/src/App/registry/keyboardShortcutRegistry.js +12 -2
  214. package/src/App/registry/keyboardShortcutRegistry.test.js +28 -0
  215. package/src/App/registry/mergeManifests.js +1 -1
  216. package/src/App/registry/panelRegistry.js +1 -1
  217. package/src/App/registry/panelRegistry.test.js +30 -1
  218. package/src/App/registry/pluginRegistry.js +14 -51
  219. package/src/App/registry/pluginRegistry.test.js +54 -6
  220. package/src/App/renderer/HtmlElementHost.jsx +186 -0
  221. package/src/App/renderer/HtmlElementHost.test.jsx +231 -0
  222. package/src/App/renderer/mapButtons.js +12 -9
  223. package/src/App/renderer/mapButtons.test.js +6 -4
  224. package/src/App/renderer/mapControls.js +6 -0
  225. package/src/App/renderer/mapControls.test.js +10 -2
  226. package/src/App/renderer/mapPanels.js +39 -26
  227. package/src/App/renderer/mapPanels.test.js +29 -0
  228. package/src/App/renderer/pluginWrapper.test.js +28 -0
  229. package/src/App/renderer/slotHelpers.js +60 -0
  230. package/src/App/renderer/slotHelpers.test.js +82 -0
  231. package/src/App/store/AppProvider.jsx +3 -0
  232. package/src/App/store/AppProvider.test.jsx +40 -0
  233. package/src/App/store/PluginProvider.jsx +7 -5
  234. package/src/App/store/appActionsMap.js +46 -1
  235. package/src/App/store/appActionsMap.test.js +21 -0
  236. package/src/App/store/appReducer.js +1 -0
  237. package/src/App/store/mapActionsMap.js +12 -24
  238. package/src/App/store/mapReducer.js +1 -1
  239. package/src/InteractiveMap/InteractiveMap.js +194 -13
  240. package/src/InteractiveMap/InteractiveMap.test.js +164 -2
  241. package/src/InteractiveMap/behaviourController.js +12 -2
  242. package/src/InteractiveMap/behaviourController.test.js +82 -1
  243. package/src/InteractiveMap/deviceChecker.js +1 -1
  244. package/src/InteractiveMap/deviceChecker.test.js +2 -2
  245. package/src/InteractiveMap/historyManager.js +41 -5
  246. package/src/InteractiveMap/historyManager.test.js +37 -6
  247. package/src/InteractiveMap/polyfills.js +39 -0
  248. package/src/InteractiveMap/polyfills.test.js +127 -0
  249. package/src/config/appConfig.js +5 -5
  250. package/src/config/appConfig.test.js +107 -42
  251. package/src/config/defaults.js +30 -20
  252. package/src/config/events.js +110 -4
  253. package/src/index.js +5 -2
  254. package/src/index.umd.js +1 -1
  255. package/src/scss/main.scss +1 -0
  256. package/src/scss/settings/_dimensions.scss +6 -2
  257. package/src/scss/tools/_border-focus.scss +20 -4
  258. package/src/services/eventBus.test.js +24 -1
  259. package/src/test-utils.js +1 -0
  260. package/src/types.js +590 -0
  261. package/src/utils/detectBreakpoint.js +78 -77
  262. package/src/utils/detectBreakpoint.test.js +50 -4
  263. package/src/utils/detectInterfaceType.js +4 -4
  264. package/src/utils/getIsFullscreen.js +3 -1
  265. package/src/utils/getSafeZoneInset.js +23 -7
  266. package/src/utils/getSafeZoneInset.test.js +18 -0
  267. package/src/utils/mapStateSync.js +3 -3
  268. package/src/utils/queryString.js +1 -1
  269. package/src/utils/stringToKebab.js +1 -1
  270. package/src/utils/toggleInertElements.js +37 -12
  271. package/webpack.dev.mjs +3 -4
  272. package/webpack.esm.mjs +8 -8
  273. package/webpack.umd.mjs +8 -8
  274. package/plugins/datasets/dist/esm/im-datasets-plugin.js +0 -1
  275. package/plugins/datasets/dist/esm/index.js +0 -2
  276. package/plugins/datasets/dist/umd/im-datasets-plugin.js +0 -1
  277. package/plugins/datasets/dist/umd/index.js +0 -2
  278. package/plugins/datasets/src/datasets.js +0 -60
  279. package/plugins/datasets/src/utils/bbox.js +0 -4
  280. package/plugins/draw-ml/dist/css/index.css +0 -1
  281. package/plugins/draw-ml/dist/esm/im-draw-ml-plugin.js +0 -1
  282. package/plugins/draw-ml/dist/esm/index.js +0 -2
  283. package/plugins/draw-ml/dist/umd/im-draw-ml-plugin.js +0 -1
  284. package/plugins/draw-ml/dist/umd/index.js +0 -2
  285. package/plugins/draw-ml/src/api/addFeature.js +0 -15
  286. package/plugins/draw-ml/src/api/newPolygon.js +0 -43
  287. package/plugins/draw-ml/src/draw.scss +0 -9
  288. package/plugins/draw-ml/src/events.js +0 -127
  289. package/plugins/draw-ml/src/manifest.js +0 -100
  290. package/plugins/draw-ml/src/modes/drawVertexMode.js +0 -275
  291. package/plugins/draw-ml/src/modes/editVertexMode.js +0 -426
  292. package/plugins/draw-ml/src/utils.js +0 -102
  293. package/plugins/frame/dist/esm/im-frame-plugin.js +0 -1
  294. package/plugins/frame/dist/esm/index.js +0 -2
  295. package/plugins/frame/dist/umd/im-frame-plugin.js +0 -1
  296. package/plugins/frame/dist/umd/index.js +0 -2
  297. package/plugins/map-styles/dist/esm/im-map-styles-plugin.js +0 -1
  298. package/plugins/map-styles/dist/esm/index.js +0 -2
  299. package/plugins/map-styles/dist/umd/im-map-styles-plugin.js +0 -1
  300. package/plugins/map-styles/dist/umd/index.js +0 -2
  301. package/plugins/scale-bar/dist/esm/im-scale-bar-plugin.js +0 -1
  302. package/plugins/scale-bar/dist/esm/index.js +0 -2
  303. package/plugins/scale-bar/dist/umd/im-scale-bar-plugin.js +0 -1
  304. package/plugins/scale-bar/dist/umd/index.js +0 -2
  305. package/plugins/search/src/utils/fetchDataset.js +0 -23
  306. package/plugins/use-location/dist/esm/im-use-location-plugin.js +0 -1
  307. package/plugins/use-location/dist/esm/index.js +0 -2
  308. package/plugins/use-location/dist/umd/im-use-location-plugin.js +0 -1
  309. package/plugins/use-location/dist/umd/index.js +0 -2
  310. package/providers/maplibre/dist/esm/im-maplibre-legacy-framework.js +0 -1
  311. package/providers/maplibre/dist/umd/im-maplibre-legacy-framework.js +0 -1
  312. package/providers/open-names/dist/esm/im-reverse-geocode.js +0 -2
  313. package/providers/open-names/dist/esm/index.js +0 -2
  314. package/providers/open-names/dist/umd/im-reverse-geocode.js +0 -2
  315. package/providers/open-names/dist/umd/index.js +0 -2
  316. /package/docs/architecture/{ARCHITECTURE_DIAGRAMS.md → architecture-diagrams.md} +0 -0
  317. /package/plugins/{datasets → beta/datasets}/dist/css/index.css +0 -0
  318. /package/plugins/{datasets/dist/esm/index.js.LICENSE.txt → beta/datasets/dist/esm/im-datasets-plugin.js.LICENSE.txt} +0 -0
  319. /package/plugins/{datasets/dist/umd → beta/datasets/dist/esm}/index.js.LICENSE.txt +0 -0
  320. /package/plugins/{draw-ml/dist/esm/index.js.LICENSE.txt → beta/datasets/dist/umd/im-datasets-plugin.js.LICENSE.txt} +0 -0
  321. /package/plugins/{draw-ml → beta/datasets}/dist/umd/index.js.LICENSE.txt +0 -0
  322. /package/plugins/{datasets → beta/datasets}/src/DatasetsInit.jsx +0 -0
  323. /package/plugins/{datasets → beta/datasets}/src/api/addDataset.js +0 -0
  324. /package/plugins/{datasets → beta/datasets}/src/api/hideDataset.js +0 -0
  325. /package/plugins/{datasets → beta/datasets}/src/api/hideFeatures.js +0 -0
  326. /package/plugins/{datasets → beta/datasets}/src/api/removeDataset.js +0 -0
  327. /package/plugins/{datasets → beta/datasets}/src/api/showDataset.js +0 -0
  328. /package/plugins/{datasets → beta/datasets}/src/api/showFeatures.js +0 -0
  329. /package/plugins/{datasets → beta/datasets}/src/datasets.scss +0 -0
  330. /package/plugins/{datasets → beta/datasets}/src/defaults.js +0 -0
  331. /package/plugins/{datasets → beta/datasets}/src/index.js +0 -0
  332. /package/plugins/{datasets → beta/datasets}/src/panels/Key.module.scss +0 -0
  333. /package/plugins/{datasets → beta/datasets}/src/panels/Layers.jsx +0 -0
  334. /package/plugins/{datasets → beta/datasets}/src/panels/Layers.module.scss +0 -0
  335. /package/plugins/{datasets → beta/datasets}/src/reducer.js +0 -0
  336. /package/plugins/{datasets → beta/datasets}/src/utils/debounce.js +0 -0
  337. /package/plugins/{datasets → beta/datasets}/src/utils/filters.js +0 -0
  338. /package/plugins/{draw-es → beta/draw-es}/src/DrawInit.jsx +0 -0
  339. /package/plugins/{draw-es → beta/draw-es}/src/api/addFeature.js +0 -0
  340. /package/plugins/{draw-es → beta/draw-es}/src/api/deleteFeature.js +0 -0
  341. /package/plugins/{draw-es → beta/draw-es}/src/api/editFeature.js +0 -0
  342. /package/plugins/{draw-es → beta/draw-es}/src/api/newPolygon.js +0 -0
  343. /package/plugins/{draw-es → beta/draw-es}/src/events.js +0 -0
  344. /package/plugins/{draw-es → beta/draw-es}/src/graphic.js +0 -0
  345. /package/plugins/{draw-es → beta/draw-es}/src/index.js +0 -0
  346. /package/plugins/{draw-es → beta/draw-es}/src/manifest.js +0 -0
  347. /package/plugins/{draw-es → beta/draw-es}/src/reducer.js +0 -0
  348. /package/plugins/{draw-es → beta/draw-es}/src/sketchViewModel.js +0 -0
  349. /package/plugins/{frame → beta/draw-ml}/dist/esm/index.js.LICENSE.txt +0 -0
  350. /package/plugins/{frame → beta/draw-ml}/dist/umd/index.js.LICENSE.txt +0 -0
  351. /package/plugins/{draw-ml → beta/draw-ml}/src/DrawInit.jsx +0 -0
  352. /package/plugins/{draw-ml → beta/draw-ml}/src/api/deleteFeature.js +0 -0
  353. /package/plugins/{draw-ml → beta/draw-ml}/src/index.js +0 -0
  354. /package/plugins/{draw-ml → beta/draw-ml}/src/modes/disabledMode.js +0 -0
  355. /package/plugins/{draw-ml/src → beta/draw-ml/src/utils}/snapHelpers.js +0 -0
  356. /package/plugins/{frame → beta/frame}/dist/css/index.css +0 -0
  357. /package/plugins/{map-styles → beta/frame}/dist/esm/index.js.LICENSE.txt +0 -0
  358. /package/plugins/{map-styles → beta/frame}/dist/umd/index.js.LICENSE.txt +0 -0
  359. /package/plugins/{frame → beta/frame}/package.json +0 -0
  360. /package/plugins/{frame → beta/frame}/src/FrameInit.jsx +0 -0
  361. /package/plugins/{frame → beta/frame}/src/api/addFrame.js +0 -0
  362. /package/plugins/{frame → beta/frame}/src/api/editFeature.js +0 -0
  363. /package/plugins/{frame → beta/frame}/src/config.js +0 -0
  364. /package/plugins/{frame → beta/frame}/src/frame.scss +0 -0
  365. /package/plugins/{frame → beta/frame}/src/index.js +0 -0
  366. /package/plugins/{frame → beta/frame}/src/manifest.js +0 -0
  367. /package/plugins/{frame → beta/frame}/src/reducer.js +0 -0
  368. /package/plugins/{frame → beta/frame}/src/utils.js +0 -0
  369. /package/plugins/{scale-bar → beta/map-styles}/dist/esm/index.js.LICENSE.txt +0 -0
  370. /package/plugins/{scale-bar → beta/map-styles}/dist/umd/index.js.LICENSE.txt +0 -0
  371. /package/plugins/{map-styles → beta/map-styles}/src/MapStylesInit.jsx +0 -0
  372. /package/plugins/{map-styles → beta/map-styles}/src/config.js +0 -0
  373. /package/plugins/{map-styles → beta/map-styles}/src/index.js +0 -0
  374. /package/plugins/{scale-bar → beta/scale-bar}/dist/css/index.css +0 -0
  375. /package/plugins/{use-location → beta/scale-bar}/dist/esm/index.js.LICENSE.txt +0 -0
  376. /package/plugins/{use-location → beta/scale-bar}/dist/umd/index.js.LICENSE.txt +0 -0
  377. /package/plugins/{scale-bar → beta/scale-bar}/package.json +0 -0
  378. /package/plugins/{scale-bar → beta/scale-bar}/src/ScaleBar.jsx +0 -0
  379. /package/plugins/{scale-bar → beta/scale-bar}/src/ScaleBar.test.jsx +0 -0
  380. /package/plugins/{scale-bar → beta/scale-bar}/src/index.js +0 -0
  381. /package/plugins/{scale-bar → beta/scale-bar}/src/index.test.js +0 -0
  382. /package/plugins/{scale-bar → beta/scale-bar}/src/manifest.js +0 -0
  383. /package/plugins/{scale-bar → beta/scale-bar}/src/scaleBar.scss +0 -0
  384. /package/plugins/{scale-bar → beta/scale-bar}/src/utils.js +0 -0
  385. /package/plugins/{scale-bar → beta/scale-bar}/src/utils.test.js +0 -0
  386. /package/{providers/open-names → plugins/beta/use-location}/dist/esm/index.js.LICENSE.txt +0 -0
  387. /package/{providers/open-names → plugins/beta/use-location}/dist/umd/index.js.LICENSE.txt +0 -0
  388. /package/plugins/{use-location → beta/use-location}/src/UseLocation.jsx +0 -0
  389. /package/plugins/{use-location → beta/use-location}/src/UseLocationInit.jsx +0 -0
  390. /package/plugins/{use-location → beta/use-location}/src/defaults.js +0 -0
  391. /package/plugins/{use-location → beta/use-location}/src/events.js +0 -0
  392. /package/plugins/{use-location → beta/use-location}/src/index.js +0 -0
  393. /package/plugins/{use-location → beta/use-location}/src/manifest.js +0 -0
  394. /package/plugins/{use-location → beta/use-location}/src/reducer.js +0 -0
  395. /package/providers/{esri → beta/esri}/src/appEvents.js +0 -0
  396. /package/providers/{esri → beta/esri}/src/defaults.js +0 -0
  397. /package/providers/{esri → beta/esri}/src/esriProvider.scss +0 -0
  398. /package/providers/{esri → beta/esri}/src/utils/coords.js +0 -0
  399. /package/providers/{esri → beta/esri}/src/utils/detectWebGL.js +0 -0
  400. /package/providers/{esri → beta/esri}/src/utils/esriFixes.js +0 -0
  401. /package/providers/{esri → beta/esri}/src/utils/query.js +0 -0
  402. /package/providers/{esri → beta/esri}/src/utils/spatial.js +0 -0
  403. /package/providers/{open-names → beta/open-names}/dist/esm/im-reverse-geocode.js.LICENSE.txt +0 -0
  404. /package/providers/{open-names/dist/umd/im-reverse-geocode.js.LICENSE.txt → beta/open-names/dist/esm/index.js.LICENSE.txt} +0 -0
  405. /package/providers/{open-names → beta/open-names}/src/index.js +0 -0
  406. /package/providers/{open-names → beta/open-names}/src/reverseGeocode.js +0 -0
  407. /package/providers/{open-names → beta/open-names}/src/utils/mapToLocationModel.js +0 -0
@@ -1,17 +1,7 @@
1
- /**
2
- * Update highlighted features using pure filters.
3
- * Supports fill + line geometry, multi-source, cleanup, and bounds.
4
- */
5
- function updateHighlightedFeatures({ LngLatBounds, map, selectedFeatures, stylesMap }) {
6
- if (!map) {
7
- return null
8
- }
9
-
1
+ const groupFeaturesBySource = (map, selectedFeatures) => {
10
2
  const featuresBySource = {}
11
- const renderedFeatures = []
12
3
 
13
- // Group features by source
14
- selectedFeatures?.forEach(({ featureId, layerId, idProperty }) => {
4
+ selectedFeatures?.forEach(({ featureId, layerId, idProperty, geometry }) => {
15
5
  const layer = map.getLayer(layerId)
16
6
 
17
7
  if (!layer) {
@@ -24,16 +14,23 @@ function updateHighlightedFeatures({ LngLatBounds, map, selectedFeatures, styles
24
14
  featuresBySource[sourceId] = {
25
15
  ids: new Set(),
26
16
  idProperty,
27
- layerId
17
+ layerId,
18
+ hasFillGeometry: false
28
19
  }
29
20
  }
21
+
22
+ // Track whether any selected feature on this source is a polygon
23
+ if (geometry && (geometry.type === 'Polygon' || geometry.type === 'MultiPolygon')) {
24
+ featuresBySource[sourceId].hasFillGeometry = true
25
+ }
26
+
30
27
  featuresBySource[sourceId].ids.add(featureId)
31
28
  })
32
29
 
33
- const currentSources = new Set(Object.keys(featuresBySource))
34
- const previousSources = map._highlightedSources || new Set()
30
+ return featuresBySource
31
+ }
35
32
 
36
- // Cleanup for sources no longer selected
33
+ const cleanupStaleSources = (map, previousSources, currentSources) => {
37
34
  previousSources.forEach(src => {
38
35
  if (!currentSources.has(src)) {
39
36
  const base = `highlight-${src}`
@@ -45,80 +42,94 @@ function updateHighlightedFeatures({ LngLatBounds, map, selectedFeatures, styles
45
42
  })
46
43
  }
47
44
  })
45
+ }
46
+
47
+ const applyHighlightLayer = (map, id, type, sourceId, srcLayer, paint, filter) => {
48
+ if (!map.getLayer(id)) {
49
+ map.addLayer({
50
+ id,
51
+ type,
52
+ source: sourceId,
53
+ ...(srcLayer && { 'source-layer': srcLayer }),
54
+ paint
55
+ })
56
+ }
57
+ Object.entries(paint).forEach(([prop, value]) => {
58
+ map.setPaintProperty(id, prop, value)
59
+ })
60
+ map.setFilter(id, filter)
61
+ }
62
+
63
+ const calculateBounds = (LngLatBounds, renderedFeatures) => {
64
+ if (!renderedFeatures.length) {
65
+ return null
66
+ }
67
+
68
+ const bounds = new LngLatBounds()
69
+
70
+ renderedFeatures.forEach(f => {
71
+ const add = (c) => typeof c[0] === 'number' ? bounds.extend(c) : c.forEach(add)
72
+ add(f.geometry.coordinates)
73
+ })
74
+
75
+ return [bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()]
76
+ }
77
+
78
+ /**
79
+ * Update highlighted features using pure filters.
80
+ * Supports fill + line geometry, multi-source, cleanup, and bounds.
81
+ */
82
+ export function updateHighlightedFeatures({ LngLatBounds, map, selectedFeatures, stylesMap }) {
83
+ if (!map) {
84
+ return null
85
+ }
86
+
87
+ const featuresBySource = groupFeaturesBySource(map, selectedFeatures)
88
+ const renderedFeatures = []
89
+
90
+ const currentSources = new Set(Object.keys(featuresBySource))
91
+ const previousSources = map._highlightedSources || new Set()
48
92
 
93
+ cleanupStaleSources(map, previousSources, currentSources)
49
94
  map._highlightedSources = currentSources
50
95
 
51
96
  // Apply highlights for current sources
52
97
  currentSources.forEach(sourceId => {
53
- const { ids, idProperty, layerId } = featuresBySource[sourceId]
98
+ const { ids, idProperty, layerId, hasFillGeometry } = featuresBySource[sourceId]
54
99
  const baseLayer = map.getLayer(layerId)
55
100
  const srcLayer = baseLayer.sourceLayer
56
- const geom = baseLayer.type
101
+
102
+ // Use the actual feature geometry to determine highlight type
103
+ const geom = hasFillGeometry ? 'fill' : baseLayer.type
57
104
  const base = `highlight-${sourceId}`
58
105
 
59
106
  const { stroke, strokeWidth, fill } = stylesMap[layerId]
60
- const filter = ['in', ['get', idProperty], ['literal', [...ids]]]
107
+ // Use ['id'] for feature.id, ['get', idProperty] for properties
108
+ const idExpression = idProperty ? ['get', idProperty] : ['id']
109
+ const filter = ['in', idExpression, ['literal', [...ids]]]
110
+
111
+ const linePaint = { 'line-color': stroke, 'line-width': strokeWidth }
61
112
 
62
- // Ensure layers
63
113
  if (geom === 'fill') {
64
- if (!map.getLayer(`${base}-fill`)) {
65
- map.addLayer({
66
- id: `${base}-fill`,
67
- type: 'fill',
68
- source: sourceId,
69
- ...(srcLayer && { 'source-layer': srcLayer }),
70
- paint: { 'fill-color': fill }
71
- })
72
- }
73
- map.setPaintProperty(`${base}-fill`, 'fill-color', fill)
74
- map.setFilter(`${base}-fill`, filter)
75
- if (!map.getLayer(`${base}-line`)) {
76
- map.addLayer({
77
- id: `${base}-line`,
78
- type: 'line',
79
- source: sourceId,
80
- ...(srcLayer && { 'source-layer': srcLayer }),
81
- paint: { 'line-color': stroke, 'line-width': strokeWidth }
82
- })
83
- }
84
- map.setPaintProperty(`${base}-line`, 'line-color', stroke)
85
- map.setPaintProperty(`${base}-line`, 'line-width', strokeWidth)
86
- map.setFilter(`${base}-line`, filter)
114
+ applyHighlightLayer(map, `${base}-fill`, 'fill', sourceId, srcLayer, { 'fill-color': fill }, filter)
115
+ applyHighlightLayer(map, `${base}-line`, 'line', sourceId, srcLayer, linePaint, filter)
87
116
  }
88
117
 
89
118
  if (geom === 'line') {
90
- if (!map.getLayer(`${base}-line`)) {
91
- map.addLayer({
92
- id: `${base}-line`,
93
- type: 'line',
94
- source: sourceId,
95
- ...(srcLayer && { 'source-layer': srcLayer }),
96
- paint: { 'line-color': stroke, 'line-width': strokeWidth }
97
- })
119
+ // Clear any fill highlight from a previous polygon selection on the same source
120
+ if (map.getLayer(`${base}-fill`)) {
121
+ map.setFilter(`${base}-fill`, ['==', 'id', ''])
98
122
  }
99
- map.setFilter(`${base}-line`, filter)
123
+ applyHighlightLayer(map, `${base}-line`, 'line', sourceId, srcLayer, linePaint, filter)
100
124
  }
101
125
 
102
126
  // Bounds only from rendered tiles
103
127
  renderedFeatures.push(
104
128
  ...map
105
129
  .queryRenderedFeatures({ layers: [layerId] })
106
- .filter(f => ids.has(f.properties?.[idProperty]))
130
+ .filter(f => ids.has(idProperty ? f.properties?.[idProperty] : f.id))
107
131
  )
108
132
  })
109
133
 
110
- // Calculate bounds
111
- if (!renderedFeatures.length) {
112
- return null
113
- }
114
-
115
- let bounds = new LngLatBounds()
116
- renderedFeatures.forEach(f => {
117
- const add = (c) => typeof c[0] === 'number' ? bounds.extend(c) : c.forEach(add)
118
- add(f.geometry.coordinates)
119
- })
120
-
121
- return [bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()]
134
+ return calculateBounds(LngLatBounds, renderedFeatures)
122
135
  }
123
-
124
- export { updateHighlightedFeatures }
@@ -2,8 +2,9 @@ import { spatialNavigate } from './spatial.js'
2
2
  import { calculateLinearTextSize } from './calculateLinearTextSize.js'
3
3
 
4
4
  const HIGHLIGHT_SCALE_FACTOR = 1.5
5
+ const HIGHLIGHT_LABEL_SOURCE = 'highlighted-label'
5
6
 
6
- function getGeometryCenter(geometry) {
7
+ export function getGeometryCenter(geometry) {
7
8
  const { type, coordinates } = geometry
8
9
  if (type === 'Point') {
9
10
  return coordinates
@@ -23,13 +24,12 @@ function getGeometryCenter(geometry) {
23
24
  return null
24
25
  }
25
26
 
26
- function evalInterpolate(expr, zoom) {
27
+ export function evalInterpolate(expr, zoom) {
27
28
  if (typeof expr === 'number') {
28
29
  return expr
29
30
  }
30
31
  if (!Array.isArray(expr) || expr[0] !== 'interpolate') {
31
32
  return calculateLinearTextSize(expr, zoom)
32
- // throw new Error('Only interpolate expressions supported')
33
33
  }
34
34
  const [, , input, ...stops] = expr
35
35
  if (input[0] !== 'zoom') {
@@ -39,7 +39,7 @@ function evalInterpolate(expr, zoom) {
39
39
  const z0 = stops[i]
40
40
  const v0 = stops[i + 1]
41
41
  const z1 = stops[i + 2]
42
- const v1 = stops[i + 3]
42
+ const v1 = stops[i + 3] // NOSONAR: array index offset for interpolation pairs
43
43
  if (zoom <= z0) {
44
44
  return v0
45
45
  }
@@ -50,166 +50,220 @@ function evalInterpolate(expr, zoom) {
50
50
  return stops[stops.length - 1]
51
51
  }
52
52
 
53
- export function createMapLabelNavigator(map, mapColorScheme, events, eventBus) {
54
- let isDarkStyle = mapColorScheme === 'dark'
55
- let labels = []
56
- let currentPixel = null
57
- let highlightLayerId = null
58
- let highlightedExpr = null
59
- let highlightedFeature = null
60
-
61
- const colors = {
62
- get current() {
63
- if (isDarkStyle) {
64
- return { text: '#ffffff', halo: '#000000' }
65
- }
66
- return { text: '#000000', halo: '#ffffff' }
67
- }
53
+ export function getHighlightColors(isDarkStyle) {
54
+ if (isDarkStyle) {
55
+ return { text: '#ffffff', halo: '#000000' }
68
56
  }
57
+ return { text: '#000000', halo: '#ffffff' }
58
+ }
69
59
 
70
- const initLabelSource = () => {
71
- if (!map.getSource('highlighted-label')) {
72
- map.addSource('highlighted-label', { type: 'geojson', data: { type: 'FeatureCollection', features: [] } })
73
- }
60
+ export function extractTextPropertyName(textField) {
61
+ if (typeof textField === 'string') {
62
+ return /^{(.+)}$/.exec(textField)?.[1]
74
63
  }
64
+ if (Array.isArray(textField)) {
65
+ return textField.find(e => Array.isArray(e) && e[0] === 'get')?.[1]
66
+ }
67
+ return null
68
+ }
75
69
 
76
- map.getStyle().layers.filter(l => l.layout?.['symbol-placement'] === 'line').forEach(l => {
77
- map.setLayoutProperty(l.id, 'symbol-placement', 'line-center')
78
- })
79
- initLabelSource()
70
+ export function buildLabelFromFeature(feature, layer, propName, map) {
71
+ const center = getGeometryCenter(feature.geometry)
72
+ if (!center) {
73
+ return null
74
+ }
75
+ const projected = map.project({ lng: center[0], lat: center[1] })
76
+ return { text: feature.properties[propName], x: projected.x, y: projected.y, feature, layer }
77
+ }
80
78
 
81
- eventBus?.on(events.MAP_SET_STYLE, style => {
82
- map.once('styledata', () => map.once('idle', () => {
83
- map.getStyle().layers.filter(l => l.layout?.['symbol-placement'] === 'line').forEach(l => {
84
- map.setLayoutProperty(l.id, 'symbol-placement', 'line-center')
85
- })
86
- initLabelSource()
87
- isDarkStyle = style?.mapColorScheme === 'dark'
88
- }))
79
+ export function buildLabelsFromLayers(map, symbolLayers, features) {
80
+ return symbolLayers.flatMap(layer => {
81
+ const textField = layer.layout?.['text-field']
82
+ const propName = extractTextPropertyName(textField)
83
+ if (!propName) {
84
+ return []
85
+ }
86
+ return features
87
+ .filter(f => f.layer.id === layer.id && f.properties?.[propName])
88
+ .map(f => buildLabelFromFeature(f, layer, propName, map))
89
+ .filter(Boolean)
89
90
  })
91
+ }
90
92
 
91
- function refreshLabels() {
92
- const symbolLayers = map.getStyle().layers.filter(l => l.type === 'symbol')
93
- const features = map.queryRenderedFeatures({ layers: symbolLayers.map(l => l.id) })
94
- labels = symbolLayers.flatMap(layer => {
95
- const textField = layer.layout?.['text-field']
96
- const propName = typeof textField === 'string'
97
- ? textField.match(/^{(.+)}$/)?.[1]
98
- : Array.isArray(textField)
99
- ? textField.find(e => Array.isArray(e) && e[0] === 'get')?.[1]
100
- : null
101
- if (!propName) {
102
- return []
103
- }
104
- return features.filter(f => f.layer.id === layer.id && f.properties?.[propName]).map(f => {
105
- const center = getGeometryCenter(f.geometry)
106
- if (!center) {
107
- return null
108
- }
109
- const projected = map.project({ lng: center[0], lat: center[1] })
110
- return { text: f.properties[propName], x: projected.x, y: projected.y, feature: f, layer }
111
- }).filter(Boolean)
112
- })
113
- }
93
+ export function findClosestLabel(labels, centerPoint) {
94
+ return labels.reduce((best, label) => {
95
+ const dist = (label.x - centerPoint.x) ** 2 + (label.y - centerPoint.y) ** 2
96
+ if (!best || dist < best.dist) {
97
+ return { label, dist }
98
+ }
99
+ return best
100
+ }, null)?.label
101
+ }
114
102
 
115
- function removeHighlight() {
116
- if (highlightLayerId && map.getLayer(highlightLayerId)) {
117
- try {
118
- map.removeLayer(highlightLayerId)
119
- }
120
- catch {}
121
- highlightLayerId = null
122
- highlightedExpr = null
123
- highlightedFeature = null
103
+ export function createHighlightLayerConfig(sourceLayer, highlightSize, colors) {
104
+ return {
105
+ id: `highlight-${sourceLayer.id}`,
106
+ type: sourceLayer.type,
107
+ source: HIGHLIGHT_LABEL_SOURCE,
108
+ layout: {
109
+ ...sourceLayer.layout,
110
+ 'text-size': highlightSize,
111
+ 'text-allow-overlap': true,
112
+ 'text-ignore-placement': true,
113
+ 'text-max-angle': 90
114
+ },
115
+ paint: {
116
+ ...sourceLayer.paint,
117
+ 'text-color': colors.text,
118
+ 'text-halo-color': colors.halo,
119
+ 'text-halo-width': 3,
120
+ 'text-halo-blur': 1,
121
+ 'text-opacity': 1
124
122
  }
125
123
  }
124
+ }
126
125
 
127
- function highlight(labelData) {
128
- if (!labelData?.feature?.layer) {
129
- return
126
+ export function removeHighlightLayer(map, state) {
127
+ if (state.highlightLayerId && map.getLayer(state.highlightLayerId)) {
128
+ try {
129
+ map.removeLayer(state.highlightLayerId)
130
130
  }
131
- removeHighlight()
132
- const { feature, layer } = labelData
133
- highlightLayerId = `highlight-${layer.id}`
134
-
135
- const { id, type, properties, geometry } = feature
136
- highlightedFeature = { id, type, properties, geometry }
137
- map.getSource('highlighted-label').setData(highlightedFeature)
138
- highlightedExpr = layer.layout['text-size']
139
- const zoom = map.getZoom()
140
- const baseSize = evalInterpolate(highlightedExpr, zoom)
141
- const highlightSize = baseSize * HIGHLIGHT_SCALE_FACTOR
142
- map.addLayer({
143
- id: highlightLayerId,
144
- type: layer.type,
145
- source: 'highlighted-label',
146
- layout: { ...layer.layout, 'text-size': highlightSize, 'text-allow-overlap': true, 'text-ignore-placement': true, 'text-max-angle': 90 },
147
- paint: { ...layer.paint, 'text-color': colors.current.text, 'text-halo-color': colors.current.halo, 'text-halo-width': 3, 'text-halo-blur': 1, 'text-opacity': 1 }
131
+ catch {}
132
+ state.highlightLayerId = null
133
+ state.highlightedExpr = null
134
+ }
135
+ }
136
+
137
+ export function applyHighlight(map, labelData, state) {
138
+ if (!labelData?.feature?.layer) {
139
+ return
140
+ }
141
+ removeHighlightLayer(map, state)
142
+ const { feature, layer } = labelData
143
+ state.highlightLayerId = `highlight-${layer.id}`
144
+
145
+ const { id, type, properties, geometry } = feature
146
+ map.getSource(HIGHLIGHT_LABEL_SOURCE).setData({ id, type, properties, geometry })
147
+ state.highlightedExpr = layer.layout['text-size']
148
+
149
+ const zoom = map.getZoom()
150
+ const baseSize = evalInterpolate(state.highlightedExpr, zoom)
151
+ const highlightSize = baseSize * HIGHLIGHT_SCALE_FACTOR
152
+ const colors = getHighlightColors(state.isDarkStyle)
153
+ const layerConfig = createHighlightLayerConfig(layer, highlightSize, colors)
154
+
155
+ map.addLayer(layerConfig)
156
+ map.moveLayer(state.highlightLayerId)
157
+ }
158
+
159
+ export function navigateToNextLabel(direction, state) {
160
+ if (!state.currentPixel) {
161
+ return null
162
+ }
163
+ const filtered = state.labels
164
+ .map((l, i) => ({ pixel: [l.x, l.y], index: i }))
165
+ .filter(l => l.pixel[0] !== state.currentPixel.x || l.pixel[1] !== state.currentPixel.y)
166
+ if (!filtered.length) {
167
+ return null
168
+ }
169
+ const pixelArray = filtered.map(l => l.pixel)
170
+ let nextFilteredIndex = spatialNavigate(direction, [state.currentPixel.x, state.currentPixel.y], pixelArray)
171
+ if (nextFilteredIndex == null || nextFilteredIndex < 0 || nextFilteredIndex >= filtered.length) {
172
+ nextFilteredIndex = 0
173
+ }
174
+ return state.labels[filtered[nextFilteredIndex].index]
175
+ }
176
+
177
+ function initLabelSource(map) {
178
+ if (!map.getSource(HIGHLIGHT_LABEL_SOURCE)) {
179
+ map.addSource(HIGHLIGHT_LABEL_SOURCE, { type: 'geojson', data: { type: 'FeatureCollection', features: [] } })
180
+ }
181
+ }
182
+
183
+ function setLineCenterPlacement(map) {
184
+ map.getStyle().layers
185
+ .filter(l => l.layout?.['symbol-placement'] === 'line')
186
+ .forEach(l => map.setLayoutProperty(l.id, 'symbol-placement', 'line-center'))
187
+ }
188
+
189
+ function setSymbolTextOpacity(map) {
190
+ map.getStyle().layers
191
+ .filter(l => l.type === 'symbol')
192
+ .forEach(layer => {
193
+ map.setPaintProperty(layer.id, 'text-opacity', ['case', ['boolean', ['feature-state', 'highlighted'], false], 0, 1])
148
194
  })
149
- map.moveLayer(highlightLayerId)
195
+ }
196
+
197
+ export function createMapLabelNavigator(map, mapColorScheme, events, eventBus) {
198
+ const state = {
199
+ isDarkStyle: mapColorScheme === 'dark',
200
+ labels: [],
201
+ currentPixel: null,
202
+ highlightLayerId: null,
203
+ highlightedExpr: null
150
204
  }
151
205
 
206
+ setLineCenterPlacement(map)
207
+ initLabelSource(map)
208
+
209
+ eventBus?.on(events.MAP_SET_STYLE, style => {
210
+ map.once('styledata', () => map.once('idle', () => {
211
+ setLineCenterPlacement(map)
212
+ initLabelSource(map)
213
+ state.isDarkStyle = style?.mapColorScheme === 'dark'
214
+ }))
215
+ })
216
+
152
217
  map.on('zoom', () => {
153
- if (highlightLayerId && highlightedExpr) {
154
- const zoom = map.getZoom()
155
- const baseSize = evalInterpolate(highlightedExpr, zoom)
156
- map.setLayoutProperty(highlightLayerId, 'text-size', baseSize * HIGHLIGHT_SCALE_FACTOR)
218
+ if (state.highlightLayerId && state.highlightedExpr) {
219
+ const baseSize = evalInterpolate(state.highlightedExpr, map.getZoom())
220
+ map.setLayoutProperty(state.highlightLayerId, 'text-size', baseSize * HIGHLIGHT_SCALE_FACTOR)
157
221
  }
158
222
  })
159
223
 
224
+ function refreshLabels() {
225
+ const symbolLayers = map.getStyle().layers.filter(l => l.type === 'symbol')
226
+ const features = map.queryRenderedFeatures({ layers: symbolLayers.map(l => l.id) })
227
+ state.labels = buildLabelsFromLayers(map, symbolLayers, features)
228
+ }
229
+
160
230
  function highlightCenter() {
161
231
  refreshLabels()
162
- if (!labels.length) {
232
+ if (!state.labels.length) {
163
233
  return null
164
234
  }
165
235
  const centerPoint = map.project(map.getCenter())
166
- const closest = labels.reduce((best, label) => {
167
- const dist = (label.x - centerPoint.x) ** 2 + (label.y - centerPoint.y) ** 2
168
- if (!best || dist < best.dist) {
169
- return { label, dist }
170
- }
171
- return best
172
- }, null)?.label
236
+ const closest = findClosestLabel(state.labels, centerPoint)
173
237
  if (closest) {
174
- currentPixel = { x: closest.x, y: closest.y }
238
+ state.currentPixel = { x: closest.x, y: closest.y }
175
239
  }
176
- highlight(closest)
240
+ applyHighlight(map, closest, state)
177
241
  return `${closest.text} (${closest.layer.id})`
178
242
  }
179
243
 
180
244
  function highlightNext(direction) {
181
245
  refreshLabels()
182
- if (!labels.length) {
246
+ if (!state.labels.length) {
183
247
  return null
184
248
  }
185
- if (!currentPixel) {
249
+ if (!state.currentPixel) {
186
250
  return highlightCenter()
187
251
  }
188
- const filtered = labels
189
- .map((l, i) => ({ pixel: [l.x, l.y], index: i }))
190
- .filter(l => l.pixel[0] !== currentPixel.x || l.pixel[1] !== currentPixel.y)
191
- if (!filtered.length) {
252
+ const labelData = navigateToNextLabel(direction, state)
253
+ if (!labelData) {
192
254
  return null
193
255
  }
194
- const pixelArray = filtered.map(l => l.pixel)
195
- let nextFilteredIndex = spatialNavigate(direction, [currentPixel.x, currentPixel.y], pixelArray)
196
- if (nextFilteredIndex == null || nextFilteredIndex < 0 || nextFilteredIndex >= filtered.length) {
197
- nextFilteredIndex = 0
198
- }
199
- const labelData = labels[filtered[nextFilteredIndex].index]
200
- currentPixel = { x: labelData.x, y: labelData.y }
201
- highlight(labelData)
256
+ state.currentPixel = { x: labelData.x, y: labelData.y }
257
+ applyHighlight(map, labelData, state)
202
258
  return `${labelData.text} (${labelData.layer.id})`
203
259
  }
204
260
 
205
- map.getStyle().layers.filter(l => l.type === 'symbol').forEach(layer => {
206
- map.setPaintProperty(layer.id, 'text-opacity', ['case', ['boolean', ['feature-state', 'highlighted'], false], 0, 1])
207
- })
261
+ setSymbolTextOpacity(map)
208
262
 
209
263
  return {
210
264
  refreshLabels,
211
265
  highlightNextLabel: highlightNext,
212
266
  highlightLabelAtCenter: highlightCenter,
213
- clearHighlightedLabel: removeHighlight
267
+ clearHighlightedLabel: () => removeHighlightLayer(map, state)
214
268
  }
215
269
  }
@@ -13,7 +13,7 @@ export function applyPreventDefaultFix (map) {
13
13
  const originalPreventDefault = Event.prototype.preventDefault
14
14
 
15
15
  // Override preventDefault only for events targeting our map
16
- Event.prototype.preventDefault = function () {
16
+ Event.prototype.preventDefault = function () { // NOSONAR: intentional monkey-patch to fix MapLibre touch event bug
17
17
  if ((this.type === 'touchmove' || this.type === 'touchstart') && !this.cancelable) {
18
18
  // Check if the event target is within our map container
19
19
  const canvas = map.getCanvas()
@@ -21,6 +21,6 @@ export function applyPreventDefaultFix (map) {
21
21
  return
22
22
  }
23
23
  }
24
- return originalPreventDefault.call(this)
24
+ originalPreventDefault.call(this)
25
25
  }
26
26
  }