@sindicum/libre-draw 0.1.0
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/LICENSE +21 -0
- package/README.md +149 -0
- package/dist/LibreDraw.d.ts +359 -0
- package/dist/core/EventBus.d.ts +37 -0
- package/dist/core/FeatureStore.d.ts +68 -0
- package/dist/core/HistoryManager.d.ts +47 -0
- package/dist/core/ModeManager.d.ts +44 -0
- package/dist/core/errors.d.ts +6 -0
- package/dist/index.d.ts +7 -0
- package/dist/input/InputHandler.d.ts +29 -0
- package/dist/input/KeyboardInput.d.ts +31 -0
- package/dist/input/MouseInput.d.ts +41 -0
- package/dist/input/TouchInput.d.ts +58 -0
- package/dist/libre-draw.cjs +2 -0
- package/dist/libre-draw.cjs.map +1 -0
- package/dist/libre-draw.js +2116 -0
- package/dist/libre-draw.js.map +1 -0
- package/dist/modes/DrawMode.d.ts +70 -0
- package/dist/modes/IdleMode.d.ts +16 -0
- package/dist/modes/Mode.d.ts +26 -0
- package/dist/modes/SelectMode.d.ts +158 -0
- package/dist/rendering/RenderManager.d.ts +80 -0
- package/dist/rendering/SourceManager.d.ts +52 -0
- package/dist/types/events.d.ts +43 -0
- package/dist/types/features.d.ts +80 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/input.d.ts +23 -0
- package/dist/types/options.d.ts +30 -0
- package/dist/ui/Toolbar.d.ts +59 -0
- package/dist/ui/ToolbarButton.d.ts +53 -0
- package/dist/ui/icons/delete.d.ts +4 -0
- package/dist/ui/icons/draw.d.ts +4 -0
- package/dist/ui/icons/redo.d.ts +4 -0
- package/dist/ui/icons/select.d.ts +4 -0
- package/dist/ui/icons/undo.d.ts +4 -0
- package/dist/validation/geojson.d.ts +20 -0
- package/dist/validation/intersection.d.ts +28 -0
- package/package.json +76 -0
|
@@ -0,0 +1,2116 @@
|
|
|
1
|
+
class ue {
|
|
2
|
+
constructor(e) {
|
|
3
|
+
this.feature = e, this.type = "create";
|
|
4
|
+
}
|
|
5
|
+
apply(e) {
|
|
6
|
+
e.add(this.feature);
|
|
7
|
+
}
|
|
8
|
+
revert(e) {
|
|
9
|
+
e.remove(this.feature.id);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
class $ {
|
|
13
|
+
constructor(e, t, s) {
|
|
14
|
+
this.id = e, this.oldFeature = t, this.newFeature = s, this.type = "update";
|
|
15
|
+
}
|
|
16
|
+
apply(e) {
|
|
17
|
+
e.update(this.id, this.newFeature);
|
|
18
|
+
}
|
|
19
|
+
revert(e) {
|
|
20
|
+
e.update(this.id, this.oldFeature);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
class oe {
|
|
24
|
+
constructor(e) {
|
|
25
|
+
this.feature = e, this.type = "delete";
|
|
26
|
+
}
|
|
27
|
+
apply(e) {
|
|
28
|
+
e.remove(this.feature.id);
|
|
29
|
+
}
|
|
30
|
+
revert(e) {
|
|
31
|
+
e.add(this.feature);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
class ge {
|
|
35
|
+
constructor() {
|
|
36
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Register a listener for a specific event type.
|
|
40
|
+
* @param type - The event type to listen for.
|
|
41
|
+
* @param listener - The callback to invoke when the event fires.
|
|
42
|
+
*/
|
|
43
|
+
on(e, t) {
|
|
44
|
+
let s = this.listeners.get(e);
|
|
45
|
+
s || (s = /* @__PURE__ */ new Set(), this.listeners.set(e, s)), s.add(t);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Remove a listener for a specific event type.
|
|
49
|
+
* @param type - The event type to stop listening for.
|
|
50
|
+
* @param listener - The callback to remove.
|
|
51
|
+
*/
|
|
52
|
+
off(e, t) {
|
|
53
|
+
const s = this.listeners.get(e);
|
|
54
|
+
s && s.delete(t);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Emit an event, invoking all registered listeners.
|
|
58
|
+
* @param type - The event type to emit.
|
|
59
|
+
* @param payload - The event payload.
|
|
60
|
+
*/
|
|
61
|
+
emit(e, t) {
|
|
62
|
+
const s = this.listeners.get(e);
|
|
63
|
+
if (s)
|
|
64
|
+
for (const i of s)
|
|
65
|
+
i(t);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Remove all listeners for all event types.
|
|
69
|
+
*/
|
|
70
|
+
removeAllListeners() {
|
|
71
|
+
this.listeners.clear();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
class R {
|
|
75
|
+
constructor() {
|
|
76
|
+
this.features = /* @__PURE__ */ new Map();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Add a feature to the store. If the feature has no ID,
|
|
80
|
+
* a new UUID will be generated.
|
|
81
|
+
* @param feature - The feature to add.
|
|
82
|
+
* @returns The added feature (with ID assigned).
|
|
83
|
+
*/
|
|
84
|
+
add(e) {
|
|
85
|
+
const t = e.id || crypto.randomUUID(), s = { ...e, id: t };
|
|
86
|
+
return this.features.set(t, s), s;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Update a feature in the store by ID.
|
|
90
|
+
* @param id - The ID of the feature to update.
|
|
91
|
+
* @param feature - The new feature data.
|
|
92
|
+
*/
|
|
93
|
+
update(e, t) {
|
|
94
|
+
this.features.has(e) && this.features.set(e, { ...t, id: e });
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Remove a feature from the store by ID.
|
|
98
|
+
* @param id - The ID of the feature to remove.
|
|
99
|
+
* @returns The removed feature, or undefined if not found.
|
|
100
|
+
*/
|
|
101
|
+
remove(e) {
|
|
102
|
+
const t = this.features.get(e);
|
|
103
|
+
return t && this.features.delete(e), t;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get all features in the store.
|
|
107
|
+
* @returns An array of all features.
|
|
108
|
+
*/
|
|
109
|
+
getAll() {
|
|
110
|
+
return Array.from(this.features.values());
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get a feature by its ID.
|
|
114
|
+
* @param id - The feature ID.
|
|
115
|
+
* @returns The feature, or undefined if not found.
|
|
116
|
+
*/
|
|
117
|
+
getById(e) {
|
|
118
|
+
return this.features.get(e);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Remove all features from the store.
|
|
122
|
+
*/
|
|
123
|
+
clear() {
|
|
124
|
+
this.features.clear();
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Replace all features in the store with the given array.
|
|
128
|
+
* @param features - The features to set.
|
|
129
|
+
*/
|
|
130
|
+
setAll(e) {
|
|
131
|
+
this.features.clear();
|
|
132
|
+
for (const t of e) {
|
|
133
|
+
const s = t.id || crypto.randomUUID();
|
|
134
|
+
this.features.set(s, { ...t, id: s });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Export all features as a GeoJSON FeatureCollection.
|
|
139
|
+
* @returns A GeoJSON FeatureCollection.
|
|
140
|
+
*/
|
|
141
|
+
toGeoJSON() {
|
|
142
|
+
return {
|
|
143
|
+
type: "FeatureCollection",
|
|
144
|
+
features: this.getAll()
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Create a deep clone of a feature suitable for history snapshots.
|
|
149
|
+
* @param feature - The feature to clone.
|
|
150
|
+
* @returns A deep-cloned feature.
|
|
151
|
+
*/
|
|
152
|
+
static cloneFeature(e) {
|
|
153
|
+
return {
|
|
154
|
+
id: e.id,
|
|
155
|
+
type: "Feature",
|
|
156
|
+
geometry: {
|
|
157
|
+
type: "Polygon",
|
|
158
|
+
coordinates: e.geometry.coordinates.map(
|
|
159
|
+
(t) => t.map((s) => [...s])
|
|
160
|
+
)
|
|
161
|
+
},
|
|
162
|
+
properties: { ...e.properties }
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
class fe {
|
|
167
|
+
/**
|
|
168
|
+
* Create a new HistoryManager.
|
|
169
|
+
* @param limit - Maximum number of actions to retain. Defaults to 100.
|
|
170
|
+
*/
|
|
171
|
+
constructor(e = 100) {
|
|
172
|
+
this.undoStack = [], this.redoStack = [], this.limit = e;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Push a new action onto the history stack.
|
|
176
|
+
* Clears the redo stack since a new action invalidates any redo history.
|
|
177
|
+
* @param action - The action to record.
|
|
178
|
+
*/
|
|
179
|
+
push(e) {
|
|
180
|
+
this.undoStack.push(e), this.redoStack = [], this.undoStack.length > this.limit && this.undoStack.shift();
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Undo the most recent action.
|
|
184
|
+
* @param store - The feature store to revert the action on.
|
|
185
|
+
* @returns True if an action was undone, false if the stack was empty.
|
|
186
|
+
*/
|
|
187
|
+
undo(e) {
|
|
188
|
+
const t = this.undoStack.pop();
|
|
189
|
+
return t ? (t.revert(e), this.redoStack.push(t), !0) : !1;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Redo the most recently undone action.
|
|
193
|
+
* @param store - The feature store to re-apply the action on.
|
|
194
|
+
* @returns True if an action was redone, false if the stack was empty.
|
|
195
|
+
*/
|
|
196
|
+
redo(e) {
|
|
197
|
+
const t = this.redoStack.pop();
|
|
198
|
+
return t ? (t.apply(e), this.undoStack.push(t), !0) : !1;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Whether there are actions that can be undone.
|
|
202
|
+
*/
|
|
203
|
+
canUndo() {
|
|
204
|
+
return this.undoStack.length > 0;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Whether there are actions that can be redone.
|
|
208
|
+
*/
|
|
209
|
+
canRedo() {
|
|
210
|
+
return this.redoStack.length > 0;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Clear all history.
|
|
214
|
+
*/
|
|
215
|
+
clear() {
|
|
216
|
+
this.undoStack = [], this.redoStack = [];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
class pe {
|
|
220
|
+
constructor() {
|
|
221
|
+
this.modes = /* @__PURE__ */ new Map(), this.currentModeName = "idle";
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Register a mode implementation.
|
|
225
|
+
* @param name - The mode name.
|
|
226
|
+
* @param mode - The mode implementation.
|
|
227
|
+
*/
|
|
228
|
+
registerMode(e, t) {
|
|
229
|
+
this.modes.set(e, t);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Set a callback to be invoked on mode changes.
|
|
233
|
+
* @param callback - The callback receiving (newMode, previousMode).
|
|
234
|
+
*/
|
|
235
|
+
setOnModeChange(e) {
|
|
236
|
+
this.onModeChange = e;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Switch to a new mode.
|
|
240
|
+
* Deactivates the current mode and activates the new one.
|
|
241
|
+
* @param name - The mode to switch to.
|
|
242
|
+
*/
|
|
243
|
+
setMode(e) {
|
|
244
|
+
if (e === this.currentModeName)
|
|
245
|
+
return;
|
|
246
|
+
const t = this.currentModeName, s = this.modes.get(this.currentModeName);
|
|
247
|
+
s && s.deactivate(), this.currentModeName = e;
|
|
248
|
+
const i = this.modes.get(e);
|
|
249
|
+
i && i.activate(), this.onModeChange && this.onModeChange(e, t);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Get the current mode name.
|
|
253
|
+
* @returns The active mode name.
|
|
254
|
+
*/
|
|
255
|
+
getMode() {
|
|
256
|
+
return this.currentModeName;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Get the current mode implementation.
|
|
260
|
+
* @returns The active Mode object, or undefined.
|
|
261
|
+
*/
|
|
262
|
+
getCurrentMode() {
|
|
263
|
+
return this.modes.get(this.currentModeName);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
class E extends Error {
|
|
267
|
+
constructor(e) {
|
|
268
|
+
super(e), this.name = "LibreDrawError";
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function z(r, e, t) {
|
|
272
|
+
const s = (e[1] - r[1]) * (t[0] - e[0]) - (e[0] - r[0]) * (t[1] - e[1]);
|
|
273
|
+
return Math.abs(s) < 1e-10 ? 0 : s > 0 ? 1 : 2;
|
|
274
|
+
}
|
|
275
|
+
function j(r, e, t) {
|
|
276
|
+
return e[0] <= Math.max(r[0], t[0]) && e[0] >= Math.min(r[0], t[0]) && e[1] <= Math.max(r[1], t[1]) && e[1] >= Math.min(r[1], t[1]);
|
|
277
|
+
}
|
|
278
|
+
function X(r, e) {
|
|
279
|
+
return Math.abs(r[0] - e[0]) < 1e-10 && Math.abs(r[1] - e[1]) < 1e-10;
|
|
280
|
+
}
|
|
281
|
+
function q(r, e, t, s) {
|
|
282
|
+
if (X(r, t) || X(r, s) || X(e, t) || X(e, s))
|
|
283
|
+
return !1;
|
|
284
|
+
const i = z(r, e, t), n = z(r, e, s), o = z(t, s, r), l = z(t, s, e);
|
|
285
|
+
return !!(i !== n && o !== l || i === 0 && j(r, t, e) || n === 0 && j(r, s, e) || o === 0 && j(t, r, s) || l === 0 && j(t, e, s));
|
|
286
|
+
}
|
|
287
|
+
function ae(r) {
|
|
288
|
+
const e = r.length - 1;
|
|
289
|
+
if (e < 3) return !1;
|
|
290
|
+
for (let t = 0; t < e; t++)
|
|
291
|
+
for (let s = t + 2; s < e; s++)
|
|
292
|
+
if (!(t === 0 && s === e - 1) && q(r[t], r[t + 1], r[s], r[s + 1]))
|
|
293
|
+
return !0;
|
|
294
|
+
return !1;
|
|
295
|
+
}
|
|
296
|
+
function ye(r, e) {
|
|
297
|
+
if (r.length < 2) return !1;
|
|
298
|
+
const t = r[r.length - 1];
|
|
299
|
+
for (let s = 0; s < r.length - 2; s++)
|
|
300
|
+
if (q(t, e, r[s], r[s + 1]))
|
|
301
|
+
return !0;
|
|
302
|
+
return !1;
|
|
303
|
+
}
|
|
304
|
+
function J(r) {
|
|
305
|
+
if (r.length < 3) return !1;
|
|
306
|
+
const e = r[0], t = r[r.length - 1];
|
|
307
|
+
for (let s = 1; s < r.length - 2; s++)
|
|
308
|
+
if (q(t, e, r[s], r[s + 1]))
|
|
309
|
+
return !0;
|
|
310
|
+
return !1;
|
|
311
|
+
}
|
|
312
|
+
function me(r, e) {
|
|
313
|
+
return r[0] === e[0] && r[1] === e[1];
|
|
314
|
+
}
|
|
315
|
+
function be(r) {
|
|
316
|
+
const [e, t] = r;
|
|
317
|
+
if (typeof e != "number" || typeof t != "number")
|
|
318
|
+
throw new E(
|
|
319
|
+
`Invalid coordinate: expected [number, number], got [${typeof e}, ${typeof t}]`
|
|
320
|
+
);
|
|
321
|
+
if (e < -180 || e > 180)
|
|
322
|
+
throw new E(
|
|
323
|
+
`Invalid longitude: ${e}. Must be between -180 and 180.`
|
|
324
|
+
);
|
|
325
|
+
if (t < -90 || t > 90)
|
|
326
|
+
throw new E(
|
|
327
|
+
`Invalid latitude: ${t}. Must be between -90 and 90.`
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
function ve(r) {
|
|
331
|
+
if (!Array.isArray(r))
|
|
332
|
+
throw new E("Ring must be an array of positions.");
|
|
333
|
+
if (r.length < 4)
|
|
334
|
+
throw new E(
|
|
335
|
+
`Ring must have at least 4 positions (got ${r.length}). A valid polygon ring requires 3 unique vertices plus a closing vertex.`
|
|
336
|
+
);
|
|
337
|
+
const e = r[0], t = r[r.length - 1];
|
|
338
|
+
if (!me(e, t))
|
|
339
|
+
throw new E(
|
|
340
|
+
"Ring is not closed. The first and last positions must be identical."
|
|
341
|
+
);
|
|
342
|
+
for (const s of r) {
|
|
343
|
+
if (!Array.isArray(s) || s.length < 2)
|
|
344
|
+
throw new E(
|
|
345
|
+
"Each position in a ring must be an array of at least 2 numbers."
|
|
346
|
+
);
|
|
347
|
+
be(s);
|
|
348
|
+
}
|
|
349
|
+
if (ae(r))
|
|
350
|
+
throw new E(
|
|
351
|
+
"Ring has self-intersections. Polygon edges must not cross each other."
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
function le(r) {
|
|
355
|
+
if (r == null || typeof r != "object")
|
|
356
|
+
throw new E("Feature must be a non-null object.");
|
|
357
|
+
const e = r;
|
|
358
|
+
if (e.type !== "Feature")
|
|
359
|
+
throw new E(
|
|
360
|
+
`Feature.type must be "Feature", got "${String(e.type)}".`
|
|
361
|
+
);
|
|
362
|
+
if (e.geometry === null || e.geometry === void 0 || typeof e.geometry != "object")
|
|
363
|
+
throw new E("Feature.geometry must be a non-null object.");
|
|
364
|
+
const t = e.geometry;
|
|
365
|
+
if (t.type !== "Polygon")
|
|
366
|
+
throw new E(
|
|
367
|
+
`Feature.geometry.type must be "Polygon", got "${String(t.type)}".`
|
|
368
|
+
);
|
|
369
|
+
if (!Array.isArray(t.coordinates))
|
|
370
|
+
throw new E(
|
|
371
|
+
"Feature.geometry.coordinates must be an array."
|
|
372
|
+
);
|
|
373
|
+
const s = t.coordinates;
|
|
374
|
+
if (s.length === 0)
|
|
375
|
+
throw new E(
|
|
376
|
+
"Polygon must have at least one ring (outer ring)."
|
|
377
|
+
);
|
|
378
|
+
for (const i of s)
|
|
379
|
+
ve(i);
|
|
380
|
+
return r;
|
|
381
|
+
}
|
|
382
|
+
function Ee(r) {
|
|
383
|
+
if (r == null || typeof r != "object")
|
|
384
|
+
throw new E("GeoJSON must be a non-null object.");
|
|
385
|
+
const e = r;
|
|
386
|
+
if (e.type !== "FeatureCollection")
|
|
387
|
+
throw new E(
|
|
388
|
+
`GeoJSON.type must be "FeatureCollection", got "${String(e.type)}".`
|
|
389
|
+
);
|
|
390
|
+
if (!Array.isArray(e.features))
|
|
391
|
+
throw new E("GeoJSON.features must be an array.");
|
|
392
|
+
const t = r, s = [];
|
|
393
|
+
for (let i = 0; i < t.features.length; i++)
|
|
394
|
+
try {
|
|
395
|
+
s.push(le(t.features[i]));
|
|
396
|
+
} catch (n) {
|
|
397
|
+
throw n instanceof E ? new E(
|
|
398
|
+
`Invalid feature at index ${i}: ${n.message}`
|
|
399
|
+
) : n;
|
|
400
|
+
}
|
|
401
|
+
return {
|
|
402
|
+
type: "FeatureCollection",
|
|
403
|
+
features: s
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
class Ie {
|
|
407
|
+
activate() {
|
|
408
|
+
}
|
|
409
|
+
deactivate() {
|
|
410
|
+
}
|
|
411
|
+
onPointerDown(e) {
|
|
412
|
+
}
|
|
413
|
+
onPointerMove(e) {
|
|
414
|
+
}
|
|
415
|
+
onPointerUp(e) {
|
|
416
|
+
}
|
|
417
|
+
onDoubleClick(e) {
|
|
418
|
+
}
|
|
419
|
+
onLongPress(e) {
|
|
420
|
+
}
|
|
421
|
+
onKeyDown(e, t) {
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
const we = 10, W = 3;
|
|
425
|
+
class Se {
|
|
426
|
+
constructor(e) {
|
|
427
|
+
this.vertices = [], this.isActive = !1, this.callbacks = e;
|
|
428
|
+
}
|
|
429
|
+
activate() {
|
|
430
|
+
this.isActive = !0, this.vertices = [];
|
|
431
|
+
}
|
|
432
|
+
deactivate() {
|
|
433
|
+
this.isActive = !1, this.vertices = [], this.callbacks.clearPreview();
|
|
434
|
+
}
|
|
435
|
+
onPointerDown(e) {
|
|
436
|
+
if (!this.isActive) return;
|
|
437
|
+
const t = [e.lngLat.lng, e.lngLat.lat];
|
|
438
|
+
if (this.vertices.length >= W) {
|
|
439
|
+
const s = this.vertices[0], i = this.callbacks.getScreenPoint({
|
|
440
|
+
lng: s[0],
|
|
441
|
+
lat: s[1]
|
|
442
|
+
}), n = e.point.x - i.x, o = e.point.y - i.y;
|
|
443
|
+
if (Math.sqrt(n * n + o * o) <= we) {
|
|
444
|
+
if (J(this.vertices)) return;
|
|
445
|
+
this.finalizePolygon();
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
ye(this.vertices, t) || (this.vertices.push(t), this.updatePreview(e));
|
|
450
|
+
}
|
|
451
|
+
onPointerMove(e) {
|
|
452
|
+
!this.isActive || this.vertices.length === 0 || this.updatePreview(e);
|
|
453
|
+
}
|
|
454
|
+
onPointerUp(e) {
|
|
455
|
+
}
|
|
456
|
+
onDoubleClick(e) {
|
|
457
|
+
this.isActive && (this.vertices.length > W && this.vertices.pop(), this.vertices.length >= W && (J(this.vertices) || this.finalizePolygon()), e.originalEvent.preventDefault(), e.originalEvent.stopPropagation());
|
|
458
|
+
}
|
|
459
|
+
onLongPress(e) {
|
|
460
|
+
this.isActive && this.vertices.length > 0 && (this.vertices.pop(), this.vertices.length === 0 ? this.callbacks.clearPreview() : this.callbacks.renderPreview(this.buildPreviewCoordinates()));
|
|
461
|
+
}
|
|
462
|
+
onKeyDown(e, t) {
|
|
463
|
+
this.isActive && e === "Escape" && this.cancelDrawing();
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Build the preview coordinate ring for rendering,
|
|
467
|
+
* including cursor position if available.
|
|
468
|
+
*/
|
|
469
|
+
buildPreviewCoordinates(e) {
|
|
470
|
+
const t = [...this.vertices];
|
|
471
|
+
return e && t.push(e), t.length > 0 && t.push([...t[0]]), t;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Update the preview rendering with the current cursor position.
|
|
475
|
+
*/
|
|
476
|
+
updatePreview(e) {
|
|
477
|
+
const t = [e.lngLat.lng, e.lngLat.lat], s = this.buildPreviewCoordinates(t);
|
|
478
|
+
this.callbacks.renderPreview(s);
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Finalize the polygon: create the feature, push to history, emit event.
|
|
482
|
+
*/
|
|
483
|
+
finalizePolygon() {
|
|
484
|
+
if (this.vertices.length < W) return;
|
|
485
|
+
const e = [
|
|
486
|
+
...this.vertices,
|
|
487
|
+
[...this.vertices[0]]
|
|
488
|
+
], t = {
|
|
489
|
+
id: crypto.randomUUID(),
|
|
490
|
+
type: "Feature",
|
|
491
|
+
geometry: {
|
|
492
|
+
type: "Polygon",
|
|
493
|
+
coordinates: [e]
|
|
494
|
+
},
|
|
495
|
+
properties: {}
|
|
496
|
+
}, s = this.callbacks.addFeatureToStore(t), i = new ue(s);
|
|
497
|
+
this.callbacks.pushToHistory(i), this.callbacks.emitEvent("create", { feature: s }), this.callbacks.renderFeatures(), this.vertices = [], this.callbacks.clearPreview();
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Cancel the current drawing operation.
|
|
501
|
+
*/
|
|
502
|
+
cancelDrawing() {
|
|
503
|
+
this.vertices = [], this.callbacks.clearPreview();
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
const N = 11102230246251565e-32, F = 134217729, Te = (3 + 8 * N) * N;
|
|
507
|
+
function Y(r, e, t, s, i) {
|
|
508
|
+
let n, o, l, h, d = e[0], u = s[0], a = 0, c = 0;
|
|
509
|
+
u > d == u > -d ? (n = d, d = e[++a]) : (n = u, u = s[++c]);
|
|
510
|
+
let g = 0;
|
|
511
|
+
if (a < r && c < t)
|
|
512
|
+
for (u > d == u > -d ? (o = d + n, l = n - (o - d), d = e[++a]) : (o = u + n, l = n - (o - u), u = s[++c]), n = o, l !== 0 && (i[g++] = l); a < r && c < t; )
|
|
513
|
+
u > d == u > -d ? (o = n + d, h = o - n, l = n - (o - h) + (d - h), d = e[++a]) : (o = n + u, h = o - n, l = n - (o - h) + (u - h), u = s[++c]), n = o, l !== 0 && (i[g++] = l);
|
|
514
|
+
for (; a < r; )
|
|
515
|
+
o = n + d, h = o - n, l = n - (o - h) + (d - h), d = e[++a], n = o, l !== 0 && (i[g++] = l);
|
|
516
|
+
for (; c < t; )
|
|
517
|
+
o = n + u, h = o - n, l = n - (o - h) + (u - h), u = s[++c], n = o, l !== 0 && (i[g++] = l);
|
|
518
|
+
return (n !== 0 || g === 0) && (i[g++] = n), g;
|
|
519
|
+
}
|
|
520
|
+
function Me(r, e) {
|
|
521
|
+
let t = e[0];
|
|
522
|
+
for (let s = 1; s < r; s++) t += e[s];
|
|
523
|
+
return t;
|
|
524
|
+
}
|
|
525
|
+
function H(r) {
|
|
526
|
+
return new Float64Array(r);
|
|
527
|
+
}
|
|
528
|
+
const Pe = (3 + 16 * N) * N, ke = (2 + 12 * N) * N, Fe = (9 + 64 * N) * N * N, O = H(4), Q = H(8), Z = H(12), ee = H(16), C = H(4);
|
|
529
|
+
function Ce(r, e, t, s, i, n, o) {
|
|
530
|
+
let l, h, d, u, a, c, g, y, p, m, f, b, T, I, M, P, D, k;
|
|
531
|
+
const L = r - i, x = t - i, V = e - n, A = s - n;
|
|
532
|
+
I = L * A, c = F * L, g = c - (c - L), y = L - g, c = F * A, p = c - (c - A), m = A - p, M = y * m - (I - g * p - y * p - g * m), P = V * x, c = F * V, g = c - (c - V), y = V - g, c = F * x, p = c - (c - x), m = x - p, D = y * m - (P - g * p - y * p - g * m), f = M - D, a = M - f, O[0] = M - (f + a) + (a - D), b = I + f, a = b - I, T = I - (b - a) + (f - a), f = T - P, a = T - f, O[1] = T - (f + a) + (a - P), k = b + f, a = k - b, O[2] = b - (k - a) + (f - a), O[3] = k;
|
|
533
|
+
let _ = Me(4, O), U = ke * o;
|
|
534
|
+
if (_ >= U || -_ >= U || (a = r - L, l = r - (L + a) + (a - i), a = t - x, d = t - (x + a) + (a - i), a = e - V, h = e - (V + a) + (a - n), a = s - A, u = s - (A + a) + (a - n), l === 0 && h === 0 && d === 0 && u === 0) || (U = Fe * o + Te * Math.abs(_), _ += L * u + A * l - (V * d + x * h), _ >= U || -_ >= U)) return _;
|
|
535
|
+
I = l * A, c = F * l, g = c - (c - l), y = l - g, c = F * A, p = c - (c - A), m = A - p, M = y * m - (I - g * p - y * p - g * m), P = h * x, c = F * h, g = c - (c - h), y = h - g, c = F * x, p = c - (c - x), m = x - p, D = y * m - (P - g * p - y * p - g * m), f = M - D, a = M - f, C[0] = M - (f + a) + (a - D), b = I + f, a = b - I, T = I - (b - a) + (f - a), f = T - P, a = T - f, C[1] = T - (f + a) + (a - P), k = b + f, a = k - b, C[2] = b - (k - a) + (f - a), C[3] = k;
|
|
536
|
+
const ce = Y(4, O, 4, C, Q);
|
|
537
|
+
I = L * u, c = F * L, g = c - (c - L), y = L - g, c = F * u, p = c - (c - u), m = u - p, M = y * m - (I - g * p - y * p - g * m), P = V * d, c = F * V, g = c - (c - V), y = V - g, c = F * d, p = c - (c - d), m = d - p, D = y * m - (P - g * p - y * p - g * m), f = M - D, a = M - f, C[0] = M - (f + a) + (a - D), b = I + f, a = b - I, T = I - (b - a) + (f - a), f = T - P, a = T - f, C[1] = T - (f + a) + (a - P), k = b + f, a = k - b, C[2] = b - (k - a) + (f - a), C[3] = k;
|
|
538
|
+
const he = Y(ce, Q, 4, C, Z);
|
|
539
|
+
I = l * u, c = F * l, g = c - (c - l), y = l - g, c = F * u, p = c - (c - u), m = u - p, M = y * m - (I - g * p - y * p - g * m), P = h * d, c = F * h, g = c - (c - h), y = h - g, c = F * d, p = c - (c - d), m = d - p, D = y * m - (P - g * p - y * p - g * m), f = M - D, a = M - f, C[0] = M - (f + a) + (a - D), b = I + f, a = b - I, T = I - (b - a) + (f - a), f = T - P, a = T - f, C[1] = T - (f + a) + (a - P), k = b + f, a = k - b, C[2] = b - (k - a) + (f - a), C[3] = k;
|
|
540
|
+
const de = Y(he, Z, 4, C, ee);
|
|
541
|
+
return ee[de - 1];
|
|
542
|
+
}
|
|
543
|
+
function De(r, e, t, s, i, n) {
|
|
544
|
+
const o = (e - n) * (t - i), l = (r - i) * (s - n), h = o - l, d = Math.abs(o + l);
|
|
545
|
+
return Math.abs(h) >= Pe * d ? h : -Ce(r, e, t, s, i, n, d);
|
|
546
|
+
}
|
|
547
|
+
function Le(r, e) {
|
|
548
|
+
var t, s, i = 0, n, o, l, h, d, u, a, c = r[0], g = r[1], y = e.length;
|
|
549
|
+
for (t = 0; t < y; t++) {
|
|
550
|
+
s = 0;
|
|
551
|
+
var p = e[t], m = p.length - 1;
|
|
552
|
+
if (u = p[0], u[0] !== p[m][0] && u[1] !== p[m][1])
|
|
553
|
+
throw new Error("First and last coordinates in a ring must be the same");
|
|
554
|
+
for (o = u[0] - c, l = u[1] - g, s; s < m; s++) {
|
|
555
|
+
if (a = p[s + 1], h = a[0] - c, d = a[1] - g, l === 0 && d === 0) {
|
|
556
|
+
if (h <= 0 && o >= 0 || o <= 0 && h >= 0)
|
|
557
|
+
return 0;
|
|
558
|
+
} else if (d >= 0 && l <= 0 || d <= 0 && l >= 0) {
|
|
559
|
+
if (n = De(o, h, l, d, 0, 0), n === 0)
|
|
560
|
+
return 0;
|
|
561
|
+
(n > 0 && d > 0 && l <= 0 || n < 0 && d <= 0 && l > 0) && i++;
|
|
562
|
+
}
|
|
563
|
+
u = a, l = d, o = h;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return i % 2 !== 0;
|
|
567
|
+
}
|
|
568
|
+
function xe(r, e, t = {}) {
|
|
569
|
+
const s = { type: "Feature" };
|
|
570
|
+
return (t.id === 0 || t.id) && (s.id = t.id), t.bbox && (s.bbox = t.bbox), s.properties = {}, s.geometry = r, s;
|
|
571
|
+
}
|
|
572
|
+
function te(r, e, t = {}) {
|
|
573
|
+
if (!r)
|
|
574
|
+
throw new Error("coordinates is required");
|
|
575
|
+
if (!Array.isArray(r))
|
|
576
|
+
throw new Error("coordinates must be an Array");
|
|
577
|
+
if (r.length < 2)
|
|
578
|
+
throw new Error("coordinates must be at least 2 numbers long");
|
|
579
|
+
if (!se(r[0]) || !se(r[1]))
|
|
580
|
+
throw new Error("coordinates must contain numbers");
|
|
581
|
+
return xe({
|
|
582
|
+
type: "Point",
|
|
583
|
+
coordinates: r
|
|
584
|
+
}, e, t);
|
|
585
|
+
}
|
|
586
|
+
function se(r) {
|
|
587
|
+
return !isNaN(r) && r !== null && !Array.isArray(r);
|
|
588
|
+
}
|
|
589
|
+
function Ve(r) {
|
|
590
|
+
if (!r)
|
|
591
|
+
throw new Error("coord is required");
|
|
592
|
+
if (!Array.isArray(r)) {
|
|
593
|
+
if (r.type === "Feature" && r.geometry !== null && r.geometry.type === "Point")
|
|
594
|
+
return [...r.geometry.coordinates];
|
|
595
|
+
if (r.type === "Point")
|
|
596
|
+
return [...r.coordinates];
|
|
597
|
+
}
|
|
598
|
+
if (Array.isArray(r) && r.length >= 2 && !Array.isArray(r[0]) && !Array.isArray(r[1]))
|
|
599
|
+
return [...r];
|
|
600
|
+
throw new Error("coord must be GeoJSON Point or an Array of numbers");
|
|
601
|
+
}
|
|
602
|
+
function Ae(r) {
|
|
603
|
+
return r.type === "Feature" ? r.geometry : r;
|
|
604
|
+
}
|
|
605
|
+
function _e(r, e, t = {}) {
|
|
606
|
+
if (!r)
|
|
607
|
+
throw new Error("point is required");
|
|
608
|
+
if (!e)
|
|
609
|
+
throw new Error("polygon is required");
|
|
610
|
+
const s = Ve(r), i = Ae(e), n = i.type, o = e.bbox;
|
|
611
|
+
let l = i.coordinates;
|
|
612
|
+
if (o && Re(s, o) === !1)
|
|
613
|
+
return !1;
|
|
614
|
+
n === "Polygon" && (l = [l]);
|
|
615
|
+
let h = !1;
|
|
616
|
+
for (var d = 0; d < l.length; ++d) {
|
|
617
|
+
const u = Le(s, l[d]);
|
|
618
|
+
if (u === 0) return !t.ignoreBoundary;
|
|
619
|
+
u && (h = !0);
|
|
620
|
+
}
|
|
621
|
+
return h;
|
|
622
|
+
}
|
|
623
|
+
function Re(r, e) {
|
|
624
|
+
return e[0] <= r[0] && e[1] <= r[1] && e[2] >= r[0] && e[3] >= r[1];
|
|
625
|
+
}
|
|
626
|
+
var ie = _e;
|
|
627
|
+
const re = 10, Ne = 24, ne = 3;
|
|
628
|
+
class Oe {
|
|
629
|
+
constructor(e, t) {
|
|
630
|
+
this.selectedIds = /* @__PURE__ */ new Set(), this.isActive = !1, this.dragging = !1, this.dragVertexIndex = -1, this.dragStartFeature = null, this.draggingPolygon = !1, this.dragPolygonStartLngLat = null, this.highlightedVertexIndex = -1, this.callbacks = e, this.onSelectionChange = t;
|
|
631
|
+
}
|
|
632
|
+
activate() {
|
|
633
|
+
this.isActive = !0;
|
|
634
|
+
}
|
|
635
|
+
deactivate() {
|
|
636
|
+
this.isActive = !1, this.highlightedVertexIndex = -1, this.endDrag(), this.selectedIds.size > 0 && (this.selectedIds.clear(), this.callbacks.clearVertices(), this.notifySelectionChange());
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Get the currently selected feature IDs.
|
|
640
|
+
*/
|
|
641
|
+
getSelectedIds() {
|
|
642
|
+
return Array.from(this.selectedIds);
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Programmatically select a feature by ID.
|
|
646
|
+
*
|
|
647
|
+
* Replaces any existing selection. Renders vertex handles and
|
|
648
|
+
* emits a selectionchange event. Cancels any in-progress drag.
|
|
649
|
+
*
|
|
650
|
+
* @param id - The feature ID to select.
|
|
651
|
+
* @returns `true` if the feature was found and selected, `false` otherwise.
|
|
652
|
+
*/
|
|
653
|
+
selectFeature(e) {
|
|
654
|
+
if (!this.isActive) return !1;
|
|
655
|
+
const t = this.callbacks.getFeatureById(e);
|
|
656
|
+
return t ? (this.highlightedVertexIndex = -1, this.endDrag(), this.selectedIds.clear(), this.selectedIds.add(e), this.showVertexHandles(t), this.notifySelectionChange(), this.callbacks.renderFeatures(), !0) : !1;
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Programmatically clear the current selection.
|
|
660
|
+
*
|
|
661
|
+
* Removes vertex handles and emits a selectionchange event.
|
|
662
|
+
* No-op if nothing is selected or mode is not active.
|
|
663
|
+
*/
|
|
664
|
+
clearSelection() {
|
|
665
|
+
this.isActive && this.selectedIds.size !== 0 && (this.highlightedVertexIndex = -1, this.endDrag(), this.selectedIds.clear(), this.callbacks.clearVertices(), this.notifySelectionChange(), this.callbacks.renderFeatures());
|
|
666
|
+
}
|
|
667
|
+
onPointerDown(e) {
|
|
668
|
+
if (!this.isActive) return;
|
|
669
|
+
const t = this.getFirstSelectedId();
|
|
670
|
+
if (t) {
|
|
671
|
+
const o = this.callbacks.getFeatureById(t);
|
|
672
|
+
if (o) {
|
|
673
|
+
const l = this.getVertices(o), h = this.getThreshold(e), d = this.findNearestVertex(
|
|
674
|
+
l,
|
|
675
|
+
e.point,
|
|
676
|
+
h
|
|
677
|
+
);
|
|
678
|
+
if (d >= 0) {
|
|
679
|
+
this.startDrag(o, d);
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
const u = this.computeMidpoints(l), a = this.findNearestPoint(u, e.point, h);
|
|
683
|
+
if (a >= 0) {
|
|
684
|
+
const g = R.cloneFeature(o), y = this.insertVertex(o, a + 1, u[a]);
|
|
685
|
+
this.callbacks.updateFeatureInStore(t, y), this.showVertexHandles(y), this.startDrag(y, a + 1), this.dragStartFeature = g;
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
const c = te([e.lngLat.lng, e.lngLat.lat]);
|
|
689
|
+
if (ie(c, o.geometry)) {
|
|
690
|
+
this.startPolygonDrag(o, e.lngLat);
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
this.highlightedVertexIndex = -1;
|
|
696
|
+
const s = te([e.lngLat.lng, e.lngLat.lat]), i = this.callbacks.getAllFeatures();
|
|
697
|
+
let n;
|
|
698
|
+
for (let o = i.length - 1; o >= 0; o--) {
|
|
699
|
+
const l = i[o], h = l.geometry;
|
|
700
|
+
if (ie(s, h)) {
|
|
701
|
+
n = l;
|
|
702
|
+
break;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
n ? this.selectedIds.has(n.id) ? (this.selectedIds.delete(n.id), this.callbacks.clearVertices()) : (this.selectedIds.clear(), this.selectedIds.add(n.id), this.showVertexHandles(n)) : (this.selectedIds.clear(), this.callbacks.clearVertices()), this.notifySelectionChange(), this.callbacks.renderFeatures();
|
|
706
|
+
}
|
|
707
|
+
onPointerMove(e) {
|
|
708
|
+
if (!this.isActive) return;
|
|
709
|
+
if (this.dragging) {
|
|
710
|
+
const l = this.getFirstSelectedId();
|
|
711
|
+
if (!l) return;
|
|
712
|
+
const h = this.callbacks.getFeatureById(l);
|
|
713
|
+
if (!h) return;
|
|
714
|
+
const d = [e.lngLat.lng, e.lngLat.lat], u = this.moveVertex(h, this.dragVertexIndex, d);
|
|
715
|
+
if (ae(u.geometry.coordinates[0]))
|
|
716
|
+
return;
|
|
717
|
+
this.callbacks.updateFeatureInStore(l, u), this.callbacks.renderFeatures(), this.showVertexHandles(u);
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
if (this.draggingPolygon) {
|
|
721
|
+
const l = this.getFirstSelectedId();
|
|
722
|
+
if (!l || !this.dragStartFeature || !this.dragPolygonStartLngLat) return;
|
|
723
|
+
const h = e.lngLat.lng - this.dragPolygonStartLngLat.lng, d = e.lngLat.lat - this.dragPolygonStartLngLat.lat, u = this.movePolygon(this.dragStartFeature, h, d);
|
|
724
|
+
this.callbacks.updateFeatureInStore(l, u), this.callbacks.renderFeatures(), this.showVertexHandles(u);
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
const t = this.getFirstSelectedId();
|
|
728
|
+
if (!t) return;
|
|
729
|
+
const s = this.callbacks.getFeatureById(t);
|
|
730
|
+
if (!s) return;
|
|
731
|
+
const i = this.getVertices(s), n = this.getThreshold(e), o = this.findNearestVertex(i, e.point, n);
|
|
732
|
+
o !== this.highlightedVertexIndex && (this.highlightedVertexIndex = o, this.showVertexHandles(s));
|
|
733
|
+
}
|
|
734
|
+
onPointerUp(e) {
|
|
735
|
+
if (!this.isActive || !this.dragging && !this.draggingPolygon) return;
|
|
736
|
+
const t = this.getFirstSelectedId();
|
|
737
|
+
if (!t || !this.dragStartFeature) {
|
|
738
|
+
this.endDrag();
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
const s = this.callbacks.getFeatureById(t);
|
|
742
|
+
if (s) {
|
|
743
|
+
const i = new $(
|
|
744
|
+
t,
|
|
745
|
+
this.dragStartFeature,
|
|
746
|
+
R.cloneFeature(s)
|
|
747
|
+
);
|
|
748
|
+
this.callbacks.pushToHistory(i), this.callbacks.emitEvent("update", {
|
|
749
|
+
feature: s,
|
|
750
|
+
oldFeature: this.dragStartFeature
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
this.endDrag();
|
|
754
|
+
}
|
|
755
|
+
onDoubleClick(e) {
|
|
756
|
+
if (!this.isActive) return;
|
|
757
|
+
const t = this.getFirstSelectedId();
|
|
758
|
+
if (!t) return;
|
|
759
|
+
const s = this.callbacks.getFeatureById(t);
|
|
760
|
+
if (!s) return;
|
|
761
|
+
const i = this.getVertices(s), n = this.getThreshold(e), o = this.findNearestVertex(i, e.point, n);
|
|
762
|
+
if (o >= 0 && i.length > ne) {
|
|
763
|
+
const l = R.cloneFeature(s), h = this.removeVertex(s, o);
|
|
764
|
+
this.callbacks.updateFeatureInStore(t, h);
|
|
765
|
+
const d = new $(t, l, R.cloneFeature(h));
|
|
766
|
+
this.callbacks.pushToHistory(d), this.callbacks.emitEvent("update", {
|
|
767
|
+
feature: h,
|
|
768
|
+
oldFeature: l
|
|
769
|
+
}), this.callbacks.renderFeatures(), this.showVertexHandles(h), e.originalEvent.preventDefault(), e.originalEvent.stopPropagation();
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
onLongPress(e) {
|
|
773
|
+
if (!this.isActive) return;
|
|
774
|
+
const t = this.getFirstSelectedId();
|
|
775
|
+
if (!t)
|
|
776
|
+
return;
|
|
777
|
+
const s = this.callbacks.getFeatureById(t);
|
|
778
|
+
if (!s) return;
|
|
779
|
+
const i = this.getVertices(s), n = this.getThreshold(e), o = this.findNearestVertex(i, e.point, n);
|
|
780
|
+
if (o >= 0 && i.length > ne) {
|
|
781
|
+
const l = R.cloneFeature(s), h = this.removeVertex(s, o);
|
|
782
|
+
this.callbacks.updateFeatureInStore(t, h);
|
|
783
|
+
const d = new $(t, l, R.cloneFeature(h));
|
|
784
|
+
this.callbacks.pushToHistory(d), this.callbacks.emitEvent("update", {
|
|
785
|
+
feature: h,
|
|
786
|
+
oldFeature: l
|
|
787
|
+
}), this.callbacks.renderFeatures(), this.showVertexHandles(h);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
onKeyDown(e, t) {
|
|
791
|
+
this.isActive && (e === "Delete" || e === "Backspace") && this.deleteSelected();
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Get the hit-test threshold based on input type.
|
|
795
|
+
*/
|
|
796
|
+
getThreshold(e) {
|
|
797
|
+
return e.inputType === "touch" ? Ne : re;
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Get the unique vertices (excluding closing point) of a polygon.
|
|
801
|
+
*/
|
|
802
|
+
getVertices(e) {
|
|
803
|
+
const t = e.geometry.coordinates[0];
|
|
804
|
+
return t.slice(0, t.length - 1);
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Find the nearest vertex within the hit threshold.
|
|
808
|
+
* @returns The vertex index, or -1 if none is close enough.
|
|
809
|
+
*/
|
|
810
|
+
findNearestVertex(e, t, s) {
|
|
811
|
+
return this.findNearestPoint(e, t, s);
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Find the nearest point (vertex or midpoint) within the hit threshold.
|
|
815
|
+
* @returns The index, or -1 if none is close enough.
|
|
816
|
+
*/
|
|
817
|
+
findNearestPoint(e, t, s = re) {
|
|
818
|
+
let i = 1 / 0, n = -1;
|
|
819
|
+
for (let o = 0; o < e.length; o++) {
|
|
820
|
+
const l = this.callbacks.getScreenPoint({
|
|
821
|
+
lng: e[o][0],
|
|
822
|
+
lat: e[o][1]
|
|
823
|
+
}), h = t.x - l.x, d = t.y - l.y, u = Math.sqrt(h * h + d * d);
|
|
824
|
+
u <= s && u < i && (i = u, n = o);
|
|
825
|
+
}
|
|
826
|
+
return n;
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Compute midpoints for each edge of the polygon.
|
|
830
|
+
*/
|
|
831
|
+
computeMidpoints(e) {
|
|
832
|
+
const t = [];
|
|
833
|
+
for (let s = 0; s < e.length; s++) {
|
|
834
|
+
const i = (s + 1) % e.length;
|
|
835
|
+
t.push([
|
|
836
|
+
(e[s][0] + e[i][0]) / 2,
|
|
837
|
+
(e[s][1] + e[i][1]) / 2
|
|
838
|
+
]);
|
|
839
|
+
}
|
|
840
|
+
return t;
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Start a vertex drag operation.
|
|
844
|
+
*/
|
|
845
|
+
startDrag(e, t) {
|
|
846
|
+
this.dragging = !0, this.dragVertexIndex = t, this.dragStartFeature = R.cloneFeature(e), this.callbacks.setDragPan(!1);
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Start a polygon drag (whole-polygon move) operation.
|
|
850
|
+
*/
|
|
851
|
+
startPolygonDrag(e, t) {
|
|
852
|
+
this.draggingPolygon = !0, this.dragPolygonStartLngLat = t, this.dragStartFeature = R.cloneFeature(e), this.callbacks.setDragPan(!1);
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* End a drag operation and restore map interactions.
|
|
856
|
+
*/
|
|
857
|
+
endDrag() {
|
|
858
|
+
(this.dragging || this.draggingPolygon) && this.callbacks.setDragPan(!0), this.dragging = !1, this.dragVertexIndex = -1, this.dragStartFeature = null, this.draggingPolygon = !1, this.dragPolygonStartLngLat = null, this.highlightedVertexIndex = -1;
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Create a new feature with a vertex moved to a new position.
|
|
862
|
+
*/
|
|
863
|
+
moveVertex(e, t, s) {
|
|
864
|
+
const i = [...e.geometry.coordinates[0]];
|
|
865
|
+
return i[t] = s, t === 0 && (i[i.length - 1] = s), t === i.length - 1 && (i[0] = s), {
|
|
866
|
+
...e,
|
|
867
|
+
geometry: {
|
|
868
|
+
type: "Polygon",
|
|
869
|
+
coordinates: [i]
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Create a new feature with all vertices translated by the given delta.
|
|
875
|
+
*/
|
|
876
|
+
movePolygon(e, t, s) {
|
|
877
|
+
const i = e.geometry.coordinates[0].map(
|
|
878
|
+
(n) => [n[0] + t, n[1] + s]
|
|
879
|
+
);
|
|
880
|
+
return {
|
|
881
|
+
...e,
|
|
882
|
+
geometry: {
|
|
883
|
+
type: "Polygon",
|
|
884
|
+
coordinates: [i]
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Create a new feature with a vertex inserted at the given index.
|
|
890
|
+
*/
|
|
891
|
+
insertVertex(e, t, s) {
|
|
892
|
+
const i = [...e.geometry.coordinates[0]];
|
|
893
|
+
return i.splice(t, 0, s), {
|
|
894
|
+
...e,
|
|
895
|
+
geometry: {
|
|
896
|
+
type: "Polygon",
|
|
897
|
+
coordinates: [i]
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Create a new feature with a vertex removed at the given index.
|
|
903
|
+
*/
|
|
904
|
+
removeVertex(e, t) {
|
|
905
|
+
const i = this.getVertices(e).filter((o, l) => l !== t), n = [...i, [...i[0]]];
|
|
906
|
+
return {
|
|
907
|
+
...e,
|
|
908
|
+
geometry: {
|
|
909
|
+
type: "Polygon",
|
|
910
|
+
coordinates: [n]
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Refresh vertex/midpoint handles for the currently selected feature.
|
|
916
|
+
* Call this after external geometry changes (e.g. undo/redo).
|
|
917
|
+
*/
|
|
918
|
+
refreshVertexHandles() {
|
|
919
|
+
if (!this.isActive) return;
|
|
920
|
+
const e = this.getFirstSelectedId();
|
|
921
|
+
if (!e) return;
|
|
922
|
+
const t = this.callbacks.getFeatureById(e);
|
|
923
|
+
t ? this.showVertexHandles(t) : (this.selectedIds.delete(e), this.callbacks.clearVertices(), this.notifySelectionChange());
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Show vertex and midpoint handles for a selected feature.
|
|
927
|
+
*/
|
|
928
|
+
showVertexHandles(e) {
|
|
929
|
+
const t = this.getVertices(e), s = this.computeMidpoints(t);
|
|
930
|
+
this.callbacks.renderVertices(
|
|
931
|
+
e.id,
|
|
932
|
+
t,
|
|
933
|
+
s,
|
|
934
|
+
this.highlightedVertexIndex >= 0 ? this.highlightedVertexIndex : void 0
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Get the first selected feature ID.
|
|
939
|
+
*/
|
|
940
|
+
getFirstSelectedId() {
|
|
941
|
+
return this.selectedIds.values().next().value;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Delete all currently selected features.
|
|
945
|
+
*/
|
|
946
|
+
deleteSelected() {
|
|
947
|
+
if (this.selectedIds.size === 0) return;
|
|
948
|
+
const e = Array.from(this.selectedIds);
|
|
949
|
+
for (const t of e) {
|
|
950
|
+
const s = this.callbacks.getFeatureById(t);
|
|
951
|
+
if (s) {
|
|
952
|
+
this.callbacks.removeFeatureFromStore(t);
|
|
953
|
+
const i = new oe(s);
|
|
954
|
+
this.callbacks.pushToHistory(i), this.callbacks.emitEvent("delete", { feature: s });
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
this.selectedIds.clear(), this.callbacks.clearVertices(), this.notifySelectionChange(), this.callbacks.renderFeatures();
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Notify the host about selection changes.
|
|
961
|
+
*/
|
|
962
|
+
notifySelectionChange() {
|
|
963
|
+
const e = this.getSelectedIds();
|
|
964
|
+
this.callbacks.emitEvent("selectionchange", { selectedIds: e }), this.onSelectionChange && this.onSelectionChange(e);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
class Ue {
|
|
968
|
+
constructor(e, t) {
|
|
969
|
+
this.handleMouseDown = (s) => {
|
|
970
|
+
this.callbacks.onPointerDown(this.normalize(s));
|
|
971
|
+
}, this.handleMouseMove = (s) => {
|
|
972
|
+
this.callbacks.onPointerMove(this.normalize(s));
|
|
973
|
+
}, this.handleMouseUp = (s) => {
|
|
974
|
+
this.callbacks.onPointerUp(this.normalize(s));
|
|
975
|
+
}, this.handleDblClick = (s) => {
|
|
976
|
+
this.callbacks.onDoubleClick(this.normalize(s));
|
|
977
|
+
}, this.map = e, this.callbacks = t, this.canvas = e.getCanvasContainer();
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Start listening for mouse events on the map canvas.
|
|
981
|
+
*/
|
|
982
|
+
enable() {
|
|
983
|
+
this.canvas.addEventListener("mousedown", this.handleMouseDown), this.canvas.addEventListener("mousemove", this.handleMouseMove), this.canvas.addEventListener("mouseup", this.handleMouseUp), this.canvas.addEventListener("dblclick", this.handleDblClick);
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Stop listening for mouse events.
|
|
987
|
+
*/
|
|
988
|
+
disable() {
|
|
989
|
+
this.canvas.removeEventListener("mousedown", this.handleMouseDown), this.canvas.removeEventListener("mousemove", this.handleMouseMove), this.canvas.removeEventListener("mouseup", this.handleMouseUp), this.canvas.removeEventListener("dblclick", this.handleDblClick);
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Destroy the mouse input handler and remove all listeners.
|
|
993
|
+
*/
|
|
994
|
+
destroy() {
|
|
995
|
+
this.disable();
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Convert a raw MouseEvent into a NormalizedInputEvent.
|
|
999
|
+
*/
|
|
1000
|
+
normalize(e) {
|
|
1001
|
+
const t = this.canvas.getBoundingClientRect(), s = e.clientX - t.left, i = e.clientY - t.top, n = this.map.unproject([s, i]);
|
|
1002
|
+
return {
|
|
1003
|
+
lngLat: { lng: n.lng, lat: n.lat },
|
|
1004
|
+
point: { x: s, y: i },
|
|
1005
|
+
originalEvent: e,
|
|
1006
|
+
inputType: "mouse"
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
const Be = 300, He = 500, ze = 15;
|
|
1011
|
+
class je {
|
|
1012
|
+
constructor(e, t) {
|
|
1013
|
+
this.lastTapTime = 0, this.longPressTimer = null, this.touchStartPos = null, this.isPinching = !1, this.handleTouchStart = (s) => {
|
|
1014
|
+
if (s.touches.length >= 2) {
|
|
1015
|
+
this.isPinching = !0, this.cancelLongPress();
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
this.isPinching = !1;
|
|
1019
|
+
const i = this.normalize(s);
|
|
1020
|
+
this.touchStartPos = { x: i.point.x, y: i.point.y }, this.cancelLongPress(), this.longPressTimer = setTimeout(() => {
|
|
1021
|
+
this.touchStartPos && (this.callbacks.onLongPress(i), this.touchStartPos = null);
|
|
1022
|
+
}, He), this.callbacks.onPointerDown(i);
|
|
1023
|
+
}, this.handleTouchMove = (s) => {
|
|
1024
|
+
if (this.isPinching || s.touches.length >= 2) {
|
|
1025
|
+
this.cancelLongPress();
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
const i = this.normalize(s);
|
|
1029
|
+
if (this.touchStartPos) {
|
|
1030
|
+
const n = i.point.x - this.touchStartPos.x, o = i.point.y - this.touchStartPos.y;
|
|
1031
|
+
Math.sqrt(n * n + o * o) > ze && this.cancelLongPress();
|
|
1032
|
+
}
|
|
1033
|
+
this.callbacks.onPointerMove(i);
|
|
1034
|
+
}, this.handleTouchEnd = (s) => {
|
|
1035
|
+
if (this.cancelLongPress(), this.isPinching) {
|
|
1036
|
+
s.touches.length === 0 && (this.isPinching = !1);
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
if (s.changedTouches.length === 0) return;
|
|
1040
|
+
const i = this.normalizeChangedTouch(s), n = Date.now();
|
|
1041
|
+
n - this.lastTapTime < Be ? (this.callbacks.onDoubleClick(i), this.lastTapTime = 0) : this.lastTapTime = n, this.touchStartPos && (this.callbacks.onPointerUp(i), this.touchStartPos = null);
|
|
1042
|
+
}, this.map = e, this.callbacks = t, this.canvas = e.getCanvasContainer();
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Start listening for touch events on the map canvas.
|
|
1046
|
+
*/
|
|
1047
|
+
enable() {
|
|
1048
|
+
this.canvas.addEventListener("touchstart", this.handleTouchStart, {
|
|
1049
|
+
passive: !1
|
|
1050
|
+
}), this.canvas.addEventListener("touchmove", this.handleTouchMove, {
|
|
1051
|
+
passive: !1
|
|
1052
|
+
}), this.canvas.addEventListener("touchend", this.handleTouchEnd);
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Stop listening for touch events.
|
|
1056
|
+
*/
|
|
1057
|
+
disable() {
|
|
1058
|
+
this.canvas.removeEventListener("touchstart", this.handleTouchStart), this.canvas.removeEventListener("touchmove", this.handleTouchMove), this.canvas.removeEventListener("touchend", this.handleTouchEnd), this.cancelLongPress();
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Destroy the touch input handler and remove all listeners.
|
|
1062
|
+
*/
|
|
1063
|
+
destroy() {
|
|
1064
|
+
this.disable();
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Cancel the long press detection timer.
|
|
1068
|
+
*/
|
|
1069
|
+
cancelLongPress() {
|
|
1070
|
+
this.longPressTimer !== null && (clearTimeout(this.longPressTimer), this.longPressTimer = null);
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Convert a TouchEvent into a NormalizedInputEvent using the first touch.
|
|
1074
|
+
*/
|
|
1075
|
+
normalize(e) {
|
|
1076
|
+
const t = e.touches[0], s = this.canvas.getBoundingClientRect(), i = t.clientX - s.left, n = t.clientY - s.top, o = this.map.unproject([i, n]);
|
|
1077
|
+
return {
|
|
1078
|
+
lngLat: { lng: o.lng, lat: o.lat },
|
|
1079
|
+
point: { x: i, y: n },
|
|
1080
|
+
originalEvent: e,
|
|
1081
|
+
inputType: "touch"
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Convert a TouchEvent into a NormalizedInputEvent using changedTouches
|
|
1086
|
+
* (for touchend events where touches array is empty).
|
|
1087
|
+
*/
|
|
1088
|
+
normalizeChangedTouch(e) {
|
|
1089
|
+
const t = e.changedTouches[0], s = this.canvas.getBoundingClientRect(), i = t.clientX - s.left, n = t.clientY - s.top, o = this.map.unproject([i, n]);
|
|
1090
|
+
return {
|
|
1091
|
+
lngLat: { lng: o.lng, lat: o.lat },
|
|
1092
|
+
point: { x: i, y: n },
|
|
1093
|
+
originalEvent: e,
|
|
1094
|
+
inputType: "touch"
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
const K = class K {
|
|
1099
|
+
constructor(e) {
|
|
1100
|
+
this.handleKeyDown = (t) => {
|
|
1101
|
+
K.RELEVANT_KEYS.has(t.key) && this.callbacks.onKeyDown(t.key, t);
|
|
1102
|
+
}, this.callbacks = e;
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Start listening for keyboard events.
|
|
1106
|
+
*/
|
|
1107
|
+
enable() {
|
|
1108
|
+
document.addEventListener("keydown", this.handleKeyDown);
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Stop listening for keyboard events.
|
|
1112
|
+
*/
|
|
1113
|
+
disable() {
|
|
1114
|
+
document.removeEventListener("keydown", this.handleKeyDown);
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Destroy the keyboard input handler and remove all listeners.
|
|
1118
|
+
*/
|
|
1119
|
+
destroy() {
|
|
1120
|
+
this.disable();
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
K.RELEVANT_KEYS = /* @__PURE__ */ new Set([
|
|
1124
|
+
"Escape",
|
|
1125
|
+
"Delete",
|
|
1126
|
+
"Backspace"
|
|
1127
|
+
]);
|
|
1128
|
+
let G = K;
|
|
1129
|
+
class Xe {
|
|
1130
|
+
constructor(e, t) {
|
|
1131
|
+
this.getActiveMode = t;
|
|
1132
|
+
const s = {
|
|
1133
|
+
onPointerDown: (i) => {
|
|
1134
|
+
var n;
|
|
1135
|
+
(n = this.getActiveMode()) == null || n.onPointerDown(i);
|
|
1136
|
+
},
|
|
1137
|
+
onPointerMove: (i) => {
|
|
1138
|
+
var n;
|
|
1139
|
+
(n = this.getActiveMode()) == null || n.onPointerMove(i);
|
|
1140
|
+
},
|
|
1141
|
+
onPointerUp: (i) => {
|
|
1142
|
+
var n;
|
|
1143
|
+
(n = this.getActiveMode()) == null || n.onPointerUp(i);
|
|
1144
|
+
},
|
|
1145
|
+
onDoubleClick: (i) => {
|
|
1146
|
+
var n;
|
|
1147
|
+
(n = this.getActiveMode()) == null || n.onDoubleClick(i);
|
|
1148
|
+
},
|
|
1149
|
+
onLongPress: (i) => {
|
|
1150
|
+
var n;
|
|
1151
|
+
(n = this.getActiveMode()) == null || n.onLongPress(i);
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
this.mouseInput = new Ue(e, s), this.touchInput = new je(e, s), this.keyboardInput = new G({
|
|
1155
|
+
onKeyDown: (i, n) => {
|
|
1156
|
+
var o;
|
|
1157
|
+
(o = this.getActiveMode()) == null || o.onKeyDown(i, n);
|
|
1158
|
+
}
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* Enable all input handlers.
|
|
1163
|
+
*/
|
|
1164
|
+
enable() {
|
|
1165
|
+
this.mouseInput.enable(), this.touchInput.enable(), this.keyboardInput.enable();
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Disable all input handlers.
|
|
1169
|
+
*/
|
|
1170
|
+
disable() {
|
|
1171
|
+
this.mouseInput.disable(), this.touchInput.disable(), this.keyboardInput.disable();
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Destroy all input handlers and remove event listeners.
|
|
1175
|
+
*/
|
|
1176
|
+
destroy() {
|
|
1177
|
+
this.mouseInput.destroy(), this.touchInput.destroy(), this.keyboardInput.destroy();
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
const v = {
|
|
1181
|
+
FEATURES: "libre-draw-features",
|
|
1182
|
+
PREVIEW: "libre-draw-preview",
|
|
1183
|
+
EDIT_VERTICES: "libre-draw-edit-vertices"
|
|
1184
|
+
}, B = {
|
|
1185
|
+
type: "FeatureCollection",
|
|
1186
|
+
features: []
|
|
1187
|
+
};
|
|
1188
|
+
class We {
|
|
1189
|
+
constructor(e) {
|
|
1190
|
+
this.initialized = !1, this.map = e;
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Initialize the GeoJSON sources on the map.
|
|
1194
|
+
* Should be called after the map style has loaded.
|
|
1195
|
+
*/
|
|
1196
|
+
initialize() {
|
|
1197
|
+
this.initialized || (this.map.getSource(v.FEATURES) || this.map.addSource(v.FEATURES, {
|
|
1198
|
+
type: "geojson",
|
|
1199
|
+
data: B
|
|
1200
|
+
}), this.map.getSource(v.PREVIEW) || this.map.addSource(v.PREVIEW, {
|
|
1201
|
+
type: "geojson",
|
|
1202
|
+
data: B
|
|
1203
|
+
}), this.map.getSource(v.EDIT_VERTICES) || this.map.addSource(v.EDIT_VERTICES, {
|
|
1204
|
+
type: "geojson",
|
|
1205
|
+
data: B
|
|
1206
|
+
}), this.initialized = !0);
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Update the features source with new GeoJSON data.
|
|
1210
|
+
* @param data - A GeoJSON FeatureCollection.
|
|
1211
|
+
*/
|
|
1212
|
+
updateFeatures(e) {
|
|
1213
|
+
const t = this.map.getSource(v.FEATURES);
|
|
1214
|
+
t && t.setData(e);
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Update the preview source with new GeoJSON data.
|
|
1218
|
+
* @param data - A GeoJSON FeatureCollection.
|
|
1219
|
+
*/
|
|
1220
|
+
updatePreview(e) {
|
|
1221
|
+
const t = this.map.getSource(v.PREVIEW);
|
|
1222
|
+
t && t.setData(e);
|
|
1223
|
+
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Clear the preview source.
|
|
1226
|
+
*/
|
|
1227
|
+
clearPreview() {
|
|
1228
|
+
this.updatePreview(B);
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Update the edit vertices source with new GeoJSON data.
|
|
1232
|
+
* @param data - A GeoJSON FeatureCollection of Point features.
|
|
1233
|
+
*/
|
|
1234
|
+
updateEditVertices(e) {
|
|
1235
|
+
const t = this.map.getSource(v.EDIT_VERTICES);
|
|
1236
|
+
t && t.setData(e);
|
|
1237
|
+
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Clear the edit vertices source.
|
|
1240
|
+
*/
|
|
1241
|
+
clearEditVertices() {
|
|
1242
|
+
this.updateEditVertices(B);
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Remove all sources from the map.
|
|
1246
|
+
*/
|
|
1247
|
+
destroy() {
|
|
1248
|
+
this.map.getSource(v.FEATURES) && this.map.removeSource(v.FEATURES), this.map.getSource(v.PREVIEW) && this.map.removeSource(v.PREVIEW), this.map.getSource(v.EDIT_VERTICES) && this.map.removeSource(v.EDIT_VERTICES), this.initialized = !1;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
const S = {
|
|
1252
|
+
FILL: "libre-draw-fill",
|
|
1253
|
+
OUTLINE: "libre-draw-outline",
|
|
1254
|
+
VERTICES: "libre-draw-vertices",
|
|
1255
|
+
PREVIEW: "libre-draw-preview",
|
|
1256
|
+
EDIT_VERTICES: "libre-draw-edit-vertices",
|
|
1257
|
+
EDIT_MIDPOINTS: "libre-draw-edit-midpoints"
|
|
1258
|
+
}, w = {
|
|
1259
|
+
FILL: "#3bb2d0",
|
|
1260
|
+
FILL_OPACITY: 0.2,
|
|
1261
|
+
FILL_SELECTED: "#fbb03b",
|
|
1262
|
+
FILL_SELECTED_OPACITY: 0.4,
|
|
1263
|
+
OUTLINE: "#3bb2d0",
|
|
1264
|
+
OUTLINE_WIDTH: 2,
|
|
1265
|
+
OUTLINE_SELECTED: "#fbb03b",
|
|
1266
|
+
VERTEX_COLOR: "#ffffff",
|
|
1267
|
+
VERTEX_STROKE: "#3bb2d0",
|
|
1268
|
+
VERTEX_RADIUS: 4,
|
|
1269
|
+
PREVIEW_OUTLINE: "#3bb2d0",
|
|
1270
|
+
PREVIEW_OUTLINE_DASH: [2, 2],
|
|
1271
|
+
EDIT_VERTEX_COLOR: "#ffffff",
|
|
1272
|
+
EDIT_VERTEX_STROKE: "#3bb2d0",
|
|
1273
|
+
EDIT_VERTEX_RADIUS: 5,
|
|
1274
|
+
EDIT_VERTEX_STROKE_WIDTH: 2,
|
|
1275
|
+
MIDPOINT_COLOR: "#3bb2d0",
|
|
1276
|
+
MIDPOINT_OPACITY: 0.5,
|
|
1277
|
+
MIDPOINT_RADIUS: 3
|
|
1278
|
+
};
|
|
1279
|
+
class Ke {
|
|
1280
|
+
constructor(e, t) {
|
|
1281
|
+
this.selectedIds = /* @__PURE__ */ new Set(), this.pendingRender = !1, this.pendingFeatures = null, this.initialized = !1, this.map = e, this.sourceManager = t;
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Initialize rendering layers on the map.
|
|
1285
|
+
* Should be called after the map style and sources are ready.
|
|
1286
|
+
*/
|
|
1287
|
+
initialize() {
|
|
1288
|
+
this.initialized || (this.sourceManager.initialize(), this.map.getLayer(S.FILL) || this.map.addLayer({
|
|
1289
|
+
id: S.FILL,
|
|
1290
|
+
type: "fill",
|
|
1291
|
+
source: v.FEATURES,
|
|
1292
|
+
paint: {
|
|
1293
|
+
"fill-color": [
|
|
1294
|
+
"case",
|
|
1295
|
+
["boolean", ["get", "_selected"], !1],
|
|
1296
|
+
w.FILL_SELECTED,
|
|
1297
|
+
w.FILL
|
|
1298
|
+
],
|
|
1299
|
+
"fill-opacity": [
|
|
1300
|
+
"case",
|
|
1301
|
+
["boolean", ["get", "_selected"], !1],
|
|
1302
|
+
w.FILL_SELECTED_OPACITY,
|
|
1303
|
+
w.FILL_OPACITY
|
|
1304
|
+
]
|
|
1305
|
+
}
|
|
1306
|
+
}), this.map.getLayer(S.OUTLINE) || this.map.addLayer({
|
|
1307
|
+
id: S.OUTLINE,
|
|
1308
|
+
type: "line",
|
|
1309
|
+
source: v.FEATURES,
|
|
1310
|
+
paint: {
|
|
1311
|
+
"line-color": [
|
|
1312
|
+
"case",
|
|
1313
|
+
["boolean", ["get", "_selected"], !1],
|
|
1314
|
+
w.OUTLINE_SELECTED,
|
|
1315
|
+
w.OUTLINE
|
|
1316
|
+
],
|
|
1317
|
+
"line-width": w.OUTLINE_WIDTH
|
|
1318
|
+
}
|
|
1319
|
+
}), this.map.getLayer(S.VERTICES) || this.map.addLayer({
|
|
1320
|
+
id: S.VERTICES,
|
|
1321
|
+
type: "circle",
|
|
1322
|
+
source: v.FEATURES,
|
|
1323
|
+
filter: ["==", "$type", "Point"],
|
|
1324
|
+
paint: {
|
|
1325
|
+
"circle-radius": w.VERTEX_RADIUS,
|
|
1326
|
+
"circle-color": w.VERTEX_COLOR,
|
|
1327
|
+
"circle-stroke-color": w.VERTEX_STROKE,
|
|
1328
|
+
"circle-stroke-width": 2
|
|
1329
|
+
}
|
|
1330
|
+
}), this.map.getLayer(S.PREVIEW) || this.map.addLayer({
|
|
1331
|
+
id: S.PREVIEW,
|
|
1332
|
+
type: "line",
|
|
1333
|
+
source: v.PREVIEW,
|
|
1334
|
+
paint: {
|
|
1335
|
+
"line-color": w.PREVIEW_OUTLINE,
|
|
1336
|
+
"line-width": 2,
|
|
1337
|
+
"line-dasharray": w.PREVIEW_OUTLINE_DASH
|
|
1338
|
+
}
|
|
1339
|
+
}), this.map.getLayer(S.EDIT_MIDPOINTS) || this.map.addLayer({
|
|
1340
|
+
id: S.EDIT_MIDPOINTS,
|
|
1341
|
+
type: "circle",
|
|
1342
|
+
source: v.EDIT_VERTICES,
|
|
1343
|
+
filter: ["==", ["get", "_type"], "midpoint"],
|
|
1344
|
+
paint: {
|
|
1345
|
+
"circle-radius": w.MIDPOINT_RADIUS,
|
|
1346
|
+
"circle-color": w.MIDPOINT_COLOR,
|
|
1347
|
+
"circle-opacity": w.MIDPOINT_OPACITY
|
|
1348
|
+
}
|
|
1349
|
+
}), this.map.getLayer(S.EDIT_VERTICES) || this.map.addLayer({
|
|
1350
|
+
id: S.EDIT_VERTICES,
|
|
1351
|
+
type: "circle",
|
|
1352
|
+
source: v.EDIT_VERTICES,
|
|
1353
|
+
filter: ["==", ["get", "_type"], "vertex"],
|
|
1354
|
+
paint: {
|
|
1355
|
+
"circle-radius": [
|
|
1356
|
+
"case",
|
|
1357
|
+
["boolean", ["get", "_highlighted"], !1],
|
|
1358
|
+
7,
|
|
1359
|
+
w.EDIT_VERTEX_RADIUS
|
|
1360
|
+
],
|
|
1361
|
+
"circle-color": [
|
|
1362
|
+
"case",
|
|
1363
|
+
["boolean", ["get", "_highlighted"], !1],
|
|
1364
|
+
"#ff4444",
|
|
1365
|
+
w.EDIT_VERTEX_COLOR
|
|
1366
|
+
],
|
|
1367
|
+
"circle-stroke-color": [
|
|
1368
|
+
"case",
|
|
1369
|
+
["boolean", ["get", "_highlighted"], !1],
|
|
1370
|
+
"#cc0000",
|
|
1371
|
+
w.EDIT_VERTEX_STROKE
|
|
1372
|
+
],
|
|
1373
|
+
"circle-stroke-width": w.EDIT_VERTEX_STROKE_WIDTH
|
|
1374
|
+
}
|
|
1375
|
+
}), this.initialized = !0);
|
|
1376
|
+
}
|
|
1377
|
+
/**
|
|
1378
|
+
* Render features to the map. Uses requestAnimationFrame
|
|
1379
|
+
* to batch multiple render calls within a single frame.
|
|
1380
|
+
* @param features - The features to render.
|
|
1381
|
+
*/
|
|
1382
|
+
render(e) {
|
|
1383
|
+
this.pendingFeatures = e, this.pendingRender || (this.pendingRender = !0, requestAnimationFrame(() => {
|
|
1384
|
+
this.performRender(), this.pendingRender = !1;
|
|
1385
|
+
}));
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Render a polygon preview for in-progress drawing.
|
|
1389
|
+
* @param coordinates - The preview polygon coordinates (ring).
|
|
1390
|
+
*/
|
|
1391
|
+
renderPreview(e) {
|
|
1392
|
+
if (e.length < 2) {
|
|
1393
|
+
this.clearPreview();
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
const t = e.map(
|
|
1397
|
+
(i) => [i[0], i[1]]
|
|
1398
|
+
), s = {
|
|
1399
|
+
type: "FeatureCollection",
|
|
1400
|
+
features: [
|
|
1401
|
+
{
|
|
1402
|
+
type: "Feature",
|
|
1403
|
+
properties: {},
|
|
1404
|
+
geometry: {
|
|
1405
|
+
type: "LineString",
|
|
1406
|
+
coordinates: t
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
]
|
|
1410
|
+
};
|
|
1411
|
+
this.sourceManager.updatePreview(s);
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Clear the drawing preview.
|
|
1415
|
+
*/
|
|
1416
|
+
clearPreview() {
|
|
1417
|
+
this.sourceManager.clearPreview();
|
|
1418
|
+
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Render vertex and midpoint markers for editing a selected polygon.
|
|
1421
|
+
* @param vertices - The polygon vertex positions.
|
|
1422
|
+
* @param midpoints - The edge midpoint positions.
|
|
1423
|
+
* @param highlightIndex - Optional index of the vertex to highlight.
|
|
1424
|
+
*/
|
|
1425
|
+
renderVertices(e, t, s) {
|
|
1426
|
+
const i = [];
|
|
1427
|
+
for (let n = 0; n < e.length; n++) {
|
|
1428
|
+
const o = e[n];
|
|
1429
|
+
i.push({
|
|
1430
|
+
type: "Feature",
|
|
1431
|
+
properties: {
|
|
1432
|
+
_type: "vertex",
|
|
1433
|
+
_highlighted: n === s
|
|
1434
|
+
},
|
|
1435
|
+
geometry: { type: "Point", coordinates: [o[0], o[1]] }
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
for (const n of t)
|
|
1439
|
+
i.push({
|
|
1440
|
+
type: "Feature",
|
|
1441
|
+
properties: { _type: "midpoint" },
|
|
1442
|
+
geometry: { type: "Point", coordinates: [n[0], n[1]] }
|
|
1443
|
+
});
|
|
1444
|
+
this.sourceManager.updateEditVertices({
|
|
1445
|
+
type: "FeatureCollection",
|
|
1446
|
+
features: i
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Clear the vertex/midpoint markers.
|
|
1451
|
+
*/
|
|
1452
|
+
clearVertices() {
|
|
1453
|
+
this.sourceManager.clearEditVertices();
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* Set the IDs of selected features for visual highlighting.
|
|
1457
|
+
* @param ids - The selected feature IDs.
|
|
1458
|
+
*/
|
|
1459
|
+
setSelectedIds(e) {
|
|
1460
|
+
this.selectedIds = new Set(e);
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Remove all layers and sources from the map.
|
|
1464
|
+
*/
|
|
1465
|
+
destroy() {
|
|
1466
|
+
const e = [
|
|
1467
|
+
S.EDIT_VERTICES,
|
|
1468
|
+
S.EDIT_MIDPOINTS,
|
|
1469
|
+
S.PREVIEW,
|
|
1470
|
+
S.VERTICES,
|
|
1471
|
+
S.OUTLINE,
|
|
1472
|
+
S.FILL
|
|
1473
|
+
];
|
|
1474
|
+
for (const t of e)
|
|
1475
|
+
this.map.getLayer(t) && this.map.removeLayer(t);
|
|
1476
|
+
this.sourceManager.destroy(), this.initialized = !1;
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Perform the actual render, converting features to GeoJSON
|
|
1480
|
+
* with selection state embedded in properties.
|
|
1481
|
+
*/
|
|
1482
|
+
performRender() {
|
|
1483
|
+
if (!this.pendingFeatures) return;
|
|
1484
|
+
const t = {
|
|
1485
|
+
type: "FeatureCollection",
|
|
1486
|
+
features: this.pendingFeatures.map(
|
|
1487
|
+
(s) => ({
|
|
1488
|
+
type: "Feature",
|
|
1489
|
+
id: s.id,
|
|
1490
|
+
properties: {
|
|
1491
|
+
...s.properties,
|
|
1492
|
+
_id: s.id,
|
|
1493
|
+
_selected: this.selectedIds.has(s.id)
|
|
1494
|
+
},
|
|
1495
|
+
geometry: s.geometry
|
|
1496
|
+
})
|
|
1497
|
+
)
|
|
1498
|
+
};
|
|
1499
|
+
this.sourceManager.updateFeatures(t), this.pendingFeatures = null;
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
class $e {
|
|
1503
|
+
constructor(e) {
|
|
1504
|
+
this.options = e, this.element = document.createElement("button"), this.element.type = "button", this.element.title = e.title, this.element.setAttribute("aria-label", e.title), this.element.dataset.libreDrawButton = e.id, this.applyStyles(), this.iconContainer = document.createElement("span"), this.iconContainer.style.display = "flex", this.iconContainer.style.alignItems = "center", this.iconContainer.style.justifyContent = "center", this.setIcon(e.icon), this.element.appendChild(this.iconContainer), this.element.addEventListener("click", (t) => {
|
|
1505
|
+
t.preventDefault(), t.stopPropagation(), this.element.disabled || e.onClick();
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
/**
|
|
1509
|
+
* Get the DOM element for this button.
|
|
1510
|
+
*/
|
|
1511
|
+
getElement() {
|
|
1512
|
+
return this.element;
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Set the active state of the button.
|
|
1516
|
+
* @param active - Whether the button should appear active.
|
|
1517
|
+
*/
|
|
1518
|
+
setActive(e) {
|
|
1519
|
+
e ? (this.element.style.backgroundColor = "#3bb2d0", this.element.style.color = "#ffffff") : (this.element.style.backgroundColor = "#ffffff", this.element.style.color = "#333333"), this.element.setAttribute("aria-pressed", String(e));
|
|
1520
|
+
}
|
|
1521
|
+
/**
|
|
1522
|
+
* Set the disabled state of the button.
|
|
1523
|
+
* @param disabled - Whether the button should be disabled.
|
|
1524
|
+
*/
|
|
1525
|
+
setDisabled(e) {
|
|
1526
|
+
this.element.disabled = e, this.element.style.opacity = e ? "0.4" : "1", this.element.style.cursor = e ? "not-allowed" : "pointer";
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Clean up the button element.
|
|
1530
|
+
*/
|
|
1531
|
+
destroy() {
|
|
1532
|
+
this.element.remove();
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* Set the icon SVG content using DOM parsing (no innerHTML).
|
|
1536
|
+
*/
|
|
1537
|
+
setIcon(e) {
|
|
1538
|
+
const i = new DOMParser().parseFromString(e, "image/svg+xml").documentElement;
|
|
1539
|
+
for (; this.iconContainer.firstChild; )
|
|
1540
|
+
this.iconContainer.removeChild(this.iconContainer.firstChild);
|
|
1541
|
+
this.iconContainer.appendChild(
|
|
1542
|
+
document.importNode(i, !0)
|
|
1543
|
+
);
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* Apply the base CSS styles to the button.
|
|
1547
|
+
*/
|
|
1548
|
+
applyStyles() {
|
|
1549
|
+
const e = this.element.style;
|
|
1550
|
+
e.display = "flex", e.alignItems = "center", e.justifyContent = "center", e.width = "44px", e.height = "44px", e.padding = "0", e.margin = "0", e.border = "1px solid #ddd", e.borderRadius = "4px", e.backgroundColor = "#ffffff", e.color = "#333333", e.cursor = "pointer", e.outline = "none", e.transition = "background-color 0.15s, color 0.15s", e.boxSizing = "border-box";
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
const Ye = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2l8.5 6.2-3.2 9.8H6.7L3.5 8.2z"/></svg>', Ge = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"/><path d="M13 13l6 6"/></svg>', qe = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>', Je = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 14 4 9 9 4"/><path d="M20 20v-7a4 4 0 00-4-4H4"/></svg>', Qe = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 14 20 9 15 4"/><path d="M4 20v-7a4 4 0 014-4h12"/></svg>', Ze = {
|
|
1554
|
+
draw: !0,
|
|
1555
|
+
select: !0,
|
|
1556
|
+
delete: !0,
|
|
1557
|
+
undo: !0,
|
|
1558
|
+
redo: !0
|
|
1559
|
+
};
|
|
1560
|
+
class et {
|
|
1561
|
+
constructor(e, t, s = {}) {
|
|
1562
|
+
this.buttons = /* @__PURE__ */ new Map(), this.map = e, this.callbacks = t, this.options = s, this.container = document.createElement("div"), this.container.className = "libre-draw-toolbar", this.applyContainerStyles(), this.createButtons(), this.mount();
|
|
1563
|
+
}
|
|
1564
|
+
/**
|
|
1565
|
+
* Update the active mode displayed in the toolbar.
|
|
1566
|
+
* @param mode - The active mode name ('idle', 'draw', 'select').
|
|
1567
|
+
*/
|
|
1568
|
+
setActiveMode(e) {
|
|
1569
|
+
const t = this.buttons.get("draw"), s = this.buttons.get("select");
|
|
1570
|
+
t && t.setActive(e === "draw"), s && s.setActive(e === "select");
|
|
1571
|
+
}
|
|
1572
|
+
/**
|
|
1573
|
+
* Update the undo/redo button states.
|
|
1574
|
+
* @param canUndo - Whether undo is available.
|
|
1575
|
+
* @param canRedo - Whether redo is available.
|
|
1576
|
+
*/
|
|
1577
|
+
setHistoryState(e, t) {
|
|
1578
|
+
const s = this.buttons.get("undo"), i = this.buttons.get("redo");
|
|
1579
|
+
s && s.setDisabled(!e), i && i.setDisabled(!t);
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Remove the toolbar from the map and clean up.
|
|
1583
|
+
*/
|
|
1584
|
+
destroy() {
|
|
1585
|
+
for (const e of this.buttons.values())
|
|
1586
|
+
e.destroy();
|
|
1587
|
+
this.buttons.clear(), this.container.remove();
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* Create all toolbar buttons based on the configured controls.
|
|
1591
|
+
*/
|
|
1592
|
+
createButtons() {
|
|
1593
|
+
const e = {
|
|
1594
|
+
...Ze,
|
|
1595
|
+
...this.options.controls
|
|
1596
|
+
};
|
|
1597
|
+
e.draw && this.addButton("draw", Ye, "Draw polygon", () => {
|
|
1598
|
+
this.callbacks.onDrawClick();
|
|
1599
|
+
}, !0), e.select && this.addButton("select", Ge, "Select feature", () => {
|
|
1600
|
+
this.callbacks.onSelectClick();
|
|
1601
|
+
}, !0), e.delete && this.addButton("delete", qe, "Delete selected", () => {
|
|
1602
|
+
this.callbacks.onDeleteClick();
|
|
1603
|
+
}), e.undo && this.addButton("undo", Je, "Undo", () => {
|
|
1604
|
+
this.callbacks.onUndoClick();
|
|
1605
|
+
}), e.redo && this.addButton("redo", Qe, "Redo", () => {
|
|
1606
|
+
this.callbacks.onRedoClick();
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* Create a button and add it to the toolbar.
|
|
1611
|
+
*/
|
|
1612
|
+
addButton(e, t, s, i, n) {
|
|
1613
|
+
const o = new $e({ id: e, icon: t, title: s, onClick: i, isToggle: n });
|
|
1614
|
+
this.buttons.set(e, o), this.container.appendChild(o.getElement());
|
|
1615
|
+
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Mount the toolbar container to the map's control container.
|
|
1618
|
+
*/
|
|
1619
|
+
mount() {
|
|
1620
|
+
const e = this.options.position || "top-right", t = this.map.getContainer(), s = t.querySelector(
|
|
1621
|
+
`.maplibregl-ctrl-${e}`
|
|
1622
|
+
);
|
|
1623
|
+
s ? s.appendChild(this.container) : t.appendChild(this.container);
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Apply CSS styles to the toolbar container.
|
|
1627
|
+
*/
|
|
1628
|
+
applyContainerStyles() {
|
|
1629
|
+
const e = this.container.style;
|
|
1630
|
+
e.display = "flex", e.flexDirection = "column", e.gap = "4px", e.padding = "4px", e.backgroundColor = "rgba(255, 255, 255, 0.9)", e.borderRadius = "4px", e.boxShadow = "0 1px 4px rgba(0, 0, 0, 0.3)", e.zIndex = "1", e.pointerEvents = "auto";
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
class tt {
|
|
1634
|
+
/**
|
|
1635
|
+
* Create a new LibreDraw instance attached to a MapLibre GL JS map.
|
|
1636
|
+
*
|
|
1637
|
+
* Initializes all internal modules and sets up map integration.
|
|
1638
|
+
* The instance is ready to use once the map's style is loaded.
|
|
1639
|
+
*
|
|
1640
|
+
* @param map - The MapLibre GL JS map instance to draw on.
|
|
1641
|
+
* @param options - Configuration options. Defaults to toolbar enabled
|
|
1642
|
+
* and 100-action history limit.
|
|
1643
|
+
*
|
|
1644
|
+
* @example
|
|
1645
|
+
* ```ts
|
|
1646
|
+
* const draw = new LibreDraw(map);
|
|
1647
|
+
* // Or with options:
|
|
1648
|
+
* const draw = new LibreDraw(map, {
|
|
1649
|
+
* toolbar: { position: 'top-right' },
|
|
1650
|
+
* historyLimit: 50,
|
|
1651
|
+
* });
|
|
1652
|
+
* ```
|
|
1653
|
+
*/
|
|
1654
|
+
constructor(e, t = {}) {
|
|
1655
|
+
this.toolbar = null, this.destroyed = !1, this.map = e, this.eventBus = new ge(), this.featureStore = new R(), this.historyManager = new fe(t.historyLimit ?? 100), this.modeManager = new pe(), this.sourceManager = new We(e), this.renderManager = new Ke(e, this.sourceManager);
|
|
1656
|
+
const s = new Se({
|
|
1657
|
+
addFeatureToStore: (i) => this.featureStore.add(i),
|
|
1658
|
+
pushToHistory: (i) => {
|
|
1659
|
+
this.historyManager.push(i), this.updateToolbarHistoryState();
|
|
1660
|
+
},
|
|
1661
|
+
emitEvent: (i, n) => this.eventBus.emit(i, n),
|
|
1662
|
+
renderPreview: (i) => this.renderManager.renderPreview(i),
|
|
1663
|
+
clearPreview: () => this.renderManager.clearPreview(),
|
|
1664
|
+
renderFeatures: () => this.renderAllFeatures(),
|
|
1665
|
+
getScreenPoint: (i) => {
|
|
1666
|
+
const n = e.project([i.lng, i.lat]);
|
|
1667
|
+
return { x: n.x, y: n.y };
|
|
1668
|
+
}
|
|
1669
|
+
});
|
|
1670
|
+
if (this.selectMode = new Oe(
|
|
1671
|
+
{
|
|
1672
|
+
removeFeatureFromStore: (i) => this.featureStore.remove(i),
|
|
1673
|
+
pushToHistory: (i) => {
|
|
1674
|
+
this.historyManager.push(i), this.updateToolbarHistoryState();
|
|
1675
|
+
},
|
|
1676
|
+
emitEvent: (i, n) => this.eventBus.emit(i, n),
|
|
1677
|
+
renderFeatures: () => this.renderAllFeatures(),
|
|
1678
|
+
getFeatureById: (i) => this.featureStore.getById(i),
|
|
1679
|
+
getAllFeatures: () => this.featureStore.getAll(),
|
|
1680
|
+
getScreenPoint: (i) => {
|
|
1681
|
+
const n = e.project([i.lng, i.lat]);
|
|
1682
|
+
return { x: n.x, y: n.y };
|
|
1683
|
+
},
|
|
1684
|
+
updateFeatureInStore: (i, n) => this.featureStore.update(i, n),
|
|
1685
|
+
renderVertices: (i, n, o, l) => this.renderManager.renderVertices(n, o, l),
|
|
1686
|
+
clearVertices: () => this.renderManager.clearVertices(),
|
|
1687
|
+
setDragPan: (i) => {
|
|
1688
|
+
i ? e.dragPan.enable() : e.dragPan.disable();
|
|
1689
|
+
}
|
|
1690
|
+
},
|
|
1691
|
+
(i) => {
|
|
1692
|
+
this.renderManager.setSelectedIds(i);
|
|
1693
|
+
}
|
|
1694
|
+
), this.modeManager.registerMode("idle", new Ie()), this.modeManager.registerMode("draw", s), this.modeManager.registerMode("select", this.selectMode), this.modeManager.setOnModeChange((i, n) => {
|
|
1695
|
+
this.eventBus.emit("modechange", { mode: i, previousMode: n }), this.toolbar && this.toolbar.setActiveMode(i), i === "draw" ? (e.dragPan.disable(), e.doubleClickZoom.disable()) : i === "select" ? e.doubleClickZoom.disable() : (e.dragPan.enable(), e.doubleClickZoom.enable());
|
|
1696
|
+
}), this.inputHandler = new Xe(
|
|
1697
|
+
e,
|
|
1698
|
+
() => this.modeManager.getCurrentMode()
|
|
1699
|
+
), t.toolbar !== !1 && t.toolbar !== void 0) {
|
|
1700
|
+
const i = typeof t.toolbar == "object" ? t.toolbar : {};
|
|
1701
|
+
this.createToolbar(i);
|
|
1702
|
+
}
|
|
1703
|
+
e.isStyleLoaded() ? this.initialize() : e.once("load", () => {
|
|
1704
|
+
this.initialize();
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
/**
|
|
1708
|
+
* Set the active drawing mode.
|
|
1709
|
+
*
|
|
1710
|
+
* Switching modes deactivates the current mode (clearing any
|
|
1711
|
+
* in-progress state) and activates the new mode. A `'modechange'`
|
|
1712
|
+
* event is emitted on every transition.
|
|
1713
|
+
*
|
|
1714
|
+
* @param mode - `'idle'` (no interaction), `'draw'` (create polygons),
|
|
1715
|
+
* or `'select'` (select/edit existing polygons).
|
|
1716
|
+
*
|
|
1717
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
1718
|
+
*
|
|
1719
|
+
* @example
|
|
1720
|
+
* ```ts
|
|
1721
|
+
* draw.setMode('draw');
|
|
1722
|
+
* draw.on('modechange', (e) => {
|
|
1723
|
+
* console.log(`${e.previousMode} -> ${e.mode}`);
|
|
1724
|
+
* });
|
|
1725
|
+
* ```
|
|
1726
|
+
*/
|
|
1727
|
+
setMode(e) {
|
|
1728
|
+
this.assertNotDestroyed(), this.modeManager.setMode(e);
|
|
1729
|
+
}
|
|
1730
|
+
/**
|
|
1731
|
+
* Get the current drawing mode.
|
|
1732
|
+
*
|
|
1733
|
+
* @returns The active mode name: `'idle'`, `'draw'`, or `'select'`.
|
|
1734
|
+
*
|
|
1735
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
1736
|
+
*
|
|
1737
|
+
* @example
|
|
1738
|
+
* ```ts
|
|
1739
|
+
* if (draw.getMode() === 'draw') {
|
|
1740
|
+
* console.log('Currently drawing');
|
|
1741
|
+
* }
|
|
1742
|
+
* ```
|
|
1743
|
+
*/
|
|
1744
|
+
getMode() {
|
|
1745
|
+
return this.assertNotDestroyed(), this.modeManager.getMode();
|
|
1746
|
+
}
|
|
1747
|
+
/**
|
|
1748
|
+
* Get all features as an array.
|
|
1749
|
+
*
|
|
1750
|
+
* Returns a snapshot of all polygon features currently in the store.
|
|
1751
|
+
*
|
|
1752
|
+
* @returns An array of all {@link LibreDrawFeature} objects.
|
|
1753
|
+
*
|
|
1754
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
1755
|
+
*
|
|
1756
|
+
* @example
|
|
1757
|
+
* ```ts
|
|
1758
|
+
* const features = draw.getFeatures();
|
|
1759
|
+
* console.log(`${features.length} polygons on the map`);
|
|
1760
|
+
* ```
|
|
1761
|
+
*/
|
|
1762
|
+
getFeatures() {
|
|
1763
|
+
return this.assertNotDestroyed(), this.featureStore.getAll();
|
|
1764
|
+
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Replace all features in the store with the given GeoJSON FeatureCollection.
|
|
1767
|
+
*
|
|
1768
|
+
* Validates the input, clears the current store and history, and
|
|
1769
|
+
* re-renders the map. Undo/redo history is reset after this call.
|
|
1770
|
+
*
|
|
1771
|
+
* @param geojson - A GeoJSON FeatureCollection containing Polygon features.
|
|
1772
|
+
*
|
|
1773
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
1774
|
+
* @throws {LibreDrawError} If the input is not a valid FeatureCollection
|
|
1775
|
+
* or contains invalid polygon geometries.
|
|
1776
|
+
*
|
|
1777
|
+
* @example
|
|
1778
|
+
* ```ts
|
|
1779
|
+
* draw.setFeatures({
|
|
1780
|
+
* type: 'FeatureCollection',
|
|
1781
|
+
* features: [{
|
|
1782
|
+
* type: 'Feature',
|
|
1783
|
+
* geometry: {
|
|
1784
|
+
* type: 'Polygon',
|
|
1785
|
+
* coordinates: [[[0,0],[10,0],[10,10],[0,10],[0,0]]]
|
|
1786
|
+
* },
|
|
1787
|
+
* properties: {}
|
|
1788
|
+
* }]
|
|
1789
|
+
* });
|
|
1790
|
+
* ```
|
|
1791
|
+
*/
|
|
1792
|
+
setFeatures(e) {
|
|
1793
|
+
this.assertNotDestroyed();
|
|
1794
|
+
const t = Ee(e);
|
|
1795
|
+
this.featureStore.setAll(t.features), this.historyManager.clear(), this.renderAllFeatures(), this.updateToolbarHistoryState();
|
|
1796
|
+
}
|
|
1797
|
+
/**
|
|
1798
|
+
* Add features to the store from an array of GeoJSON Feature objects.
|
|
1799
|
+
*
|
|
1800
|
+
* Each feature is validated and added. Unlike {@link setFeatures},
|
|
1801
|
+
* this does not clear existing features or history.
|
|
1802
|
+
*
|
|
1803
|
+
* @param features - An array of GeoJSON Feature objects with Polygon geometry.
|
|
1804
|
+
*
|
|
1805
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
1806
|
+
* @throws {LibreDrawError} If any feature has invalid geometry.
|
|
1807
|
+
*
|
|
1808
|
+
* @example
|
|
1809
|
+
* ```ts
|
|
1810
|
+
* draw.addFeatures([{
|
|
1811
|
+
* type: 'Feature',
|
|
1812
|
+
* geometry: {
|
|
1813
|
+
* type: 'Polygon',
|
|
1814
|
+
* coordinates: [[[0,0],[5,0],[5,5],[0,5],[0,0]]]
|
|
1815
|
+
* },
|
|
1816
|
+
* properties: { name: 'Zone A' }
|
|
1817
|
+
* }]);
|
|
1818
|
+
* ```
|
|
1819
|
+
*/
|
|
1820
|
+
addFeatures(e) {
|
|
1821
|
+
this.assertNotDestroyed();
|
|
1822
|
+
for (const t of e) {
|
|
1823
|
+
const s = le(t);
|
|
1824
|
+
this.featureStore.add(s);
|
|
1825
|
+
}
|
|
1826
|
+
this.renderAllFeatures();
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Get the IDs of currently selected features.
|
|
1830
|
+
*
|
|
1831
|
+
* Returns selected IDs in select mode. In other modes, returns
|
|
1832
|
+
* an empty array since selection is cleared on mode transition.
|
|
1833
|
+
*
|
|
1834
|
+
* @returns An array of selected feature IDs.
|
|
1835
|
+
*
|
|
1836
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
1837
|
+
*
|
|
1838
|
+
* @example
|
|
1839
|
+
* ```ts
|
|
1840
|
+
* draw.on('selectionchange', (e) => {
|
|
1841
|
+
* const ids = draw.getSelectedFeatureIds();
|
|
1842
|
+
* console.log('Selected:', ids);
|
|
1843
|
+
* });
|
|
1844
|
+
* ```
|
|
1845
|
+
*/
|
|
1846
|
+
getSelectedFeatureIds() {
|
|
1847
|
+
return this.assertNotDestroyed(), this.selectMode.getSelectedIds();
|
|
1848
|
+
}
|
|
1849
|
+
/**
|
|
1850
|
+
* Get a feature by its ID.
|
|
1851
|
+
*
|
|
1852
|
+
* @param id - The unique identifier of the feature.
|
|
1853
|
+
* @returns The feature, or `undefined` if not found.
|
|
1854
|
+
*
|
|
1855
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
1856
|
+
*
|
|
1857
|
+
* @example
|
|
1858
|
+
* ```ts
|
|
1859
|
+
* const feature = draw.getFeatureById('abc-123');
|
|
1860
|
+
* if (feature) {
|
|
1861
|
+
* console.log(feature.geometry.coordinates);
|
|
1862
|
+
* }
|
|
1863
|
+
* ```
|
|
1864
|
+
*/
|
|
1865
|
+
getFeatureById(e) {
|
|
1866
|
+
return this.assertNotDestroyed(), this.featureStore.getById(e);
|
|
1867
|
+
}
|
|
1868
|
+
/**
|
|
1869
|
+
* Delete a feature by its ID.
|
|
1870
|
+
*
|
|
1871
|
+
* Removes the feature from the store, records a {@link DeleteAction}
|
|
1872
|
+
* in the history (making it undoable), and emits a `'delete'` event.
|
|
1873
|
+
* If the feature is currently selected, the selection is also cleared.
|
|
1874
|
+
*
|
|
1875
|
+
* @param id - The unique identifier of the feature to delete.
|
|
1876
|
+
* @returns The deleted feature, or `undefined` if not found.
|
|
1877
|
+
*
|
|
1878
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
1879
|
+
*
|
|
1880
|
+
* @example
|
|
1881
|
+
* ```ts
|
|
1882
|
+
* const deleted = draw.deleteFeature('abc-123');
|
|
1883
|
+
* if (deleted) {
|
|
1884
|
+
* draw.undo(); // restores the deleted feature
|
|
1885
|
+
* }
|
|
1886
|
+
* ```
|
|
1887
|
+
*/
|
|
1888
|
+
deleteFeature(e) {
|
|
1889
|
+
this.assertNotDestroyed();
|
|
1890
|
+
const t = this.featureStore.getById(e);
|
|
1891
|
+
if (!t) return;
|
|
1892
|
+
this.selectMode.getSelectedIds().includes(e) && this.selectMode.clearSelection(), this.featureStore.remove(e);
|
|
1893
|
+
const i = new oe(t);
|
|
1894
|
+
return this.historyManager.push(i), this.eventBus.emit("delete", { feature: t }), this.renderAllFeatures(), this.updateToolbarHistoryState(), t;
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Programmatically select a feature by its ID.
|
|
1898
|
+
*
|
|
1899
|
+
* Switches to select mode if not already active. The feature
|
|
1900
|
+
* must exist in the store.
|
|
1901
|
+
*
|
|
1902
|
+
* @param id - The unique identifier of the feature to select.
|
|
1903
|
+
*
|
|
1904
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
1905
|
+
* @throws {LibreDrawError} If no feature with the given ID exists.
|
|
1906
|
+
*
|
|
1907
|
+
* @example
|
|
1908
|
+
* ```ts
|
|
1909
|
+
* draw.selectFeature('abc-123');
|
|
1910
|
+
* console.log(draw.getSelectedFeatureIds()); // ['abc-123']
|
|
1911
|
+
* console.log(draw.getMode()); // 'select'
|
|
1912
|
+
* ```
|
|
1913
|
+
*/
|
|
1914
|
+
selectFeature(e) {
|
|
1915
|
+
if (this.assertNotDestroyed(), !this.featureStore.getById(e))
|
|
1916
|
+
throw new E(`Feature not found: ${e}`);
|
|
1917
|
+
this.modeManager.getMode() !== "select" && this.modeManager.setMode("select"), this.selectMode.selectFeature(e);
|
|
1918
|
+
}
|
|
1919
|
+
/**
|
|
1920
|
+
* Clear the current feature selection.
|
|
1921
|
+
*
|
|
1922
|
+
* Deselects all features, removes vertex handles, and emits
|
|
1923
|
+
* a `'selectionchange'` event. No-op if nothing is selected.
|
|
1924
|
+
*
|
|
1925
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
1926
|
+
*
|
|
1927
|
+
* @example
|
|
1928
|
+
* ```ts
|
|
1929
|
+
* draw.selectFeature('abc-123');
|
|
1930
|
+
* draw.clearSelection();
|
|
1931
|
+
* console.log(draw.getSelectedFeatureIds()); // []
|
|
1932
|
+
* ```
|
|
1933
|
+
*/
|
|
1934
|
+
clearSelection() {
|
|
1935
|
+
this.assertNotDestroyed(), this.selectMode.clearSelection();
|
|
1936
|
+
}
|
|
1937
|
+
/**
|
|
1938
|
+
* Undo the last action.
|
|
1939
|
+
*
|
|
1940
|
+
* Reverts the most recent action (create, update, or delete) and
|
|
1941
|
+
* updates the map rendering. If a feature is selected and its
|
|
1942
|
+
* geometry changes, vertex handles are refreshed.
|
|
1943
|
+
*
|
|
1944
|
+
* @returns `true` if an action was undone, `false` if nothing to undo.
|
|
1945
|
+
*
|
|
1946
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
1947
|
+
*
|
|
1948
|
+
* @example
|
|
1949
|
+
* ```ts
|
|
1950
|
+
* if (draw.undo()) {
|
|
1951
|
+
* console.log('Action undone');
|
|
1952
|
+
* }
|
|
1953
|
+
* ```
|
|
1954
|
+
*/
|
|
1955
|
+
undo() {
|
|
1956
|
+
this.assertNotDestroyed();
|
|
1957
|
+
const e = this.historyManager.undo(this.featureStore);
|
|
1958
|
+
return e && (this.renderAllFeatures(), this.selectMode.refreshVertexHandles(), this.updateToolbarHistoryState()), e;
|
|
1959
|
+
}
|
|
1960
|
+
/**
|
|
1961
|
+
* Redo the last undone action.
|
|
1962
|
+
*
|
|
1963
|
+
* Re-applies the most recently undone action. The redo stack is
|
|
1964
|
+
* cleared whenever a new action is performed.
|
|
1965
|
+
*
|
|
1966
|
+
* @returns `true` if an action was redone, `false` if nothing to redo.
|
|
1967
|
+
*
|
|
1968
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
1969
|
+
*
|
|
1970
|
+
* @example
|
|
1971
|
+
* ```ts
|
|
1972
|
+
* draw.undo();
|
|
1973
|
+
* draw.redo(); // re-applies the undone action
|
|
1974
|
+
* ```
|
|
1975
|
+
*/
|
|
1976
|
+
redo() {
|
|
1977
|
+
this.assertNotDestroyed();
|
|
1978
|
+
const e = this.historyManager.redo(this.featureStore);
|
|
1979
|
+
return e && (this.renderAllFeatures(), this.selectMode.refreshVertexHandles(), this.updateToolbarHistoryState()), e;
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
* Register an event listener.
|
|
1983
|
+
*
|
|
1984
|
+
* Supported events: `'create'`, `'update'`, `'delete'`,
|
|
1985
|
+
* `'selectionchange'`, `'modechange'`.
|
|
1986
|
+
*
|
|
1987
|
+
* @param type - The event type to listen for.
|
|
1988
|
+
* @param listener - The callback to invoke when the event fires.
|
|
1989
|
+
*
|
|
1990
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
1991
|
+
*
|
|
1992
|
+
* @example
|
|
1993
|
+
* ```ts
|
|
1994
|
+
* draw.on('create', (e) => console.log('Created:', e.feature.id));
|
|
1995
|
+
* draw.on('update', (e) => console.log('Updated:', e.feature.id));
|
|
1996
|
+
* draw.on('delete', (e) => console.log('Deleted:', e.feature.id));
|
|
1997
|
+
* draw.on('selectionchange', (e) => console.log('Selected:', e.selectedIds));
|
|
1998
|
+
* draw.on('modechange', (e) => console.log(`${e.previousMode} -> ${e.mode}`));
|
|
1999
|
+
* ```
|
|
2000
|
+
*/
|
|
2001
|
+
on(e, t) {
|
|
2002
|
+
this.assertNotDestroyed(), this.eventBus.on(e, t);
|
|
2003
|
+
}
|
|
2004
|
+
/**
|
|
2005
|
+
* Remove an event listener.
|
|
2006
|
+
*
|
|
2007
|
+
* The listener must be the same function reference passed to {@link on}.
|
|
2008
|
+
*
|
|
2009
|
+
* @param type - The event type to stop listening for.
|
|
2010
|
+
* @param listener - The callback to remove.
|
|
2011
|
+
*
|
|
2012
|
+
* @throws {LibreDrawError} If this instance has been destroyed.
|
|
2013
|
+
*
|
|
2014
|
+
* @example
|
|
2015
|
+
* ```ts
|
|
2016
|
+
* const handler = (e: CreateEvent) => console.log(e.feature);
|
|
2017
|
+
* draw.on('create', handler);
|
|
2018
|
+
* draw.off('create', handler);
|
|
2019
|
+
* ```
|
|
2020
|
+
*/
|
|
2021
|
+
off(e, t) {
|
|
2022
|
+
this.assertNotDestroyed(), this.eventBus.off(e, t);
|
|
2023
|
+
}
|
|
2024
|
+
/**
|
|
2025
|
+
* Destroy the LibreDraw instance, cleaning up all resources.
|
|
2026
|
+
*
|
|
2027
|
+
* Switches to idle mode, removes all map layers/sources, clears
|
|
2028
|
+
* the event bus, history, and feature store, and removes the toolbar.
|
|
2029
|
+
* After calling destroy, all other methods will throw
|
|
2030
|
+
* {@link LibreDrawError}. Calling destroy on an already-destroyed
|
|
2031
|
+
* instance is a no-op.
|
|
2032
|
+
*
|
|
2033
|
+
* @example
|
|
2034
|
+
* ```ts
|
|
2035
|
+
* draw.destroy();
|
|
2036
|
+
* // draw.getFeatures(); // throws LibreDrawError
|
|
2037
|
+
* ```
|
|
2038
|
+
*/
|
|
2039
|
+
destroy() {
|
|
2040
|
+
this.destroyed || (this.destroyed = !0, this.modeManager.setMode("idle"), this.inputHandler.destroy(), this.renderManager.destroy(), this.eventBus.removeAllListeners(), this.historyManager.clear(), this.featureStore.clear(), this.toolbar && (this.toolbar.destroy(), this.toolbar = null));
|
|
2041
|
+
}
|
|
2042
|
+
/**
|
|
2043
|
+
* Initialize rendering and input handling after the map is ready.
|
|
2044
|
+
*/
|
|
2045
|
+
initialize() {
|
|
2046
|
+
this.destroyed || (this.renderManager.initialize(), this.inputHandler.enable());
|
|
2047
|
+
}
|
|
2048
|
+
/**
|
|
2049
|
+
* Render all features from the store to the map.
|
|
2050
|
+
*/
|
|
2051
|
+
renderAllFeatures() {
|
|
2052
|
+
const e = this.featureStore.getAll();
|
|
2053
|
+
this.renderManager.render(e);
|
|
2054
|
+
}
|
|
2055
|
+
/**
|
|
2056
|
+
* Create the toolbar UI.
|
|
2057
|
+
*/
|
|
2058
|
+
createToolbar(e) {
|
|
2059
|
+
this.toolbar = new et(
|
|
2060
|
+
this.map,
|
|
2061
|
+
{
|
|
2062
|
+
onDrawClick: () => {
|
|
2063
|
+
const t = this.modeManager.getMode();
|
|
2064
|
+
this.modeManager.setMode(t === "draw" ? "idle" : "draw");
|
|
2065
|
+
},
|
|
2066
|
+
onSelectClick: () => {
|
|
2067
|
+
const t = this.modeManager.getMode();
|
|
2068
|
+
this.modeManager.setMode(
|
|
2069
|
+
t === "select" ? "idle" : "select"
|
|
2070
|
+
);
|
|
2071
|
+
},
|
|
2072
|
+
onDeleteClick: () => {
|
|
2073
|
+
if (this.modeManager.getMode() === "select") {
|
|
2074
|
+
const t = this.selectMode.getSelectedIds();
|
|
2075
|
+
for (const s of t)
|
|
2076
|
+
this.deleteFeature(s);
|
|
2077
|
+
}
|
|
2078
|
+
},
|
|
2079
|
+
onUndoClick: () => {
|
|
2080
|
+
this.undo();
|
|
2081
|
+
},
|
|
2082
|
+
onRedoClick: () => {
|
|
2083
|
+
this.redo();
|
|
2084
|
+
}
|
|
2085
|
+
},
|
|
2086
|
+
e
|
|
2087
|
+
), this.toolbar.setActiveMode(this.modeManager.getMode()), this.toolbar.setHistoryState(
|
|
2088
|
+
this.historyManager.canUndo(),
|
|
2089
|
+
this.historyManager.canRedo()
|
|
2090
|
+
);
|
|
2091
|
+
}
|
|
2092
|
+
/**
|
|
2093
|
+
* Update toolbar undo/redo button states.
|
|
2094
|
+
*/
|
|
2095
|
+
updateToolbarHistoryState() {
|
|
2096
|
+
this.toolbar && this.toolbar.setHistoryState(
|
|
2097
|
+
this.historyManager.canUndo(),
|
|
2098
|
+
this.historyManager.canRedo()
|
|
2099
|
+
);
|
|
2100
|
+
}
|
|
2101
|
+
/**
|
|
2102
|
+
* Assert that this instance has not been destroyed.
|
|
2103
|
+
* @throws LibreDrawError if destroyed.
|
|
2104
|
+
*/
|
|
2105
|
+
assertNotDestroyed() {
|
|
2106
|
+
if (this.destroyed)
|
|
2107
|
+
throw new E(
|
|
2108
|
+
"This LibreDraw instance has been destroyed."
|
|
2109
|
+
);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
export {
|
|
2113
|
+
tt as LibreDraw,
|
|
2114
|
+
E as LibreDrawError
|
|
2115
|
+
};
|
|
2116
|
+
//# sourceMappingURL=libre-draw.js.map
|