@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.
- package/README.md +16 -57
- package/dist/css/index.css +1 -1
- package/dist/esm/im-core.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/umd/im-core.js +1 -1
- package/dist/umd/index.js +1 -1
- package/docs/api/button-definition.md +176 -0
- package/docs/api/context.md +124 -0
- package/docs/api/control-definition.md +72 -0
- package/docs/api/icon-definition.md +28 -0
- package/docs/api/map-style-config.md +76 -0
- package/docs/api/marker-config.md +51 -0
- package/docs/api/panel-definition.md +117 -0
- package/docs/api/slots.md +25 -0
- package/docs/api.md +721 -0
- package/docs/architecture.md +139 -0
- package/docs/building-a-plugin.md +149 -0
- package/docs/getting-started.md +46 -0
- package/docs/govuk-prototype.md +0 -0
- package/docs/plugins/interact.md +205 -0
- package/docs/plugins/map-styles.md +84 -0
- package/docs/plugins/plugin-context.md +67 -0
- package/docs/plugins/plugin-descriptor.md +71 -0
- package/docs/plugins/plugin-manifest.md +118 -0
- package/docs/plugins/scale-bar.md +62 -0
- package/docs/plugins/search.md +162 -0
- package/docs/plugins.md +116 -0
- package/govuk-prototype-kit.config.json +7 -7
- package/package.json +16 -2
- package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +2 -0
- package/plugins/beta/datasets/dist/esm/index.js +2 -0
- package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +2 -0
- package/plugins/beta/datasets/dist/umd/index.js +2 -0
- package/plugins/beta/datasets/src/datasets.js +113 -0
- package/plugins/beta/datasets/src/fetch/createDynamicSource.js +206 -0
- package/plugins/beta/datasets/src/fetch/fetchGeoJSON.js +38 -0
- package/plugins/{datasets → beta/datasets}/src/handleSetMapStyle.js +8 -1
- package/plugins/{datasets → beta/datasets}/src/manifest.js +1 -1
- package/plugins/{datasets → beta/datasets}/src/mapLayers.js +42 -4
- package/plugins/{datasets → beta/datasets}/src/panels/Key.jsx +1 -1
- package/plugins/beta/datasets/src/utils/bbox.js +115 -0
- package/plugins/beta/draw-ml/dist/css/index.css +1 -0
- package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -0
- package/plugins/beta/draw-ml/dist/esm/index.js +2 -0
- package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -0
- package/plugins/beta/draw-ml/dist/umd/index.js +2 -0
- package/plugins/beta/draw-ml/src/api/addFeature.js +27 -0
- package/plugins/{draw-ml → beta/draw-ml}/src/api/editFeature.js +17 -6
- package/plugins/beta/draw-ml/src/api/merge.js +9 -0
- package/plugins/beta/draw-ml/src/api/newLine.js +68 -0
- package/plugins/beta/draw-ml/src/api/newPolygon.js +68 -0
- package/plugins/beta/draw-ml/src/api/split.js +81 -0
- package/plugins/{draw-ml → beta/draw-ml}/src/defaults.js +3 -0
- package/plugins/beta/draw-ml/src/draw.scss +32 -0
- package/plugins/beta/draw-ml/src/events.js +171 -0
- package/plugins/beta/draw-ml/src/manifest.js +132 -0
- package/plugins/{draw-ml → beta/draw-ml}/src/mapboxDraw.js +51 -2
- package/plugins/{draw-ml → beta/draw-ml}/src/mapboxSnap.js +18 -12
- package/plugins/beta/draw-ml/src/modes/createDrawMode.js +497 -0
- package/plugins/beta/draw-ml/src/modes/drawLineMode.js +18 -0
- package/plugins/beta/draw-ml/src/modes/drawPolygonMode.js +16 -0
- package/plugins/beta/draw-ml/src/modes/editVertex/geometryHelpers.js +135 -0
- package/plugins/beta/draw-ml/src/modes/editVertex/helpers.js +2 -0
- package/plugins/beta/draw-ml/src/modes/editVertex/touchHandlers.js +134 -0
- package/plugins/beta/draw-ml/src/modes/editVertex/undoHandlers.js +133 -0
- package/plugins/beta/draw-ml/src/modes/editVertex/vertexOperations.js +141 -0
- package/plugins/beta/draw-ml/src/modes/editVertex/vertexQueries.js +121 -0
- package/plugins/beta/draw-ml/src/modes/editVertexMode.js +437 -0
- package/plugins/{draw-ml → beta/draw-ml}/src/reducer.js +30 -2
- package/plugins/{draw-ml → beta/draw-ml}/src/styles.js +66 -36
- package/plugins/beta/draw-ml/src/undoStack.js +50 -0
- package/plugins/beta/draw-ml/src/utils/debounce.js +16 -0
- package/plugins/beta/draw-ml/src/utils/flattenStyleProperties.js +49 -0
- package/plugins/beta/draw-ml/src/utils/spatial.js +258 -0
- package/plugins/beta/frame/dist/esm/im-frame-plugin.js +1 -0
- package/plugins/beta/frame/dist/esm/index.js +2 -0
- package/plugins/beta/frame/dist/umd/im-frame-plugin.js +1 -0
- package/plugins/beta/frame/dist/umd/index.js +2 -0
- package/plugins/{frame → beta/frame}/src/Frame.jsx +1 -1
- package/plugins/{map-styles → beta/map-styles}/dist/css/index.css +1 -1
- package/plugins/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -0
- package/plugins/beta/map-styles/dist/esm/index.js +2 -0
- package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -0
- package/plugins/beta/map-styles/dist/umd/index.js +2 -0
- package/plugins/{map-styles → beta/map-styles}/src/MapStyles.jsx +1 -1
- package/plugins/{map-styles → beta/map-styles}/src/manifest.js +8 -10
- package/plugins/{map-styles → beta/map-styles}/src/mapStyles.scss +4 -1
- package/plugins/beta/scale-bar/dist/esm/im-scale-bar-plugin.js +1 -0
- package/plugins/beta/scale-bar/dist/esm/index.js +2 -0
- package/plugins/beta/scale-bar/dist/umd/im-scale-bar-plugin.js +1 -0
- package/plugins/beta/scale-bar/dist/umd/index.js +2 -0
- package/plugins/beta/use-location/dist/esm/im-use-location-plugin.js +1 -0
- package/plugins/beta/use-location/dist/esm/index.js +2 -0
- package/plugins/beta/use-location/dist/umd/im-use-location-plugin.js +1 -0
- package/plugins/beta/use-location/dist/umd/index.js +2 -0
- package/plugins/interact/dist/css/index.css +1 -1
- package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
- package/plugins/interact/dist/esm/index.js +1 -1
- package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
- package/plugins/interact/dist/umd/index.js +1 -1
- package/plugins/interact/src/InteractInit.jsx +1 -1
- package/plugins/interact/src/InteractInit.test.js +97 -0
- package/plugins/interact/src/api/clear.test.js +18 -0
- package/plugins/interact/src/api/disable.test.js +12 -0
- package/plugins/interact/src/api/enable.test.js +48 -0
- package/plugins/interact/src/api/selectFeature.test.js +33 -0
- package/plugins/interact/src/api/unselectFeature.test.js +33 -0
- package/plugins/interact/src/defaults.js +2 -0
- package/plugins/interact/src/events.js +37 -52
- package/plugins/interact/src/events.test.js +186 -0
- package/plugins/interact/src/hooks/useHighlightSync.js +1 -1
- package/plugins/interact/src/hooks/useHighlightSync.test.js +212 -0
- package/plugins/interact/src/hooks/useInteractionHandlers.js +80 -41
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +316 -0
- package/plugins/interact/src/index.test.js +22 -0
- package/plugins/interact/src/interact.scss +6 -0
- package/plugins/interact/src/manifest.js +1 -1
- package/plugins/interact/src/manifest.test.js +94 -0
- package/plugins/interact/src/reducer.js +28 -21
- package/plugins/interact/src/reducer.test.js +155 -0
- package/plugins/interact/src/utils/buildStylesMap.test.js +57 -0
- package/plugins/interact/src/utils/featureQueries.js +2 -2
- package/plugins/interact/src/utils/featureQueries.test.js +56 -0
- package/plugins/interact/src/utils/spatial.js +95 -0
- package/plugins/interact/src/utils/spatial.test.js +93 -0
- package/plugins/search/dist/css/index.css +1 -1
- package/plugins/search/dist/esm/im-search-plugin.js +1 -1
- package/plugins/search/dist/esm/index.js +1 -1
- package/plugins/search/dist/umd/im-search-plugin.js +1 -1
- package/plugins/search/dist/umd/index.js +1 -1
- package/plugins/search/src/Search.jsx +2 -2
- package/plugins/search/src/components/Form/Form.jsx +1 -1
- package/plugins/search/src/components/Suggestions/Suggestions.jsx +5 -5
- package/plugins/search/src/datasets.js +1 -1
- package/plugins/search/src/defaults.js +3 -0
- package/plugins/search/src/events/fetchSuggestions.js +48 -53
- package/plugins/search/src/events/formHandlers.js +3 -2
- package/plugins/search/src/events/index.js +1 -1
- package/plugins/search/src/events/inputHandlers.js +3 -1
- package/plugins/search/src/events/suggestionHandlers.js +12 -3
- package/plugins/search/src/search.scss +4 -1
- package/plugins/search/src/utils/parseOsNamesResults.js +15 -19
- package/providers/{esri → beta/esri}/src/esriProvider.js +1 -1
- package/providers/{esri → beta/esri}/src/index.js +1 -1
- package/providers/{esri → beta/esri}/src/mapEvents.js +2 -2
- package/providers/beta/open-names/dist/esm/im-reverse-geocode.js +2 -0
- package/providers/beta/open-names/dist/esm/index.js +2 -0
- package/providers/beta/open-names/dist/umd/im-reverse-geocode.js +2 -0
- package/providers/beta/open-names/dist/umd/im-reverse-geocode.js.LICENSE.txt +1 -0
- package/providers/beta/open-names/dist/umd/index.js +2 -0
- package/providers/beta/open-names/dist/umd/index.js.LICENSE.txt +1 -0
- package/providers/maplibre/dist/esm/im-maplibre-framework.js +1 -1
- package/providers/maplibre/dist/esm/im-maplibre-framework.js.LICENSE.txt +1 -1
- package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/esm/index.js +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-framework.js +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-framework.js.LICENSE.txt +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/umd/index.js +1 -1
- package/providers/maplibre/src/defaults.js +3 -2
- package/providers/maplibre/src/index.js +36 -20
- package/providers/maplibre/src/maplibreProvider.js +140 -11
- package/providers/maplibre/src/utils/detectWebgl.js +1 -2
- package/providers/maplibre/src/utils/highlightFeatures.js +78 -67
- package/providers/maplibre/src/utils/labels.js +174 -120
- package/providers/maplibre/src/utils/maplibreFixes.js +2 -2
- package/providers/maplibre/src/utils/queryFeatures.js +146 -0
- package/providers/maplibre/src/utils/spatial.js +14 -17
- package/sonar-project.properties +40 -3
- package/src/App/components/Actions/Actions.jsx +2 -2
- package/src/App/components/Actions/Actions.module.scss +2 -2
- package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +3 -1
- package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +9 -0
- package/src/App/components/Logo/Logo.jsx +1 -1
- package/src/App/components/MapButton/MapButton.jsx +217 -41
- package/src/App/components/MapButton/MapButton.module.scss +32 -21
- package/src/App/components/MapButton/MapButton.test.jsx +128 -84
- package/src/App/components/Markers/Markers.jsx +1 -1
- package/src/App/components/Markers/Markers.module.scss +0 -5
- package/src/App/components/Panel/Panel.jsx +75 -45
- package/src/App/components/Panel/Panel.module.scss +18 -24
- package/src/App/components/Panel/Panel.test.jsx +18 -0
- package/src/App/components/PopupMenu/PopupMenu.jsx +246 -0
- package/src/App/components/PopupMenu/PopupMenu.module.scss +70 -0
- package/src/App/components/PopupMenu/PopupMenu.test.jsx +304 -0
- package/src/App/components/Tooltip/Tooltip.jsx +4 -2
- package/src/App/components/Tooltip/getTooltipPosition.js +1 -1
- package/src/App/components/Viewport/MapController.jsx +1 -1
- package/src/App/components/Viewport/MapStatus.jsx +1 -1
- package/src/App/components/Viewport/Viewport.jsx +2 -2
- package/src/App/components/Viewport/Viewport.module.scss +2 -2
- package/src/App/controls/keyboardActions.js +3 -2
- package/src/App/controls/keyboardShortcuts.js +4 -2
- package/src/App/hooks/useButtonStateEvaluator.js +58 -45
- package/src/App/hooks/useButtonStateEvaluator.test.js +36 -3
- package/src/App/hooks/useCrossHairAPI.js +49 -47
- package/src/App/hooks/useFocusVisible.js +2 -2
- package/src/App/hooks/useInterfaceAPI.js +36 -1
- package/src/App/hooks/useKeyboardHint.js +1 -1
- package/src/App/hooks/useKeyboardShortcuts.js +4 -2
- package/src/App/hooks/useMapAnnouncements.js +34 -32
- package/src/App/hooks/useMapEvents.js +12 -1
- package/src/App/hooks/useMapProviderOverrides.js +3 -3
- package/src/App/hooks/useMapStateSync.js +7 -1
- package/src/App/hooks/useMapURLSync.js +6 -1
- package/src/App/hooks/useMarkersAPI.js +88 -50
- package/src/App/hooks/useMediaQueryDispatch.test.js +48 -0
- package/src/App/hooks/useModalPanelBehaviour.js +46 -40
- package/src/App/hooks/useResizeObserver.js +2 -2
- package/src/App/initialiseApp.js +45 -38
- package/src/App/layout/Layout.jsx +6 -4
- package/src/App/layout/layout.module.scss +13 -17
- package/src/App/registry/keyboardShortcutRegistry.js +12 -2
- package/src/App/registry/keyboardShortcutRegistry.test.js +28 -0
- package/src/App/registry/mergeManifests.js +1 -1
- package/src/App/registry/panelRegistry.js +1 -1
- package/src/App/registry/panelRegistry.test.js +30 -1
- package/src/App/registry/pluginRegistry.js +14 -51
- package/src/App/registry/pluginRegistry.test.js +54 -6
- package/src/App/renderer/HtmlElementHost.jsx +186 -0
- package/src/App/renderer/HtmlElementHost.test.jsx +231 -0
- package/src/App/renderer/mapButtons.js +12 -9
- package/src/App/renderer/mapButtons.test.js +6 -4
- package/src/App/renderer/mapControls.js +6 -0
- package/src/App/renderer/mapControls.test.js +10 -2
- package/src/App/renderer/mapPanels.js +39 -26
- package/src/App/renderer/mapPanels.test.js +29 -0
- package/src/App/renderer/pluginWrapper.test.js +28 -0
- package/src/App/renderer/slotHelpers.js +60 -0
- package/src/App/renderer/slotHelpers.test.js +82 -0
- package/src/App/store/AppProvider.jsx +3 -0
- package/src/App/store/AppProvider.test.jsx +40 -0
- package/src/App/store/PluginProvider.jsx +7 -5
- package/src/App/store/appActionsMap.js +46 -1
- package/src/App/store/appActionsMap.test.js +21 -0
- package/src/App/store/appReducer.js +1 -0
- package/src/App/store/mapActionsMap.js +12 -24
- package/src/App/store/mapReducer.js +1 -1
- package/src/InteractiveMap/InteractiveMap.js +194 -13
- package/src/InteractiveMap/InteractiveMap.test.js +164 -2
- package/src/InteractiveMap/behaviourController.js +12 -2
- package/src/InteractiveMap/behaviourController.test.js +82 -1
- package/src/InteractiveMap/deviceChecker.js +1 -1
- package/src/InteractiveMap/deviceChecker.test.js +2 -2
- package/src/InteractiveMap/historyManager.js +41 -5
- package/src/InteractiveMap/historyManager.test.js +37 -6
- package/src/InteractiveMap/polyfills.js +39 -0
- package/src/InteractiveMap/polyfills.test.js +127 -0
- package/src/config/appConfig.js +5 -5
- package/src/config/appConfig.test.js +107 -42
- package/src/config/defaults.js +30 -20
- package/src/config/events.js +110 -4
- package/src/index.js +5 -2
- package/src/index.umd.js +1 -1
- package/src/scss/main.scss +1 -0
- package/src/scss/settings/_dimensions.scss +6 -2
- package/src/scss/tools/_border-focus.scss +20 -4
- package/src/services/eventBus.test.js +24 -1
- package/src/test-utils.js +1 -0
- package/src/types.js +590 -0
- package/src/utils/detectBreakpoint.js +78 -77
- package/src/utils/detectBreakpoint.test.js +50 -4
- package/src/utils/detectInterfaceType.js +4 -4
- package/src/utils/getIsFullscreen.js +3 -1
- package/src/utils/getSafeZoneInset.js +23 -7
- package/src/utils/getSafeZoneInset.test.js +18 -0
- package/src/utils/mapStateSync.js +3 -3
- package/src/utils/queryString.js +1 -1
- package/src/utils/stringToKebab.js +1 -1
- package/src/utils/toggleInertElements.js +37 -12
- package/webpack.dev.mjs +3 -4
- package/webpack.esm.mjs +8 -8
- package/webpack.umd.mjs +8 -8
- package/plugins/datasets/dist/esm/im-datasets-plugin.js +0 -1
- package/plugins/datasets/dist/esm/index.js +0 -2
- package/plugins/datasets/dist/umd/im-datasets-plugin.js +0 -1
- package/plugins/datasets/dist/umd/index.js +0 -2
- package/plugins/datasets/src/datasets.js +0 -60
- package/plugins/datasets/src/utils/bbox.js +0 -4
- package/plugins/draw-ml/dist/css/index.css +0 -1
- package/plugins/draw-ml/dist/esm/im-draw-ml-plugin.js +0 -1
- package/plugins/draw-ml/dist/esm/index.js +0 -2
- package/plugins/draw-ml/dist/umd/im-draw-ml-plugin.js +0 -1
- package/plugins/draw-ml/dist/umd/index.js +0 -2
- package/plugins/draw-ml/src/api/addFeature.js +0 -15
- package/plugins/draw-ml/src/api/newPolygon.js +0 -43
- package/plugins/draw-ml/src/draw.scss +0 -9
- package/plugins/draw-ml/src/events.js +0 -127
- package/plugins/draw-ml/src/manifest.js +0 -100
- package/plugins/draw-ml/src/modes/drawVertexMode.js +0 -275
- package/plugins/draw-ml/src/modes/editVertexMode.js +0 -426
- package/plugins/draw-ml/src/utils.js +0 -102
- package/plugins/frame/dist/esm/im-frame-plugin.js +0 -1
- package/plugins/frame/dist/esm/index.js +0 -2
- package/plugins/frame/dist/umd/im-frame-plugin.js +0 -1
- package/plugins/frame/dist/umd/index.js +0 -2
- package/plugins/map-styles/dist/esm/im-map-styles-plugin.js +0 -1
- package/plugins/map-styles/dist/esm/index.js +0 -2
- package/plugins/map-styles/dist/umd/im-map-styles-plugin.js +0 -1
- package/plugins/map-styles/dist/umd/index.js +0 -2
- package/plugins/scale-bar/dist/esm/im-scale-bar-plugin.js +0 -1
- package/plugins/scale-bar/dist/esm/index.js +0 -2
- package/plugins/scale-bar/dist/umd/im-scale-bar-plugin.js +0 -1
- package/plugins/scale-bar/dist/umd/index.js +0 -2
- package/plugins/search/src/utils/fetchDataset.js +0 -23
- package/plugins/use-location/dist/esm/im-use-location-plugin.js +0 -1
- package/plugins/use-location/dist/esm/index.js +0 -2
- package/plugins/use-location/dist/umd/im-use-location-plugin.js +0 -1
- package/plugins/use-location/dist/umd/index.js +0 -2
- package/providers/maplibre/dist/esm/im-maplibre-legacy-framework.js +0 -1
- package/providers/maplibre/dist/umd/im-maplibre-legacy-framework.js +0 -1
- package/providers/open-names/dist/esm/im-reverse-geocode.js +0 -2
- package/providers/open-names/dist/esm/index.js +0 -2
- package/providers/open-names/dist/umd/im-reverse-geocode.js +0 -2
- package/providers/open-names/dist/umd/index.js +0 -2
- /package/docs/architecture/{ARCHITECTURE_DIAGRAMS.md → architecture-diagrams.md} +0 -0
- /package/plugins/{datasets → beta/datasets}/dist/css/index.css +0 -0
- /package/plugins/{datasets/dist/esm/index.js.LICENSE.txt → beta/datasets/dist/esm/im-datasets-plugin.js.LICENSE.txt} +0 -0
- /package/plugins/{datasets/dist/umd → beta/datasets/dist/esm}/index.js.LICENSE.txt +0 -0
- /package/plugins/{draw-ml/dist/esm/index.js.LICENSE.txt → beta/datasets/dist/umd/im-datasets-plugin.js.LICENSE.txt} +0 -0
- /package/plugins/{draw-ml → beta/datasets}/dist/umd/index.js.LICENSE.txt +0 -0
- /package/plugins/{datasets → beta/datasets}/src/DatasetsInit.jsx +0 -0
- /package/plugins/{datasets → beta/datasets}/src/api/addDataset.js +0 -0
- /package/plugins/{datasets → beta/datasets}/src/api/hideDataset.js +0 -0
- /package/plugins/{datasets → beta/datasets}/src/api/hideFeatures.js +0 -0
- /package/plugins/{datasets → beta/datasets}/src/api/removeDataset.js +0 -0
- /package/plugins/{datasets → beta/datasets}/src/api/showDataset.js +0 -0
- /package/plugins/{datasets → beta/datasets}/src/api/showFeatures.js +0 -0
- /package/plugins/{datasets → beta/datasets}/src/datasets.scss +0 -0
- /package/plugins/{datasets → beta/datasets}/src/defaults.js +0 -0
- /package/plugins/{datasets → beta/datasets}/src/index.js +0 -0
- /package/plugins/{datasets → beta/datasets}/src/panels/Key.module.scss +0 -0
- /package/plugins/{datasets → beta/datasets}/src/panels/Layers.jsx +0 -0
- /package/plugins/{datasets → beta/datasets}/src/panels/Layers.module.scss +0 -0
- /package/plugins/{datasets → beta/datasets}/src/reducer.js +0 -0
- /package/plugins/{datasets → beta/datasets}/src/utils/debounce.js +0 -0
- /package/plugins/{datasets → beta/datasets}/src/utils/filters.js +0 -0
- /package/plugins/{draw-es → beta/draw-es}/src/DrawInit.jsx +0 -0
- /package/plugins/{draw-es → beta/draw-es}/src/api/addFeature.js +0 -0
- /package/plugins/{draw-es → beta/draw-es}/src/api/deleteFeature.js +0 -0
- /package/plugins/{draw-es → beta/draw-es}/src/api/editFeature.js +0 -0
- /package/plugins/{draw-es → beta/draw-es}/src/api/newPolygon.js +0 -0
- /package/plugins/{draw-es → beta/draw-es}/src/events.js +0 -0
- /package/plugins/{draw-es → beta/draw-es}/src/graphic.js +0 -0
- /package/plugins/{draw-es → beta/draw-es}/src/index.js +0 -0
- /package/plugins/{draw-es → beta/draw-es}/src/manifest.js +0 -0
- /package/plugins/{draw-es → beta/draw-es}/src/reducer.js +0 -0
- /package/plugins/{draw-es → beta/draw-es}/src/sketchViewModel.js +0 -0
- /package/plugins/{frame → beta/draw-ml}/dist/esm/index.js.LICENSE.txt +0 -0
- /package/plugins/{frame → beta/draw-ml}/dist/umd/index.js.LICENSE.txt +0 -0
- /package/plugins/{draw-ml → beta/draw-ml}/src/DrawInit.jsx +0 -0
- /package/plugins/{draw-ml → beta/draw-ml}/src/api/deleteFeature.js +0 -0
- /package/plugins/{draw-ml → beta/draw-ml}/src/index.js +0 -0
- /package/plugins/{draw-ml → beta/draw-ml}/src/modes/disabledMode.js +0 -0
- /package/plugins/{draw-ml/src → beta/draw-ml/src/utils}/snapHelpers.js +0 -0
- /package/plugins/{frame → beta/frame}/dist/css/index.css +0 -0
- /package/plugins/{map-styles → beta/frame}/dist/esm/index.js.LICENSE.txt +0 -0
- /package/plugins/{map-styles → beta/frame}/dist/umd/index.js.LICENSE.txt +0 -0
- /package/plugins/{frame → beta/frame}/package.json +0 -0
- /package/plugins/{frame → beta/frame}/src/FrameInit.jsx +0 -0
- /package/plugins/{frame → beta/frame}/src/api/addFrame.js +0 -0
- /package/plugins/{frame → beta/frame}/src/api/editFeature.js +0 -0
- /package/plugins/{frame → beta/frame}/src/config.js +0 -0
- /package/plugins/{frame → beta/frame}/src/frame.scss +0 -0
- /package/plugins/{frame → beta/frame}/src/index.js +0 -0
- /package/plugins/{frame → beta/frame}/src/manifest.js +0 -0
- /package/plugins/{frame → beta/frame}/src/reducer.js +0 -0
- /package/plugins/{frame → beta/frame}/src/utils.js +0 -0
- /package/plugins/{scale-bar → beta/map-styles}/dist/esm/index.js.LICENSE.txt +0 -0
- /package/plugins/{scale-bar → beta/map-styles}/dist/umd/index.js.LICENSE.txt +0 -0
- /package/plugins/{map-styles → beta/map-styles}/src/MapStylesInit.jsx +0 -0
- /package/plugins/{map-styles → beta/map-styles}/src/config.js +0 -0
- /package/plugins/{map-styles → beta/map-styles}/src/index.js +0 -0
- /package/plugins/{scale-bar → beta/scale-bar}/dist/css/index.css +0 -0
- /package/plugins/{use-location → beta/scale-bar}/dist/esm/index.js.LICENSE.txt +0 -0
- /package/plugins/{use-location → beta/scale-bar}/dist/umd/index.js.LICENSE.txt +0 -0
- /package/plugins/{scale-bar → beta/scale-bar}/package.json +0 -0
- /package/plugins/{scale-bar → beta/scale-bar}/src/ScaleBar.jsx +0 -0
- /package/plugins/{scale-bar → beta/scale-bar}/src/ScaleBar.test.jsx +0 -0
- /package/plugins/{scale-bar → beta/scale-bar}/src/index.js +0 -0
- /package/plugins/{scale-bar → beta/scale-bar}/src/index.test.js +0 -0
- /package/plugins/{scale-bar → beta/scale-bar}/src/manifest.js +0 -0
- /package/plugins/{scale-bar → beta/scale-bar}/src/scaleBar.scss +0 -0
- /package/plugins/{scale-bar → beta/scale-bar}/src/utils.js +0 -0
- /package/plugins/{scale-bar → beta/scale-bar}/src/utils.test.js +0 -0
- /package/{providers/open-names → plugins/beta/use-location}/dist/esm/index.js.LICENSE.txt +0 -0
- /package/{providers/open-names → plugins/beta/use-location}/dist/umd/index.js.LICENSE.txt +0 -0
- /package/plugins/{use-location → beta/use-location}/src/UseLocation.jsx +0 -0
- /package/plugins/{use-location → beta/use-location}/src/UseLocationInit.jsx +0 -0
- /package/plugins/{use-location → beta/use-location}/src/defaults.js +0 -0
- /package/plugins/{use-location → beta/use-location}/src/events.js +0 -0
- /package/plugins/{use-location → beta/use-location}/src/index.js +0 -0
- /package/plugins/{use-location → beta/use-location}/src/manifest.js +0 -0
- /package/plugins/{use-location → beta/use-location}/src/reducer.js +0 -0
- /package/providers/{esri → beta/esri}/src/appEvents.js +0 -0
- /package/providers/{esri → beta/esri}/src/defaults.js +0 -0
- /package/providers/{esri → beta/esri}/src/esriProvider.scss +0 -0
- /package/providers/{esri → beta/esri}/src/utils/coords.js +0 -0
- /package/providers/{esri → beta/esri}/src/utils/detectWebGL.js +0 -0
- /package/providers/{esri → beta/esri}/src/utils/esriFixes.js +0 -0
- /package/providers/{esri → beta/esri}/src/utils/query.js +0 -0
- /package/providers/{esri → beta/esri}/src/utils/spatial.js +0 -0
- /package/providers/{open-names → beta/open-names}/dist/esm/im-reverse-geocode.js.LICENSE.txt +0 -0
- /package/providers/{open-names/dist/umd/im-reverse-geocode.js.LICENSE.txt → beta/open-names/dist/esm/index.js.LICENSE.txt} +0 -0
- /package/providers/{open-names → beta/open-names}/src/index.js +0 -0
- /package/providers/{open-names → beta/open-names}/src/reverseGeocode.js +0 -0
- /package/providers/{open-names → beta/open-names}/src/utils/mapToLocationModel.js +0 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure geometry helper functions for multi-ring/multi-part geometry support.
|
|
3
|
+
* These functions handle coordinate transformations between flat arrays and
|
|
4
|
+
* hierarchical GeoJSON structures for Polygon, MultiPolygon, LineString, and MultiLineString.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get flat coordinates array from feature for all geometry types.
|
|
9
|
+
* Flattens all rings/parts into a single array for unified vertex navigation.
|
|
10
|
+
*
|
|
11
|
+
* @param {Object} feature - GeoJSON geometry object
|
|
12
|
+
* @returns {Array<[number, number]>} Flat array of all coordinates
|
|
13
|
+
*/
|
|
14
|
+
export const getCoords = (feature) => {
|
|
15
|
+
if (!feature?.coordinates) return []
|
|
16
|
+
switch (feature.type) {
|
|
17
|
+
case 'LineString':
|
|
18
|
+
return feature.coordinates
|
|
19
|
+
case 'Polygon':
|
|
20
|
+
return feature.coordinates.flat(1)
|
|
21
|
+
case 'MultiLineString':
|
|
22
|
+
return feature.coordinates.flat(1)
|
|
23
|
+
case 'MultiPolygon':
|
|
24
|
+
return feature.coordinates.flat(2)
|
|
25
|
+
default:
|
|
26
|
+
return []
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get segment metadata for multi-ring/multi-part geometries.
|
|
32
|
+
* Each segment represents a ring (for Polygon) or part (for Multi*).
|
|
33
|
+
*
|
|
34
|
+
* @param {Object} feature - GeoJSON geometry object
|
|
35
|
+
* @returns {Array<{start: number, length: number, path: number[], closed: boolean}>}
|
|
36
|
+
* Array of segment metadata objects where:
|
|
37
|
+
* - start: Starting index in flat coordinates array
|
|
38
|
+
* - length: Number of coordinates in this segment
|
|
39
|
+
* - path: Hierarchical path indices to reach this segment in GeoJSON
|
|
40
|
+
* - closed: Whether this segment is closed (true for Polygon rings)
|
|
41
|
+
*/
|
|
42
|
+
export const getRingSegments = (feature) => {
|
|
43
|
+
if (!feature?.coordinates) return []
|
|
44
|
+
const segments = []
|
|
45
|
+
let start = 0
|
|
46
|
+
|
|
47
|
+
switch (feature.type) {
|
|
48
|
+
case 'LineString':
|
|
49
|
+
segments.push({ start: 0, length: feature.coordinates.length, path: [], closed: false })
|
|
50
|
+
break
|
|
51
|
+
case 'Polygon':
|
|
52
|
+
feature.coordinates.forEach((ring, ringIdx) => {
|
|
53
|
+
segments.push({ start, length: ring.length, path: [ringIdx], closed: true })
|
|
54
|
+
start += ring.length
|
|
55
|
+
})
|
|
56
|
+
break
|
|
57
|
+
case 'MultiLineString':
|
|
58
|
+
feature.coordinates.forEach((line, lineIdx) => {
|
|
59
|
+
segments.push({ start, length: line.length, path: [lineIdx], closed: false })
|
|
60
|
+
start += line.length
|
|
61
|
+
})
|
|
62
|
+
break
|
|
63
|
+
case 'MultiPolygon':
|
|
64
|
+
feature.coordinates.forEach((polygon, polyIdx) => {
|
|
65
|
+
polygon.forEach((ring, ringIdx) => {
|
|
66
|
+
segments.push({ start, length: ring.length, path: [polyIdx, ringIdx], closed: true })
|
|
67
|
+
start += ring.length
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
break
|
|
71
|
+
default:
|
|
72
|
+
break
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return segments
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Find which segment a flat vertex index belongs to.
|
|
80
|
+
*
|
|
81
|
+
* @param {Array} segments - Array of segment metadata from getRingSegments
|
|
82
|
+
* @param {number} flatIdx - Flat vertex index
|
|
83
|
+
* @returns {{segment: Object, localIdx: number}|null}
|
|
84
|
+
* Object with segment metadata and local index within that segment, or null if not found
|
|
85
|
+
*/
|
|
86
|
+
export const getSegmentForIndex = (segments, flatIdx) => {
|
|
87
|
+
for (const seg of segments) {
|
|
88
|
+
if (flatIdx >= seg.start && flatIdx < seg.start + seg.length) {
|
|
89
|
+
return { segment: seg, localIdx: flatIdx - seg.start }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get modifiable coordinate array at a specific hierarchical path.
|
|
97
|
+
* Returns a reference to the actual coordinate array in the GeoJSON structure.
|
|
98
|
+
*
|
|
99
|
+
* @param {Object} geojson - Full GeoJSON feature object
|
|
100
|
+
* @param {number[]} path - Hierarchical path indices (e.g., [0] for first ring, [1, 0] for second polygon's first ring)
|
|
101
|
+
* @returns {Array<[number, number]>} Reference to coordinate array at path
|
|
102
|
+
*/
|
|
103
|
+
export const getModifiableCoords = (geojson, path) => {
|
|
104
|
+
let coords = geojson.geometry.coordinates
|
|
105
|
+
for (const idx of path) {
|
|
106
|
+
coords = coords[idx]
|
|
107
|
+
}
|
|
108
|
+
return coords
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Convert mapbox-gl-draw coord_path string to flat vertex index.
|
|
113
|
+
* coord_path format: "ringIdx.vertexIdx" for Polygon, "polyIdx.ringIdx.vertexIdx" for MultiPolygon, etc.
|
|
114
|
+
*
|
|
115
|
+
* @param {Object} feature - GeoJSON geometry object
|
|
116
|
+
* @param {string} coordPath - coord_path string from mapbox-gl-draw
|
|
117
|
+
* @returns {number} Flat vertex index in the unified coordinate array
|
|
118
|
+
*/
|
|
119
|
+
export const coordPathToFlatIndex = (feature, coordPath) => {
|
|
120
|
+
const parts = coordPath.split('.').map(Number)
|
|
121
|
+
const segments = getRingSegments(feature)
|
|
122
|
+
|
|
123
|
+
// Match coord_path to segment
|
|
124
|
+
for (const seg of segments) {
|
|
125
|
+
// Check if path matches (compare all but last element which is the local vertex index)
|
|
126
|
+
const pathMatches = seg.path.every((val, idx) => val === parts[idx])
|
|
127
|
+
if (pathMatches && parts.length === seg.path.length + 1) {
|
|
128
|
+
const localIdx = parts[parts.length - 1]
|
|
129
|
+
return seg.start + localIdx
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Fallback: just use the last number (works for simple geometries)
|
|
134
|
+
return parts[parts.length - 1]
|
|
135
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getSnapInstance, isSnapEnabled, triggerSnapAtPoint, getSnapLngLat,
|
|
3
|
+
clearSnapState, clearSnapIndicator
|
|
4
|
+
} from '../../utils/snapHelpers.js'
|
|
5
|
+
import { coordPathToFlatIndex } from './geometryHelpers.js'
|
|
6
|
+
import { isOnSVG } from './helpers.js'
|
|
7
|
+
|
|
8
|
+
const touchVertexTarget = `
|
|
9
|
+
<svg width='48' height='48' viewBox='0 0 48 48' fill-rule='evenodd' style='display:none;position:absolute;top:50%;left:50%;margin:24px 0 0 -24px' class='touch-vertex-target' data-touch-vertex-target>
|
|
10
|
+
<circle cx='24' cy='24' r='24' fill='currentColor'/>
|
|
11
|
+
<path d="M37.543 25H34a1 1 0 1 1 0-2h3.629l-.836-.837a1 1 0 0 1 1.414-1.414l2.5 2.501A1 1 0 0 1 41 24a1 1 0 0 1-.487.858l-2.306 2.306a1 1 0 0 1-1.414-1.414l.75-.75zM23 10.414l-.793.793a1 1 0 0 1-1.414-1.414l2.5-2.5C23.481 7.105 23.734 7 24 7s.519.105.707.293l2.5 2.5a1 1 0 0 1-1.414 1.414L25 10.414V14a1 1 0 1 1-2 0v-3.586zM7 24a1 1 0 0 1 .293-.75l2.5-2.501a1 1 0 0 1 1.414 1.414l-.836.837H14a1 1 0 1 1 0 2h-3.543l.75.75a1 1 0 0 1-1.414 1.414l-2.306-2.306A1 1 0 0 1 7 24zm16.293 16.707l-2.5-2.5a1 1 0 0 1 1.414-1.414l.793.793V34a1 1 0 1 1 2 0v3.586l.793-.793a1 1 0 0 1 1.414 1.414l-2.5 2.5c-.188.188-.441.293-.707.293s-.519-.105-.707-.293zM24 20c2.208 0 4 1.792 4 4s-1.792 4-4 4-4-1.792-4-4 1.792-4 4-4z" fill="#fff"/>
|
|
12
|
+
</svg>
|
|
13
|
+
`
|
|
14
|
+
|
|
15
|
+
export const touchHandlers = {
|
|
16
|
+
addTouchVertexTarget(state) {
|
|
17
|
+
let el = state.container.querySelector('[data-touch-vertex-target]')
|
|
18
|
+
if (!el) {
|
|
19
|
+
state.container.insertAdjacentHTML('beforeend', touchVertexTarget)
|
|
20
|
+
el = state.container.querySelector('[data-touch-vertex-target]')
|
|
21
|
+
}
|
|
22
|
+
state.touchVertexTarget = el
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
updateTouchVertexTarget(state, point) {
|
|
26
|
+
if (point && state.interfaceType === 'touch' && state.selectedVertexIndex >= 0) {
|
|
27
|
+
Object.assign(state.touchVertexTarget.style, { display: 'block', top: `${point.y}px`, left: `${point.x}px` })
|
|
28
|
+
} else {
|
|
29
|
+
state.touchVertexTarget.style.display = 'none'
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
hideTouchVertexIndicator(state) {
|
|
34
|
+
state.touchVertexTarget.style.display = 'none'
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
onPointerevent(state, e) {
|
|
38
|
+
state.interfaceType = e.pointerType === 'touch' ? 'touch' : 'pointer'
|
|
39
|
+
state.isPanEnabled = true
|
|
40
|
+
if (e.pointerType === 'touch' && e.type === 'pointermove' && !isOnSVG(e.target.parentNode) && !state._ignorePointermoveDeselect) {
|
|
41
|
+
this.changeMode(state, { selectedVertexIndex: -1, selectedVertexType: null, coordPath: null })
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// Empty stubs required by DirectSelect
|
|
46
|
+
onTouchStart() {},
|
|
47
|
+
onTouchMove() {},
|
|
48
|
+
onTouchEnd() {},
|
|
49
|
+
|
|
50
|
+
onTouchend(state) {
|
|
51
|
+
clearSnapState(getSnapInstance(this.map))
|
|
52
|
+
if (state?.featureId) {
|
|
53
|
+
this.syncVertices(state)
|
|
54
|
+
|
|
55
|
+
// Push undo for the move if touch actually moved
|
|
56
|
+
if (state._touchMoved && state._moveStartPosition && state._moveStartIndex !== undefined) {
|
|
57
|
+
this.pushUndo({
|
|
58
|
+
type: 'move_vertex',
|
|
59
|
+
featureId: state.featureId,
|
|
60
|
+
vertexIndex: state._moveStartIndex,
|
|
61
|
+
previousPosition: state._moveStartPosition
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
state._moveStartPosition = null
|
|
65
|
+
state._moveStartIndex = undefined
|
|
66
|
+
state._touchMoved = false
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
onTap(state, e) {
|
|
71
|
+
// Hide snap indicator on any tap
|
|
72
|
+
const snap = getSnapInstance(this.map)
|
|
73
|
+
if (snap) {
|
|
74
|
+
clearSnapIndicator(snap, this.map)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const meta = e.featureTarget?.properties.meta
|
|
78
|
+
const coordPath = e.featureTarget?.properties.coord_path
|
|
79
|
+
|
|
80
|
+
if (meta === 'vertex') {
|
|
81
|
+
const feature = this.getFeature(state.featureId)
|
|
82
|
+
const idx = coordPathToFlatIndex(feature, coordPath)
|
|
83
|
+
this.changeMode(state, {
|
|
84
|
+
selectedVertexIndex: idx,
|
|
85
|
+
selectedVertexType: 'vertex',
|
|
86
|
+
coordPath
|
|
87
|
+
})
|
|
88
|
+
} else if (meta === 'midpoint') {
|
|
89
|
+
this.insertVertex({ ...state, selectedVertexIndex: this.getVertexIndexFromMidpoint(state, coordPath), selectedVertexType: 'midpoint' })
|
|
90
|
+
} else {
|
|
91
|
+
this.clickNoTarget(state)
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
onTouchstart(state, e) {
|
|
96
|
+
clearSnapState(getSnapInstance(this.map))
|
|
97
|
+
const vertex = state.vertecies?.[state.selectedVertexIndex]
|
|
98
|
+
if (!vertex || !isOnSVG(e.target.parentNode)) {
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Save starting position for undo
|
|
103
|
+
state._moveStartPosition = [...vertex]
|
|
104
|
+
state._moveStartIndex = state.selectedVertexIndex
|
|
105
|
+
state._touchMoved = false
|
|
106
|
+
|
|
107
|
+
const touch = { x: e.touches[0].clientX, y: e.touches[0].clientY }
|
|
108
|
+
const style = window.getComputedStyle(state.touchVertexTarget)
|
|
109
|
+
state.deltaTarget = { x: touch.x - Number.parseFloat(style.left), y: touch.y - Number.parseFloat(style.top) }
|
|
110
|
+
const vertexPt = this.map.project(vertex)
|
|
111
|
+
state.deltaVertex = { x: (touch.x / state.scale) - vertexPt.x, y: (touch.y / state.scale) - vertexPt.y }
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
onTouchmove(state, e) {
|
|
115
|
+
if (state.selectedVertexIndex < 0 || !isOnSVG(e.target.parentNode)) {
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
state._touchMoved = true
|
|
120
|
+
|
|
121
|
+
const touch = { x: e.touches[0].clientX, y: e.touches[0].clientY }
|
|
122
|
+
const screenPt = { x: (touch.x / state.scale) - state.deltaVertex.x, y: (touch.y / state.scale) - state.deltaVertex.y }
|
|
123
|
+
|
|
124
|
+
let finalCoord = this.map.unproject(screenPt)
|
|
125
|
+
if (isSnapEnabled(state)) {
|
|
126
|
+
const snap = getSnapInstance(this.map)
|
|
127
|
+
triggerSnapAtPoint(snap, this.map, screenPt)
|
|
128
|
+
finalCoord = getSnapLngLat(snap) || finalCoord
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this.moveVertex(state, finalCoord)
|
|
132
|
+
this.updateTouchVertexTarget(state, { x: touch.x - state.deltaTarget.x, y: touch.y - state.deltaTarget.y })
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getRingSegments,
|
|
3
|
+
getSegmentForIndex,
|
|
4
|
+
getModifiableCoords
|
|
5
|
+
} from './geometryHelpers.js'
|
|
6
|
+
import { scalePoint } from './helpers.js'
|
|
7
|
+
|
|
8
|
+
export const undoHandlers = {
|
|
9
|
+
// Fire geometry change event (for external listeners)
|
|
10
|
+
fireGeometryChange(state) {
|
|
11
|
+
const feature = this.getFeature(state.featureId)
|
|
12
|
+
if (feature) {
|
|
13
|
+
this.map.fire('draw.update', {
|
|
14
|
+
features: [feature.toGeoJSON()],
|
|
15
|
+
action: 'change_coordinates'
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
// Undo support
|
|
21
|
+
pushUndo(operation) {
|
|
22
|
+
const undoStack = this.map._undoStack
|
|
23
|
+
if (!undoStack) {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
undoStack.push(operation)
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
handleUndo(state) {
|
|
30
|
+
const undoStack = this.map._undoStack
|
|
31
|
+
if (!undoStack || undoStack.length === 0) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const op = undoStack.pop()
|
|
36
|
+
|
|
37
|
+
if (op.type === 'move_vertex') {
|
|
38
|
+
this.undoMoveVertex(state, op)
|
|
39
|
+
} else if (op.type === 'insert_vertex') {
|
|
40
|
+
this.undoInsertVertex(state, op)
|
|
41
|
+
} else if (op.type === 'delete_vertex') {
|
|
42
|
+
this.undoDeleteVertex(state, op)
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
undoMoveVertex(state, op) {
|
|
47
|
+
const { vertexIndex, previousPosition, featureId } = op
|
|
48
|
+
const feature = this.getFeature(featureId)
|
|
49
|
+
if (!feature) return
|
|
50
|
+
|
|
51
|
+
const geojson = feature.toGeoJSON()
|
|
52
|
+
const segments = getRingSegments(feature)
|
|
53
|
+
const result = getSegmentForIndex(segments, vertexIndex)
|
|
54
|
+
if (!result) return
|
|
55
|
+
|
|
56
|
+
const coords = getModifiableCoords(geojson, result.segment.path)
|
|
57
|
+
coords[result.localIdx] = previousPosition
|
|
58
|
+
this._applyUndoAndSync(state, geojson, featureId)
|
|
59
|
+
|
|
60
|
+
// Update touch vertex target position
|
|
61
|
+
const vertex = state.vertecies[state.selectedVertexIndex]
|
|
62
|
+
if (vertex) {
|
|
63
|
+
this.updateTouchVertexTarget(state, scalePoint(this.map.project(vertex), state.scale))
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
undoInsertVertex(state, op) {
|
|
68
|
+
const { vertexIndex, featureId } = op
|
|
69
|
+
const feature = this.getFeature(featureId)
|
|
70
|
+
if (!feature) return
|
|
71
|
+
|
|
72
|
+
const geojson = feature.toGeoJSON()
|
|
73
|
+
const segments = getRingSegments(feature)
|
|
74
|
+
const result = getSegmentForIndex(segments, vertexIndex)
|
|
75
|
+
if (!result) return
|
|
76
|
+
|
|
77
|
+
const coords = getModifiableCoords(geojson, result.segment.path)
|
|
78
|
+
coords.splice(result.localIdx, 1)
|
|
79
|
+
this._applyUndoAndSync(state, geojson, featureId)
|
|
80
|
+
|
|
81
|
+
// Clear DirectSelect's coordinate selection
|
|
82
|
+
this.clearSelectedCoordinates()
|
|
83
|
+
this.hideTouchVertexIndicator(state)
|
|
84
|
+
this.changeMode(state, { selectedVertexIndex: -1, selectedVertexType: null })
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
undoDeleteVertex(state, op) {
|
|
88
|
+
const { vertexIndex, position, featureId } = op
|
|
89
|
+
const feature = this.getFeature(featureId)
|
|
90
|
+
if (!feature) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const geojson = feature.toGeoJSON()
|
|
95
|
+
const segments = getRingSegments(feature)
|
|
96
|
+
|
|
97
|
+
// Try to find segment containing vertexIndex
|
|
98
|
+
let result = getSegmentForIndex(segments, vertexIndex)
|
|
99
|
+
|
|
100
|
+
// If not found, vertex might be at segment boundary
|
|
101
|
+
if (!result) {
|
|
102
|
+
for (const seg of segments) {
|
|
103
|
+
if (vertexIndex === seg.start + seg.length) {
|
|
104
|
+
result = { segment: seg, localIdx: seg.length }
|
|
105
|
+
break
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!result) {
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const coords = getModifiableCoords(geojson, result.segment.path)
|
|
115
|
+
coords.splice(result.localIdx, 0, position)
|
|
116
|
+
this._applyUndoAndSync(state, geojson, featureId)
|
|
117
|
+
|
|
118
|
+
// Update touch vertex target to restored vertex position
|
|
119
|
+
const vertex = state.vertecies[vertexIndex]
|
|
120
|
+
if (vertex) {
|
|
121
|
+
this.updateTouchVertexTarget(state, scalePoint(this.map.project(vertex), state.scale))
|
|
122
|
+
}
|
|
123
|
+
this.changeMode(state, { selectedVertexIndex: vertexIndex, selectedVertexType: 'vertex', coordPath: this.getCoordPath(state, vertexIndex) })
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
_applyUndoAndSync(state, geojson, featureId) {
|
|
127
|
+
this._ctx.api.add(geojson)
|
|
128
|
+
state.vertecies = this.getVerticies(featureId)
|
|
129
|
+
state.midpoints = this.getMidpoints(featureId)
|
|
130
|
+
this._ctx.store.render()
|
|
131
|
+
this.fireGeometryChange(state)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCoords,
|
|
3
|
+
getRingSegments,
|
|
4
|
+
getSegmentForIndex,
|
|
5
|
+
getModifiableCoords
|
|
6
|
+
} from './geometryHelpers.js'
|
|
7
|
+
|
|
8
|
+
const ARROW_OFFSETS = { ArrowUp: [0, -1], ArrowDown: [0, 1], ArrowLeft: [-1, 0], ArrowRight: [1, 0] }
|
|
9
|
+
const NUDGE = 1, STEP = 5
|
|
10
|
+
|
|
11
|
+
export const vertexOperations = {
|
|
12
|
+
updateMidpoint(coordinates) {
|
|
13
|
+
setTimeout(() => {
|
|
14
|
+
this.map.getSource('mapbox-gl-draw-hot').setData({
|
|
15
|
+
type: 'Feature',
|
|
16
|
+
properties: { meta: 'midpoint', active: 'true', id: 'active-midpoint' },
|
|
17
|
+
geometry: { type: 'Point', coordinates }
|
|
18
|
+
})
|
|
19
|
+
}, 0)
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
updateVertex(state, direction) {
|
|
23
|
+
const [idx, type] = this.getVertexOrMidpoint(state, direction)
|
|
24
|
+
if (idx < 0 || !type) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
this.changeMode(state, { selectedVertexIndex: idx, selectedVertexType: type, ...(type === 'vertex' && { coordPath: this.getCoordPath(state, idx) }) })
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
getOffset(coord, e) {
|
|
31
|
+
const pt = this.map.project(coord)
|
|
32
|
+
const offset = e?.shiftKey ? NUDGE : STEP
|
|
33
|
+
const [dx, dy] = e ? ARROW_OFFSETS[e.key].map(v => v * offset) : [0, 0]
|
|
34
|
+
return this.map.unproject({ x: pt.x + dx, y: pt.y + dy })
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
getNewCoord(state, e) {
|
|
38
|
+
return this.getOffset(getCoords(this.getFeature(state.featureId))[state.selectedVertexIndex], e)
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
insertVertex(state, e) {
|
|
42
|
+
const midIdx = state.selectedVertexIndex - state.vertecies.length
|
|
43
|
+
const newCoord = this.getOffset(state.midpoints[midIdx], e)
|
|
44
|
+
const feature = this.getFeature(state.featureId)
|
|
45
|
+
const geojson = feature.toGeoJSON()
|
|
46
|
+
|
|
47
|
+
// Find which segment this midpoint belongs to and calculate insertion position
|
|
48
|
+
const segments = getRingSegments(feature)
|
|
49
|
+
let globalInsertIdx = midIdx + 1
|
|
50
|
+
let insertSegment = null
|
|
51
|
+
let localInsertIdx = 0
|
|
52
|
+
|
|
53
|
+
// Map midpoint index to segment and local position
|
|
54
|
+
let midpointCounter = 0
|
|
55
|
+
for (const seg of segments) {
|
|
56
|
+
// Must match getMidpoints calculation
|
|
57
|
+
const segMidpoints = seg.closed ? seg.length : seg.length - 1
|
|
58
|
+
if (midIdx < midpointCounter + segMidpoints) {
|
|
59
|
+
insertSegment = seg
|
|
60
|
+
localInsertIdx = (midIdx - midpointCounter) + 1
|
|
61
|
+
globalInsertIdx = seg.start + localInsertIdx
|
|
62
|
+
break
|
|
63
|
+
}
|
|
64
|
+
midpointCounter += segMidpoints
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!insertSegment) return
|
|
68
|
+
|
|
69
|
+
const coords = getModifiableCoords(geojson, insertSegment.path)
|
|
70
|
+
coords.splice(localInsertIdx, 0, [newCoord.lng, newCoord.lat])
|
|
71
|
+
this._ctx.api.add(geojson)
|
|
72
|
+
|
|
73
|
+
this.pushUndo({ type: 'insert_vertex', featureId: state.featureId, vertexIndex: globalInsertIdx })
|
|
74
|
+
this.changeMode(state, { selectedVertexIndex: globalInsertIdx, selectedVertexType: 'vertex', coordPath: this.getCoordPath(state, globalInsertIdx) })
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
moveVertex(state, coord, options = {}) {
|
|
78
|
+
if (options.checkSnap && state.enableSnap !== false) {
|
|
79
|
+
const snap = this.map._snapInstance
|
|
80
|
+
if (snap?.snapStatus && snap.snapCoords?.length >= 2) {
|
|
81
|
+
coord = { lng: snap.snapCoords[0], lat: snap.snapCoords[1] }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const feature = this.getFeature(state.featureId)
|
|
86
|
+
const geojson = feature.toGeoJSON()
|
|
87
|
+
const segments = getRingSegments(feature)
|
|
88
|
+
const result = getSegmentForIndex(segments, state.selectedVertexIndex)
|
|
89
|
+
if (!result) return
|
|
90
|
+
|
|
91
|
+
const coords = getModifiableCoords(geojson, result.segment.path)
|
|
92
|
+
coords[result.localIdx] = [coord.lng, coord.lat]
|
|
93
|
+
this._ctx.api.add(geojson)
|
|
94
|
+
state.vertecies = this.getVerticies(state.featureId)
|
|
95
|
+
|
|
96
|
+
this.map.fire('draw.geometrychange', state.feature)
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
deleteVertex(state) {
|
|
100
|
+
const feature = this.getFeature(state.featureId)
|
|
101
|
+
if (!feature) {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const segments = getRingSegments(feature)
|
|
106
|
+
const result = getSegmentForIndex(segments, state.selectedVertexIndex)
|
|
107
|
+
if (!result) {
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const { segment } = result
|
|
112
|
+
// Minimum vertices per segment: 3 for closed rings (mapbox-gl-draw's internal representation), 2 for lines
|
|
113
|
+
const minVertices = segment.closed ? 3 : 2
|
|
114
|
+
if (segment.length <= minVertices) {
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Save position for undo before deletion
|
|
119
|
+
const deletedPosition = [...state.vertecies[state.selectedVertexIndex]]
|
|
120
|
+
const deletedIndex = state.selectedVertexIndex
|
|
121
|
+
|
|
122
|
+
this._ctx.api.trash()
|
|
123
|
+
|
|
124
|
+
// Clear DirectSelect's coordinate selection to prevent visual artifacts
|
|
125
|
+
this.clearSelectedCoordinates()
|
|
126
|
+
// Force feature re-render to clear vertex highlights
|
|
127
|
+
feature.changed()
|
|
128
|
+
this._ctx.store.render()
|
|
129
|
+
|
|
130
|
+
// Push undo operation
|
|
131
|
+
this.pushUndo({
|
|
132
|
+
type: 'delete_vertex',
|
|
133
|
+
featureId: state.featureId,
|
|
134
|
+
vertexIndex: deletedIndex,
|
|
135
|
+
position: deletedPosition
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
// Clear selection after delete
|
|
139
|
+
this.changeMode(state, { selectedVertexIndex: -1, selectedVertexType: null })
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCoords,
|
|
3
|
+
getRingSegments,
|
|
4
|
+
getSegmentForIndex
|
|
5
|
+
} from './geometryHelpers.js'
|
|
6
|
+
import { spatialNavigate } from '../../utils/spatial.js'
|
|
7
|
+
|
|
8
|
+
export const vertexQueries = {
|
|
9
|
+
findVertexIndex(coords, targetCoord, currentIdx) {
|
|
10
|
+
// Search for vertex, preferring matches near currentIdx to handle duplicate coords (e.g., closing vertices)
|
|
11
|
+
const matches = []
|
|
12
|
+
coords.forEach((c, i) => {
|
|
13
|
+
if (c[0] === targetCoord[0] && c[1] === targetCoord[1]) {
|
|
14
|
+
matches.push(i)
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
if (matches.length === 0) return -1
|
|
19
|
+
if (matches.length === 1) return matches[0]
|
|
20
|
+
|
|
21
|
+
// Multiple matches - pick closest to current selection
|
|
22
|
+
if (currentIdx >= 0) {
|
|
23
|
+
return matches.reduce((best, idx) =>
|
|
24
|
+
Math.abs(idx - currentIdx) < Math.abs(best - currentIdx) ? idx : best
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
return matches[0]
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
getCoordPath(state, idx) {
|
|
31
|
+
const feature = this.getFeature(state.featureId)
|
|
32
|
+
if (!feature) return '0'
|
|
33
|
+
|
|
34
|
+
const segments = getRingSegments(feature)
|
|
35
|
+
const result = getSegmentForIndex(segments, idx)
|
|
36
|
+
if (!result) return '0'
|
|
37
|
+
|
|
38
|
+
const { segment, localIdx } = result
|
|
39
|
+
return [...segment.path, localIdx].join('.')
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
syncVertices(state) {
|
|
43
|
+
state.vertecies = this.getVerticies(state.featureId)
|
|
44
|
+
state.midpoints = this.getMidpoints(state.featureId)
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
getVerticies(featureId) {
|
|
48
|
+
return getCoords(this.getFeature(featureId)) || []
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
getMidpoints(featureId) {
|
|
52
|
+
const feature = this.getFeature(featureId)
|
|
53
|
+
const coords = getCoords(feature)
|
|
54
|
+
const segments = getRingSegments(feature)
|
|
55
|
+
if (!coords?.length || !segments.length) {
|
|
56
|
+
return []
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const midpoints = []
|
|
60
|
+
// Create midpoints within each segment, respecting boundaries
|
|
61
|
+
for (const seg of segments) {
|
|
62
|
+
// For closed rings, create midpoint between every vertex including last→first
|
|
63
|
+
// For open lines, create midpoints only between consecutive vertices (no wrap-around)
|
|
64
|
+
const count = seg.closed ? seg.length : seg.length - 1
|
|
65
|
+
for (let i = 0; i < count; i++) {
|
|
66
|
+
const idx = seg.start + i
|
|
67
|
+
const nextIdx = seg.start + ((i + 1) % seg.length)
|
|
68
|
+
const [x1, y1] = coords[idx]
|
|
69
|
+
const [x2, y2] = coords[nextIdx]
|
|
70
|
+
midpoints.push([(x1 + x2) / 2, (y1 + y2) / 2])
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return midpoints
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
getVertexOrMidpoint(state, direction) {
|
|
77
|
+
// Ensure vertices and midpoints are populated
|
|
78
|
+
if (!state.vertecies?.length) {
|
|
79
|
+
state.vertecies = this.getVerticies(state.featureId)
|
|
80
|
+
state.midpoints = this.getMidpoints(state.featureId)
|
|
81
|
+
}
|
|
82
|
+
if (!state.vertecies?.length) {
|
|
83
|
+
return [-1, null]
|
|
84
|
+
}
|
|
85
|
+
const project = (p) => p ? Object.values(this.map.project(p)) : null
|
|
86
|
+
const pixels = [...state.vertecies.map(project), ...state.midpoints.map(project)].filter(Boolean)
|
|
87
|
+
if (!pixels.length) {
|
|
88
|
+
return [-1, null]
|
|
89
|
+
}
|
|
90
|
+
const start = pixels[state.selectedVertexIndex] || Object.values(this.map.project(this.map.getCenter()))
|
|
91
|
+
const idx = spatialNavigate(start, pixels, direction)
|
|
92
|
+
return [idx, idx < state.vertecies.length ? 'vertex' : 'midpoint']
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
getVertexIndexFromMidpoint(state, coordPath) {
|
|
96
|
+
const feature = this.getFeature(state.featureId)
|
|
97
|
+
const segments = getRingSegments(feature)
|
|
98
|
+
const parts = coordPath.split('.').map(Number)
|
|
99
|
+
|
|
100
|
+
// Find which segment this coord_path belongs to
|
|
101
|
+
let midpointOffset = 0
|
|
102
|
+
for (const seg of segments) {
|
|
103
|
+
const pathMatches = seg.path.every((val, idx) => val === parts[idx])
|
|
104
|
+
if (pathMatches && parts.length === seg.path.length + 1) {
|
|
105
|
+
// In DirectSelect, midpoint coord_path represents the insertion index
|
|
106
|
+
// The midpoint between vertex N and N+1 has coord_path ending in N+1
|
|
107
|
+
// So our flat midpoint index is one less than the coord_path index
|
|
108
|
+
const insertionIdx = parts[parts.length - 1]
|
|
109
|
+
const localMidpointIdx = insertionIdx > 0 ? insertionIdx - 1 : seg.length - 2
|
|
110
|
+
// Midpoints are indexed after all vertices
|
|
111
|
+
return state.vertecies.length + midpointOffset + localMidpointIdx
|
|
112
|
+
}
|
|
113
|
+
// Count midpoints in this segment (must match getMidpoints calculation)
|
|
114
|
+
const segMidpoints = seg.closed ? seg.length : seg.length - 1
|
|
115
|
+
midpointOffset += segMidpoints
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Fallback
|
|
119
|
+
return state.vertecies.length
|
|
120
|
+
}
|
|
121
|
+
}
|