@deck.gl-community/editable-layers 9.2.0-beta.8 → 9.2.8
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 +3 -0
- package/dist/edit-modes/draw-90degree-polygon-mode.d.ts.map +1 -1
- package/dist/edit-modes/draw-90degree-polygon-mode.js.map +1 -1
- package/dist/edit-modes/draw-line-string-mode.d.ts.map +1 -1
- package/dist/edit-modes/draw-line-string-mode.js.map +1 -1
- package/dist/edit-modes/draw-polygon-mode.d.ts.map +1 -1
- package/dist/edit-modes/draw-polygon-mode.js +48 -52
- package/dist/edit-modes/draw-polygon-mode.js.map +1 -1
- package/dist/edit-modes/draw-square-mode.d.ts.map +1 -1
- package/dist/edit-modes/draw-square-mode.js.map +1 -1
- package/dist/edit-modes/extend-line-string-mode.d.ts.map +1 -1
- package/dist/edit-modes/extend-line-string-mode.js.map +1 -1
- package/dist/edit-modes/geojson-edit-mode.d.ts.map +1 -1
- package/dist/edit-modes/geojson-edit-mode.js.map +1 -1
- package/dist/edit-modes/modify-mode.d.ts.map +1 -1
- package/dist/edit-modes/modify-mode.js +23 -19
- package/dist/edit-modes/modify-mode.js.map +1 -1
- package/dist/edit-modes/resize-circle-mode.d.ts.map +1 -1
- package/dist/edit-modes/resize-circle-mode.js.map +1 -1
- package/dist/edit-modes/rotate-mode.d.ts.map +1 -1
- package/dist/edit-modes/rotate-mode.js.map +1 -1
- package/dist/edit-modes/scale-mode.d.ts.map +1 -1
- package/dist/edit-modes/scale-mode.js.map +1 -1
- package/dist/edit-modes/snappable-mode.d.ts.map +1 -1
- package/dist/edit-modes/snappable-mode.js.map +1 -1
- package/dist/edit-modes/split-polygon-mode.d.ts.map +1 -1
- package/dist/edit-modes/split-polygon-mode.js.map +1 -1
- package/dist/edit-modes/three-click-polygon-mode.d.ts.map +1 -1
- package/dist/edit-modes/three-click-polygon-mode.js +14 -18
- package/dist/edit-modes/three-click-polygon-mode.js.map +1 -1
- package/dist/edit-modes/translate-mode.d.ts.map +1 -1
- package/dist/edit-modes/translate-mode.js +2 -2
- package/dist/edit-modes/translate-mode.js.map +1 -1
- package/dist/edit-modes/two-click-polygon-mode.d.ts.map +1 -1
- package/dist/edit-modes/two-click-polygon-mode.js.map +1 -1
- package/dist/edit-modes/utils.js +1 -1
- package/dist/edit-modes/utils.js.map +1 -1
- package/dist/editable-layers/editable-geojson-layer.d.ts.map +1 -1
- package/dist/editable-layers/editable-geojson-layer.js +20 -4
- package/dist/editable-layers/editable-geojson-layer.js.map +1 -1
- package/dist/index.cjs +246 -55
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mode-handlers/mode-handler.d.ts.map +1 -1
- package/dist/mode-handlers/mode-handler.js +1 -1
- package/dist/mode-handlers/mode-handler.js.map +1 -1
- package/dist/mode-handlers/rotate-handler.d.ts.map +1 -1
- package/dist/mode-handlers/rotate-handler.js.map +1 -1
- package/dist/mode-handlers/scale-handler.d.ts.map +1 -1
- package/dist/mode-handlers/scale-handler.js.map +1 -1
- package/dist/mode-handlers/split-polygon-handler.d.ts.map +1 -1
- package/dist/mode-handlers/split-polygon-handler.js.map +1 -1
- package/dist/utils/geojson-types.d.ts.map +1 -1
- package/dist/utils/translate-from-center.d.ts.map +1 -1
- package/dist/utils/translate-from-center.js.map +1 -1
- package/dist/utils/utils.js +1 -1
- package/dist/utils/utils.js.map +1 -1
- package/dist/widgets/edit-mode-tray-widget.d.ts +1 -0
- package/dist/widgets/edit-mode-tray-widget.d.ts.map +1 -1
- package/dist/widgets/edit-mode-tray-widget.js +1 -0
- package/dist/widgets/edit-mode-tray-widget.js.map +1 -1
- package/dist/widgets/editor-toolbar-widget.d.ts +40 -0
- package/dist/widgets/editor-toolbar-widget.d.ts.map +1 -0
- package/dist/widgets/editor-toolbar-widget.js +170 -0
- package/dist/widgets/editor-toolbar-widget.js.map +1 -0
- package/package.json +16 -12
- package/src/edit-modes/draw-90degree-polygon-mode.ts +7 -1
- package/src/edit-modes/draw-line-string-mode.ts +6 -1
- package/src/edit-modes/draw-polygon-mode.ts +96 -117
- package/src/edit-modes/draw-square-mode.ts +0 -1
- package/src/edit-modes/extend-line-string-mode.ts +9 -2
- package/src/edit-modes/geojson-edit-mode.ts +15 -3
- package/src/edit-modes/immutable-feature-collection.ts +1 -1
- package/src/edit-modes/modify-mode.ts +33 -21
- package/src/edit-modes/resize-circle-mode.ts +8 -3
- package/src/edit-modes/rotate-mode.ts +3 -8
- package/src/edit-modes/scale-mode.ts +5 -6
- package/src/edit-modes/snappable-mode.ts +6 -1
- package/src/edit-modes/split-polygon-mode.ts +9 -3
- package/src/edit-modes/three-click-polygon-mode.ts +28 -31
- package/src/edit-modes/translate-mode.ts +8 -3
- package/src/edit-modes/two-click-polygon-mode.ts +7 -1
- package/src/edit-modes/utils.ts +1 -1
- package/src/editable-layers/editable-geojson-layer.ts +20 -4
- package/src/index.ts +3 -0
- package/src/mode-handlers/mode-handler.ts +21 -6
- package/src/mode-handlers/rotate-handler.ts +1 -4
- package/src/mode-handlers/scale-handler.ts +3 -7
- package/src/mode-handlers/split-polygon-handler.ts +3 -1
- package/src/utils/geojson-types.ts +22 -6
- package/src/utils/translate-from-center.ts +11 -24
- package/src/utils/utils.ts +1 -1
- package/src/widgets/edit-mode-tray-widget.tsx +2 -6
- package/src/widgets/editor-toolbar-widget.tsx +348 -0
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@deck.gl-community/editable-layers",
|
|
3
3
|
"description": "A suite of 3D-enabled data editing overlays, suitable for deck.gl",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "9.2.
|
|
5
|
+
"version": "9.2.8",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
8
8
|
},
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"src"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
+
"@math.gl/web-mercator": ">=4.0.1",
|
|
39
40
|
"@turf/along": "^7.2.0",
|
|
40
41
|
"@turf/area": "^7.2.0",
|
|
41
42
|
"@turf/bbox": "^7.2.0",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"@turf/helpers": "^7.2.0",
|
|
56
57
|
"@turf/intersect": "^7.2.0",
|
|
57
58
|
"@turf/invariant": "^7.2.0",
|
|
59
|
+
"@turf/kinks": "^7.2.0",
|
|
58
60
|
"@turf/line-intersect": "^7.2.0",
|
|
59
61
|
"@turf/meta": "^7.2.0",
|
|
60
62
|
"@turf/midpoint": "^7.2.0",
|
|
@@ -62,6 +64,9 @@
|
|
|
62
64
|
"@turf/point-to-line-distance": "^7.2.0",
|
|
63
65
|
"@turf/polygon-to-line": "^7.2.0",
|
|
64
66
|
"@turf/rewind": "^7.2.0",
|
|
67
|
+
"@turf/rhumb-bearing": "^7.2.0",
|
|
68
|
+
"@turf/rhumb-destination": "^7.2.0",
|
|
69
|
+
"@turf/rhumb-distance": "^7.2.0",
|
|
65
70
|
"@turf/transform-rotate": "^7.2.0",
|
|
66
71
|
"@turf/transform-scale": "^7.2.0",
|
|
67
72
|
"@turf/transform-translate": "^7.2.0",
|
|
@@ -71,20 +76,19 @@
|
|
|
71
76
|
"lodash.omit": "^4.1.1",
|
|
72
77
|
"lodash.throttle": "^4.1.1",
|
|
73
78
|
"preact": "^10.17.0",
|
|
74
|
-
"uuid": "9.0.0"
|
|
75
|
-
"viewport-mercator-project": ">=6.2.3"
|
|
79
|
+
"uuid": "9.0.0"
|
|
76
80
|
},
|
|
77
81
|
"peerDependencies": {
|
|
78
82
|
"@deck.gl-community/layers": "^9.2.0-beta",
|
|
79
|
-
"@deck.gl/core": "~9.2.
|
|
80
|
-
"@deck.gl/extensions": "~9.2.
|
|
81
|
-
"@deck.gl/geo-layers": "~9.2.
|
|
82
|
-
"@deck.gl/layers": "~9.2.
|
|
83
|
-
"@deck.gl/mesh-layers": "~9.2.
|
|
84
|
-
"@luma.gl/constants": "~9.2.
|
|
85
|
-
"@luma.gl/core": "~9.2.
|
|
86
|
-
"@luma.gl/engine": "~9.2.
|
|
83
|
+
"@deck.gl/core": "~9.2.8",
|
|
84
|
+
"@deck.gl/extensions": "~9.2.8",
|
|
85
|
+
"@deck.gl/geo-layers": "~9.2.8",
|
|
86
|
+
"@deck.gl/layers": "~9.2.8",
|
|
87
|
+
"@deck.gl/mesh-layers": "~9.2.8",
|
|
88
|
+
"@luma.gl/constants": "~9.2.6",
|
|
89
|
+
"@luma.gl/core": "~9.2.6",
|
|
90
|
+
"@luma.gl/engine": "~9.2.6",
|
|
87
91
|
"@math.gl/core": ">=4.0.1"
|
|
88
92
|
},
|
|
89
|
-
"gitHead": "
|
|
93
|
+
"gitHead": "9cb179aa3d1707f32116034150ab7130ce88b18c"
|
|
90
94
|
}
|
|
@@ -19,7 +19,13 @@ import {
|
|
|
19
19
|
GuideFeatureCollection,
|
|
20
20
|
TentativeFeature
|
|
21
21
|
} from './types';
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
Polygon,
|
|
24
|
+
LineString,
|
|
25
|
+
Position,
|
|
26
|
+
FeatureCollection,
|
|
27
|
+
SimpleFeatureCollection
|
|
28
|
+
} from '../utils/geojson-types';
|
|
23
29
|
import {GeoJsonEditMode} from './geojson-edit-mode';
|
|
24
30
|
|
|
25
31
|
export class Draw90DegreePolygonMode extends GeoJsonEditMode {
|
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
import distance from '@turf/distance';
|
|
6
6
|
import {memoize} from '../utils/memoize';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
LineString,
|
|
9
|
+
FeatureCollection,
|
|
10
|
+
Position,
|
|
11
|
+
SimpleFeatureCollection
|
|
12
|
+
} from '../utils/geojson-types';
|
|
8
13
|
import {
|
|
9
14
|
ClickEvent,
|
|
10
15
|
PointerMoveEvent,
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import lineIntersect from '@turf/line-intersect';
|
|
6
|
-
import {
|
|
7
|
-
import booleanWithin from
|
|
8
|
-
import type {Geometry} from 'geojson'
|
|
5
|
+
import {lineIntersect} from '@turf/line-intersect';
|
|
6
|
+
import {polygon as turfPolygon} from '@turf/helpers';
|
|
7
|
+
import {booleanWithin} from '@turf/boolean-within';
|
|
8
|
+
import type {Geometry} from 'geojson';
|
|
9
|
+
import {kinks} from '@turf/kinks';
|
|
9
10
|
|
|
10
11
|
import {
|
|
11
12
|
ClickEvent,
|
|
@@ -19,82 +20,76 @@ import {
|
|
|
19
20
|
import {Position, FeatureCollection, SimpleFeatureCollection} from '../utils/geojson-types';
|
|
20
21
|
import {getPickedEditHandle} from './utils';
|
|
21
22
|
import {GeoJsonEditMode} from './geojson-edit-mode';
|
|
22
|
-
import {
|
|
23
|
-
|
|
23
|
+
import {ImmutableFeatureCollection} from './immutable-feature-collection';
|
|
24
24
|
|
|
25
25
|
export class DrawPolygonMode extends GeoJsonEditMode {
|
|
26
|
-
|
|
27
|
-
holeSequence: Position[] = [];
|
|
26
|
+
holeSequence: Position[] = [];
|
|
28
27
|
isDrawingHole = false;
|
|
29
28
|
|
|
30
29
|
createTentativeFeature(props: ModeProps<FeatureCollection>): TentativeFeature {
|
|
31
|
-
const {
|
|
30
|
+
const {lastPointerMoveEvent} = props;
|
|
32
31
|
const clickSequence = this.getClickSequence();
|
|
33
32
|
const holeSequence = this.holeSequence;
|
|
34
|
-
const lastCoords = lastPointerMoveEvent
|
|
35
|
-
? [lastPointerMoveEvent.mapCoords]
|
|
36
|
-
: [];
|
|
33
|
+
const lastCoords = lastPointerMoveEvent ? [lastPointerMoveEvent.mapCoords] : [];
|
|
37
34
|
|
|
38
35
|
let geometry: Geometry;
|
|
39
36
|
|
|
40
37
|
if (this.isDrawingHole && holeSequence.length > 1) {
|
|
41
38
|
geometry = {
|
|
42
|
-
type:
|
|
39
|
+
type: 'Polygon',
|
|
43
40
|
coordinates: [
|
|
44
41
|
[...clickSequence, clickSequence[0]],
|
|
45
|
-
[...holeSequence, ...lastCoords, holeSequence[0]]
|
|
46
|
-
]
|
|
42
|
+
[...holeSequence, ...lastCoords, holeSequence[0]]
|
|
43
|
+
]
|
|
47
44
|
};
|
|
48
45
|
} else if (clickSequence.length > 2) {
|
|
49
46
|
geometry = {
|
|
50
|
-
type:
|
|
51
|
-
coordinates: [[...clickSequence, ...lastCoords, clickSequence[0]]]
|
|
47
|
+
type: 'Polygon',
|
|
48
|
+
coordinates: [[...clickSequence, ...lastCoords, clickSequence[0]]]
|
|
52
49
|
};
|
|
53
50
|
} else {
|
|
54
51
|
geometry = {
|
|
55
|
-
type:
|
|
56
|
-
coordinates: [...clickSequence, ...lastCoords]
|
|
52
|
+
type: 'LineString',
|
|
53
|
+
coordinates: [...clickSequence, ...lastCoords]
|
|
57
54
|
};
|
|
58
55
|
}
|
|
59
56
|
|
|
60
57
|
return {
|
|
61
|
-
type:
|
|
58
|
+
type: 'Feature',
|
|
62
59
|
properties: {
|
|
63
|
-
guideType:
|
|
60
|
+
guideType: 'tentative'
|
|
64
61
|
},
|
|
65
|
-
geometry
|
|
62
|
+
geometry
|
|
66
63
|
};
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
getGuides(props: ModeProps<FeatureCollection>): GuideFeatureCollection {
|
|
70
67
|
const guides: GuideFeatureCollection = {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
guides.features.push(...handles);
|
|
97
|
-
return guides;
|
|
68
|
+
type: 'FeatureCollection',
|
|
69
|
+
features: []
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const tentative = this.createTentativeFeature(props);
|
|
73
|
+
if (tentative) guides.features.push(tentative);
|
|
74
|
+
|
|
75
|
+
const sequence = this.isDrawingHole ? this.holeSequence : this.getClickSequence();
|
|
76
|
+
|
|
77
|
+
const handles: GuideFeature[] = sequence.map((coord, index) => ({
|
|
78
|
+
type: 'Feature',
|
|
79
|
+
properties: {
|
|
80
|
+
guideType: 'editHandle',
|
|
81
|
+
editHandleType: 'existing',
|
|
82
|
+
featureIndex: -1,
|
|
83
|
+
positionIndexes: [index]
|
|
84
|
+
},
|
|
85
|
+
geometry: {
|
|
86
|
+
type: 'Point',
|
|
87
|
+
coordinates: coord
|
|
88
|
+
}
|
|
89
|
+
}));
|
|
90
|
+
|
|
91
|
+
guides.features.push(...handles);
|
|
92
|
+
return guides;
|
|
98
93
|
}
|
|
99
94
|
|
|
100
95
|
// eslint-disable-next-line complexity, max-statements
|
|
@@ -103,7 +98,7 @@ export class DrawPolygonMode extends GeoJsonEditMode {
|
|
|
103
98
|
const clickedEditHandle = getPickedEditHandle(picks);
|
|
104
99
|
const clickSequence = this.getClickSequence();
|
|
105
100
|
const coords = event.mapCoords;
|
|
106
|
-
|
|
101
|
+
|
|
107
102
|
// Check if they clicked on an edit handle to complete the polygon
|
|
108
103
|
if (
|
|
109
104
|
!this.isDrawingHole &&
|
|
@@ -117,7 +112,7 @@ export class DrawPolygonMode extends GeoJsonEditMode {
|
|
|
117
112
|
this.finishDrawing(props);
|
|
118
113
|
return;
|
|
119
114
|
}
|
|
120
|
-
|
|
115
|
+
|
|
121
116
|
// Check if they clicked near the first point to complete the polygon
|
|
122
117
|
if (!this.isDrawingHole && clickSequence.length > 2) {
|
|
123
118
|
if (isNearFirstPoint(coords, clickSequence[0])) {
|
|
@@ -126,28 +121,25 @@ export class DrawPolygonMode extends GeoJsonEditMode {
|
|
|
126
121
|
return;
|
|
127
122
|
}
|
|
128
123
|
}
|
|
129
|
-
|
|
124
|
+
|
|
130
125
|
if (this.isDrawingHole) {
|
|
131
126
|
const current = this.holeSequence;
|
|
132
127
|
current.push(coords);
|
|
133
128
|
|
|
134
129
|
if (current.length > 2) {
|
|
135
130
|
const poly: Geometry = {
|
|
136
|
-
type:
|
|
131
|
+
type: 'Polygon',
|
|
137
132
|
coordinates: [
|
|
138
133
|
[...clickSequence, clickSequence[0]],
|
|
139
|
-
[...current, current[0]]
|
|
140
|
-
]
|
|
134
|
+
[...current, current[0]]
|
|
135
|
+
]
|
|
141
136
|
};
|
|
142
137
|
|
|
143
138
|
this.resetClickSequence();
|
|
144
139
|
this.holeSequence = [];
|
|
145
140
|
this.isDrawingHole = false;
|
|
146
141
|
|
|
147
|
-
const editAction = this.getAddFeatureOrBooleanPolygonAction(
|
|
148
|
-
poly,
|
|
149
|
-
props,
|
|
150
|
-
);
|
|
142
|
+
const editAction = this.getAddFeatureOrBooleanPolygonAction(poly, props);
|
|
151
143
|
if (editAction) props.onEdit(editAction);
|
|
152
144
|
}
|
|
153
145
|
return;
|
|
@@ -179,18 +171,18 @@ export class DrawPolygonMode extends GeoJsonEditMode {
|
|
|
179
171
|
}
|
|
180
172
|
|
|
181
173
|
handleKeyUp(event: KeyboardEvent, props: ModeProps<SimpleFeatureCollection>) {
|
|
182
|
-
if (event.key ===
|
|
174
|
+
if (event.key === 'Enter') {
|
|
183
175
|
this.finishDrawing(props);
|
|
184
176
|
this.resetClickSequence();
|
|
185
|
-
} else if (event.key ===
|
|
177
|
+
} else if (event.key === 'Escape') {
|
|
186
178
|
this.resetClickSequence();
|
|
187
179
|
this.holeSequence = [];
|
|
188
180
|
this.isDrawingHole = false;
|
|
189
181
|
|
|
190
182
|
props.onEdit({
|
|
191
183
|
updatedData: props.data,
|
|
192
|
-
editType:
|
|
193
|
-
editContext: {}
|
|
184
|
+
editType: 'cancelFeature',
|
|
185
|
+
editContext: {}
|
|
194
186
|
});
|
|
195
187
|
}
|
|
196
188
|
}
|
|
@@ -210,27 +202,16 @@ export class DrawPolygonMode extends GeoJsonEditMode {
|
|
|
210
202
|
const canAddHole = canAddHoleToPolygon(props);
|
|
211
203
|
const canOverlap = canPolygonOverlap(props);
|
|
212
204
|
|
|
213
|
-
|
|
214
205
|
// Check if the polygon intersects itself (excluding shared start/end point)
|
|
215
206
|
if (!canOverlap) {
|
|
216
|
-
const overlapping =
|
|
217
|
-
|
|
218
|
-
newPolygon,
|
|
219
|
-
).features.filter(
|
|
220
|
-
(intersection) =>
|
|
221
|
-
!newPolygon.geometry.coordinates[0].some(
|
|
222
|
-
(coord) =>
|
|
223
|
-
coord[0] === intersection.geometry.coordinates[0] &&
|
|
224
|
-
coord[1] === intersection.geometry.coordinates[1],
|
|
225
|
-
),
|
|
226
|
-
);
|
|
227
|
-
|
|
207
|
+
const overlapping = kinks(newPolygon).features;
|
|
208
|
+
|
|
228
209
|
if (overlapping.length > 0) {
|
|
229
210
|
// ❌ Invalid polygon: overlaps
|
|
230
211
|
props.onEdit({
|
|
231
212
|
updatedData: props.data,
|
|
232
|
-
editType:
|
|
233
|
-
editContext: {
|
|
213
|
+
editType: 'invalidPolygon',
|
|
214
|
+
editContext: {reason: 'overlaps'}
|
|
234
215
|
});
|
|
235
216
|
this.resetClickSequence();
|
|
236
217
|
return;
|
|
@@ -244,14 +225,14 @@ export class DrawPolygonMode extends GeoJsonEditMode {
|
|
|
244
225
|
return;
|
|
245
226
|
}
|
|
246
227
|
}
|
|
247
|
-
|
|
228
|
+
|
|
248
229
|
// If no valid hole was found, add the polygon as a new feature
|
|
249
230
|
const editAction = this.getAddFeatureOrBooleanPolygonAction(
|
|
250
231
|
{
|
|
251
|
-
type:
|
|
252
|
-
coordinates: [[...this.getClickSequence(), this.getClickSequence()[0]]]
|
|
232
|
+
type: 'Polygon',
|
|
233
|
+
coordinates: [[...this.getClickSequence(), this.getClickSequence()[0]]]
|
|
253
234
|
},
|
|
254
|
-
props
|
|
235
|
+
props
|
|
255
236
|
);
|
|
256
237
|
if (editAction) props.onEdit(editAction);
|
|
257
238
|
this.resetClickSequence();
|
|
@@ -262,17 +243,23 @@ export class DrawPolygonMode extends GeoJsonEditMode {
|
|
|
262
243
|
newPolygon: any,
|
|
263
244
|
polygon: Position[],
|
|
264
245
|
props: ModeProps<SimpleFeatureCollection>
|
|
265
|
-
): {
|
|
246
|
+
): {handled: boolean} {
|
|
266
247
|
for (const [featureIndex, feature] of props.data.features.entries()) {
|
|
267
|
-
if (feature.geometry.type ===
|
|
268
|
-
const result = this.validateAndCreateHole(
|
|
248
|
+
if (feature.geometry.type === 'Polygon') {
|
|
249
|
+
const result = this.validateAndCreateHole(
|
|
250
|
+
feature,
|
|
251
|
+
featureIndex,
|
|
252
|
+
newPolygon,
|
|
253
|
+
polygon,
|
|
254
|
+
props
|
|
255
|
+
);
|
|
269
256
|
if (result.handled) {
|
|
270
257
|
return result;
|
|
271
258
|
}
|
|
272
259
|
}
|
|
273
260
|
}
|
|
274
|
-
|
|
275
|
-
return {
|
|
261
|
+
|
|
262
|
+
return {handled: false};
|
|
276
263
|
}
|
|
277
264
|
|
|
278
265
|
private validateAndCreateHole(
|
|
@@ -281,30 +268,30 @@ export class DrawPolygonMode extends GeoJsonEditMode {
|
|
|
281
268
|
newPolygon: any,
|
|
282
269
|
polygon: Position[],
|
|
283
270
|
props: ModeProps<SimpleFeatureCollection>
|
|
284
|
-
): {
|
|
271
|
+
): {handled: boolean} {
|
|
285
272
|
const outer = turfPolygon(feature.geometry.coordinates);
|
|
286
273
|
|
|
287
274
|
// Check existing holes for conflicts
|
|
288
275
|
for (let i = 1; i < feature.geometry.coordinates.length; i++) {
|
|
289
276
|
const hole = turfPolygon([feature.geometry.coordinates[i]]);
|
|
290
277
|
const intersection = lineIntersect(hole, newPolygon);
|
|
291
|
-
|
|
278
|
+
|
|
292
279
|
if (intersection.features.length > 0) {
|
|
293
280
|
props.onEdit({
|
|
294
281
|
updatedData: props.data,
|
|
295
|
-
editType:
|
|
296
|
-
editContext: {
|
|
282
|
+
editType: 'invalidHole',
|
|
283
|
+
editContext: {reason: 'intersects-existing-hole'}
|
|
297
284
|
});
|
|
298
|
-
return {
|
|
285
|
+
return {handled: true};
|
|
299
286
|
}
|
|
300
287
|
|
|
301
288
|
if (booleanWithin(hole, newPolygon) || booleanWithin(newPolygon, hole)) {
|
|
302
289
|
props.onEdit({
|
|
303
290
|
updatedData: props.data,
|
|
304
|
-
editType:
|
|
305
|
-
editContext: {
|
|
291
|
+
editType: 'invalidHole',
|
|
292
|
+
editContext: {reason: 'contains-or-contained-by-existing-hole'}
|
|
306
293
|
});
|
|
307
|
-
return {
|
|
294
|
+
return {handled: true};
|
|
308
295
|
}
|
|
309
296
|
}
|
|
310
297
|
|
|
@@ -313,19 +300,19 @@ export class DrawPolygonMode extends GeoJsonEditMode {
|
|
|
313
300
|
if (intersectionWithOuter.features.length > 0) {
|
|
314
301
|
props.onEdit({
|
|
315
302
|
updatedData: props.data,
|
|
316
|
-
editType:
|
|
317
|
-
editContext: {
|
|
303
|
+
editType: 'invalidPolygon',
|
|
304
|
+
editContext: {reason: 'intersects-existing-polygon'}
|
|
318
305
|
});
|
|
319
|
-
return {
|
|
306
|
+
return {handled: true};
|
|
320
307
|
}
|
|
321
308
|
|
|
322
309
|
if (booleanWithin(outer, newPolygon)) {
|
|
323
310
|
props.onEdit({
|
|
324
311
|
updatedData: props.data,
|
|
325
|
-
editType:
|
|
326
|
-
editContext: {
|
|
312
|
+
editType: 'invalidPolygon',
|
|
313
|
+
editContext: {reason: 'contains-existing-polygon'}
|
|
327
314
|
});
|
|
328
|
-
return {
|
|
315
|
+
return {handled: true};
|
|
329
316
|
}
|
|
330
317
|
|
|
331
318
|
// Check if new polygon is within outer polygon (valid hole)
|
|
@@ -333,45 +320,37 @@ export class DrawPolygonMode extends GeoJsonEditMode {
|
|
|
333
320
|
const updatedData = new ImmutableFeatureCollection(props.data)
|
|
334
321
|
.replaceGeometry(featureIndex, {
|
|
335
322
|
...feature.geometry,
|
|
336
|
-
coordinates: [...feature.geometry.coordinates, polygon]
|
|
323
|
+
coordinates: [...feature.geometry.coordinates, polygon]
|
|
337
324
|
})
|
|
338
325
|
.getObject();
|
|
339
326
|
|
|
340
327
|
props.onEdit({
|
|
341
328
|
updatedData,
|
|
342
|
-
editType:
|
|
343
|
-
editContext: {
|
|
329
|
+
editType: 'addHole',
|
|
330
|
+
editContext: {hole: newPolygon.geometry}
|
|
344
331
|
});
|
|
345
|
-
return {
|
|
332
|
+
return {handled: true};
|
|
346
333
|
}
|
|
347
|
-
return {
|
|
334
|
+
return {handled: false};
|
|
348
335
|
}
|
|
349
336
|
}
|
|
350
337
|
|
|
351
338
|
// Helper function to check if a point is near the first point in the sequence
|
|
352
|
-
function isNearFirstPoint(
|
|
353
|
-
click: Position,
|
|
354
|
-
first: Position,
|
|
355
|
-
threshold = 1e-4,
|
|
356
|
-
): boolean {
|
|
339
|
+
function isNearFirstPoint(click: Position, first: Position, threshold = 1e-4): boolean {
|
|
357
340
|
const dx = click[0] - first[0];
|
|
358
341
|
const dy = click[1] - first[1];
|
|
359
342
|
return dx * dx + dy * dy < threshold * threshold;
|
|
360
343
|
}
|
|
361
344
|
|
|
362
345
|
// Helper function to determine if a hole can be added to a polygon
|
|
363
|
-
function canAddHoleToPolygon(
|
|
364
|
-
props: ModeProps<FeatureCollection>
|
|
365
|
-
): boolean {
|
|
346
|
+
function canAddHoleToPolygon(props: ModeProps<FeatureCollection>): boolean {
|
|
366
347
|
// For simplicity, always return true in this example.
|
|
367
348
|
// Implement your own logic based on application requirements.
|
|
368
349
|
return props.modeConfig?.allowHoles ?? false;
|
|
369
350
|
}
|
|
370
351
|
|
|
371
352
|
// Helper function to determine if a polygon can intersect itself
|
|
372
|
-
function canPolygonOverlap(
|
|
373
|
-
props: ModeProps<FeatureCollection>
|
|
374
|
-
): boolean {
|
|
353
|
+
function canPolygonOverlap(props: ModeProps<FeatureCollection>): boolean {
|
|
375
354
|
// Return the value of allowSelfIntersection (defaults to false for safety)
|
|
376
355
|
return props.modeConfig?.allowSelfIntersection ?? false;
|
|
377
356
|
}
|
|
@@ -2,13 +2,20 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
Position,
|
|
7
|
+
LineString,
|
|
8
|
+
FeatureCollection,
|
|
9
|
+
SimpleFeatureCollection
|
|
10
|
+
} from '../utils/geojson-types';
|
|
6
11
|
import {ClickEvent, PointerMoveEvent, ModeProps, GuideFeatureCollection} from './types';
|
|
7
12
|
import {GeoJsonEditMode} from './geojson-edit-mode';
|
|
8
13
|
import {ImmutableFeatureCollection} from './immutable-feature-collection';
|
|
9
14
|
|
|
10
15
|
export class ExtendLineStringMode extends GeoJsonEditMode {
|
|
11
|
-
getSingleSelectedLineString(
|
|
16
|
+
getSingleSelectedLineString(
|
|
17
|
+
props: ModeProps<SimpleFeatureCollection>
|
|
18
|
+
): LineString | null | undefined {
|
|
12
19
|
const selectedGeometry = this.getSelectedGeometry(props);
|
|
13
20
|
|
|
14
21
|
if (selectedGeometry && selectedGeometry.type === 'LineString') {
|
|
@@ -21,7 +21,15 @@ import {
|
|
|
21
21
|
GuideFeatureCollection,
|
|
22
22
|
TentativeFeature
|
|
23
23
|
} from './types';
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
FeatureCollection,
|
|
26
|
+
Feature,
|
|
27
|
+
Polygon,
|
|
28
|
+
SimpleGeometry,
|
|
29
|
+
Position,
|
|
30
|
+
SimpleFeatureCollection,
|
|
31
|
+
SimpleFeature
|
|
32
|
+
} from '../utils/geojson-types';
|
|
25
33
|
import {getPickedEditHandles, getNonGuidePicks} from './utils';
|
|
26
34
|
import {EditMode} from './edit-mode';
|
|
27
35
|
import {ImmutableFeatureCollection} from './immutable-feature-collection';
|
|
@@ -59,7 +67,9 @@ export class GeoJsonEditMode implements EditMode<FeatureCollection, GuideFeature
|
|
|
59
67
|
return null;
|
|
60
68
|
}
|
|
61
69
|
|
|
62
|
-
getSelectedGeometry(
|
|
70
|
+
getSelectedGeometry(
|
|
71
|
+
props: ModeProps<SimpleFeatureCollection>
|
|
72
|
+
): SimpleGeometry | null | undefined {
|
|
63
73
|
const feature = this.getSelectedFeature(props);
|
|
64
74
|
if (feature) {
|
|
65
75
|
return feature.geometry;
|
|
@@ -67,7 +77,9 @@ export class GeoJsonEditMode implements EditMode<FeatureCollection, GuideFeature
|
|
|
67
77
|
return null;
|
|
68
78
|
}
|
|
69
79
|
|
|
70
|
-
getSelectedFeaturesAsFeatureCollection(
|
|
80
|
+
getSelectedFeaturesAsFeatureCollection(
|
|
81
|
+
props: ModeProps<SimpleFeatureCollection>
|
|
82
|
+
): SimpleFeatureCollection {
|
|
71
83
|
const {features} = props.data;
|
|
72
84
|
const selectedFeatures = props.selectedIndexes.map((selectedIndex) => features[selectedIndex]);
|
|
73
85
|
return {
|
|
@@ -16,7 +16,14 @@ import {
|
|
|
16
16
|
NearestPointType,
|
|
17
17
|
shouldCancelPan
|
|
18
18
|
} from './utils';
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
LineString,
|
|
21
|
+
Point,
|
|
22
|
+
Polygon,
|
|
23
|
+
FeatureCollection,
|
|
24
|
+
Feature,
|
|
25
|
+
SimpleFeatureCollection
|
|
26
|
+
} from '../utils/geojson-types';
|
|
20
27
|
import {
|
|
21
28
|
ModeProps,
|
|
22
29
|
ClickEvent,
|
|
@@ -76,7 +83,6 @@ export class ModifyMode extends GeoJsonEditMode {
|
|
|
76
83
|
(lineString, prefix) => {
|
|
77
84
|
const lineStringFeature = toLineString(lineString);
|
|
78
85
|
const candidateIntermediatePoint = this.getNearestPoint(
|
|
79
|
-
|
|
80
86
|
lineStringFeature,
|
|
81
87
|
referencePoint,
|
|
82
88
|
props.modeConfig && props.modeConfig.viewport
|
|
@@ -144,27 +150,33 @@ export class ModifyMode extends GeoJsonEditMode {
|
|
|
144
150
|
const pickedIntermediateHandle = getPickedIntermediateEditHandle(event.picks);
|
|
145
151
|
|
|
146
152
|
if (pickedExistingHandle) {
|
|
147
|
-
const {
|
|
153
|
+
const {featureIndex, positionIndexes} = pickedExistingHandle.properties;
|
|
148
154
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
const feature = props.data.features[featureIndex];
|
|
156
|
+
const canRemovePosition = !(
|
|
157
|
+
props.modeConfig?.lockRectangles && feature?.properties.shape === 'Rectangle'
|
|
158
|
+
);
|
|
159
|
+
if (canRemovePosition) {
|
|
160
|
+
let updatedData;
|
|
161
|
+
try {
|
|
162
|
+
updatedData = new ImmutableFeatureCollection(props.data)
|
|
163
|
+
.removePosition(featureIndex, positionIndexes)
|
|
164
|
+
.getObject();
|
|
165
|
+
} catch (ignored) {
|
|
166
|
+
// This happens if user attempts to remove the last point
|
|
167
|
+
}
|
|
157
168
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
169
|
+
if (updatedData) {
|
|
170
|
+
props.onEdit({
|
|
171
|
+
updatedData,
|
|
172
|
+
editType: 'removePosition',
|
|
173
|
+
editContext: {
|
|
174
|
+
featureIndexes: [featureIndex],
|
|
175
|
+
positionIndexes,
|
|
176
|
+
position: pickedExistingHandle.geometry.coordinates
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
168
180
|
}
|
|
169
181
|
} else if (pickedIntermediateHandle) {
|
|
170
182
|
const {featureIndex, positionIndexes} = pickedIntermediateHandle.properties;
|