@defra/interactive-map 0.0.37-alpha → 0.0.38-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/assets/templates/draw-tools.njk +4 -3
- package/dist/css/index.css +1 -1
- package/dist/esm/im-core.js +1 -1
- package/dist/esm/im-shell.js +1 -1
- package/dist/umd/im-core.js +1 -1
- package/dist/umd/index.js +1 -1
- package/docs/api.md +32 -0
- package/docs/examples/draw-tools.mdx +0 -4
- package/docs/plugins/draw.md +482 -0
- package/docs/plugins.md +2 -6
- package/govuk-prototype-kit.config.json +3 -2
- package/package.json +2 -4
- package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
- package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ol/dist/esm/im-draw-ol-plugin.js +1 -1
- package/plugins/beta/map-styles/dist/css/index.css +1 -97
- package/plugins/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/draw/dist/esm/im-draw-ml-adapter.js +1 -0
- package/plugins/draw/dist/esm/im-draw-ol-adapter.js +1 -0
- package/plugins/draw/dist/esm/im-draw-plugin.js +1 -0
- package/plugins/draw/dist/esm/index.js +1 -0
- package/plugins/draw/dist/umd/im-draw-ml-adapter.js +1 -0
- package/plugins/draw/dist/umd/im-draw-ol-adapter.js +2 -0
- package/plugins/draw/dist/umd/im-draw-ol-adapter.js.LICENSE.txt +1 -0
- package/plugins/draw/dist/umd/im-draw-plugin.js +2 -0
- package/plugins/draw/dist/umd/im-draw-plugin.js.LICENSE.txt +1 -0
- package/plugins/draw/dist/umd/index.js +2 -0
- package/plugins/draw/dist/umd/index.js.LICENSE.txt +1 -0
- package/plugins/draw/src/DrawInit.jsx +89 -0
- package/plugins/draw/src/DrawInit.test.jsx +184 -0
- package/plugins/draw/src/adapterEvents.js +127 -0
- package/plugins/draw/src/adapterEvents.test.js +20 -0
- package/plugins/draw/src/adapters/adapterContract.test.js +48 -0
- package/plugins/draw/src/adapters/loadDrawAdapter.js +14 -0
- package/plugins/draw/src/adapters/loadDrawAdapter.test.js +53 -0
- package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.js +367 -0
- package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.test.js +799 -0
- package/plugins/draw/src/adapters/maplibre/defaults.js +1 -0
- package/plugins/draw/src/adapters/maplibre/drawEvents.js +35 -0
- package/plugins/draw/src/adapters/maplibre/mapboxDraw.js +126 -0
- package/plugins/draw/src/adapters/maplibre/mapboxDraw.test.js +268 -0
- package/plugins/draw/src/adapters/maplibre/mapboxSnap.js +39 -0
- package/plugins/draw/src/adapters/maplibre/mapboxSnap.test.js +91 -0
- package/plugins/draw/src/adapters/maplibre/modes/createDrawMode.js +58 -0
- package/plugins/draw/src/adapters/maplibre/modes/createDrawMode.test.js +53 -0
- package/plugins/draw/src/adapters/maplibre/modes/disabledMode.js +23 -0
- package/plugins/draw/src/adapters/maplibre/modes/disabledMode.test.js +21 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawLineMode.js +15 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawLineMode.test.js +37 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/__helpers__/harness.js +115 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/clickHandlers.js +193 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/clickHandlers.test.js +241 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/keyboardHandlers.js +78 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/keyboardHandlers.test.js +75 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.js +78 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.test.js +48 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/pointerHandlers.js +103 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/pointerHandlers.test.js +127 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/renderHelpers.js +59 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/renderHelpers.test.js +34 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/undoHandlers.js +193 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/undoHandlers.test.js +195 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawPolygonMode.js +16 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawPolygonMode.test.js +27 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/__helpers__/harness.js +107 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/geometryHelpers.js +139 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/geometryHelpers.test.js +46 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/helpers.js +2 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/helpers.test.js +17 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.js +151 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.test.js +123 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/pointerHandlers.js +161 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/pointerHandlers.test.js +98 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.js +133 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.test.js +105 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.js +166 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.test.js +133 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.js +210 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.test.js +167 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexQueries.js +121 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexQueries.test.js +73 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.js +249 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.test.js +153 -0
- package/plugins/draw/src/adapters/maplibre/snap/constants.js +2 -0
- package/plugins/draw/src/adapters/maplibre/snap/mapHandlers.js +39 -0
- package/plugins/draw/src/adapters/maplibre/snap/mapHandlers.test.js +106 -0
- package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.js +148 -0
- package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.test.js +238 -0
- package/plugins/draw/src/adapters/maplibre/snap/snapInstance.js +115 -0
- package/plugins/draw/src/adapters/maplibre/snap/snapInstance.test.js +151 -0
- package/plugins/draw/src/adapters/maplibre/snap/sourceData.js +34 -0
- package/plugins/draw/src/adapters/maplibre/snap/sourceData.test.js +62 -0
- package/plugins/draw/src/adapters/maplibre/styles.js +207 -0
- package/plugins/draw/src/adapters/maplibre/styles.test.js +155 -0
- package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.js +202 -0
- package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.test.js +253 -0
- package/plugins/draw/src/adapters/maplibre/utils/touchClickWorkaround.js +57 -0
- package/plugins/draw/src/adapters/maplibre/utils/touchClickWorkaround.test.js +106 -0
- package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.js +117 -0
- package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.test.js +159 -0
- package/plugins/draw/src/adapters/openlayers/__helpers__/harness.js +90 -0
- package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.js +178 -0
- package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.test.js +153 -0
- package/plugins/draw/src/adapters/openlayers/core/featureStore.js +69 -0
- package/plugins/draw/src/adapters/openlayers/core/featureStore.test.js +58 -0
- package/plugins/draw/src/adapters/openlayers/core/internalEvents.js +8 -0
- package/plugins/draw/src/adapters/openlayers/core/styles.js +155 -0
- package/plugins/draw/src/adapters/openlayers/core/styles.test.js +143 -0
- package/plugins/draw/src/adapters/openlayers/defaults.js +1 -0
- package/plugins/draw/src/adapters/openlayers/draw/DrawMode.js +272 -0
- package/plugins/draw/src/adapters/openlayers/draw/DrawMode.test.js +431 -0
- package/plugins/draw/src/adapters/openlayers/draw/drawInput.js +139 -0
- package/plugins/draw/src/adapters/openlayers/draw/drawInput.test.js +140 -0
- package/plugins/draw/src/adapters/openlayers/draw/vertexPlacement.js +144 -0
- package/plugins/draw/src/adapters/openlayers/draw/vertexPlacement.test.js +196 -0
- package/plugins/draw/src/adapters/openlayers/edit/EditMode.js +385 -0
- package/plugins/draw/src/adapters/openlayers/edit/EditMode.test.js +303 -0
- package/plugins/draw/src/adapters/openlayers/edit/activeVertexLayer.js +55 -0
- package/plugins/draw/src/adapters/openlayers/edit/activeVertexLayer.test.js +53 -0
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.js +157 -0
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.test.js +195 -0
- package/plugins/draw/src/adapters/openlayers/edit/midpointLayer.js +57 -0
- package/plugins/draw/src/adapters/openlayers/edit/midpointLayer.test.js +42 -0
- package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.js +83 -0
- package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.test.js +80 -0
- package/plugins/draw/src/adapters/openlayers/edit/nudge.js +118 -0
- package/plugins/draw/src/adapters/openlayers/edit/nudge.test.js +165 -0
- package/plugins/draw/src/adapters/openlayers/edit/pointerHandlers.js +79 -0
- package/plugins/draw/src/adapters/openlayers/edit/pointerHandlers.test.js +78 -0
- package/plugins/draw/src/adapters/openlayers/edit/selectionState.js +116 -0
- package/plugins/draw/src/adapters/openlayers/edit/selectionState.test.js +109 -0
- package/plugins/draw/src/adapters/openlayers/edit/touchHandler.js +184 -0
- package/plugins/draw/src/adapters/openlayers/edit/touchHandler.test.js +133 -0
- package/plugins/draw/src/adapters/openlayers/edit/undoOps.js +94 -0
- package/plugins/draw/src/adapters/openlayers/edit/undoOps.test.js +64 -0
- package/plugins/draw/src/adapters/openlayers/edit/vertexHitTest.js +74 -0
- package/plugins/draw/src/adapters/openlayers/edit/vertexHitTest.test.js +28 -0
- package/plugins/draw/src/adapters/openlayers/edit/vertexLayer.js +49 -0
- package/plugins/draw/src/adapters/openlayers/edit/vertexLayer.test.js +42 -0
- package/plugins/draw/src/adapters/openlayers/edit/vertexOps.js +100 -0
- package/plugins/draw/src/adapters/openlayers/edit/vertexOps.test.js +80 -0
- package/plugins/draw/src/adapters/openlayers/olDraw.js +40 -0
- package/plugins/draw/src/adapters/openlayers/olDraw.test.js +70 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapEngine.js +220 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapEngine.test.js +232 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapGeometry.js +198 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapGeometry.test.js +136 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapIndicator.js +81 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapIndicator.test.js +72 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.js +57 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.test.js +68 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapManager.js +99 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapManager.test.js +101 -0
- package/plugins/draw/src/adapters/openlayers/utils/geometryHelpers.js +103 -0
- package/plugins/draw/src/adapters/openlayers/utils/geometryHelpers.test.js +76 -0
- package/plugins/draw/src/adapters/openlayers/utils/olCoords.js +35 -0
- package/plugins/draw/src/adapters/openlayers/utils/olCoords.test.js +22 -0
- package/plugins/draw/src/adapters/openlayers/utils/sketchHelpers.js +22 -0
- package/plugins/draw/src/adapters/openlayers/utils/sketchHelpers.test.js +18 -0
- package/plugins/draw/src/adapters/openlayers/utils/touchTarget.js +8 -0
- package/plugins/draw/src/api/addFeature.js +22 -0
- package/plugins/draw/src/api/addFeature.test.js +41 -0
- package/plugins/draw/src/api/deleteFeature.js +11 -0
- package/plugins/draw/src/api/deleteFeature.test.js +24 -0
- package/plugins/draw/src/api/editFeature.js +54 -0
- package/plugins/draw/src/api/editFeature.test.js +134 -0
- package/plugins/draw/src/api/merge.js +30 -0
- package/plugins/draw/src/api/merge.test.js +57 -0
- package/plugins/draw/src/api/newLine.js +41 -0
- package/plugins/draw/src/api/newLine.test.js +93 -0
- package/plugins/draw/src/api/newPolygon.js +41 -0
- package/plugins/draw/src/api/newPolygon.test.js +93 -0
- package/plugins/draw/src/api/split.js +117 -0
- package/plugins/draw/src/api/split.test.js +204 -0
- package/plugins/draw/src/defaults.js +52 -0
- package/plugins/draw/src/defaults.test.js +18 -0
- package/plugins/draw/src/draw.scss +43 -0
- package/plugins/draw/src/events.js +237 -0
- package/plugins/draw/src/events.test.js +369 -0
- package/plugins/draw/src/index.js +10 -0
- package/plugins/draw/src/index.test.js +24 -0
- package/plugins/draw/src/manifest.js +170 -0
- package/plugins/draw/src/manifest.test.js +147 -0
- package/plugins/draw/src/reducer.js +69 -0
- package/plugins/draw/src/reducer.test.js +85 -0
- package/plugins/draw/src/utils/debounce.js +16 -0
- package/plugins/draw/src/utils/debounce.test.js +40 -0
- package/plugins/draw/src/utils/eventBus.js +36 -0
- package/plugins/draw/src/utils/eventBus.test.js +47 -0
- package/plugins/draw/src/utils/flattenStyleProperties.js +25 -0
- package/plugins/draw/src/utils/flattenStyleProperties.test.js +33 -0
- package/plugins/draw/src/utils/getValueForStyle.js +33 -0
- package/plugins/draw/src/utils/getValueForStyle.test.js +32 -0
- package/plugins/draw/src/utils/resolveColors.js +39 -0
- package/plugins/draw/src/utils/resolveColors.test.js +34 -0
- package/plugins/draw/src/utils/spatial.js +243 -0
- package/plugins/draw/src/utils/spatial.test.js +243 -0
- package/plugins/draw/src/utils/touchTarget.js +95 -0
- package/plugins/draw/src/utils/touchTarget.test.js +107 -0
- package/plugins/draw/src/utils/undoStack.js +31 -0
- package/plugins/draw/src/utils/undoStack.test.js +49 -0
- package/plugins/draw/src/validation/liveDrawChecks.js +120 -0
- package/plugins/draw/src/validation/liveDrawChecks.test.js +159 -0
- package/plugins/draw/src/validation/liveStroke.js +85 -0
- package/plugins/draw/src/validation/liveStroke.test.js +150 -0
- package/plugins/draw/src/validation/rules.areaError.test.js +12 -0
- package/plugins/draw/src/validation/rules.js +186 -0
- package/plugins/draw/src/validation/rules.test.js +120 -0
- package/plugins/draw/src/validation/validateGeometry.js +119 -0
- package/plugins/draw/src/validation/validateGeometry.test.js +200 -0
- package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
- package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
- package/plugins/interact/src/hooks/useInteractionHandlers.js +1 -1
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +1 -1
- package/plugins/interact/src/utils/buildStylesMap.js +1 -0
- package/plugins/search/dist/css/index.css +1 -317
- package/rollup.esm.mjs +35 -28
- package/sonar-project.properties +2 -2
- package/src/App/components/Attributions/Attributions.module.scss +1 -0
- package/src/App/components/Hints/Hints.module.scss +5 -4
- package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +1 -1
- package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +11 -0
- package/src/App/components/MoveControl/MoveControl.jsx +73 -9
- package/src/App/components/MoveControl/MoveControl.module.scss +12 -2
- package/src/App/components/MoveControl/MoveControl.test.jsx +136 -0
- package/src/App/hooks/useHintsAPI.js +67 -0
- package/src/App/hooks/useHintsAPI.test.js +145 -0
- package/src/App/renderer/PluginInits.jsx +4 -0
- package/src/App/renderer/PluginInits.test.jsx +7 -1
- package/src/InteractiveMap/InteractiveMap.js +20 -0
- package/src/InteractiveMap/InteractiveMap.test.js +7 -0
- package/src/config/events.js +4 -0
- package/src/scss/settings/_dimensions.scss +3 -0
- package/src/services/announcer.js +38 -3
- package/src/services/announcer.test.js +133 -1
- package/src/services/hints.js +3 -0
- package/src/services/hints.test.js +19 -0
- package/src/types.js +12 -0
- package/src/utils/detectInterfaceType.js +5 -1
- package/src/utils/isMac.js +15 -0
- package/src/utils/isMac.test.js +37 -0
- package/webpack.umd.mjs +1 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What stage of the draw/edit lifecycle a geometry validation call is running at.
|
|
3
|
+
* Passed as `context.phase` through validateGeometry (validation/validateGeometry.js)
|
|
4
|
+
* to the default rules (validation/rules.js) and the user's `onGeometryChange` callback.
|
|
5
|
+
*
|
|
6
|
+
* @typedef {'preview' | 'place' | 'create' | 'edit-start' |
|
|
7
|
+
* 'commit-add' | 'commit-move' | 'commit-insert' | 'commit-delete'} GeometryChangePhase
|
|
8
|
+
*
|
|
9
|
+
* 'preview' - live, in-progress feedback (drag / rubber-band); nothing
|
|
10
|
+
* has committed yet, and nothing can be vetoed at this stage.
|
|
11
|
+
* While drawing, this single check also drives the
|
|
12
|
+
* Add-point button (see liveDrawChecks.js) — it isn't only
|
|
13
|
+
* a display concern, just never a commit/veto one.
|
|
14
|
+
* 'place' - a candidate vertex is genuinely about to be committed
|
|
15
|
+
* (a real click/tap/Add-point press), evaluated once,
|
|
16
|
+
* synchronously; a HARD_RULES failure vetoes it outright
|
|
17
|
+
* and it never appears. Never fires continuously — that's
|
|
18
|
+
* 'preview''s job (see above).
|
|
19
|
+
* 'create' - a whole new feature just finished being drawn.
|
|
20
|
+
* 'edit-start' - an existing feature was just loaded into an edit session;
|
|
21
|
+
* nothing has changed yet, this is the baseline check.
|
|
22
|
+
* 'commit-add' - a vertex was added while drawing.
|
|
23
|
+
* 'commit-move' - a vertex was dragged/nudged while editing.
|
|
24
|
+
* 'commit-insert' - a vertex was inserted at a midpoint while editing.
|
|
25
|
+
* 'commit-delete' - a vertex was removed while editing.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The single event object passed to a user's `onGeometryChange` callback
|
|
30
|
+
* (validateGeometry.js) — `onGeometryChange({ feature, ...context })`. Not every
|
|
31
|
+
* field is present on every phase; see each field's own doc below.
|
|
32
|
+
*
|
|
33
|
+
* While drawing, every rubber-band mouse-move invokes this callback ONCE,
|
|
34
|
+
* throttled to one call per animation frame, always with `phase: 'preview'` —
|
|
35
|
+
* even before any vertex is committed, since a location-based rule can be
|
|
36
|
+
* meaningful against a single candidate point. Its verdict is combined
|
|
37
|
+
* (internally, in liveDrawChecks.js) with two independently-floored built-in
|
|
38
|
+
* checks to drive BOTH the dashed stroke / Done gate AND the Add-point button —
|
|
39
|
+
* you don't need to do anything to support both; they share this one call. A
|
|
40
|
+
* separate, genuinely momentary call with `phase: 'place'` (see
|
|
41
|
+
* GeometryChangePhase) fires only for a real placement attempt, evaluated
|
|
42
|
+
* synchronously, never continuously. Earlier versions of this plugin called
|
|
43
|
+
* the callback twice per mouse-move (once per phase, for the same instant) —
|
|
44
|
+
* that's been unified into the single 'preview' call described here.
|
|
45
|
+
*
|
|
46
|
+
* @typedef {object} GeometryChangeEvent
|
|
47
|
+
* @property {object} feature - GeoJSON feature representing the shape at this
|
|
48
|
+
* point. Always present. During 'preview' this is the in-progress feature
|
|
49
|
+
* (placed vertices + rubber-band cursor); at every other phase it's the
|
|
50
|
+
* committed/candidate feature for that specific change.
|
|
51
|
+
* @property {GeometryChangePhase} phase - what stage of the draw/edit lifecycle
|
|
52
|
+
* this is. Always present.
|
|
53
|
+
* @property {'draw_polygon' | 'draw_line' | 'edit_vertex'} mode - the active draw
|
|
54
|
+
* mode. Always present.
|
|
55
|
+
* @property {number} [vertexIndex] - index of the vertex being placed, added,
|
|
56
|
+
* moved, inserted, or deleted. Present on 'place' and every 'commit-*' phase;
|
|
57
|
+
* absent on 'preview', 'create', and 'edit-start' (those have no single vertex
|
|
58
|
+
* to point at).
|
|
59
|
+
* @property {number} [numVertices] - count of already-committed vertices,
|
|
60
|
+
* excluding any in-progress rubber-band cursor point. Present on every
|
|
61
|
+
* 'preview' call, with no minimum — including 0, against the very first
|
|
62
|
+
* candidate point, before any vertex is committed. Note this is a lower bar
|
|
63
|
+
* than the built-in live rules, which stay skipped until the geometry type's
|
|
64
|
+
* minimum vertex count is reached (self-intersection/area are meaningless
|
|
65
|
+
* below that).
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Shared adapter event contract.
|
|
70
|
+
*
|
|
71
|
+
* Both draw adapters (MaplibreDrawAdapter, OLDrawAdapter) expose an on/off bus
|
|
72
|
+
* that emits these events. events.js and the api entry points consume them, so
|
|
73
|
+
* the names and payload shapes below are the contract between the adapters and
|
|
74
|
+
* the framework-agnostic plugin layer.
|
|
75
|
+
*
|
|
76
|
+
* Engine-specific event names stay with their adapter — e.g. the map-level
|
|
77
|
+
* `draw.*` events mapbox-gl-draw fires live in adapters/maplibre/drawEvents.js,
|
|
78
|
+
* and the MapLibre adapter normalises them onto this contract.
|
|
79
|
+
*
|
|
80
|
+
* Payloads:
|
|
81
|
+
* CREATE GeoJSON feature that was drawn
|
|
82
|
+
* EDIT_FINISH GeoJSON feature after editing
|
|
83
|
+
* CANCEL (none)
|
|
84
|
+
* VERTEX_SELECTION { index, numVertices }
|
|
85
|
+
* VERTEX_CHANGE { numVertices }
|
|
86
|
+
* UNDO_CHANGE number — current undo stack length
|
|
87
|
+
* UPDATE GeoJSON feature after a vertex operation
|
|
88
|
+
* GEOMETRY_CHANGE Two payload shapes share this event:
|
|
89
|
+
* - Preview (draw/split live update): the in-progress
|
|
90
|
+
* feature itself (has `coordinates`, no `phase`).
|
|
91
|
+
* - Commit-level validation: `{ feature, phase, vertexIndex }`
|
|
92
|
+
* where phase ∈ 'commit-add' | 'commit-move' |
|
|
93
|
+
* 'commit-insert' | 'commit-delete' (see GeometryChangePhase).
|
|
94
|
+
* events.js validates these and reverts invalid ones.
|
|
95
|
+
* Listeners must guard for the shape they expect.
|
|
96
|
+
* INTERFACE_TYPE_CHANGE { interfaceType: 'mouse' | 'touch' | 'keyboard' }
|
|
97
|
+
* PLACEMENT_BLOCKED { feature, reason, phase: 'place', mode, vertexIndex } —
|
|
98
|
+
* a REAL placement attempt (click/tap/Add-point press)
|
|
99
|
+
* was rejected by validatePlacement (hard rule or user
|
|
100
|
+
* callback), evaluated once, synchronously; feature is
|
|
101
|
+
* the candidate geometry that was refused. Distinct from
|
|
102
|
+
* CAN_PLACE_CHANGE below, which is a continuous forecast
|
|
103
|
+
* of this same outcome, not an attempt.
|
|
104
|
+
* VALIDITY_CHANGE { valid, reason } — the live validity of the displayed
|
|
105
|
+
* shape flipped while editing (drag/nudge in progress).
|
|
106
|
+
* Drives the Done gate in edit mode, where the displayed
|
|
107
|
+
* shape is exactly what Done finishes. Fires on flips only.
|
|
108
|
+
* CAN_PLACE_CHANGE { canPlace, reason } — whether placing a vertex at the
|
|
109
|
+
* crosshair would be vetoed flipped while drawing. Drives
|
|
110
|
+
* the Add-point button. Derived from the same 'preview'
|
|
111
|
+
* check that drives the dashed stroke (liveDrawChecks.js),
|
|
112
|
+
* not a separate attempt — fires on flips only.
|
|
113
|
+
*/
|
|
114
|
+
export const ADAPTER_EVENTS = {
|
|
115
|
+
CREATE: 'create',
|
|
116
|
+
EDIT_FINISH: 'editfinish',
|
|
117
|
+
CANCEL: 'cancel',
|
|
118
|
+
VERTEX_SELECTION: 'vertexselection',
|
|
119
|
+
VERTEX_CHANGE: 'vertexchange',
|
|
120
|
+
UNDO_CHANGE: 'undochange',
|
|
121
|
+
UPDATE: 'update',
|
|
122
|
+
GEOMETRY_CHANGE: 'geometrychange',
|
|
123
|
+
INTERFACE_TYPE_CHANGE: 'interfacetypechange',
|
|
124
|
+
PLACEMENT_BLOCKED: 'placementblocked',
|
|
125
|
+
VALIDITY_CHANGE: 'validitychange',
|
|
126
|
+
CAN_PLACE_CHANGE: 'canplacechange'
|
|
127
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ADAPTER_EVENTS } from './adapterEvents.js'
|
|
2
|
+
|
|
3
|
+
describe('adapter event contract', () => {
|
|
4
|
+
test('event names are stable — renaming a value breaks adapter consumers', () => {
|
|
5
|
+
expect(ADAPTER_EVENTS).toEqual({
|
|
6
|
+
CREATE: 'create',
|
|
7
|
+
EDIT_FINISH: 'editfinish',
|
|
8
|
+
CANCEL: 'cancel',
|
|
9
|
+
VERTEX_SELECTION: 'vertexselection',
|
|
10
|
+
VERTEX_CHANGE: 'vertexchange',
|
|
11
|
+
UNDO_CHANGE: 'undochange',
|
|
12
|
+
UPDATE: 'update',
|
|
13
|
+
GEOMETRY_CHANGE: 'geometrychange',
|
|
14
|
+
INTERFACE_TYPE_CHANGE: 'interfacetypechange',
|
|
15
|
+
PLACEMENT_BLOCKED: 'placementblocked',
|
|
16
|
+
VALIDITY_CHANGE: 'validitychange',
|
|
17
|
+
CAN_PLACE_CHANGE: 'canplacechange'
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { MaplibreDrawAdapter } from './maplibre/MaplibreDrawAdapter.js'
|
|
2
|
+
import { OLDrawAdapter } from './openlayers/OLDrawAdapter.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The shared adapter contract: events.js, DrawInit and the api entry points are
|
|
6
|
+
* written against this surface, so both adapters must implement all of it — even
|
|
7
|
+
* where an engine needs only a documented no-op (deleteVertex on MapLibre,
|
|
8
|
+
* setFeatureProperty on OpenLayers). A method added to one adapter but not the
|
|
9
|
+
* other fails here before it fails at runtime on the other engine.
|
|
10
|
+
*/
|
|
11
|
+
const CONTRACT_METHODS = [
|
|
12
|
+
'changeMode',
|
|
13
|
+
'getMode',
|
|
14
|
+
'setInterfaceType',
|
|
15
|
+
'done',
|
|
16
|
+
'cancel',
|
|
17
|
+
'undo',
|
|
18
|
+
'deleteVertex',
|
|
19
|
+
'nudgeSelectedVertex',
|
|
20
|
+
'get',
|
|
21
|
+
'add',
|
|
22
|
+
'delete',
|
|
23
|
+
'deleteAll',
|
|
24
|
+
'setSnapEnabled',
|
|
25
|
+
'setSnapLayers',
|
|
26
|
+
'isSnapEnabled',
|
|
27
|
+
'setFeatureProperty',
|
|
28
|
+
'setGeometryValid',
|
|
29
|
+
'setInvalid',
|
|
30
|
+
'on',
|
|
31
|
+
'off',
|
|
32
|
+
'remove'
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
describe.each([
|
|
36
|
+
['MaplibreDrawAdapter', MaplibreDrawAdapter],
|
|
37
|
+
['OLDrawAdapter', OLDrawAdapter]
|
|
38
|
+
])('%s adapter contract', (name, Adapter) => {
|
|
39
|
+
test.each(CONTRACT_METHODS)('implements %s()', (method) => {
|
|
40
|
+
expect(typeof Adapter.prototype[method]).toBe('function')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('exposes the _geometryValidator accessor pair (user callback storage)', () => {
|
|
44
|
+
const descriptor = Object.getOwnPropertyDescriptor(Adapter.prototype, '_geometryValidator')
|
|
45
|
+
expect(typeof descriptor?.get).toBe('function')
|
|
46
|
+
expect(typeof descriptor?.set).toBe('function')
|
|
47
|
+
})
|
|
48
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const loadDrawAdapter = async (mapProvider, options) => {
|
|
2
|
+
switch (mapProvider.name) {
|
|
3
|
+
case 'MapLibreProvider': {
|
|
4
|
+
const { MaplibreDrawAdapter } = await import(/* webpackChunkName: "im-draw-ml-adapter" */ './maplibre/MaplibreDrawAdapter.js')
|
|
5
|
+
return new MaplibreDrawAdapter(mapProvider, options)
|
|
6
|
+
}
|
|
7
|
+
case 'OpenLayersProvider': {
|
|
8
|
+
const { OLDrawAdapter } = await import(/* webpackChunkName: "im-draw-ol-adapter" */ './openlayers/OLDrawAdapter.js')
|
|
9
|
+
return new OLDrawAdapter(mapProvider, options)
|
|
10
|
+
}
|
|
11
|
+
default:
|
|
12
|
+
throw new Error(`No draw adapter available for map provider "${mapProvider.name}"`)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { loadDrawAdapter } from './loadDrawAdapter.js'
|
|
2
|
+
import { MaplibreDrawAdapter } from './maplibre/MaplibreDrawAdapter.js'
|
|
3
|
+
import { OLDrawAdapter } from './openlayers/OLDrawAdapter.js'
|
|
4
|
+
|
|
5
|
+
jest.mock('./maplibre/MaplibreDrawAdapter.js', () => ({
|
|
6
|
+
MaplibreDrawAdapter: jest.fn(function (mapProvider, options) {
|
|
7
|
+
this.mapProvider = mapProvider
|
|
8
|
+
this.options = options
|
|
9
|
+
})
|
|
10
|
+
}))
|
|
11
|
+
|
|
12
|
+
jest.mock('./openlayers/OLDrawAdapter.js', () => ({
|
|
13
|
+
OLDrawAdapter: jest.fn(function (mapProvider, options) {
|
|
14
|
+
this.mapProvider = mapProvider
|
|
15
|
+
this.options = options
|
|
16
|
+
})
|
|
17
|
+
}))
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
jest.clearAllMocks()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe('loadDrawAdapter', () => {
|
|
24
|
+
test('loads the MapLibre adapter for a MapLibreProvider', async () => {
|
|
25
|
+
const mapProvider = { name: 'MapLibreProvider' }
|
|
26
|
+
const options = { mapStyle: 'light' }
|
|
27
|
+
|
|
28
|
+
const adapter = await loadDrawAdapter(mapProvider, options)
|
|
29
|
+
|
|
30
|
+
expect(MaplibreDrawAdapter).toHaveBeenCalledWith(mapProvider, options)
|
|
31
|
+
expect(OLDrawAdapter).not.toHaveBeenCalled()
|
|
32
|
+
expect(adapter).toBeInstanceOf(MaplibreDrawAdapter)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('loads the OpenLayers adapter for an OpenLayersProvider', async () => {
|
|
36
|
+
const mapProvider = { name: 'OpenLayersProvider' }
|
|
37
|
+
const options = { mapStyle: 'dark' }
|
|
38
|
+
|
|
39
|
+
const adapter = await loadDrawAdapter(mapProvider, options)
|
|
40
|
+
|
|
41
|
+
expect(OLDrawAdapter).toHaveBeenCalledWith(mapProvider, options)
|
|
42
|
+
expect(MaplibreDrawAdapter).not.toHaveBeenCalled()
|
|
43
|
+
expect(adapter).toBeInstanceOf(OLDrawAdapter)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('throws for an unsupported map provider', async () => {
|
|
47
|
+
await expect(loadDrawAdapter({ name: 'SomethingElse' }, {}))
|
|
48
|
+
.rejects.toThrow('No draw adapter available for map provider "SomethingElse"')
|
|
49
|
+
|
|
50
|
+
expect(MaplibreDrawAdapter).not.toHaveBeenCalled()
|
|
51
|
+
expect(OLDrawAdapter).not.toHaveBeenCalled()
|
|
52
|
+
})
|
|
53
|
+
})
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { createMapboxDraw } from './mapboxDraw.js'
|
|
2
|
+
import { getSnapInstance, clearSnapState, clearSnapIndicator } from './utils/snapHelpers.js'
|
|
3
|
+
import { createEventBus } from '../../utils/eventBus.js'
|
|
4
|
+
import { MAPBOX_DRAW_EVENTS, CUSTOM_DRAW_EVENTS, STYLE_DATA_EVENT } from './drawEvents.js'
|
|
5
|
+
import { ADAPTER_EVENTS } from '../../adapterEvents.js'
|
|
6
|
+
import { createLiveStroke } from '../../validation/liveStroke.js'
|
|
7
|
+
import { createLiveDrawChecks } from '../../validation/liveDrawChecks.js'
|
|
8
|
+
|
|
9
|
+
const polygonFeature = (coordinates) => ({ type: 'Feature', geometry: { type: 'Polygon', coordinates } })
|
|
10
|
+
const lineFeature = (coordinates) => ({ type: 'Feature', geometry: { type: 'LineString', coordinates } })
|
|
11
|
+
|
|
12
|
+
// The displayed feature + placed-vertex count for the live stroke check. MapLibre's
|
|
13
|
+
// fire() copies the payload onto an Event whose `type` is the event name, so the
|
|
14
|
+
// geometry type is clobbered — it comes from the draw mode, or (in edit, where the
|
|
15
|
+
// mode covers both shapes) from the coordinate nesting: a polygon's coordinates are
|
|
16
|
+
// rings (one level deeper than a line's). Draw-mode coordinates carry a trailing
|
|
17
|
+
// rubber-band point; edit-mode coordinates are all committed vertices.
|
|
18
|
+
export const displayedShape = (mode, coordinates) => {
|
|
19
|
+
if (mode === 'draw_polygon') {
|
|
20
|
+
return { feature: polygonFeature(coordinates), numVertices: (coordinates[0]?.length ?? 1) - 1 }
|
|
21
|
+
}
|
|
22
|
+
if (mode === 'draw_line') {
|
|
23
|
+
return { feature: lineFeature(coordinates), numVertices: (coordinates?.length ?? 1) - 1 }
|
|
24
|
+
}
|
|
25
|
+
if (mode === 'edit_vertex') {
|
|
26
|
+
return Array.isArray(coordinates[0]?.[0])
|
|
27
|
+
? { feature: polygonFeature(coordinates), numVertices: coordinates[0]?.length ?? 0 }
|
|
28
|
+
: { feature: lineFeature(coordinates), numVertices: coordinates?.length ?? 0 }
|
|
29
|
+
}
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Draw adapter for MapLibre GL.
|
|
35
|
+
*
|
|
36
|
+
* Wraps the MapboxDraw instance and normalises its map-event-based API into the
|
|
37
|
+
* shared adapter interface consumed by events.js, DrawInit, and the api entry points.
|
|
38
|
+
*
|
|
39
|
+
* Adapter interface (also implemented by OLDrawAdapter):
|
|
40
|
+
* changeMode(name, options)
|
|
41
|
+
* getMode()
|
|
42
|
+
* setInterfaceType(type)
|
|
43
|
+
* done() / cancel() / undo() / deleteVertex()
|
|
44
|
+
* nudgeSelectedVertex(dx, dy, isLargeStep)
|
|
45
|
+
* get(id) / add(feature) / delete(id) / deleteAll()
|
|
46
|
+
* setSnapEnabled(bool) / setSnapLayers(layers) / isSnapEnabled()
|
|
47
|
+
* setFeatureProperty(id, property, value) / setDrawingPreviewProperty(property, value)
|
|
48
|
+
* on(event, handler) / off(event, handler)
|
|
49
|
+
* remove()
|
|
50
|
+
*/
|
|
51
|
+
export class MaplibreDrawAdapter {
|
|
52
|
+
constructor (mapProvider, options) {
|
|
53
|
+
this._mapProvider = mapProvider
|
|
54
|
+
this._map = mapProvider.map
|
|
55
|
+
this._bus = createEventBus()
|
|
56
|
+
this._editingFeatureId = null
|
|
57
|
+
|
|
58
|
+
const { draw, remove } = createMapboxDraw({
|
|
59
|
+
mapStyle: options.mapStyle,
|
|
60
|
+
mapProvider,
|
|
61
|
+
events: options.events,
|
|
62
|
+
eventBus: options.eventBus,
|
|
63
|
+
snapLayers: options.snapLayers,
|
|
64
|
+
pluginConfig: options.pluginConfig ?? {}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
this._draw = draw
|
|
68
|
+
this._cleanupDraw = remove
|
|
69
|
+
|
|
70
|
+
// Single owner of the dashed-stroke state: live rubber-band / drag moves feed
|
|
71
|
+
// update() (default rules sync, user callback throttled) and committed verdicts
|
|
72
|
+
// (events.js) land via setInvalid → set(), so the cached state always mirrors
|
|
73
|
+
// the rendered layers. onChange does the actual layer toggle; in edit mode the
|
|
74
|
+
// displayed shape is exactly what Done finishes, so validity flips also gate
|
|
75
|
+
// the Done button (events.js dispatches them).
|
|
76
|
+
this._liveStroke = createLiveStroke({
|
|
77
|
+
onChange: (invalid, reason) => {
|
|
78
|
+
this._applyStrokeInvalid(invalid)
|
|
79
|
+
if (this._draw.getMode() === 'edit_vertex') {
|
|
80
|
+
this._bus.emit(ADAPTER_EVENTS.VALIDITY_CHANGE, { valid: !invalid, reason })
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
// Draw-mode-only: computes both the stroke/Done verdict and the Add-point
|
|
86
|
+
// verdict from a SINGLE throttled call to the user's callback per rubber-band
|
|
87
|
+
// move (see liveDrawChecks.js for why), and is flip-guarded itself. The
|
|
88
|
+
// stroke verdict still routes through _liveStroke.set() — that instance is
|
|
89
|
+
// also driven independently by edit mode and must stay the single source of
|
|
90
|
+
// truth for the rendered stroke across mode switches. The Add-point verdict
|
|
91
|
+
// has no such cross-mode instance to stay in sync with, so it emits directly.
|
|
92
|
+
this._liveDrawChecks = createLiveDrawChecks({
|
|
93
|
+
onStrokeChange: (invalid, reason) => this._liveStroke.set(invalid, reason),
|
|
94
|
+
onPlaceChange: (vetoed, reason) => this._bus.emit(ADAPTER_EVENTS.CAN_PLACE_CHANGE, { canPlace: !vetoed, reason })
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
// Normalise ML map events → the shared adapter event contract (adapterEvents.js).
|
|
98
|
+
// The OL adapter emits the same contract directly from OLDrawManager.
|
|
99
|
+
this._mapHandlers = {
|
|
100
|
+
create: (e) => this._bus.emit(ADAPTER_EVENTS.CREATE, e.features[0]),
|
|
101
|
+
editfinish: (e) => this._bus.emit(ADAPTER_EVENTS.EDIT_FINISH, e.features[0]),
|
|
102
|
+
cancel: () => this._bus.emit(ADAPTER_EVENTS.CANCEL),
|
|
103
|
+
// Normalise typo: the ML modes fire numVertecies, the contract uses numVertices
|
|
104
|
+
vertexselection: (e) => this._bus.emit(ADAPTER_EVENTS.VERTEX_SELECTION, { ...e, numVertices: e.numVertecies }),
|
|
105
|
+
vertexchange: (e) => this._bus.emit(ADAPTER_EVENTS.VERTEX_CHANGE, { ...e, numVertices: e.numVertecies }),
|
|
106
|
+
undochange: (e) => this._bus.emit(ADAPTER_EVENTS.UNDO_CHANGE, e.length),
|
|
107
|
+
update: (e) => this._bus.emit(ADAPTER_EVENTS.UPDATE, e.features[0]),
|
|
108
|
+
geometrychange: (e) => {
|
|
109
|
+
// Phase-less events are rubber-band moves carrying the displayed feature
|
|
110
|
+
// (placed vertices + cursor) — they drive the live invalid stroke, and are
|
|
111
|
+
// cached for setDrawingPreviewProperty (the in-progress feature has no
|
|
112
|
+
// stable id yet — only assigned once drawing actually finishes — so it
|
|
113
|
+
// can't be targeted via setFeatureProperty).
|
|
114
|
+
if (!e?.phase) {
|
|
115
|
+
this._updateLiveStroke(e)
|
|
116
|
+
this._currentDrawEvent = e
|
|
117
|
+
}
|
|
118
|
+
this._bus.emit(ADAPTER_EVENTS.GEOMETRY_CHANGE, e)
|
|
119
|
+
},
|
|
120
|
+
placementblocked: (e) => this._bus.emit(ADAPTER_EVENTS.PLACEMENT_BLOCKED, e),
|
|
121
|
+
interfacetypechange: (e) => this._bus.emit(ADAPTER_EVENTS.INTERFACE_TYPE_CHANGE, { interfaceType: e.interfaceType }),
|
|
122
|
+
modechange: (e) => this._handleModeChange(e),
|
|
123
|
+
styledata: () => this._handleStyleData()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this._map.on(MAPBOX_DRAW_EVENTS.CREATE, this._mapHandlers.create)
|
|
127
|
+
this._map.on(CUSTOM_DRAW_EVENTS.EDIT_FINISH, this._mapHandlers.editfinish)
|
|
128
|
+
this._map.on(CUSTOM_DRAW_EVENTS.CANCEL, this._mapHandlers.cancel)
|
|
129
|
+
this._map.on(CUSTOM_DRAW_EVENTS.VERTEX_SELECTION, this._mapHandlers.vertexselection)
|
|
130
|
+
this._map.on(CUSTOM_DRAW_EVENTS.VERTEX_CHANGE, this._mapHandlers.vertexchange)
|
|
131
|
+
this._map.on(CUSTOM_DRAW_EVENTS.UNDO_CHANGE, this._mapHandlers.undochange)
|
|
132
|
+
this._map.on(MAPBOX_DRAW_EVENTS.UPDATE, this._mapHandlers.update)
|
|
133
|
+
this._map.on(CUSTOM_DRAW_EVENTS.GEOMETRY_CHANGE, this._mapHandlers.geometrychange)
|
|
134
|
+
this._map.on(CUSTOM_DRAW_EVENTS.PLACEMENT_BLOCKED, this._mapHandlers.placementblocked)
|
|
135
|
+
this._map.on(CUSTOM_DRAW_EVENTS.INTERFACE_TYPE_CHANGE, this._mapHandlers.interfacetypechange)
|
|
136
|
+
this._map.on(MAPBOX_DRAW_EVENTS.MODE_CHANGE, this._mapHandlers.modechange)
|
|
137
|
+
this._map.on(STYLE_DATA_EVENT, this._mapHandlers.styledata)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
changeMode (name, options = {}) {
|
|
141
|
+
if (name === 'edit_vertex') {
|
|
142
|
+
this._editingFeatureId = options.featureId ?? null
|
|
143
|
+
}
|
|
144
|
+
// A fresh draw always starts with a solid stroke and a placeable crosshair;
|
|
145
|
+
// the live checks own both from here.
|
|
146
|
+
if (name === 'draw_polygon' || name === 'draw_line') {
|
|
147
|
+
this._liveStroke.set(false)
|
|
148
|
+
this._liveDrawChecks.reset()
|
|
149
|
+
}
|
|
150
|
+
this._draw.changeMode(name, options)
|
|
151
|
+
// The underlying mapbox-gl-draw control's public changeMode API is silent by
|
|
152
|
+
// default (it never fires 'draw.modechange'), so every mode change requested
|
|
153
|
+
// through this adapter must drive the same cleanup manually.
|
|
154
|
+
this._handleModeChange({ mode: name })
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Live invalid-stroke driver: called on every rubber-band move (draw) and vertex
|
|
158
|
+
// drag / nudge (edit) with the displayed feature. Edit mode has no Add-point
|
|
159
|
+
// gate, so it goes straight through the live-stroke controller as before. Draw
|
|
160
|
+
// mode routes through _liveDrawChecks instead, which computes both the stroke
|
|
161
|
+
// and Add-point verdicts from one throttled user-callback call (see
|
|
162
|
+
// liveDrawChecks.js).
|
|
163
|
+
_updateLiveStroke (e) {
|
|
164
|
+
if (!e?.coordinates) { return }
|
|
165
|
+
const mode = this._draw.getMode()
|
|
166
|
+
const shape = displayedShape(mode, e.coordinates)
|
|
167
|
+
if (!shape) { return }
|
|
168
|
+
if (mode === 'draw_polygon' || mode === 'draw_line') {
|
|
169
|
+
this._liveDrawChecks.update({ feature: shape.feature, numVertices: shape.numVertices, context: { mode }, onGeometryChange: this._geometryValidator })
|
|
170
|
+
} else {
|
|
171
|
+
this._liveStroke.update({ ...shape, context: { mode }, onGeometryChange: this._geometryValidator })
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
getMode () { return this._draw.getMode() }
|
|
176
|
+
|
|
177
|
+
setInterfaceType (type) {
|
|
178
|
+
this._map.fire(CUSTOM_DRAW_EVENTS.INTERFACE_TYPE_CHANGE, { interfaceType: type })
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
done () {
|
|
182
|
+
this._mapProvider.undoStack?.clear()
|
|
183
|
+
const mode = this._draw.getMode()
|
|
184
|
+
if (mode === 'edit_vertex' && this._editingFeatureId) {
|
|
185
|
+
// Leaving edit_vertex here — hide immediately rather than waiting on the
|
|
186
|
+
// async disable() the EDIT_FINISH handler fires later (see changeMode()).
|
|
187
|
+
this._handleModeChange({ mode: 'disabled' })
|
|
188
|
+
this._map.fire(CUSTOM_DRAW_EVENTS.EDIT_FINISH, { features: [this._draw.get(this._editingFeatureId)] })
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
if (mode === 'draw_polygon' || mode === 'draw_line') {
|
|
192
|
+
this._draw.changeMode('disabled')
|
|
193
|
+
this._handleModeChange({ mode: 'disabled' })
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
cancel () {
|
|
198
|
+
this._mapProvider.undoStack?.clear()
|
|
199
|
+
const mode = this._draw.getMode()
|
|
200
|
+
// trash() only belongs to an in-progress, never-committed draw — it deletes
|
|
201
|
+
// whatever's selected. For edit_vertex, events.js's handleCancel has already
|
|
202
|
+
// restored the original feature via draw.add() before this runs; trash()
|
|
203
|
+
// here would instead run mapbox-gl-draw's direct_select onTrash on the
|
|
204
|
+
// mode's own live state (removing the selected vertex, or — if the
|
|
205
|
+
// in-progress edit happened to leave the shape invalid — deleting the
|
|
206
|
+
// entire feature), silently discarding the just-restored original.
|
|
207
|
+
if (mode === 'draw_polygon' || mode === 'draw_line') {
|
|
208
|
+
this._draw.trash()
|
|
209
|
+
}
|
|
210
|
+
this._draw.changeMode('disabled')
|
|
211
|
+
this._handleModeChange({ mode: 'disabled' })
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
undo () {
|
|
215
|
+
this._map.fire(CUSTOM_DRAW_EVENTS.UNDO)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// MoveControl's D-pad, routed here via mapProvider.activeMoveTarget (see
|
|
219
|
+
// events.js) once a vertex is selected. Bridged into the running edit mode the
|
|
220
|
+
// same way setInterfaceType is, since the adapter has no direct reference to the
|
|
221
|
+
// mode's live state.
|
|
222
|
+
nudgeSelectedVertex (dx, dy, isLargeStep) {
|
|
223
|
+
this._map.fire(CUSTOM_DRAW_EVENTS.NUDGE_VERTEX, { dx, dy, isLargeStep })
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Record the current geometry validity so the draw mode can block finish gestures
|
|
227
|
+
// (double-click / click-to-close) while the in-progress shape is invalid.
|
|
228
|
+
setGeometryValid (valid) {
|
|
229
|
+
this._map._drawGeometryValid = valid
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// The api entry points assign the active user validator to the adapter; store it
|
|
233
|
+
// on the map (like _drawGeometryValid) so modes can veto placements synchronously.
|
|
234
|
+
set _geometryValidator (fn) { this._map._drawGeometryValidator = fn }
|
|
235
|
+
get _geometryValidator () { return this._map._drawGeometryValidator }
|
|
236
|
+
|
|
237
|
+
// Committed-verdict write (events.js, edit mode): routed through the live-stroke
|
|
238
|
+
// controller so its cached state stays in sync with the rendered layers.
|
|
239
|
+
setInvalid (invalid) {
|
|
240
|
+
this._liveStroke.set(invalid)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Toggle the active shape's stroke between solid (valid) and dashed (invalid) by
|
|
244
|
+
// swapping which of the two overlaid stroke layers is visible; the fill is hidden
|
|
245
|
+
// while invalid so the shape reads as an outline only. Only the live-stroke
|
|
246
|
+
// controller calls this — everything else goes through setInvalid.
|
|
247
|
+
_applyStrokeInvalid (invalid) {
|
|
248
|
+
this._setLayerVisibility('stroke-active', !invalid)
|
|
249
|
+
this._setLayerVisibility('stroke-active-invalid', invalid)
|
|
250
|
+
this._setLayerVisibility('fill-active', !invalid)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
_setLayerVisibility (id, visible) {
|
|
254
|
+
['hot', 'cold'].forEach((suffix) => {
|
|
255
|
+
const layerId = `${id}.${suffix}`
|
|
256
|
+
if (this._map.getLayer(layerId)) {
|
|
257
|
+
this._map.setLayoutProperty(layerId, 'visibility', visible ? 'visible' : 'none')
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
deleteVertex () {
|
|
263
|
+
// Intentionally a no-op on MapLibre. The shared events layer calls draw.deleteVertex()
|
|
264
|
+
// from the delete-point button, but the ML edit mode already handles deletion itself:
|
|
265
|
+
// via the keyboard (Backspace/Delete) and its own window-click listener matching
|
|
266
|
+
// deleteVertexButtonId (see editVertexMode.onButtonClick). Deleting here too would
|
|
267
|
+
// double-delete. The OL adapter implements this for real; here it only satisfies the
|
|
268
|
+
// shared adapter interface.
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
get (id) { return this._draw.get(id) }
|
|
272
|
+
add (feature) { return this._draw.add(feature) }
|
|
273
|
+
delete (id) { this._draw.delete(id) }
|
|
274
|
+
deleteAll () { this._draw.deleteAll() }
|
|
275
|
+
|
|
276
|
+
setSnapEnabled (bool) {
|
|
277
|
+
this._mapProvider.snapEnabled = bool
|
|
278
|
+
const snap = getSnapInstance(this._map)
|
|
279
|
+
if (snap?.setSnapStatus) { snap.setSnapStatus(bool) }
|
|
280
|
+
if (!bool && snap) {
|
|
281
|
+
clearSnapState(snap)
|
|
282
|
+
if (this._map.getLayer('snap-helper-circle')) {
|
|
283
|
+
this._map.setLayoutProperty('snap-helper-circle', 'visibility', 'none')
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
setSnapLayers (layers) {
|
|
289
|
+
const snap = getSnapInstance(this._map)
|
|
290
|
+
if (snap?.setSnapLayers) {
|
|
291
|
+
snap.setSnapLayers(layers)
|
|
292
|
+
} else if (layers) {
|
|
293
|
+
this._map._pendingSnapLayers = layers
|
|
294
|
+
} else {
|
|
295
|
+
// No action
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
isSnapEnabled () {
|
|
300
|
+
return this._mapProvider.snapEnabled === true
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
setFeatureProperty (id, property, value) {
|
|
304
|
+
this._draw.setFeatureProperty(id, property, value)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Tag the feature currently being drawn (rubber-band, not yet created — so it
|
|
308
|
+
// has no stable id to target via setFeatureProperty) with a property, and
|
|
309
|
+
// re-render so the change is visible immediately. Used for live preview styling
|
|
310
|
+
// while drawing, e.g. split's valid/invalid line colour. A no-op once nothing
|
|
311
|
+
// has been drawn yet this session, or outside draw_polygon/draw_line.
|
|
312
|
+
setDrawingPreviewProperty (property, value) {
|
|
313
|
+
const e = this._currentDrawEvent
|
|
314
|
+
if (e?.properties) {
|
|
315
|
+
e.properties[property] = value
|
|
316
|
+
}
|
|
317
|
+
e?.ctx?.store?.render()
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
on (type, handler) {
|
|
321
|
+
this._bus.on(type, handler)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
off (type, handler) {
|
|
325
|
+
this._bus.off(type, handler)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
_handleModeChange (e) {
|
|
329
|
+
const DRAW_MODES = new Set(['draw_polygon', 'draw_line', 'edit_vertex'])
|
|
330
|
+
if (!DRAW_MODES.has(e.mode)) {
|
|
331
|
+
clearSnapIndicator(getSnapInstance(this._map), this._map)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Keeps draw layers on top after MapLibre style reloads
|
|
336
|
+
_handleStyleData () {
|
|
337
|
+
// A style reload re-adds the draw layers with their spec-default visibility
|
|
338
|
+
// (solid stroke shown, dashed hidden) — re-assert the cached stroke state so
|
|
339
|
+
// an invalid shape stays dashed across the reload.
|
|
340
|
+
this._liveStroke.refresh()
|
|
341
|
+
const layers = this._map.getStyle().layers || []
|
|
342
|
+
if (!layers.length || layers[layers.length - 1].source?.startsWith('mapbox-gl-draw')) {
|
|
343
|
+
return
|
|
344
|
+
}
|
|
345
|
+
layers
|
|
346
|
+
.filter(l => l.source?.startsWith('mapbox-gl-draw'))
|
|
347
|
+
.forEach(l => this._map.moveLayer(l.id))
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
remove () {
|
|
351
|
+
this._map.off(MAPBOX_DRAW_EVENTS.CREATE, this._mapHandlers.create)
|
|
352
|
+
this._map.off(CUSTOM_DRAW_EVENTS.EDIT_FINISH, this._mapHandlers.editfinish)
|
|
353
|
+
this._map.off(CUSTOM_DRAW_EVENTS.CANCEL, this._mapHandlers.cancel)
|
|
354
|
+
this._map.off(CUSTOM_DRAW_EVENTS.VERTEX_SELECTION, this._mapHandlers.vertexselection)
|
|
355
|
+
this._map.off(CUSTOM_DRAW_EVENTS.VERTEX_CHANGE, this._mapHandlers.vertexchange)
|
|
356
|
+
this._map.off(CUSTOM_DRAW_EVENTS.UNDO_CHANGE, this._mapHandlers.undochange)
|
|
357
|
+
this._map.off(MAPBOX_DRAW_EVENTS.UPDATE, this._mapHandlers.update)
|
|
358
|
+
this._map.off(CUSTOM_DRAW_EVENTS.GEOMETRY_CHANGE, this._mapHandlers.geometrychange)
|
|
359
|
+
this._map.off(CUSTOM_DRAW_EVENTS.PLACEMENT_BLOCKED, this._mapHandlers.placementblocked)
|
|
360
|
+
this._map.off(CUSTOM_DRAW_EVENTS.INTERFACE_TYPE_CHANGE, this._mapHandlers.interfacetypechange)
|
|
361
|
+
this._map.off(MAPBOX_DRAW_EVENTS.MODE_CHANGE, this._mapHandlers.modechange)
|
|
362
|
+
this._map.off(STYLE_DATA_EVENT, this._mapHandlers.styledata)
|
|
363
|
+
this._liveStroke.destroy()
|
|
364
|
+
this._liveDrawChecks.destroy()
|
|
365
|
+
this._cleanupDraw()
|
|
366
|
+
}
|
|
367
|
+
}
|