@defra/interactive-map 0.0.4-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/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.md +8 -0
- package/package.json +3 -2
- package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +2 -1
- package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js.LICENSE.txt +1 -0
- package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +2 -1
- package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js.LICENSE.txt +1 -0
- package/plugins/beta/datasets/dist/umd/index.js +1 -1
- package/plugins/beta/datasets/src/datasets.js +55 -2
- package/plugins/beta/datasets/src/fetch/createDynamicSource.js +206 -0
- package/plugins/beta/datasets/src/fetch/fetchGeoJSON.js +38 -0
- package/plugins/beta/datasets/src/handleSetMapStyle.js +8 -1
- package/plugins/beta/datasets/src/mapLayers.js +41 -3
- package/plugins/beta/datasets/src/utils/bbox.js +113 -2
- package/plugins/beta/draw-ml/dist/css/index.css +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-ml/dist/umd/index.js +1 -1
- package/plugins/beta/draw-ml/src/api/addFeature.js +15 -3
- package/plugins/beta/draw-ml/src/api/editFeature.js +16 -5
- package/plugins/beta/draw-ml/src/api/newLine.js +32 -7
- package/plugins/beta/draw-ml/src/api/newPolygon.js +33 -8
- package/plugins/beta/draw-ml/src/defaults.js +1 -0
- package/plugins/beta/draw-ml/src/draw.scss +23 -0
- package/plugins/beta/draw-ml/src/events.js +55 -113
- package/plugins/beta/draw-ml/src/manifest.js +64 -38
- package/plugins/beta/draw-ml/src/mapboxSnap.js +16 -12
- package/plugins/beta/draw-ml/src/modes/createDrawMode.js +4 -3
- 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 +51 -406
- package/plugins/beta/draw-ml/src/utils/flattenStyleProperties.js +49 -0
- package/plugins/beta/frame/dist/esm/im-frame-plugin.js +1 -1
- package/plugins/beta/frame/dist/umd/im-frame-plugin.js +1 -1
- package/plugins/beta/frame/src/Frame.jsx +1 -1
- package/plugins/beta/map-styles/dist/css/index.css +1 -1
- package/plugins/beta/map-styles/dist/esm/index.js +1 -1
- package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
- package/plugins/beta/map-styles/dist/umd/index.js +1 -1
- package/plugins/beta/map-styles/src/mapStyles.scss +4 -1
- package/plugins/interact/dist/css/index.css +1 -1
- 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/dist/umd/index.js +1 -1
- package/plugins/interact/src/InteractInit.jsx +1 -1
- package/plugins/interact/src/defaults.js +1 -0
- package/plugins/interact/src/events.js +37 -52
- package/plugins/interact/src/events.test.js +35 -0
- package/plugins/interact/src/hooks/useHighlightSync.js +1 -1
- package/plugins/interact/src/hooks/useInteractionHandlers.js +80 -48
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +64 -8
- package/plugins/interact/src/interact.scss +6 -0
- package/plugins/interact/src/reducer.js +25 -27
- package/plugins/interact/src/reducer.test.js +19 -6
- package/plugins/interact/src/utils/featureQueries.js +2 -2
- 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/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 +12 -12
- package/providers/beta/esri/src/esriProvider.js +1 -1
- package/providers/beta/esri/src/index.js +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-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 +22 -19
- package/providers/maplibre/src/maplibreProvider.js +13 -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 +39 -2
- 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 +16 -4
- 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 +11 -8
- 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 +25 -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 +79 -6
- 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 +1 -0
- package/src/index.umd.js +1 -1
- package/src/scss/main.scss +1 -0
- package/src/scss/settings/_dimensions.scss +6 -2
- package/src/services/eventBus.test.js +24 -1
- package/src/test-utils.js +1 -0
- package/src/types.js +5 -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/plugins/interact/src/utils/turfHelpers.js +0 -30
- package/plugins/interact/src/utils/turfHelpers.test.js +0 -30
- package/plugins/search/src/utils/fetchDataset.js +0 -23
- package/providers/maplibre/dist/esm/im-maplibre-legacy-framework.js +0 -1
- package/providers/maplibre/dist/umd/im-maplibre-legacy-framework.js +0 -1
|
@@ -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
|
+
}
|