@artsdatabanken/nbic-map-component 0.3.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/README.md +170 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/geometry-analysis-ERCBMJB7.js +37 -0
- package/dist/index.cjs +3796 -0
- package/dist/index.d.cts +805 -0
- package/dist/index.d.ts +805 -0
- package/dist/index.js +3711 -0
- package/package.json +70 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3796 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
|
|
33
|
+
// src/core/ol/utils/geometry-analysis.ts
|
|
34
|
+
var geometry_analysis_exports = {};
|
|
35
|
+
__export(geometry_analysis_exports, {
|
|
36
|
+
checkSelfIntersections: () => checkSelfIntersections,
|
|
37
|
+
intersectionPointsToOlFeatures: () => intersectionPointsToOlFeatures,
|
|
38
|
+
toGeoJSONFeature: () => toGeoJSONFeature
|
|
39
|
+
});
|
|
40
|
+
function toGeoJSONFeature(map, input) {
|
|
41
|
+
const viewProj = String(map.getView().getProjection().getCode());
|
|
42
|
+
const feature = input instanceof import_Feature2.default ? input : new import_Feature2.default({ geometry: input });
|
|
43
|
+
return fmt.writeFeatureObject(feature, {
|
|
44
|
+
dataProjection: "EPSG:4326",
|
|
45
|
+
featureProjection: viewProj
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function checkSelfIntersections(map, input) {
|
|
49
|
+
const gj = toGeoJSONFeature(map, input);
|
|
50
|
+
const type = gj.geometry?.type;
|
|
51
|
+
if (!type || !["LineString", "Polygon", "MultiLineString", "MultiPolygon"].includes(type)) {
|
|
52
|
+
return { valid: true };
|
|
53
|
+
}
|
|
54
|
+
const out = (0, import_kinks.default)(gj);
|
|
55
|
+
const count = out?.features?.length ?? 0;
|
|
56
|
+
return count > 0 ? { valid: false, intersections: out } : { valid: true };
|
|
57
|
+
}
|
|
58
|
+
function intersectionPointsToOlFeatures(map, points) {
|
|
59
|
+
const viewProj = String(map.getView().getProjection().getCode());
|
|
60
|
+
return fmt.readFeatures(points, {
|
|
61
|
+
dataProjection: "EPSG:4326",
|
|
62
|
+
featureProjection: viewProj
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
var import_Feature2, import_GeoJSON4, import_kinks, fmt;
|
|
66
|
+
var init_geometry_analysis = __esm({
|
|
67
|
+
"src/core/ol/utils/geometry-analysis.ts"() {
|
|
68
|
+
"use strict";
|
|
69
|
+
import_Feature2 = __toESM(require("ol/Feature"), 1);
|
|
70
|
+
import_GeoJSON4 = __toESM(require("ol/format/GeoJSON"), 1);
|
|
71
|
+
import_kinks = __toESM(require("@turf/kinks"), 1);
|
|
72
|
+
fmt = new import_GeoJSON4.default();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// src/index.ts
|
|
77
|
+
var index_exports = {};
|
|
78
|
+
__export(index_exports, {
|
|
79
|
+
MapEvents: () => MapEvents,
|
|
80
|
+
NbicMapComponent: () => MapAPI,
|
|
81
|
+
createMap: () => createMap,
|
|
82
|
+
createNibToken: () => createNibToken,
|
|
83
|
+
nbicMapGeojson: () => geojson_exports,
|
|
84
|
+
nbicMapPresets: () => presets_exports,
|
|
85
|
+
nbicMapUtils: () => utils_exports
|
|
86
|
+
});
|
|
87
|
+
module.exports = __toCommonJS(index_exports);
|
|
88
|
+
|
|
89
|
+
// src/core/projections.ts
|
|
90
|
+
var import_proj4 = __toESM(require("proj4"), 1);
|
|
91
|
+
var import_proj42 = require("ol/proj/proj4");
|
|
92
|
+
var import_proj = require("ol/proj");
|
|
93
|
+
import_proj4.default.defs("EPSG:25833", "+proj=utm +zone=33 +ellps=GRS80 +units=m +no_defs +type=crs");
|
|
94
|
+
import_proj4.default.defs("EPSG:25832", "+proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs +type=crs");
|
|
95
|
+
import_proj4.default.defs("EPSG:25835", "+proj=utm +zone=35 +ellps=GRS80 +units=m +no_defs +type=crs");
|
|
96
|
+
import_proj4.default.defs("EPSG:32632", "+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs +type=crs");
|
|
97
|
+
import_proj4.default.defs("EPSG:32633", "+proj=utm +zone=33 +datum=WGS84 +units=m +no_defs +type=crs");
|
|
98
|
+
(0, import_proj42.register)(import_proj4.default);
|
|
99
|
+
var extent32 = [-2e6, 35e5, 3545984, 9045984];
|
|
100
|
+
var extent33 = [-25e5, 35e5, 3045984, 9045984];
|
|
101
|
+
var extent35 = [-35e5, 35e5, 2045984, 9045984];
|
|
102
|
+
var extentGeographic = [-180, -90, 180, 90];
|
|
103
|
+
var extentGoogle = [
|
|
104
|
+
-2003750834e-2,
|
|
105
|
+
-2003750834e-2,
|
|
106
|
+
2003750834e-2,
|
|
107
|
+
2003750834e-2
|
|
108
|
+
];
|
|
109
|
+
var mapBounds = [
|
|
110
|
+
{
|
|
111
|
+
epsg: "EPSG:25832",
|
|
112
|
+
extent: extent32
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
epsg: "EPSG:25833",
|
|
116
|
+
extent: extent33
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
epsg: "EPSG:25835",
|
|
120
|
+
extent: extent35
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
epsg: "EPSG:32632",
|
|
124
|
+
extent: extent32
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
epsg: "EPSG:32633",
|
|
128
|
+
extent: extent33
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
epsg: "EPSG:32635",
|
|
132
|
+
extent: extent35
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
epsg: "EPSG:4326",
|
|
136
|
+
extent: extentGeographic
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
epsg: "EPSG:3857",
|
|
140
|
+
extent: extentGoogle
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
epsg: "EPSG:900913",
|
|
144
|
+
extent: extentGoogle
|
|
145
|
+
}
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
// src/core/state/store.ts
|
|
149
|
+
function createEmitter() {
|
|
150
|
+
const listeners = {};
|
|
151
|
+
return {
|
|
152
|
+
on(type, cb) {
|
|
153
|
+
(listeners[type] ??= []).push(cb);
|
|
154
|
+
return () => this.off(type, cb);
|
|
155
|
+
},
|
|
156
|
+
once(type, cb) {
|
|
157
|
+
const wrapper = (payload) => {
|
|
158
|
+
cb(payload);
|
|
159
|
+
this.off(type, wrapper);
|
|
160
|
+
};
|
|
161
|
+
this.on(type, wrapper);
|
|
162
|
+
},
|
|
163
|
+
off(type, cb) {
|
|
164
|
+
listeners[type] = (listeners[type] ?? []).filter((fn) => fn !== cb);
|
|
165
|
+
},
|
|
166
|
+
emit(type, payload) {
|
|
167
|
+
(listeners[type] ?? []).forEach((fn) => fn(payload));
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/core/ol/OlMapEngine.ts
|
|
173
|
+
var import_Map = __toESM(require("ol/Map"), 1);
|
|
174
|
+
var import_ol3 = require("ol");
|
|
175
|
+
|
|
176
|
+
// src/core/ol/adapters/layers.ts
|
|
177
|
+
var import_Vector3 = __toESM(require("ol/source/Vector"), 1);
|
|
178
|
+
var import_Vector4 = __toESM(require("ol/layer/Vector"), 1);
|
|
179
|
+
var import_Tile = __toESM(require("ol/layer/Tile"), 1);
|
|
180
|
+
var import_Tile2 = __toESM(require("ol/source/Tile"), 1);
|
|
181
|
+
|
|
182
|
+
// src/core/ol/adapters/sources.ts
|
|
183
|
+
var import_OSM = __toESM(require("ol/source/OSM"), 1);
|
|
184
|
+
var import_XYZ = __toESM(require("ol/source/XYZ"), 1);
|
|
185
|
+
var import_WMTS2 = __toESM(require("ol/source/WMTS"), 1);
|
|
186
|
+
var import_Vector2 = __toESM(require("ol/source/Vector"), 1);
|
|
187
|
+
var import_GeoJSON2 = __toESM(require("ol/format/GeoJSON"), 1);
|
|
188
|
+
|
|
189
|
+
// src/core/ol/adapters/wmts-grid.ts
|
|
190
|
+
var import_WMTS = __toESM(require("ol/tilegrid/WMTS"), 1);
|
|
191
|
+
var import_extent = require("ol/extent");
|
|
192
|
+
function makeGridFromExtent(extent, tileSize = 256, levels = 19) {
|
|
193
|
+
const size = (0, import_extent.getWidth)(extent) / tileSize;
|
|
194
|
+
const resolutions = Array.from({ length: levels }, (_, z2) => size / Math.pow(2, z2));
|
|
195
|
+
const matrixIds = Array.from({ length: levels }, (_, z2) => z2.toString());
|
|
196
|
+
return new import_WMTS.default({
|
|
197
|
+
origin: (0, import_extent.getTopLeft)(extent),
|
|
198
|
+
resolutions,
|
|
199
|
+
matrixIds,
|
|
200
|
+
tileSize,
|
|
201
|
+
extent
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/core/ol/adapters/wfs-loader.ts
|
|
206
|
+
var import_loadingstrategy = require("ol/loadingstrategy");
|
|
207
|
+
var import_Vector = __toESM(require("ol/source/Vector"), 1);
|
|
208
|
+
var import_GeoJSON = __toESM(require("ol/format/GeoJSON"), 1);
|
|
209
|
+
var import_GML32 = __toESM(require("ol/format/GML32"), 1);
|
|
210
|
+
var import_GML3 = __toESM(require("ol/format/GML3"), 1);
|
|
211
|
+
var import_proj2 = require("ol/proj");
|
|
212
|
+
var import_extent2 = require("ol/extent");
|
|
213
|
+
function zoomFromResolution(resolution, projectionLike) {
|
|
214
|
+
const proj = (0, import_proj2.get)(projectionLike) || void 0;
|
|
215
|
+
const baseRes = proj?.getExtent() ? (0, import_extent2.getWidth)(proj.getExtent()) / 256 : 156543.03392804097;
|
|
216
|
+
const res = resolution > 0 ? resolution : baseRes;
|
|
217
|
+
const zoom = Math.log2(baseRes / res);
|
|
218
|
+
return Number.isFinite(zoom) ? zoom : 0;
|
|
219
|
+
}
|
|
220
|
+
function zoomGatedBboxStrategy(minZoom) {
|
|
221
|
+
return (extent, resolution, projection) => {
|
|
222
|
+
if (typeof minZoom === "number") {
|
|
223
|
+
const z2 = zoomFromResolution(resolution, projection);
|
|
224
|
+
if (z2 < minZoom) return [];
|
|
225
|
+
}
|
|
226
|
+
return (0, import_loadingstrategy.bbox)(extent, resolution);
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function buildGetFeatureUrl(o, extent, srsName, format) {
|
|
230
|
+
const version = o.version ?? "2.0.0";
|
|
231
|
+
const is20 = version === "2.0.0";
|
|
232
|
+
const qp = new URLSearchParams({
|
|
233
|
+
service: "WFS",
|
|
234
|
+
request: "GetFeature",
|
|
235
|
+
version,
|
|
236
|
+
[is20 ? "typenames" : "typename"]: o.typeName,
|
|
237
|
+
srsName
|
|
238
|
+
});
|
|
239
|
+
qp.set("bbox", `${extent.join(",")},${srsName}`);
|
|
240
|
+
if (format === "json") {
|
|
241
|
+
qp.set("outputFormat", o.outputFormat ?? "application/json");
|
|
242
|
+
} else {
|
|
243
|
+
qp.set("outputFormat", o.outputFormat ?? "text/xml; subtype=gml/3.2.1");
|
|
244
|
+
}
|
|
245
|
+
if (is20) {
|
|
246
|
+
const count = o.maxFeatures ?? o.pageSizeDefault ?? 2e3;
|
|
247
|
+
qp.set("count", String(count));
|
|
248
|
+
} else if (o.maxFeatures) {
|
|
249
|
+
qp.set("maxFeatures", String(o.maxFeatures));
|
|
250
|
+
}
|
|
251
|
+
if (o.params) Object.entries(o.params).forEach(([k, v]) => qp.set(k, String(v)));
|
|
252
|
+
const sep = o.url.includes("?") ? "&" : "?";
|
|
253
|
+
return `${o.url}${sep}${qp.toString()}`;
|
|
254
|
+
}
|
|
255
|
+
function normalizeGmlXml(xmlText) {
|
|
256
|
+
const parser = new DOMParser();
|
|
257
|
+
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
|
|
258
|
+
const serializer = new XMLSerializer();
|
|
259
|
+
const normalized = serializer.serializeToString(xmlDoc).replace(/srsdimension/g, "srsDimension").replace(/wfs:member/g, "wfs:featureMember");
|
|
260
|
+
return parser.parseFromString(normalized, "text/xml");
|
|
261
|
+
}
|
|
262
|
+
var dftCache = /* @__PURE__ */ new Map();
|
|
263
|
+
async function describeFeatureTypeOnce(baseUrl, typeName, version = "2.0.0") {
|
|
264
|
+
const cacheKey = `${baseUrl}::${version}::${typeName}`;
|
|
265
|
+
const existing = dftCache.get(cacheKey);
|
|
266
|
+
if (existing) return existing;
|
|
267
|
+
const is20 = version === "2.0.0";
|
|
268
|
+
const qp = new URLSearchParams({
|
|
269
|
+
service: "WFS",
|
|
270
|
+
request: "DescribeFeatureType",
|
|
271
|
+
version,
|
|
272
|
+
[is20 ? "typenames" : "typename"]: typeName,
|
|
273
|
+
outputFormat: "application/gml+xml; version=3.2"
|
|
274
|
+
});
|
|
275
|
+
const sep = baseUrl.includes("?") ? "&" : "?";
|
|
276
|
+
const url = `${baseUrl}${sep}${qp.toString()}`;
|
|
277
|
+
const res = await fetch(url);
|
|
278
|
+
const text = await res.text();
|
|
279
|
+
const doc = new DOMParser().parseFromString(text, "application/xml");
|
|
280
|
+
const schemaEl = doc.querySelector("schema, xs\\:schema");
|
|
281
|
+
const featureNS = schemaEl?.getAttribute("targetNamespace") || "http://mapserver.gis.umn.edu/mapserver";
|
|
282
|
+
const local = typeName.includes(":") ? typeName.split(":")[1] : typeName;
|
|
283
|
+
let geometryName;
|
|
284
|
+
const elDef = Array.from(doc.querySelectorAll("element, xs\\:element")).find((e) => e.getAttribute("name") === local) || null;
|
|
285
|
+
const typeRef = elDef?.getAttribute("type") || null;
|
|
286
|
+
if (typeRef) {
|
|
287
|
+
const localType = typeRef.includes(":") ? typeRef.split(":")[1] : typeRef;
|
|
288
|
+
const complex = Array.from(doc.querySelectorAll("complexType, xs\\:complexType")).find((ct) => ct.getAttribute("name") === localType);
|
|
289
|
+
if (complex) {
|
|
290
|
+
const propEls = Array.from(complex.querySelectorAll("element, xs\\:element"));
|
|
291
|
+
const geomProp = propEls.find((pe) => {
|
|
292
|
+
const t = pe.getAttribute("type") ?? "";
|
|
293
|
+
return /gml:.*(Geometry|Point|LineString|Curve|Surface|Polygon|Multi)/i.test(t);
|
|
294
|
+
});
|
|
295
|
+
if (geomProp) geometryName = geomProp.getAttribute("name") || void 0;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const info = { featureNS: featureNS ?? "", featureType: local ?? "", geometryName };
|
|
299
|
+
dftCache.set(cacheKey, info);
|
|
300
|
+
return info;
|
|
301
|
+
}
|
|
302
|
+
function createWfsVectorSource(opts, viewProjection) {
|
|
303
|
+
const preferJson = !opts.outputFormat || /json/i.test(opts.outputFormat);
|
|
304
|
+
const source = new import_Vector.default({
|
|
305
|
+
// Gate at the strategy level so OL re-calls the loader automatically when zoom crosses the gate.
|
|
306
|
+
strategy: zoomGatedBboxStrategy(opts.minZoomToLoad)
|
|
307
|
+
});
|
|
308
|
+
source.setLoader(async (extent, _resolution, proj) => {
|
|
309
|
+
const srs = opts.srsName ?? String(proj ?? viewProjection);
|
|
310
|
+
const tryOnce = async (fmt2) => {
|
|
311
|
+
const url = buildGetFeatureUrl(opts, extent, srs, fmt2);
|
|
312
|
+
const resp = await fetch(url, { headers: opts.headers });
|
|
313
|
+
const text = await resp.text();
|
|
314
|
+
const ct = resp.headers.get("content-type") ?? "";
|
|
315
|
+
return { ok: resp.ok, text, ct, url };
|
|
316
|
+
};
|
|
317
|
+
try {
|
|
318
|
+
if (preferJson) {
|
|
319
|
+
const r = await tryOnce("json");
|
|
320
|
+
const looksXml = r.text.trim().startsWith("<");
|
|
321
|
+
const notJsonCT = !/json/i.test(r.ct);
|
|
322
|
+
if (r.ok && !looksXml && !notJsonCT) {
|
|
323
|
+
const features2 = new import_GeoJSON.default({
|
|
324
|
+
dataProjection: srs,
|
|
325
|
+
featureProjection: String(proj ?? viewProjection)
|
|
326
|
+
}).readFeatures(r.text);
|
|
327
|
+
source.addFeatures(features2);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const r2 = await tryOnce("gml");
|
|
332
|
+
if (!r2.ok) {
|
|
333
|
+
throw new Error(`WFS error ${r2.ct} from ${r2.url}: ${r2.text.slice(0, 200)}`);
|
|
334
|
+
}
|
|
335
|
+
const xmlDoc = normalizeGmlXml(r2.text);
|
|
336
|
+
const version = opts.version ?? "2.0.0";
|
|
337
|
+
const dft = {
|
|
338
|
+
featureNS: opts.featureNS,
|
|
339
|
+
featureType: opts.featureType,
|
|
340
|
+
geometryName: opts.geometryName
|
|
341
|
+
};
|
|
342
|
+
if (!dft.featureNS || !dft.featureType) {
|
|
343
|
+
const info = await describeFeatureTypeOnce(opts.url, opts.typeName, version);
|
|
344
|
+
dft.featureNS = dft.featureNS ?? info.featureNS;
|
|
345
|
+
dft.featureType = dft.featureType ?? info.featureType;
|
|
346
|
+
dft.geometryName = dft.geometryName ?? info.geometryName;
|
|
347
|
+
}
|
|
348
|
+
const isGml32 = /gml\/3\.2/i.test(r2.ct) || /version=3\.2/i.test(r2.text) || r2.text.includes("http://www.opengis.net/gml/3.2");
|
|
349
|
+
const gml = isGml32 ? new import_GML32.default({ featureNS: dft.featureNS, featureType: dft.featureType, srsName: srs }) : new import_GML3.default({ featureNS: dft.featureNS, featureType: dft.featureType, srsName: srs });
|
|
350
|
+
const tbl = gml["FEATURE_COLLECTION_PARSERS"];
|
|
351
|
+
if (tbl && tbl["http://www.opengis.net/gml/3.2"]) {
|
|
352
|
+
tbl["http://www.opengis.net/gml"] = tbl["http://www.opengis.net/gml/3.2"];
|
|
353
|
+
tbl["http://www.opengis.net/wfs/2.0"] = tbl["http://www.opengis.net/gml/3.2"];
|
|
354
|
+
}
|
|
355
|
+
let features = gml.readFeatures(xmlDoc, {
|
|
356
|
+
dataProjection: srs,
|
|
357
|
+
featureProjection: String(proj ?? viewProjection),
|
|
358
|
+
...dft.geometryName ? { geometryName: dft.geometryName } : {}
|
|
359
|
+
});
|
|
360
|
+
if (!features.length) {
|
|
361
|
+
const serialized = new XMLSerializer().serializeToString(xmlDoc);
|
|
362
|
+
features = gml.readFeatures(serialized, {
|
|
363
|
+
dataProjection: srs,
|
|
364
|
+
featureProjection: String(proj ?? viewProjection),
|
|
365
|
+
...dft.geometryName ? { geometryName: dft.geometryName } : {}
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
if (features.length) {
|
|
369
|
+
source.addFeatures(features);
|
|
370
|
+
} else {
|
|
371
|
+
console.warn("[WFS] Parsed 0 features", {
|
|
372
|
+
url: r2.url,
|
|
373
|
+
featureNS: dft.featureNS,
|
|
374
|
+
featureType: dft.featureType,
|
|
375
|
+
geometryName: dft.geometryName,
|
|
376
|
+
isGml32
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
} catch (err) {
|
|
380
|
+
console.error("WFS loader error", err);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
return source;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// src/core/ol/adapters/sources.ts
|
|
387
|
+
var import_proj3 = require("ol/proj");
|
|
388
|
+
var import_WMTS3 = __toESM(require("ol/tilegrid/WMTS"), 1);
|
|
389
|
+
var import_extent3 = require("ol/extent");
|
|
390
|
+
|
|
391
|
+
// src/core/ol/adapters/wmts-presets.ts
|
|
392
|
+
function wmtsPreset(key) {
|
|
393
|
+
const matrixXMin = -5118180;
|
|
394
|
+
const matrixYMin = 7224500;
|
|
395
|
+
const matrixWidth = 5545984;
|
|
396
|
+
const matrixHeight = 2772992;
|
|
397
|
+
const matrixExtent = [
|
|
398
|
+
matrixXMin,
|
|
399
|
+
matrixYMin,
|
|
400
|
+
matrixXMin + matrixWidth,
|
|
401
|
+
matrixYMin + matrixHeight
|
|
402
|
+
];
|
|
403
|
+
switch (key) {
|
|
404
|
+
case "npolar-svalbard-32633": {
|
|
405
|
+
return {
|
|
406
|
+
layerExtent: [369976, 8221306, 878234, 9010718],
|
|
407
|
+
matrixExtent,
|
|
408
|
+
matrixIds: Array.from({ length: 18 }, (_, z2) => z2.toString())
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
case "npolar-janmayen-25833": {
|
|
412
|
+
return {
|
|
413
|
+
layerExtent: [-393783, 7978220, 276963, 8084965],
|
|
414
|
+
matrixExtent,
|
|
415
|
+
matrixIds: Array.from({ length: 18 }, (_, z2) => z2.toString())
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// src/core/ol/adapters/sources.ts
|
|
422
|
+
function resolveOlSource(input, resolveRef = (id) => {
|
|
423
|
+
throw new Error(`Source ref '${id}' cannot be resolved (no registry wired).`);
|
|
424
|
+
}) {
|
|
425
|
+
if ("ref" in input) {
|
|
426
|
+
return resolveRef(input.ref);
|
|
427
|
+
}
|
|
428
|
+
return toOlSource(input);
|
|
429
|
+
}
|
|
430
|
+
function toOlSource(def) {
|
|
431
|
+
switch (def.type) {
|
|
432
|
+
case "osm":
|
|
433
|
+
return new import_OSM.default();
|
|
434
|
+
case "xyz": {
|
|
435
|
+
const opts = def.options;
|
|
436
|
+
return new import_XYZ.default({
|
|
437
|
+
url: opts.url,
|
|
438
|
+
minZoom: opts.minZoom,
|
|
439
|
+
maxZoom: opts.maxZoom,
|
|
440
|
+
attributions: opts.attributions,
|
|
441
|
+
tileSize: opts.tileSize
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
case "wmts": {
|
|
445
|
+
let buildGridFromPreset2 = function(p, tileSize2, levels2) {
|
|
446
|
+
const matrixExtent = p.matrixExtent ?? p.extent;
|
|
447
|
+
const layerExtent = p.layerExtent ?? p.extent;
|
|
448
|
+
return new import_WMTS3.default({
|
|
449
|
+
origin: (0, import_extent3.getTopLeft)(matrixExtent),
|
|
450
|
+
resolutions: Array.from(
|
|
451
|
+
{ length: levels2 },
|
|
452
|
+
(_, z2) => (0, import_extent3.getWidth)(matrixExtent) / tileSize2 / Math.pow(2, z2)
|
|
453
|
+
),
|
|
454
|
+
matrixIds: p.matrixIds,
|
|
455
|
+
tileSize: tileSize2,
|
|
456
|
+
extent: layerExtent
|
|
457
|
+
// clip to visible data
|
|
458
|
+
});
|
|
459
|
+
};
|
|
460
|
+
var buildGridFromPreset = buildGridFromPreset2;
|
|
461
|
+
const o = def.options;
|
|
462
|
+
const tileSize = o.tileSize ?? 256;
|
|
463
|
+
const levels = o.levels ?? 18;
|
|
464
|
+
let tileGrid;
|
|
465
|
+
const wantsDefault028 = o.matrixSetId === "default028mm" || o.matrixSet === "default028mm";
|
|
466
|
+
if (!tileGrid && wantsDefault028) {
|
|
467
|
+
const presetKeyByProj = {
|
|
468
|
+
"EPSG:32633": "npolar-svalbard-32633",
|
|
469
|
+
"EPSG:25833": "npolar-janmayen-25833"
|
|
470
|
+
};
|
|
471
|
+
const key = o.projection ? presetKeyByProj[o.projection] : void 0;
|
|
472
|
+
if (key) {
|
|
473
|
+
const p = wmtsPreset(key);
|
|
474
|
+
tileGrid = buildGridFromPreset2(p, tileSize, levels);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (!tileGrid) {
|
|
478
|
+
const proj = o.projection ? (0, import_proj3.get)(o.projection) : void 0;
|
|
479
|
+
const extent = o.extent ?? proj?.getExtent() ?? (o.projection ? mapBounds.find((m) => m.epsg === o.projection)?.extent : void 0);
|
|
480
|
+
if (!extent) {
|
|
481
|
+
throw new Error(
|
|
482
|
+
`WMTS ${o.layer}: missing projection extent; set options.extent or ensure projection is registered with an extent.`
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
tileGrid = makeGridFromExtent(extent, tileSize, levels);
|
|
486
|
+
}
|
|
487
|
+
const isRest = /\/1\.0\.0\/?$/i.test(o.url) || /\/wmts\/1\.0\.0\//i.test(o.url);
|
|
488
|
+
const baseUrl = o.url.replace(/\/?$/, "/");
|
|
489
|
+
const src = new import_WMTS2.default({
|
|
490
|
+
layer: o.layer,
|
|
491
|
+
matrixSet: o.matrixSetId ?? o.matrixSet ?? "EPSG:3857",
|
|
492
|
+
format: o.format ?? "image/png",
|
|
493
|
+
style: o.style ?? "default",
|
|
494
|
+
tileGrid,
|
|
495
|
+
wrapX: o.wrapX ?? false,
|
|
496
|
+
// UTM usually false
|
|
497
|
+
transition: 0,
|
|
498
|
+
attributions: o.attribution,
|
|
499
|
+
...o.projection ? { projection: (0, import_proj3.get)(o.projection) } : {},
|
|
500
|
+
...isRest ? { urls: [baseUrl], requestEncoding: "REST" } : { url: o.url, requestEncoding: "KVP" },
|
|
501
|
+
crossOrigin: "anonymous",
|
|
502
|
+
tileLoadFunction: o.tileLoadFunction ? (tile, src2) => o.tileLoadFunction(tile, src2) : void 0
|
|
503
|
+
});
|
|
504
|
+
if (o.urlParamOverrides && Object.keys(o.urlParamOverrides).length) {
|
|
505
|
+
const orig = src.getTileUrlFunction();
|
|
506
|
+
src.setTileUrlFunction((coord, pr, pj) => {
|
|
507
|
+
const u = new URL(String(orig(coord, pr, pj)), window.location.origin);
|
|
508
|
+
for (const [k, v] of Object.entries(o.urlParamOverrides)) u.searchParams.set(k, v);
|
|
509
|
+
return u.toString();
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
return src;
|
|
513
|
+
}
|
|
514
|
+
case "geojson": {
|
|
515
|
+
const opts = def.options;
|
|
516
|
+
if (opts.text && opts.text.trim().length) {
|
|
517
|
+
const fmt2 = new import_GeoJSON2.default({ dataProjection: opts.dataProjection, featureProjection: opts.featureProjection });
|
|
518
|
+
const features = fmt2.readFeatures(opts.text);
|
|
519
|
+
const src = new import_Vector2.default();
|
|
520
|
+
if (features.length) src.addFeatures(features);
|
|
521
|
+
return src;
|
|
522
|
+
}
|
|
523
|
+
if (opts.url) {
|
|
524
|
+
return new import_Vector2.default({
|
|
525
|
+
url: opts.url,
|
|
526
|
+
format: new import_GeoJSON2.default({ dataProjection: opts.dataProjection, featureProjection: opts.featureProjection })
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
throw new Error("geojson source: provide either options.url or options.text");
|
|
530
|
+
}
|
|
531
|
+
case "wfs": {
|
|
532
|
+
const o = def.options;
|
|
533
|
+
const viewProj = o.srsName ?? "EPSG:3857";
|
|
534
|
+
return createWfsVectorSource(o, viewProj);
|
|
535
|
+
}
|
|
536
|
+
case "memory": {
|
|
537
|
+
return new import_Vector2.default();
|
|
538
|
+
}
|
|
539
|
+
default: {
|
|
540
|
+
const type = def.type;
|
|
541
|
+
throw new Error(`Unknown source type: ${type}`);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// src/core/ol/adapters/styles.ts
|
|
547
|
+
var import_style = require("ol/style");
|
|
548
|
+
var import_geom = require("ol/geom");
|
|
549
|
+
var import_Point = __toESM(require("ol/geom/Point"), 1);
|
|
550
|
+
var olColor = __toESM(require("ol/color"), 1);
|
|
551
|
+
|
|
552
|
+
// src/core/ol/adapters/gradient.ts
|
|
553
|
+
var import_color = require("ol/color");
|
|
554
|
+
function makeGradientRenderer(color, outerScale = 1.4, stops = [[0, 0], [0.6, 0.2], [1, 0.8]], strokeWidth = 1) {
|
|
555
|
+
const withAlpha = (c, a) => {
|
|
556
|
+
const arr = (0, import_color.asArray)(c);
|
|
557
|
+
arr[3] = a;
|
|
558
|
+
return (0, import_color.asString)(arr);
|
|
559
|
+
};
|
|
560
|
+
const rf = (pixelCoords, state) => {
|
|
561
|
+
const coords = pixelCoords;
|
|
562
|
+
if (!coords || coords.length < 2) return;
|
|
563
|
+
const center = coords[0];
|
|
564
|
+
const edge = coords[1];
|
|
565
|
+
if (!center || !edge || center.length < 2 || edge.length < 2) return;
|
|
566
|
+
const x = center[0];
|
|
567
|
+
const y = center[1];
|
|
568
|
+
const x1 = edge[0];
|
|
569
|
+
const y1 = edge[1];
|
|
570
|
+
const ctx = state.context;
|
|
571
|
+
const dx = x1 - x, dy = y1 - y;
|
|
572
|
+
const radius = Math.sqrt(dx * dx + dy * dy);
|
|
573
|
+
const innerRadius = 0;
|
|
574
|
+
const outerRadius = radius * outerScale;
|
|
575
|
+
const gradient = ctx.createRadialGradient(x, y, innerRadius, x, y, outerRadius);
|
|
576
|
+
for (const [stop, alpha] of stops) {
|
|
577
|
+
gradient.addColorStop(stop, withAlpha(color, alpha));
|
|
578
|
+
}
|
|
579
|
+
ctx.beginPath();
|
|
580
|
+
ctx.arc(x, y, radius, 0, 2 * Math.PI, true);
|
|
581
|
+
ctx.fillStyle = gradient;
|
|
582
|
+
ctx.fill();
|
|
583
|
+
ctx.beginPath();
|
|
584
|
+
ctx.arc(x, y, radius, 0, 2 * Math.PI, true);
|
|
585
|
+
ctx.lineWidth = strokeWidth;
|
|
586
|
+
ctx.strokeStyle = withAlpha(color, 1);
|
|
587
|
+
ctx.stroke();
|
|
588
|
+
};
|
|
589
|
+
return rf;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// src/core/ol/adapters/styles.ts
|
|
593
|
+
var fallback = new import_style.Style({
|
|
594
|
+
fill: new import_style.Fill({ color: "rgba(0,0,0,0.1)" }),
|
|
595
|
+
stroke: new import_style.Stroke({ color: "#666", width: 1 })
|
|
596
|
+
});
|
|
597
|
+
var thumbCache = /* @__PURE__ */ new Map();
|
|
598
|
+
function getInnerFeatureFromCluster(feature) {
|
|
599
|
+
const members = feature.get?.("features");
|
|
600
|
+
if (members && members.length === 1) {
|
|
601
|
+
const m0 = members[0];
|
|
602
|
+
return m0 ?? feature;
|
|
603
|
+
}
|
|
604
|
+
return feature;
|
|
605
|
+
}
|
|
606
|
+
function getClusterSize(feature) {
|
|
607
|
+
const members = feature.get?.("features");
|
|
608
|
+
return members?.length ?? 0;
|
|
609
|
+
}
|
|
610
|
+
function makeThumbnailMarkerStyle(thumbnail, opts) {
|
|
611
|
+
const cached = thumbCache.get(thumbnail);
|
|
612
|
+
if (cached) return cached;
|
|
613
|
+
const styles = [
|
|
614
|
+
new import_style.Style({
|
|
615
|
+
image: new import_style.RegularShape({
|
|
616
|
+
stroke: new import_style.Stroke({ color: opts.frameBorderColor, width: opts.frameBorderWidth }),
|
|
617
|
+
fill: new import_style.Fill({ color: opts.frameFillColor }),
|
|
618
|
+
points: 4,
|
|
619
|
+
radius: opts.frameRadius,
|
|
620
|
+
angle: Math.PI / 4,
|
|
621
|
+
displacement: opts.frameDisplacement
|
|
622
|
+
})
|
|
623
|
+
}),
|
|
624
|
+
new import_style.Style({
|
|
625
|
+
image: new import_style.RegularShape({
|
|
626
|
+
fill: new import_style.Fill({ color: opts.pinFillColor }),
|
|
627
|
+
points: 3,
|
|
628
|
+
radius: opts.pinRadius,
|
|
629
|
+
rotation: opts.pinRotation,
|
|
630
|
+
angle: 0
|
|
631
|
+
})
|
|
632
|
+
}),
|
|
633
|
+
new import_style.Style({
|
|
634
|
+
image: new import_style.Icon({
|
|
635
|
+
anchor: [0.52, 1],
|
|
636
|
+
anchorXUnits: "fraction",
|
|
637
|
+
anchorYUnits: "fraction",
|
|
638
|
+
src: thumbnail,
|
|
639
|
+
opacity: 1,
|
|
640
|
+
scale: opts.imageScale,
|
|
641
|
+
offsetOrigin: "top-left",
|
|
642
|
+
offset: [0, 0],
|
|
643
|
+
size: opts.imageSize,
|
|
644
|
+
crossOrigin: opts.crossOrigin
|
|
645
|
+
})
|
|
646
|
+
})
|
|
647
|
+
];
|
|
648
|
+
thumbCache.set(thumbnail, styles);
|
|
649
|
+
return styles;
|
|
650
|
+
}
|
|
651
|
+
function toOlStyle(def, label) {
|
|
652
|
+
if ("ref" in def) {
|
|
653
|
+
throw new Error(`Style ref '${def.ref}' cannot be resolved (no StyleRegistry wired).`);
|
|
654
|
+
}
|
|
655
|
+
if (def.type === "raw") {
|
|
656
|
+
const inst = def.options?.instance;
|
|
657
|
+
return inst ?? fallback;
|
|
658
|
+
}
|
|
659
|
+
if (def.type !== "simple") return fallback;
|
|
660
|
+
const opts = def.options ?? {};
|
|
661
|
+
if (opts.thumbnailMarker) {
|
|
662
|
+
const t = opts.thumbnailMarker;
|
|
663
|
+
const property = t.property ?? "thumbnail";
|
|
664
|
+
const imageSize = t.imageSize ?? [100, 100];
|
|
665
|
+
const imageScale = t.imageScale ?? 0.26;
|
|
666
|
+
const frameRadius = t.frameRadius ?? 22;
|
|
667
|
+
const frameBorderWidth = t.frameBorderWidth ?? 3;
|
|
668
|
+
const frameBorderColor = t.frameBorderColor ?? "rgba(255,255,255,1)";
|
|
669
|
+
const frameFillColor = t.frameFillColor ?? "#ffffff";
|
|
670
|
+
const frameDisplacement = t.frameDisplacement ?? [-1, 13];
|
|
671
|
+
const pinRadius = t.pinRadius ?? 10;
|
|
672
|
+
const pinFillColor = t.pinFillColor ?? "#ffffff";
|
|
673
|
+
const pinRotation = t.pinRotation ?? Math.PI / 3;
|
|
674
|
+
const crossOrigin = t.crossOrigin ?? "anonymous";
|
|
675
|
+
const fill = new import_style.Fill({ color: opts.fillColor ?? "rgba(0,0,0,0.2)" });
|
|
676
|
+
const stroke = new import_style.Stroke({ color: opts.strokeColor ?? "#333", width: opts.strokeWidth ?? 1 });
|
|
677
|
+
const image = opts.circle ? new import_style.Circle({
|
|
678
|
+
radius: opts.circle.radius ?? 4,
|
|
679
|
+
fill: new import_style.Fill({ color: opts.circle.fillColor ?? "#3388ff" }),
|
|
680
|
+
stroke: new import_style.Stroke({ color: opts.circle.strokeColor ?? "#222", width: opts.circle.strokeWidth ?? 1 })
|
|
681
|
+
}) : void 0;
|
|
682
|
+
return (feature) => {
|
|
683
|
+
const size = getClusterSize(feature);
|
|
684
|
+
if (size > 1) return void 0;
|
|
685
|
+
const inner = getInnerFeatureFromCluster(feature);
|
|
686
|
+
const thumb = inner.get?.(property);
|
|
687
|
+
const url = typeof thumb === "string" ? thumb : "";
|
|
688
|
+
if (!url) {
|
|
689
|
+
return new import_style.Style({ fill, stroke, image, text });
|
|
690
|
+
}
|
|
691
|
+
return makeThumbnailMarkerStyle(url, {
|
|
692
|
+
frameRadius,
|
|
693
|
+
frameBorderWidth,
|
|
694
|
+
frameBorderColor,
|
|
695
|
+
frameFillColor,
|
|
696
|
+
frameDisplacement,
|
|
697
|
+
pinRadius,
|
|
698
|
+
pinFillColor,
|
|
699
|
+
pinRotation,
|
|
700
|
+
imageSize,
|
|
701
|
+
imageScale,
|
|
702
|
+
crossOrigin
|
|
703
|
+
});
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
const baseFill = new import_style.Fill({ color: opts.fillColor ?? "rgba(0,0,0,0.2)" });
|
|
707
|
+
const baseStroke = new import_style.Stroke({ color: opts.strokeColor ?? "#333", width: opts.strokeWidth ?? 1 });
|
|
708
|
+
const text = opts.text || label !== void 0 ? new import_style.Text({
|
|
709
|
+
text: opts.text?.label ?? (label != null ? String(label) : ""),
|
|
710
|
+
font: opts.text?.font ?? "14px sans-serif",
|
|
711
|
+
fill: new import_style.Fill({ color: opts.text?.fillColor ?? "#fff" }),
|
|
712
|
+
stroke: opts.text?.strokeColor ? new import_style.Stroke({ color: opts.text.strokeColor, width: opts.text.strokeWidth ?? 2 }) : void 0,
|
|
713
|
+
offsetX: opts.text?.offsetX ?? 0,
|
|
714
|
+
offsetY: opts.text?.offsetY ?? 0
|
|
715
|
+
}) : void 0;
|
|
716
|
+
if (opts.icon) {
|
|
717
|
+
const i = opts.icon;
|
|
718
|
+
return new import_style.Style({
|
|
719
|
+
image: new import_style.Icon({
|
|
720
|
+
src: i.src,
|
|
721
|
+
scale: i.scale ?? 1,
|
|
722
|
+
anchor: i.anchor ?? [0.5, 1],
|
|
723
|
+
anchorXUnits: i.anchorXUnits ?? "fraction",
|
|
724
|
+
anchorYUnits: i.anchorYUnits ?? "fraction",
|
|
725
|
+
size: i.size,
|
|
726
|
+
rotation: i.rotation ?? 0,
|
|
727
|
+
opacity: i.opacity ?? 1,
|
|
728
|
+
color: i.color
|
|
729
|
+
}),
|
|
730
|
+
text
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
if (opts.circle && opts.circle.units !== "map") {
|
|
734
|
+
const c = opts.circle;
|
|
735
|
+
const image = new import_style.Circle({
|
|
736
|
+
radius: c.radius ?? 4,
|
|
737
|
+
fill: new import_style.Fill({ color: c.fillColor ?? "#3388ff" }),
|
|
738
|
+
stroke: new import_style.Stroke({ color: c.strokeColor ?? "#222", width: c.strokeWidth ?? 1 })
|
|
739
|
+
});
|
|
740
|
+
return new import_style.Style({ image, text, fill: baseFill, stroke: baseStroke });
|
|
741
|
+
}
|
|
742
|
+
if (opts.circle?.units === "map") {
|
|
743
|
+
const c = opts.circle;
|
|
744
|
+
const circleStroke = new import_style.Stroke({
|
|
745
|
+
color: c.strokeColor ?? opts.strokeColor ?? "#333",
|
|
746
|
+
width: c.strokeWidth ?? opts.strokeWidth ?? 1
|
|
747
|
+
});
|
|
748
|
+
const circleFill = new import_style.Fill({
|
|
749
|
+
color: c.fillColor ?? opts.fillColor ?? "rgba(0,0,0,0.2)"
|
|
750
|
+
});
|
|
751
|
+
return (feature) => {
|
|
752
|
+
const geom = feature.getGeometry();
|
|
753
|
+
if (!(geom instanceof import_Point.default)) {
|
|
754
|
+
return new import_style.Style({ stroke: baseStroke, fill: baseFill, text });
|
|
755
|
+
}
|
|
756
|
+
let radius = 0;
|
|
757
|
+
if (c.radiusProperty) {
|
|
758
|
+
const v = feature.get(c.radiusProperty);
|
|
759
|
+
const n = Number(v);
|
|
760
|
+
if (Number.isFinite(n)) radius = n;
|
|
761
|
+
}
|
|
762
|
+
if (!(radius > 0)) radius = c.radius ?? 0;
|
|
763
|
+
if (!(radius > 0)) return new import_style.Style({ stroke: baseStroke, fill: baseFill, text });
|
|
764
|
+
const circleGeom = new import_geom.Circle(geom.getCoordinates(), radius);
|
|
765
|
+
if (c.gradient) {
|
|
766
|
+
const renderer = makeGradientRenderer(
|
|
767
|
+
olColor.asArray(c.gradient.color),
|
|
768
|
+
c.gradient.outerScale ?? 1.4,
|
|
769
|
+
c.gradient.stops ?? [[0, 0], [0.6, 0.2], [1, 0.8]],
|
|
770
|
+
c.strokeWidth ?? 1
|
|
771
|
+
);
|
|
772
|
+
const s2 = new import_style.Style({ renderer, text });
|
|
773
|
+
s2.setGeometry(circleGeom);
|
|
774
|
+
return s2;
|
|
775
|
+
}
|
|
776
|
+
const s = new import_style.Style({ stroke: circleStroke, fill: circleFill, text });
|
|
777
|
+
s.setGeometry(circleGeom);
|
|
778
|
+
return s;
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
return new import_style.Style({ fill: baseFill, stroke: baseStroke, text });
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/core/ol/adapters/draw-style.ts
|
|
785
|
+
var import_style2 = require("ol/style");
|
|
786
|
+
function makeDrawStyle(opts) {
|
|
787
|
+
const stroke = new import_style2.Stroke({
|
|
788
|
+
color: opts?.strokeColor ?? "#1f6feb",
|
|
789
|
+
width: opts?.strokeWidth ?? 2
|
|
790
|
+
});
|
|
791
|
+
const fill = new import_style2.Fill({
|
|
792
|
+
color: opts?.fillColor ?? "rgba(31,111,235,0.15)"
|
|
793
|
+
});
|
|
794
|
+
const image = new import_style2.Circle({
|
|
795
|
+
radius: opts?.pointRadius ?? 6,
|
|
796
|
+
fill,
|
|
797
|
+
stroke
|
|
798
|
+
});
|
|
799
|
+
const s = new import_style2.Style();
|
|
800
|
+
s.setImage(image);
|
|
801
|
+
s.setFill(fill);
|
|
802
|
+
s.setStroke(stroke);
|
|
803
|
+
if (opts?.text) {
|
|
804
|
+
s.setText(
|
|
805
|
+
new import_style2.Text({
|
|
806
|
+
text: opts.text.label ?? "",
|
|
807
|
+
font: opts.text.font ?? "14px sans-serif",
|
|
808
|
+
fill: new import_style2.Fill({ color: opts.text.fillColor ?? "#111" }),
|
|
809
|
+
stroke: opts.text.strokeColor ? new import_style2.Stroke({
|
|
810
|
+
color: opts.text.strokeColor,
|
|
811
|
+
width: opts.text.strokeWidth ?? 2
|
|
812
|
+
}) : void 0,
|
|
813
|
+
offsetX: opts.text.offsetX ?? 0,
|
|
814
|
+
offsetY: opts.text.offsetY ?? 0
|
|
815
|
+
})
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
if (opts?.icon?.src) {
|
|
819
|
+
const image2 = new import_style2.Icon({
|
|
820
|
+
src: opts.icon.src,
|
|
821
|
+
scale: opts.icon.scale ?? 1,
|
|
822
|
+
size: opts.icon.size,
|
|
823
|
+
anchor: opts.icon.anchor ?? [0.5, 1],
|
|
824
|
+
anchorXUnits: opts.icon.anchorXUnits ?? "fraction",
|
|
825
|
+
anchorYUnits: opts.icon.anchorYUnits ?? "fraction",
|
|
826
|
+
rotation: opts.icon.rotation ?? 0,
|
|
827
|
+
opacity: opts.icon.opacity ?? 1,
|
|
828
|
+
color: opts.icon.color
|
|
829
|
+
});
|
|
830
|
+
return new import_style2.Style({ image: image2, stroke, fill });
|
|
831
|
+
}
|
|
832
|
+
return s;
|
|
833
|
+
}
|
|
834
|
+
function makeUpdatedDrawStyle(opts) {
|
|
835
|
+
const stroke = new import_style2.Stroke({
|
|
836
|
+
color: opts?.strokeColor ?? "#1f6feb",
|
|
837
|
+
width: opts?.strokeWidth ?? 2
|
|
838
|
+
});
|
|
839
|
+
const fill = new import_style2.Fill({
|
|
840
|
+
color: opts?.fillColor ?? "rgba(31,111,235,0.15)"
|
|
841
|
+
});
|
|
842
|
+
const image = new import_style2.Circle({
|
|
843
|
+
radius: opts?.pointRadius ?? 6,
|
|
844
|
+
fill,
|
|
845
|
+
stroke
|
|
846
|
+
});
|
|
847
|
+
const s = new import_style2.Style();
|
|
848
|
+
s.setImage(image);
|
|
849
|
+
s.setFill(fill);
|
|
850
|
+
s.setStroke(stroke);
|
|
851
|
+
if (opts?.text) {
|
|
852
|
+
s.setText(
|
|
853
|
+
new import_style2.Text({
|
|
854
|
+
text: opts.text.label ?? "",
|
|
855
|
+
font: opts.text.font ?? "14px sans-serif",
|
|
856
|
+
fill: new import_style2.Fill({ color: opts.text.fillColor ?? "#111" }),
|
|
857
|
+
stroke: opts.text.strokeColor ? new import_style2.Stroke({
|
|
858
|
+
color: opts.text.strokeColor,
|
|
859
|
+
width: opts.text.strokeWidth ?? 2
|
|
860
|
+
}) : void 0,
|
|
861
|
+
offsetX: opts.text.offsetX ?? 0,
|
|
862
|
+
offsetY: opts.text.offsetY ?? 0
|
|
863
|
+
})
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
return s;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// src/core/ol/adapters/layers.ts
|
|
870
|
+
var import_Style = require("ol/style/Style");
|
|
871
|
+
var import_Cluster = __toESM(require("ol/source/Cluster"), 1);
|
|
872
|
+
var import_style3 = require("ol/style");
|
|
873
|
+
function styleFromFeature(feature, resolution) {
|
|
874
|
+
const nbic = typeof feature.get === "function" ? feature.get("nbic:style") : void 0;
|
|
875
|
+
return nbic ? makeDrawStyle(nbic) : (0, import_Style.createDefaultStyle)(feature, resolution);
|
|
876
|
+
}
|
|
877
|
+
function toOlLayer(def, sourceFn = resolveOlSource, styleFn = toOlStyle) {
|
|
878
|
+
const src = sourceFn(def.source);
|
|
879
|
+
let layer;
|
|
880
|
+
switch (def.kind) {
|
|
881
|
+
case "tile":
|
|
882
|
+
case "raster":
|
|
883
|
+
if (src instanceof import_Tile2.default) {
|
|
884
|
+
layer = new import_Tile.default({ source: src, visible: def.visible ?? true, opacity: def.opacity ?? 1 });
|
|
885
|
+
} else {
|
|
886
|
+
throw new Error("Source for tile/raster layer must be a TileSource");
|
|
887
|
+
}
|
|
888
|
+
break;
|
|
889
|
+
case "vector":
|
|
890
|
+
if (src instanceof import_Vector3.default) {
|
|
891
|
+
let sumCount2 = function(members, field = "count") {
|
|
892
|
+
if (!members?.length) return 0;
|
|
893
|
+
let total = 0;
|
|
894
|
+
for (const m of members) {
|
|
895
|
+
const v = m.get?.(field);
|
|
896
|
+
const n = Number(v);
|
|
897
|
+
total += Number.isFinite(n) ? n : 1;
|
|
898
|
+
}
|
|
899
|
+
return total;
|
|
900
|
+
};
|
|
901
|
+
var sumCount = sumCount2;
|
|
902
|
+
const finalSource = def.cluster?.enabled ? new import_Cluster.default({
|
|
903
|
+
distance: def.cluster.distance ?? 40,
|
|
904
|
+
minDistance: def.cluster.minDistance,
|
|
905
|
+
source: src
|
|
906
|
+
}) : src;
|
|
907
|
+
const explicitLayerStyle = def.style ? styleFn(def.style) : void 0;
|
|
908
|
+
const explicitClusterStyle = def.cluster?.enabled && def.cluster.style ? styleFn(def.cluster.style) : void 0;
|
|
909
|
+
const countField = def.cluster?.countField ?? "count";
|
|
910
|
+
const style = (feature, resolution) => {
|
|
911
|
+
const members = feature.get?.("features");
|
|
912
|
+
if (def.cluster?.enabled && members) {
|
|
913
|
+
const total = sumCount2(members, countField) || members.length;
|
|
914
|
+
if (members.length === 1 && !def.cluster.keepSingleAsCluster) {
|
|
915
|
+
const inner = members[0];
|
|
916
|
+
if (inner && typeof explicitLayerStyle === "function") {
|
|
917
|
+
return explicitLayerStyle(inner, resolution);
|
|
918
|
+
}
|
|
919
|
+
if (explicitLayerStyle) {
|
|
920
|
+
if (typeof explicitLayerStyle === "function" && inner) {
|
|
921
|
+
return explicitLayerStyle(inner, resolution);
|
|
922
|
+
}
|
|
923
|
+
return typeof explicitLayerStyle === "function" ? inner ? explicitLayerStyle(inner, resolution) : void 0 : explicitLayerStyle;
|
|
924
|
+
}
|
|
925
|
+
const innerHasPerFeature = !!inner?.get && !!inner.get("nbic:style");
|
|
926
|
+
if (innerHasPerFeature) {
|
|
927
|
+
return styleFromFeature(inner, resolution);
|
|
928
|
+
}
|
|
929
|
+
if (inner) {
|
|
930
|
+
return styleFromFeature(inner, resolution);
|
|
931
|
+
}
|
|
932
|
+
return void 0;
|
|
933
|
+
}
|
|
934
|
+
if (typeof explicitClusterStyle === "function") {
|
|
935
|
+
return explicitClusterStyle(feature, resolution);
|
|
936
|
+
}
|
|
937
|
+
if (explicitClusterStyle) {
|
|
938
|
+
if (def.cluster.style) {
|
|
939
|
+
return styleFn(def.cluster.style, String(def.cluster.maxClusterPoints ? total > def.cluster.maxClusterPoints ? def.cluster.maxClusterPoints + "+" : total : total));
|
|
940
|
+
} else {
|
|
941
|
+
return explicitClusterStyle;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return new import_style3.Style({
|
|
945
|
+
image: new import_style3.Circle({
|
|
946
|
+
radius: 12,
|
|
947
|
+
fill: new import_style3.Fill({ color: "#3399CC" }),
|
|
948
|
+
stroke: new import_style3.Stroke({ color: "#fff", width: 2 })
|
|
949
|
+
}),
|
|
950
|
+
text: new import_style3.Text({
|
|
951
|
+
text: String(def.cluster.maxClusterPoints ? total > def.cluster.maxClusterPoints ? def.cluster.maxClusterPoints + "+" : total : total),
|
|
952
|
+
fill: new import_style3.Fill({ color: "#fff" })
|
|
953
|
+
})
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
const hasPerFeature = typeof feature.get === "function" && !!feature.get("nbic:style");
|
|
957
|
+
if (hasPerFeature) {
|
|
958
|
+
return styleFromFeature(feature, resolution);
|
|
959
|
+
}
|
|
960
|
+
if (typeof explicitLayerStyle === "function") {
|
|
961
|
+
return explicitLayerStyle(feature, resolution);
|
|
962
|
+
}
|
|
963
|
+
if (explicitLayerStyle) {
|
|
964
|
+
return explicitLayerStyle;
|
|
965
|
+
}
|
|
966
|
+
return styleFromFeature(feature, resolution);
|
|
967
|
+
};
|
|
968
|
+
layer = new import_Vector4.default({
|
|
969
|
+
source: finalSource,
|
|
970
|
+
visible: def.visible !== false,
|
|
971
|
+
zIndex: def.zIndexPinned ? def.zIndex : void 0,
|
|
972
|
+
style,
|
|
973
|
+
// styleFn as unknown as StyleLike,
|
|
974
|
+
properties: { id: def.id }
|
|
975
|
+
});
|
|
976
|
+
if (def.source && typeof def.source === "object") {
|
|
977
|
+
const sourceDef = def.source;
|
|
978
|
+
if (sourceDef.type === "geojson" && sourceDef.options.dataProjection) {
|
|
979
|
+
layer.set("nbic:dataProjection", sourceDef.options.dataProjection);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
} else {
|
|
983
|
+
throw new Error("Source for vector layer must be a VectorSource");
|
|
984
|
+
}
|
|
985
|
+
break;
|
|
986
|
+
default:
|
|
987
|
+
throw new Error(`Unknown layer kind: ${def.kind}`);
|
|
988
|
+
}
|
|
989
|
+
layer.set("id", def.id);
|
|
990
|
+
if (def.hover?.style) layer.set("nbic:hoverStyle", def.hover.style);
|
|
991
|
+
if (def.hover?.clusterStyle) layer.set("nbic:hoverClusterStyle", def.hover.clusterStyle);
|
|
992
|
+
if (def.hover?.singleClusterStyle) layer.set("nbic:hoverSingleClusterStyle", def.hover.singleClusterStyle);
|
|
993
|
+
if (def.hover?.hitTolerance != null) layer.set("nbic:hoverHitTol", def.hover.hitTolerance);
|
|
994
|
+
if (def.hover?.clusterBehavior) layer.set("nbic:hoverClusterBehavior", def.hover.clusterBehavior);
|
|
995
|
+
if (def.cluster?.keepSingleAsCluster) layer.set("nbic:keepSingleAsCluster", true);
|
|
996
|
+
if (def.hover?.cursor) layer.set("nbic:hoverCursor", def.hover.cursor);
|
|
997
|
+
if (def.minZoom !== void 0 && "setMinZoom" in layer) layer.setMinZoom(def.minZoom);
|
|
998
|
+
if (def.maxZoom !== void 0 && "setMaxZoom" in layer) layer.setMaxZoom(def.maxZoom);
|
|
999
|
+
if (def.zIndex !== void 0) layer.set("nbic:declaredZIndex", def.zIndex);
|
|
1000
|
+
if (def.zIndexPinned) layer.set("nbic:zIndexPinned", true);
|
|
1001
|
+
return layer;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// src/core/ol/interactions/HoverInfo.ts
|
|
1005
|
+
var import_Observable = require("ol/Observable");
|
|
1006
|
+
var import_Vector5 = __toESM(require("ol/layer/Vector"), 1);
|
|
1007
|
+
var import_Vector6 = __toESM(require("ol/source/Vector"), 1);
|
|
1008
|
+
var import_Feature = __toESM(require("ol/Feature"), 1);
|
|
1009
|
+
var import_style4 = require("ol/style");
|
|
1010
|
+
|
|
1011
|
+
// src/api/events.ts
|
|
1012
|
+
var MapEvents = {
|
|
1013
|
+
Ready: "ready",
|
|
1014
|
+
Destroy: "destroy",
|
|
1015
|
+
CameraChanged: "camera:changed",
|
|
1016
|
+
ExtentChanged: "extent:changed",
|
|
1017
|
+
LayerAdded: "layer:added",
|
|
1018
|
+
LayerRemoved: "layer:removed",
|
|
1019
|
+
PointerMove: "pointer:move",
|
|
1020
|
+
PointerClick: "pointer:click",
|
|
1021
|
+
BaselayerChanged: "baselayer:changed",
|
|
1022
|
+
BaseChanged: "base:changed",
|
|
1023
|
+
HoverInfo: "hover:info",
|
|
1024
|
+
Error: "error",
|
|
1025
|
+
DrawStart: "draw:start",
|
|
1026
|
+
DrawEnd: "draw:end",
|
|
1027
|
+
DrawImported: "draw:imported",
|
|
1028
|
+
DrawCleared: "draw:cleared",
|
|
1029
|
+
DrawVertex: "draw:vertex",
|
|
1030
|
+
DrawVertexRemoved: "draw:vertexRemoved",
|
|
1031
|
+
GeoPosition: "geo:position",
|
|
1032
|
+
GeoError: "geo:error",
|
|
1033
|
+
FullscreenChange: "fullscreen:change",
|
|
1034
|
+
ScaleLineControl: "controls:scaleline",
|
|
1035
|
+
ZoomControl: "controls:zoom",
|
|
1036
|
+
AttributionControl: "controls:attribution",
|
|
1037
|
+
BufferCreated: "buffer:created",
|
|
1038
|
+
BufferInteractiveStart: "buffer:interactive:start",
|
|
1039
|
+
EditModified: "edit:modified"
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1042
|
+
// src/core/ol/interactions/HoverInfo.ts
|
|
1043
|
+
var import_util = require("ol/util");
|
|
1044
|
+
var DEFAULTS = {
|
|
1045
|
+
hitTolerance: 5,
|
|
1046
|
+
outlineColor: "transparent",
|
|
1047
|
+
outlineWidth: 0,
|
|
1048
|
+
fillColor: "transparent"
|
|
1049
|
+
};
|
|
1050
|
+
var HoverInfoController = class {
|
|
1051
|
+
map;
|
|
1052
|
+
events;
|
|
1053
|
+
opts = { ...DEFAULTS };
|
|
1054
|
+
hoverLayer = null;
|
|
1055
|
+
hoverSource = null;
|
|
1056
|
+
pointerMoveKey = null;
|
|
1057
|
+
lastHoverKey = null;
|
|
1058
|
+
viewportLeaveHandler = null;
|
|
1059
|
+
defaultCursor = "";
|
|
1060
|
+
setCursor(cursor) {
|
|
1061
|
+
const el = this.map.getTargetElement();
|
|
1062
|
+
if (!el) return;
|
|
1063
|
+
if (!this.defaultCursor) this.defaultCursor = el.style.cursor || "";
|
|
1064
|
+
el.style.cursor = cursor ?? this.defaultCursor;
|
|
1065
|
+
}
|
|
1066
|
+
constructor(map, events) {
|
|
1067
|
+
this.map = map;
|
|
1068
|
+
this.events = events;
|
|
1069
|
+
}
|
|
1070
|
+
activate(options) {
|
|
1071
|
+
if (options) this.opts = { ...this.opts, ...options };
|
|
1072
|
+
this.ensureLayer();
|
|
1073
|
+
this.bindPointerMove();
|
|
1074
|
+
this.bindViewportLeave();
|
|
1075
|
+
}
|
|
1076
|
+
deactivate() {
|
|
1077
|
+
this.unbindPointerMove();
|
|
1078
|
+
this.unbindViewportLeave();
|
|
1079
|
+
this.destroyLayer();
|
|
1080
|
+
}
|
|
1081
|
+
bindViewportLeave() {
|
|
1082
|
+
if (this.viewportLeaveHandler) return;
|
|
1083
|
+
const vp = this.map.getViewport();
|
|
1084
|
+
this.viewportLeaveHandler = () => {
|
|
1085
|
+
if (this.lastHoverKey != null) this.clearHover();
|
|
1086
|
+
};
|
|
1087
|
+
vp.addEventListener("pointerleave", this.viewportLeaveHandler);
|
|
1088
|
+
vp.addEventListener("mouseleave", this.viewportLeaveHandler);
|
|
1089
|
+
}
|
|
1090
|
+
unbindViewportLeave() {
|
|
1091
|
+
if (!this.viewportLeaveHandler) return;
|
|
1092
|
+
const vp = this.map.getViewport();
|
|
1093
|
+
vp.removeEventListener("pointerleave", this.viewportLeaveHandler);
|
|
1094
|
+
vp.removeEventListener("mouseleave", this.viewportLeaveHandler);
|
|
1095
|
+
this.viewportLeaveHandler = null;
|
|
1096
|
+
}
|
|
1097
|
+
setOptions(options) {
|
|
1098
|
+
this.opts = { ...this.opts, ...options };
|
|
1099
|
+
}
|
|
1100
|
+
destroy() {
|
|
1101
|
+
this.deactivate();
|
|
1102
|
+
}
|
|
1103
|
+
// ---- internals ----
|
|
1104
|
+
ensureLayer() {
|
|
1105
|
+
if (this.hoverLayer) return;
|
|
1106
|
+
this.hoverSource = new import_Vector6.default();
|
|
1107
|
+
this.hoverLayer = new import_Vector5.default({
|
|
1108
|
+
source: this.hoverSource,
|
|
1109
|
+
properties: { "nbic:role": "hover" },
|
|
1110
|
+
zIndex: 9999,
|
|
1111
|
+
updateWhileInteracting: true,
|
|
1112
|
+
style: (f) => {
|
|
1113
|
+
const s = f.get("nbic:hoverStyle");
|
|
1114
|
+
const stroke = new import_style4.Stroke({
|
|
1115
|
+
color: s?.strokeColor ?? this.opts.outlineColor,
|
|
1116
|
+
width: s?.strokeWidth ?? this.opts.outlineWidth
|
|
1117
|
+
});
|
|
1118
|
+
const fill = new import_style4.Fill({
|
|
1119
|
+
color: s?.fillColor ?? this.opts.fillColor
|
|
1120
|
+
});
|
|
1121
|
+
const g = f.getGeometry();
|
|
1122
|
+
const type = g?.getType();
|
|
1123
|
+
if (type === "Point" || type === "MultiPoint") {
|
|
1124
|
+
const radius = (
|
|
1125
|
+
// let a per-layer hover style override radius if you add it to DrawStyleOptions later
|
|
1126
|
+
s?.pointRadius ?? 8
|
|
1127
|
+
);
|
|
1128
|
+
return new import_style4.Style({
|
|
1129
|
+
image: new import_style4.Circle({ radius, fill, stroke })
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
return new import_style4.Style({ stroke, fill });
|
|
1133
|
+
}
|
|
1134
|
+
});
|
|
1135
|
+
this.map.addLayer(this.hoverLayer);
|
|
1136
|
+
}
|
|
1137
|
+
clearHover() {
|
|
1138
|
+
this.lastHoverKey = null;
|
|
1139
|
+
this.hoverSource?.clear(true);
|
|
1140
|
+
this.events.emit("hover:info", null);
|
|
1141
|
+
}
|
|
1142
|
+
destroyLayer() {
|
|
1143
|
+
if (this.hoverLayer) this.map.removeLayer(this.hoverLayer);
|
|
1144
|
+
this.hoverLayer = null;
|
|
1145
|
+
this.hoverSource = null;
|
|
1146
|
+
this.lastHoverKey = null;
|
|
1147
|
+
}
|
|
1148
|
+
bindPointerMove() {
|
|
1149
|
+
if (this.pointerMoveKey) return;
|
|
1150
|
+
this.pointerMoveKey = this.map.on("pointermove", (evt) => {
|
|
1151
|
+
const pixel = evt.pixel;
|
|
1152
|
+
let picked = null;
|
|
1153
|
+
this.map.forEachFeatureAtPixel(
|
|
1154
|
+
pixel,
|
|
1155
|
+
(f, l) => {
|
|
1156
|
+
if (!l) return void 0;
|
|
1157
|
+
picked = { feature: f, layer: l };
|
|
1158
|
+
return f;
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
hitTolerance: this.opts.hitTolerance,
|
|
1162
|
+
layerFilter: (l) => l.get("nbic:role") !== "hover"
|
|
1163
|
+
}
|
|
1164
|
+
);
|
|
1165
|
+
if (!picked) {
|
|
1166
|
+
if ((this.hoverSource?.getFeatures().length ?? 0) > 0) this.clearHover();
|
|
1167
|
+
this.setCursor("default");
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
const { feature: top, layer } = picked;
|
|
1171
|
+
const topF = top;
|
|
1172
|
+
const id = topF.getId?.();
|
|
1173
|
+
const key = typeof id === "string" || typeof id === "number" ? id : (0, import_util.getUid)(topF);
|
|
1174
|
+
if (key === this.lastHoverKey) return;
|
|
1175
|
+
const keepSingleAsCluster = !!layer.get("nbic:keepSingleAsCluster");
|
|
1176
|
+
const hoverBehavior = layer.get("nbic:hoverClusterBehavior");
|
|
1177
|
+
const baseHover = layer.get("nbic:hoverStyle");
|
|
1178
|
+
const clusterHover = layer.get("nbic:hoverClusterStyle");
|
|
1179
|
+
const singleClusterHover = layer.get("nbic:hoverSingleClusterStyle");
|
|
1180
|
+
const cursor = layer.get("nbic:hoverCursor");
|
|
1181
|
+
this.setCursor(cursor ?? null);
|
|
1182
|
+
const members = topF.get("features");
|
|
1183
|
+
let geom = null;
|
|
1184
|
+
let hoverStyleToApply = baseHover;
|
|
1185
|
+
const topHover = topF.get("nbic:hoverStyle");
|
|
1186
|
+
const isCluster = !!members?.length;
|
|
1187
|
+
if (isCluster) {
|
|
1188
|
+
if (members.length >= 2) {
|
|
1189
|
+
geom = topF.getGeometry()?.clone() ?? null;
|
|
1190
|
+
hoverStyleToApply = clusterHover ?? baseHover;
|
|
1191
|
+
} else {
|
|
1192
|
+
const preferBubble = keepSingleAsCluster || hoverBehavior === "bubble";
|
|
1193
|
+
const inner = members[0];
|
|
1194
|
+
const innerHover = inner?.get("nbic:hoverStyle");
|
|
1195
|
+
if (preferBubble) {
|
|
1196
|
+
geom = topF.getGeometry()?.clone() ?? null;
|
|
1197
|
+
hoverStyleToApply = singleClusterHover ?? clusterHover ?? baseHover;
|
|
1198
|
+
} else {
|
|
1199
|
+
geom = inner?.getGeometry()?.clone() ?? null;
|
|
1200
|
+
hoverStyleToApply = innerHover ?? baseHover;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
} else {
|
|
1204
|
+
geom = topF.getGeometry()?.clone() ?? null;
|
|
1205
|
+
hoverStyleToApply = topHover ?? baseHover;
|
|
1206
|
+
}
|
|
1207
|
+
this.clearHover();
|
|
1208
|
+
if (!geom || !this.hoverSource) return;
|
|
1209
|
+
const hf = new import_Feature.default({ geometry: geom });
|
|
1210
|
+
if (hoverStyleToApply) hf.set("nbic:hoverStyle", hoverStyleToApply);
|
|
1211
|
+
this.hoverSource.addFeature(hf);
|
|
1212
|
+
this.lastHoverKey = key;
|
|
1213
|
+
this.events.emit(MapEvents.HoverInfo, {
|
|
1214
|
+
coordinate: evt.coordinate,
|
|
1215
|
+
items: [{ feature: topF, layer }]
|
|
1216
|
+
});
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
unbindPointerMove() {
|
|
1220
|
+
if (this.pointerMoveKey) {
|
|
1221
|
+
(0, import_Observable.unByKey)(this.pointerMoveKey);
|
|
1222
|
+
this.pointerMoveKey = null;
|
|
1223
|
+
}
|
|
1224
|
+
this.clearHover();
|
|
1225
|
+
}
|
|
1226
|
+
};
|
|
1227
|
+
|
|
1228
|
+
// src/core/ol/OlMapEngine.ts
|
|
1229
|
+
var import_Collection = __toESM(require("ol/Collection"), 1);
|
|
1230
|
+
|
|
1231
|
+
// src/core/ol/layers/LayerRegistry.ts
|
|
1232
|
+
function hasGetSource(obj) {
|
|
1233
|
+
return !!obj && typeof obj.getSource === "function";
|
|
1234
|
+
}
|
|
1235
|
+
function hasGetFeatures(obj) {
|
|
1236
|
+
return !!obj && typeof obj.getFeatures === "function";
|
|
1237
|
+
}
|
|
1238
|
+
var LayerRegistry = class {
|
|
1239
|
+
index = /* @__PURE__ */ new Map();
|
|
1240
|
+
add(id, layer) {
|
|
1241
|
+
this.index.set(id, layer);
|
|
1242
|
+
}
|
|
1243
|
+
remove(id) {
|
|
1244
|
+
this.index.delete(id);
|
|
1245
|
+
}
|
|
1246
|
+
get(id) {
|
|
1247
|
+
return this.index.get(id);
|
|
1248
|
+
}
|
|
1249
|
+
clear() {
|
|
1250
|
+
this.index.clear();
|
|
1251
|
+
}
|
|
1252
|
+
getVectorSource(id) {
|
|
1253
|
+
const layer = this.index.get(id);
|
|
1254
|
+
if (!layer || !hasGetSource(layer)) return null;
|
|
1255
|
+
const src0 = layer.getSource();
|
|
1256
|
+
if (!src0) return null;
|
|
1257
|
+
const inner = hasGetSource(src0) ? src0.getSource() : src0;
|
|
1258
|
+
return hasGetFeatures(inner) ? inner : null;
|
|
1259
|
+
}
|
|
1260
|
+
listIds() {
|
|
1261
|
+
return Array.from(this.index.keys());
|
|
1262
|
+
}
|
|
1263
|
+
/** Reorder overlays and bases. You’ll usually call this from BaseLayersController. */
|
|
1264
|
+
reorder(order, bases) {
|
|
1265
|
+
let overlayZ = 0;
|
|
1266
|
+
let baseZ = bases.baseBand;
|
|
1267
|
+
for (const id of order) {
|
|
1268
|
+
const l = this.index.get(id);
|
|
1269
|
+
if (!l) continue;
|
|
1270
|
+
const role = l.get?.("nbic:role");
|
|
1271
|
+
if (role === "hover" || role === "draw" || role === "draw-vertices") continue;
|
|
1272
|
+
const pinned = !!l.get?.("nbic:zIndexPinned");
|
|
1273
|
+
if (pinned) continue;
|
|
1274
|
+
if (bases.isBase(l)) {
|
|
1275
|
+
l.setZIndex(baseZ++);
|
|
1276
|
+
} else {
|
|
1277
|
+
l.setZIndex(overlayZ++);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
|
|
1283
|
+
// src/core/ol/layers/BaseLayerController.ts
|
|
1284
|
+
var BaseLayersController = class {
|
|
1285
|
+
constructor(events) {
|
|
1286
|
+
this.events = events;
|
|
1287
|
+
}
|
|
1288
|
+
baseIds = /* @__PURE__ */ new Set();
|
|
1289
|
+
superBaseIds = /* @__PURE__ */ new Set();
|
|
1290
|
+
regionalBaseIds = /* @__PURE__ */ new Set();
|
|
1291
|
+
activeRegional = null;
|
|
1292
|
+
activeSuper = null;
|
|
1293
|
+
_findLayer = () => null;
|
|
1294
|
+
/** zIndex bands (keep bases below overlays) */
|
|
1295
|
+
baseBand = -1e4;
|
|
1296
|
+
superBaseBand = -2e4;
|
|
1297
|
+
isBase(l) {
|
|
1298
|
+
return l.get("nbic:role") === "base";
|
|
1299
|
+
}
|
|
1300
|
+
roleOf(l) {
|
|
1301
|
+
return l.get("nbic:baseRole");
|
|
1302
|
+
}
|
|
1303
|
+
registerBase(layer, role, def) {
|
|
1304
|
+
const id = layer.get("id");
|
|
1305
|
+
layer.set("nbic:role", "base");
|
|
1306
|
+
layer.set("nbic:baseRole", role);
|
|
1307
|
+
this.baseIds.add(id);
|
|
1308
|
+
if (role === "super") this.superBaseIds.add(id);
|
|
1309
|
+
if (role === "regional") this.regionalBaseIds.add(id);
|
|
1310
|
+
if (def.zIndex === void 0) {
|
|
1311
|
+
if (role === "super") layer.setZIndex(this.superBaseBand + this.superBaseIds.size);
|
|
1312
|
+
else layer.setZIndex(this.baseBand + this.regionalBaseIds.size);
|
|
1313
|
+
}
|
|
1314
|
+
if (role === "super") {
|
|
1315
|
+
layer.setVisible(def.visible !== false);
|
|
1316
|
+
if (layer.getVisible()) this.activeSuper = id;
|
|
1317
|
+
} else {
|
|
1318
|
+
const wantsVisible = def.visible ?? layer.getVisible();
|
|
1319
|
+
if (wantsVisible) this.setActiveRegional(id);
|
|
1320
|
+
else this.ensureOneRegionalVisible();
|
|
1321
|
+
}
|
|
1322
|
+
this.emitChanged();
|
|
1323
|
+
}
|
|
1324
|
+
onRemoved(id) {
|
|
1325
|
+
this.baseIds.delete(id);
|
|
1326
|
+
this.superBaseIds.delete(id);
|
|
1327
|
+
this.regionalBaseIds.delete(id);
|
|
1328
|
+
if (this.activeRegional === id) this.activeRegional = null;
|
|
1329
|
+
if (this.activeSuper === id) this.activeSuper = null;
|
|
1330
|
+
this.emitChanged();
|
|
1331
|
+
}
|
|
1332
|
+
setActiveRegional(id) {
|
|
1333
|
+
if (!this.regionalBaseIds.has(id)) return;
|
|
1334
|
+
for (const rid of this.regionalBaseIds) {
|
|
1335
|
+
const lyr = this.findLayer(rid);
|
|
1336
|
+
if (!lyr) continue;
|
|
1337
|
+
lyr.setVisible(rid === id);
|
|
1338
|
+
}
|
|
1339
|
+
this.activeRegional = id;
|
|
1340
|
+
this.emitChanged();
|
|
1341
|
+
}
|
|
1342
|
+
setVisibility(layer, visible) {
|
|
1343
|
+
const id = layer.get("id");
|
|
1344
|
+
const role = this.roleOf(layer);
|
|
1345
|
+
if (role === "super") {
|
|
1346
|
+
layer.setVisible(visible);
|
|
1347
|
+
if (visible) this.activeSuper = id;
|
|
1348
|
+
this.emitChanged();
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
if (visible) this.setActiveRegional(id);
|
|
1352
|
+
else {
|
|
1353
|
+
layer.setVisible(false);
|
|
1354
|
+
this.ensureOneRegionalVisible();
|
|
1355
|
+
this.emitChanged();
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
/** Bridge: allow LayerRegistry.reorder to query base-ness. */
|
|
1359
|
+
isBaseLayer = (l) => this.isBase(l);
|
|
1360
|
+
ensureOneRegionalVisible() {
|
|
1361
|
+
let first = null;
|
|
1362
|
+
for (const id of this.regionalBaseIds) {
|
|
1363
|
+
const l = this.findLayer(id);
|
|
1364
|
+
if (!l) continue;
|
|
1365
|
+
if (l.getVisible() && !first) first = id;
|
|
1366
|
+
else l.setVisible(id === first);
|
|
1367
|
+
}
|
|
1368
|
+
this.activeRegional = first;
|
|
1369
|
+
}
|
|
1370
|
+
/** Engine should call this after creation to connect real lookup (e.g. LayerRegistry). */
|
|
1371
|
+
bindFind(fn) {
|
|
1372
|
+
this._findLayer = fn;
|
|
1373
|
+
}
|
|
1374
|
+
findLayer(id) {
|
|
1375
|
+
return this._findLayer(id);
|
|
1376
|
+
}
|
|
1377
|
+
clear() {
|
|
1378
|
+
this.baseIds.clear();
|
|
1379
|
+
this.superBaseIds.clear();
|
|
1380
|
+
this.regionalBaseIds.clear();
|
|
1381
|
+
this.activeRegional = null;
|
|
1382
|
+
this.activeSuper = null;
|
|
1383
|
+
this._findLayer = () => null;
|
|
1384
|
+
this.emitChanged();
|
|
1385
|
+
}
|
|
1386
|
+
emitChanged() {
|
|
1387
|
+
this.events.emit("base:changed", { regional: this.activeRegional, super: this.activeSuper });
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
|
|
1391
|
+
// src/core/ol/interactions/DrawController.ts
|
|
1392
|
+
var import_Vector7 = __toESM(require("ol/source/Vector"), 1);
|
|
1393
|
+
var import_Vector8 = __toESM(require("ol/layer/Vector"), 1);
|
|
1394
|
+
var import_Draw = __toESM(require("ol/interaction/Draw"), 1);
|
|
1395
|
+
var import_Modify = __toESM(require("ol/interaction/Modify"), 1);
|
|
1396
|
+
var import_Snap = __toESM(require("ol/interaction/Snap"), 1);
|
|
1397
|
+
var import_GeoJSON3 = __toESM(require("ol/format/GeoJSON"), 1);
|
|
1398
|
+
var import_Point2 = __toESM(require("ol/geom/Point"), 1);
|
|
1399
|
+
var import_ol = require("ol");
|
|
1400
|
+
var import_Observable2 = require("ol/Observable");
|
|
1401
|
+
var import_events2 = require("ol/events");
|
|
1402
|
+
var import_buffer = __toESM(require("@turf/buffer"), 1);
|
|
1403
|
+
var import_style5 = require("ol/style");
|
|
1404
|
+
var import_geom2 = require("ol/geom");
|
|
1405
|
+
var import_util2 = require("ol/util");
|
|
1406
|
+
function mapKindToDraw(kind) {
|
|
1407
|
+
switch (kind) {
|
|
1408
|
+
case "Point":
|
|
1409
|
+
return { type: "Point" };
|
|
1410
|
+
case "LineString":
|
|
1411
|
+
return { type: "LineString" };
|
|
1412
|
+
case "Polygon":
|
|
1413
|
+
return { type: "Polygon" };
|
|
1414
|
+
case "Circle":
|
|
1415
|
+
return { type: "Circle" };
|
|
1416
|
+
case "Text":
|
|
1417
|
+
return { type: "Point" };
|
|
1418
|
+
case "Box":
|
|
1419
|
+
return { type: "Circle", geometryFunction: (0, import_Draw.createBox)() };
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
function vertexCount(geom) {
|
|
1423
|
+
if (geom instanceof import_Point2.default) return 1;
|
|
1424
|
+
if (geom instanceof import_geom2.LineString) {
|
|
1425
|
+
const coords = geom.getCoordinates();
|
|
1426
|
+
return coords.length;
|
|
1427
|
+
}
|
|
1428
|
+
if (geom instanceof import_geom2.Polygon) {
|
|
1429
|
+
const rings = geom.getCoordinates();
|
|
1430
|
+
if (rings.length === 0) return 0;
|
|
1431
|
+
const firstRing = rings[0];
|
|
1432
|
+
if (!firstRing) return 0;
|
|
1433
|
+
return Math.max(0, firstRing.length - 1);
|
|
1434
|
+
}
|
|
1435
|
+
return 0;
|
|
1436
|
+
}
|
|
1437
|
+
function lastVertex(geom) {
|
|
1438
|
+
if (geom instanceof import_Point2.default) {
|
|
1439
|
+
const c = geom.getCoordinates();
|
|
1440
|
+
if (!Array.isArray(c) || c.length < 2) return null;
|
|
1441
|
+
return [c[0], c[1]];
|
|
1442
|
+
}
|
|
1443
|
+
if (geom instanceof import_geom2.LineString) {
|
|
1444
|
+
const coords = geom.getCoordinates();
|
|
1445
|
+
if (!Array.isArray(coords) || coords.length === 0) return null;
|
|
1446
|
+
const last = coords[coords.length - 1];
|
|
1447
|
+
if (!Array.isArray(last) || last.length < 2) return null;
|
|
1448
|
+
return [last[0], last[1]];
|
|
1449
|
+
}
|
|
1450
|
+
if (geom instanceof import_geom2.Polygon) {
|
|
1451
|
+
const rings = geom.getCoordinates();
|
|
1452
|
+
if (!Array.isArray(rings) || rings.length === 0) return null;
|
|
1453
|
+
const ring = rings[0];
|
|
1454
|
+
if (!Array.isArray(ring) || ring.length <= 1) return null;
|
|
1455
|
+
const last = ring[ring.length - 2];
|
|
1456
|
+
if (!Array.isArray(last) || last.length < 2) return null;
|
|
1457
|
+
return [last[0], last[1]];
|
|
1458
|
+
}
|
|
1459
|
+
return null;
|
|
1460
|
+
}
|
|
1461
|
+
function flattenOnce(arr) {
|
|
1462
|
+
const out = [];
|
|
1463
|
+
for (const a of arr) out.push(...a);
|
|
1464
|
+
return out;
|
|
1465
|
+
}
|
|
1466
|
+
function flattenTwice(arr) {
|
|
1467
|
+
const out = [];
|
|
1468
|
+
for (const a of arr) for (const b of a) out.push(...b);
|
|
1469
|
+
return out;
|
|
1470
|
+
}
|
|
1471
|
+
var DrawController = class {
|
|
1472
|
+
constructor(events) {
|
|
1473
|
+
this.events = events;
|
|
1474
|
+
}
|
|
1475
|
+
map;
|
|
1476
|
+
source;
|
|
1477
|
+
layer;
|
|
1478
|
+
draw;
|
|
1479
|
+
modify;
|
|
1480
|
+
snap;
|
|
1481
|
+
fmt = new import_GeoJSON3.default();
|
|
1482
|
+
currentDrawStyle = makeDrawStyle(void 0);
|
|
1483
|
+
vertexLayer;
|
|
1484
|
+
vertexSrc;
|
|
1485
|
+
attach(map) {
|
|
1486
|
+
this.map = map;
|
|
1487
|
+
}
|
|
1488
|
+
detach() {
|
|
1489
|
+
this.stop();
|
|
1490
|
+
this.map = void 0;
|
|
1491
|
+
}
|
|
1492
|
+
getLayer() {
|
|
1493
|
+
this.ensureLayer();
|
|
1494
|
+
return this.layer;
|
|
1495
|
+
}
|
|
1496
|
+
// --- Modify undo history ---
|
|
1497
|
+
editUndoStack = /* @__PURE__ */ new Map();
|
|
1498
|
+
// key -> snapshots
|
|
1499
|
+
editRedoStack = /* @__PURE__ */ new Map();
|
|
1500
|
+
// optional (keep for future)
|
|
1501
|
+
lastEditedKey = null;
|
|
1502
|
+
featureKey(f) {
|
|
1503
|
+
const id = f.getId();
|
|
1504
|
+
if (typeof id === "string") return id;
|
|
1505
|
+
if (typeof id === "number") return String(id);
|
|
1506
|
+
return String((0, import_util2.getUid)(f));
|
|
1507
|
+
}
|
|
1508
|
+
pushGeometrySnapshot(f) {
|
|
1509
|
+
const g = f.getGeometry();
|
|
1510
|
+
if (!g) return;
|
|
1511
|
+
const key = this.featureKey(f);
|
|
1512
|
+
this.lastEditedKey = key;
|
|
1513
|
+
const stack = this.editUndoStack.get(key) ?? [];
|
|
1514
|
+
stack.push(g.clone());
|
|
1515
|
+
const MAX = 50;
|
|
1516
|
+
if (stack.length > MAX) stack.splice(0, stack.length - MAX);
|
|
1517
|
+
this.editUndoStack.set(key, stack);
|
|
1518
|
+
this.editRedoStack.set(key, []);
|
|
1519
|
+
}
|
|
1520
|
+
restoreGeometrySnapshot(f, geom) {
|
|
1521
|
+
f.setGeometry(geom.clone());
|
|
1522
|
+
}
|
|
1523
|
+
// ————— helpers —————
|
|
1524
|
+
ensureLayer() {
|
|
1525
|
+
if (!this.map || this.layer) return;
|
|
1526
|
+
this.source = new import_Vector7.default();
|
|
1527
|
+
this.layer = new import_Vector8.default({
|
|
1528
|
+
source: this.source,
|
|
1529
|
+
properties: { "nbic:role": "draw", id: "draw-layer" },
|
|
1530
|
+
zIndex: 9e3,
|
|
1531
|
+
style: this.currentDrawStyle,
|
|
1532
|
+
updateWhileInteracting: true
|
|
1533
|
+
});
|
|
1534
|
+
this.layer.set("id", "draw-layer");
|
|
1535
|
+
this.map.addLayer(this.layer);
|
|
1536
|
+
}
|
|
1537
|
+
setStyle(f, s) {
|
|
1538
|
+
if (!s) return;
|
|
1539
|
+
f.set("nbic:style", s);
|
|
1540
|
+
f.setStyle(makeDrawStyle(s));
|
|
1541
|
+
}
|
|
1542
|
+
makeVertexStyle(opts) {
|
|
1543
|
+
const strokeColor = opts?.strokeColor ?? "#1976d2";
|
|
1544
|
+
const strokeWidth = opts?.strokeWidth ?? 2;
|
|
1545
|
+
const fillColor = opts?.fillColor ?? "#ffffff";
|
|
1546
|
+
const radius = opts?.pointRadius ?? 5;
|
|
1547
|
+
return new import_style5.Style({
|
|
1548
|
+
image: new import_style5.Circle({
|
|
1549
|
+
radius,
|
|
1550
|
+
fill: new import_style5.Fill({ color: fillColor }),
|
|
1551
|
+
stroke: new import_style5.Stroke({ color: strokeColor, width: strokeWidth })
|
|
1552
|
+
})
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
ensureVertexLayer(styleOpts) {
|
|
1556
|
+
if (!this.map || this.vertexLayer) return;
|
|
1557
|
+
this.vertexSrc = new import_Vector7.default();
|
|
1558
|
+
this.vertexLayer = new import_Vector8.default({
|
|
1559
|
+
source: this.vertexSrc,
|
|
1560
|
+
properties: { "nbic:role": "draw-vertices" },
|
|
1561
|
+
zIndex: 9001,
|
|
1562
|
+
style: this.makeVertexStyle(styleOpts),
|
|
1563
|
+
updateWhileInteracting: true
|
|
1564
|
+
});
|
|
1565
|
+
this.map.addLayer(this.vertexLayer);
|
|
1566
|
+
}
|
|
1567
|
+
destroyVertexLayer() {
|
|
1568
|
+
if (this.vertexLayer && this.map) this.map.removeLayer(this.vertexLayer);
|
|
1569
|
+
this.vertexLayer = void 0;
|
|
1570
|
+
this.vertexSrc = void 0;
|
|
1571
|
+
}
|
|
1572
|
+
verticesOfGeometry(g) {
|
|
1573
|
+
if (g instanceof import_geom2.LineString) {
|
|
1574
|
+
return g.getCoordinates();
|
|
1575
|
+
}
|
|
1576
|
+
if (g instanceof import_geom2.Polygon) {
|
|
1577
|
+
const rings = g.getCoordinates();
|
|
1578
|
+
return flattenOnce(rings);
|
|
1579
|
+
}
|
|
1580
|
+
if (g instanceof import_geom2.MultiLineString) {
|
|
1581
|
+
const lines = g.getCoordinates();
|
|
1582
|
+
return flattenOnce(lines);
|
|
1583
|
+
}
|
|
1584
|
+
if (g instanceof import_geom2.MultiPolygon) {
|
|
1585
|
+
const polys = g.getCoordinates();
|
|
1586
|
+
return flattenTwice(polys);
|
|
1587
|
+
}
|
|
1588
|
+
return [];
|
|
1589
|
+
}
|
|
1590
|
+
rebuildVertexOverlay() {
|
|
1591
|
+
if (!this.vertexSrc || !this.source) return;
|
|
1592
|
+
this.vertexSrc.clear(true);
|
|
1593
|
+
const feats = this.source.getFeatures();
|
|
1594
|
+
for (const f of feats) {
|
|
1595
|
+
const g = f.getGeometry();
|
|
1596
|
+
if (!g) continue;
|
|
1597
|
+
const verts = this.verticesOfGeometry(g);
|
|
1598
|
+
for (let i = 0; i < verts.length; i++) {
|
|
1599
|
+
const coord = verts[i];
|
|
1600
|
+
if (!coord) continue;
|
|
1601
|
+
const v = new import_ol.Feature({ geometry: new import_Point2.default(coord) });
|
|
1602
|
+
const pid = f.getId();
|
|
1603
|
+
if (pid != null) v.set("nbic:parentId", pid);
|
|
1604
|
+
v.set("nbic:vertexIndex", i);
|
|
1605
|
+
this.vertexSrc.addFeature(v);
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
createPointFeature(coord, props, style) {
|
|
1610
|
+
const f = new import_ol.Feature({ geometry: new import_Point2.default(coord) });
|
|
1611
|
+
if (props) Object.entries(props).forEach(([k, v]) => f.set(k, v));
|
|
1612
|
+
this.setStyle(f, style);
|
|
1613
|
+
return f;
|
|
1614
|
+
}
|
|
1615
|
+
// Turf buffer around any feature (returns OL Feature in view projection)
|
|
1616
|
+
async turfBufferFeature(feature, opts) {
|
|
1617
|
+
if (!this.map) return null;
|
|
1618
|
+
const viewProj = String(this.map.getView().getProjection().getCode());
|
|
1619
|
+
const gj = this.fmt.writeFeatureObject(feature, {
|
|
1620
|
+
dataProjection: "EPSG:4326",
|
|
1621
|
+
featureProjection: viewProj
|
|
1622
|
+
});
|
|
1623
|
+
const turfOpts = { units: opts.units ?? "meters", steps: opts.steps ?? 64 };
|
|
1624
|
+
const buffered = (0, import_buffer.default)(gj, opts.distance, turfOpts);
|
|
1625
|
+
if (!buffered) return null;
|
|
1626
|
+
const out = this.fmt.readFeature(buffered, {
|
|
1627
|
+
dataProjection: "EPSG:4326",
|
|
1628
|
+
featureProjection: viewProj
|
|
1629
|
+
});
|
|
1630
|
+
return out;
|
|
1631
|
+
}
|
|
1632
|
+
// ————— public API —————
|
|
1633
|
+
async start(opts) {
|
|
1634
|
+
if (!this.map) return;
|
|
1635
|
+
const drawParams = mapKindToDraw(opts.kind);
|
|
1636
|
+
this.ensureLayer();
|
|
1637
|
+
this.stop();
|
|
1638
|
+
this.currentDrawStyle = makeDrawStyle(opts.style);
|
|
1639
|
+
this.layer.setStyle(this.currentDrawStyle);
|
|
1640
|
+
this.modify = new import_Modify.default({ source: this.source });
|
|
1641
|
+
this.map.addInteraction(this.modify);
|
|
1642
|
+
this.modify.setActive(false);
|
|
1643
|
+
this.draw = new import_Draw.default({
|
|
1644
|
+
source: this.source,
|
|
1645
|
+
type: drawParams.type,
|
|
1646
|
+
geometryFunction: drawParams.geometryFunction,
|
|
1647
|
+
style: this.currentDrawStyle
|
|
1648
|
+
});
|
|
1649
|
+
this.map.addInteraction(this.draw);
|
|
1650
|
+
const snapTol = Math.max(2, Math.min(25, 10));
|
|
1651
|
+
if (opts.snap ?? true) {
|
|
1652
|
+
this.snap = new import_Snap.default({ source: this.source, pixelTolerance: snapTol });
|
|
1653
|
+
this.map.addInteraction(this.snap);
|
|
1654
|
+
}
|
|
1655
|
+
let keyHandler = null;
|
|
1656
|
+
this.draw.on("drawstart", (e) => {
|
|
1657
|
+
this.events.emit("draw:start", { kind: opts.kind });
|
|
1658
|
+
const sketch = e.feature;
|
|
1659
|
+
const geom = sketch.getGeometry();
|
|
1660
|
+
if (!geom) return;
|
|
1661
|
+
let lastLen = vertexCount(geom);
|
|
1662
|
+
const changeKey = geom.on("change", () => {
|
|
1663
|
+
const len = vertexCount(geom);
|
|
1664
|
+
if (len > lastLen) {
|
|
1665
|
+
const coord = lastVertex(geom);
|
|
1666
|
+
if (coord) {
|
|
1667
|
+
this.events.emit("draw:vertex", {
|
|
1668
|
+
kind: opts.kind,
|
|
1669
|
+
index: len - 1,
|
|
1670
|
+
// 0-based, correct for lines/polygons
|
|
1671
|
+
coordinate: coord
|
|
1672
|
+
// MapCoord
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
} else if (len < lastLen) {
|
|
1676
|
+
this.events.emit("draw:vertexRemoved", {
|
|
1677
|
+
kind: opts.kind,
|
|
1678
|
+
index: len
|
|
1679
|
+
// new last index after removal
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
lastLen = len;
|
|
1683
|
+
});
|
|
1684
|
+
keyHandler = (ev) => {
|
|
1685
|
+
const ctrlZ = (ev.key === "z" || ev.key === "Z") && (ev.ctrlKey || ev.metaKey);
|
|
1686
|
+
if (ev.key === "Backspace" || ctrlZ) {
|
|
1687
|
+
ev.preventDefault();
|
|
1688
|
+
this.draw?.removeLastPoint();
|
|
1689
|
+
} else if (ev.key === "Enter") {
|
|
1690
|
+
ev.preventDefault();
|
|
1691
|
+
this.draw?.finishDrawing();
|
|
1692
|
+
} else if (ev.key === "Escape") {
|
|
1693
|
+
ev.preventDefault();
|
|
1694
|
+
this.draw.abortDrawing?.();
|
|
1695
|
+
}
|
|
1696
|
+
};
|
|
1697
|
+
window.addEventListener("keydown", keyHandler);
|
|
1698
|
+
const detachPerSketch = () => {
|
|
1699
|
+
(0, import_Observable2.unByKey)(changeKey);
|
|
1700
|
+
if (keyHandler) {
|
|
1701
|
+
window.removeEventListener("keydown", keyHandler);
|
|
1702
|
+
keyHandler = null;
|
|
1703
|
+
}
|
|
1704
|
+
};
|
|
1705
|
+
this.draw.once("drawend", detachPerSketch);
|
|
1706
|
+
this.draw.once("drawabort", detachPerSketch);
|
|
1707
|
+
});
|
|
1708
|
+
this.draw.on("drawend", async (e) => {
|
|
1709
|
+
const f = e.feature;
|
|
1710
|
+
if (!f.getId()) {
|
|
1711
|
+
f.setId(crypto.randomUUID());
|
|
1712
|
+
}
|
|
1713
|
+
this.setStyle(f, opts.style);
|
|
1714
|
+
this.modify?.setActive(true);
|
|
1715
|
+
const b = opts.buffer;
|
|
1716
|
+
const wantsInteractive = !!b && (b === true || typeof b === "object" && b.interactive) && ["Point", "LineString", "Polygon"].includes(f.getGeometry()?.getType() || "");
|
|
1717
|
+
if (wantsInteractive && this.map && this.source) {
|
|
1718
|
+
const params = b === true ? {} : b;
|
|
1719
|
+
const g = f.getGeometry();
|
|
1720
|
+
const gType = g.getType();
|
|
1721
|
+
const previewStyle = params.style ?? { strokeColor: "#0080ff", strokeWidth: 2, fillColor: "rgba(0,128,255,0.12)" };
|
|
1722
|
+
let preview = null;
|
|
1723
|
+
let lastDist = 0;
|
|
1724
|
+
const THROTTLE_MS = 90;
|
|
1725
|
+
let lastRun = 0;
|
|
1726
|
+
const distanceToGeometry = (px) => {
|
|
1727
|
+
if (gType === "Point") {
|
|
1728
|
+
const c = g.getCoordinates();
|
|
1729
|
+
const dx2 = px[0] - c[0], dy2 = px[1] - c[1];
|
|
1730
|
+
return Math.sqrt(dx2 * dx2 + dy2 * dy2);
|
|
1731
|
+
}
|
|
1732
|
+
const closest = g.getClosestPoint(px);
|
|
1733
|
+
const dx = px[0] - closest[0], dy = px[1] - closest[1];
|
|
1734
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
1735
|
+
};
|
|
1736
|
+
this.modify?.setActive(false);
|
|
1737
|
+
this.snap?.setActive?.(false);
|
|
1738
|
+
let moveKey = this.map.on("pointermove", async (evt) => {
|
|
1739
|
+
const now = performance.now();
|
|
1740
|
+
if (now - lastRun < THROTTLE_MS) return;
|
|
1741
|
+
lastRun = now;
|
|
1742
|
+
const dist = distanceToGeometry(evt.coordinate);
|
|
1743
|
+
if (dist <= 0) return;
|
|
1744
|
+
lastDist = dist;
|
|
1745
|
+
const buffered = await this.turfBufferFeature(f, {
|
|
1746
|
+
distance: dist,
|
|
1747
|
+
units: params.units ?? "meters",
|
|
1748
|
+
steps: params.steps ?? 64
|
|
1749
|
+
});
|
|
1750
|
+
if (!buffered) return;
|
|
1751
|
+
buffered.set("nbic:style", previewStyle);
|
|
1752
|
+
buffered.setStyle(makeDrawStyle(previewStyle));
|
|
1753
|
+
if (preview) this.source.removeFeature(preview);
|
|
1754
|
+
preview = buffered;
|
|
1755
|
+
this.source.addFeature(preview);
|
|
1756
|
+
});
|
|
1757
|
+
const pointerDownListener = (0, import_events2.listen)(this.map, "pointerdown", () => finalize());
|
|
1758
|
+
const detach = () => {
|
|
1759
|
+
if (moveKey) {
|
|
1760
|
+
(0, import_Observable2.unByKey)(moveKey);
|
|
1761
|
+
moveKey = void 0;
|
|
1762
|
+
}
|
|
1763
|
+
if (pointerDownListener) {
|
|
1764
|
+
(0, import_Observable2.unByKey)(pointerDownListener);
|
|
1765
|
+
}
|
|
1766
|
+
};
|
|
1767
|
+
const finalize = () => {
|
|
1768
|
+
detach();
|
|
1769
|
+
if (preview) {
|
|
1770
|
+
if (params.replaceOriginal) this.source.removeFeature(f);
|
|
1771
|
+
this.events.emit("buffer:created", { baseFeature: f, bufferFeature: preview, distance: lastDist, units: params.units ?? "meters" });
|
|
1772
|
+
preview = null;
|
|
1773
|
+
}
|
|
1774
|
+
this.stop();
|
|
1775
|
+
};
|
|
1776
|
+
const esc = (e2) => {
|
|
1777
|
+
if (e2.key !== "Escape") return;
|
|
1778
|
+
if (moveKey) {
|
|
1779
|
+
(0, import_Observable2.unByKey)(moveKey);
|
|
1780
|
+
moveKey = void 0;
|
|
1781
|
+
}
|
|
1782
|
+
if (preview) {
|
|
1783
|
+
this.source.removeFeature(preview);
|
|
1784
|
+
preview = null;
|
|
1785
|
+
}
|
|
1786
|
+
window.removeEventListener("keydown", esc);
|
|
1787
|
+
this.modify?.setActive(true);
|
|
1788
|
+
this.snap?.setActive?.(true);
|
|
1789
|
+
this.draw?.setActive(true);
|
|
1790
|
+
};
|
|
1791
|
+
window.addEventListener("keydown", esc);
|
|
1792
|
+
this.events.emit("buffer:interactive:start", { mode: gType });
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1795
|
+
const wantsNonInteractive = !!b && typeof b === "object" && !b.interactive;
|
|
1796
|
+
if (wantsNonInteractive && this.map && this.source) {
|
|
1797
|
+
const bb = b;
|
|
1798
|
+
const buffered = await this.turfBufferFeature(f, {
|
|
1799
|
+
distance: bb.distance ?? 50,
|
|
1800
|
+
units: bb.units ?? "meters",
|
|
1801
|
+
steps: bb.steps ?? 64
|
|
1802
|
+
});
|
|
1803
|
+
if (buffered) {
|
|
1804
|
+
const finalStyle = bb.style ?? { strokeColor: "#0057ff", strokeWidth: 2, fillColor: "rgba(0,87,255,0.16)" };
|
|
1805
|
+
buffered.set("nbic:style", finalStyle);
|
|
1806
|
+
buffered.setStyle(makeDrawStyle(finalStyle));
|
|
1807
|
+
this.source.addFeature(buffered);
|
|
1808
|
+
if (bb.replaceOriginal) this.source.removeFeature(f);
|
|
1809
|
+
this.events.emit("buffer:created", { baseFeature: f, bufferFeature: buffered, distance: bb.distance ?? 0, units: bb.units ?? "meters" });
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
this.events.emit("draw:end", { feature: f });
|
|
1813
|
+
if (this.vertexLayer) this.rebuildVertexOverlay();
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
stop() {
|
|
1817
|
+
if (!this.map) return;
|
|
1818
|
+
if (this.draw) {
|
|
1819
|
+
this.map.removeInteraction(this.draw);
|
|
1820
|
+
this.draw = void 0;
|
|
1821
|
+
}
|
|
1822
|
+
if (this.modify) {
|
|
1823
|
+
this.map.removeInteraction(this.modify);
|
|
1824
|
+
this.modify = void 0;
|
|
1825
|
+
}
|
|
1826
|
+
if (this.snap) {
|
|
1827
|
+
this.map.removeInteraction(this.snap);
|
|
1828
|
+
this.snap = void 0;
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
undoLastPoint() {
|
|
1832
|
+
this.draw?.removeLastPoint();
|
|
1833
|
+
}
|
|
1834
|
+
finishCurrent() {
|
|
1835
|
+
this.draw?.finishDrawing();
|
|
1836
|
+
}
|
|
1837
|
+
abortCurrent() {
|
|
1838
|
+
this.draw?.abortDrawing();
|
|
1839
|
+
}
|
|
1840
|
+
undoEdit(target) {
|
|
1841
|
+
if (!this.source) return;
|
|
1842
|
+
let f = null;
|
|
1843
|
+
if (target && "feature" in target) {
|
|
1844
|
+
f = target.feature;
|
|
1845
|
+
} else if (target && "featureId" in target) {
|
|
1846
|
+
const found = this.source.getFeatureById(target.featureId);
|
|
1847
|
+
f = found ?? null;
|
|
1848
|
+
} else {
|
|
1849
|
+
if (!this.lastEditedKey) return;
|
|
1850
|
+
const feats = this.source.getFeatures();
|
|
1851
|
+
for (const cand of feats) {
|
|
1852
|
+
const key2 = this.featureKey(cand);
|
|
1853
|
+
if (key2 === this.lastEditedKey) {
|
|
1854
|
+
f = cand;
|
|
1855
|
+
break;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
if (!f) return;
|
|
1860
|
+
const key = this.featureKey(f);
|
|
1861
|
+
const stack = this.editUndoStack.get(key);
|
|
1862
|
+
if (!stack || stack.length === 0) return;
|
|
1863
|
+
const cur = f.getGeometry();
|
|
1864
|
+
if (cur) {
|
|
1865
|
+
const redo = this.editRedoStack.get(key) ?? [];
|
|
1866
|
+
redo.push(cur.clone());
|
|
1867
|
+
this.editRedoStack.set(key, redo);
|
|
1868
|
+
}
|
|
1869
|
+
const prev = stack.pop();
|
|
1870
|
+
if (!prev) return;
|
|
1871
|
+
this.restoreGeometrySnapshot(f, prev);
|
|
1872
|
+
if (this.vertexLayer) this.rebuildVertexOverlay();
|
|
1873
|
+
this.events.emit("edit:undo", { feature: f });
|
|
1874
|
+
}
|
|
1875
|
+
enableEditing(options) {
|
|
1876
|
+
if (!this.map) return;
|
|
1877
|
+
this.ensureLayer();
|
|
1878
|
+
let showHandles = true;
|
|
1879
|
+
let persistent = false;
|
|
1880
|
+
let vertexStyleOpts;
|
|
1881
|
+
if (typeof options === "boolean") {
|
|
1882
|
+
showHandles = options;
|
|
1883
|
+
} else if (options) {
|
|
1884
|
+
showHandles = options.showVertices !== false;
|
|
1885
|
+
persistent = !!options.showVerticesPersistent;
|
|
1886
|
+
vertexStyleOpts = options.vertexStyle;
|
|
1887
|
+
}
|
|
1888
|
+
if (this.modify) {
|
|
1889
|
+
this.map.removeInteraction(this.modify);
|
|
1890
|
+
this.modify = void 0;
|
|
1891
|
+
}
|
|
1892
|
+
const modifyStyle = showHandles ? this.makeVertexStyle(vertexStyleOpts) : () => void 0;
|
|
1893
|
+
this.modify = new import_Modify.default({ source: this.source, style: modifyStyle });
|
|
1894
|
+
this.map.addInteraction(this.modify);
|
|
1895
|
+
this.modify.on("modifystart", (e) => {
|
|
1896
|
+
const feats = e.features;
|
|
1897
|
+
for (let i = 0; i < feats.getLength(); i++) {
|
|
1898
|
+
const f = feats.item(i);
|
|
1899
|
+
this.pushGeometrySnapshot(f);
|
|
1900
|
+
}
|
|
1901
|
+
});
|
|
1902
|
+
this.modify.on("modifyend", (e) => {
|
|
1903
|
+
this.events.emit("edit:modified", {
|
|
1904
|
+
count: e.features.getLength(),
|
|
1905
|
+
feature: e.features.item(0)
|
|
1906
|
+
});
|
|
1907
|
+
if (persistent) this.rebuildVertexOverlay();
|
|
1908
|
+
});
|
|
1909
|
+
if (!this.snap) {
|
|
1910
|
+
this.snap = new import_Snap.default({ source: this.source });
|
|
1911
|
+
this.map.addInteraction(this.snap);
|
|
1912
|
+
}
|
|
1913
|
+
if (persistent) {
|
|
1914
|
+
this.ensureVertexLayer(vertexStyleOpts);
|
|
1915
|
+
this.rebuildVertexOverlay();
|
|
1916
|
+
this.source.on("addfeature", () => this.rebuildVertexOverlay());
|
|
1917
|
+
this.source.on("removefeature", () => this.rebuildVertexOverlay());
|
|
1918
|
+
this.source.on("changefeature", () => this.rebuildVertexOverlay());
|
|
1919
|
+
} else {
|
|
1920
|
+
this.destroyVertexLayer();
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
disableEditing() {
|
|
1924
|
+
if (!this.map) return;
|
|
1925
|
+
if (this.modify) {
|
|
1926
|
+
this.map.removeInteraction(this.modify);
|
|
1927
|
+
this.modify = void 0;
|
|
1928
|
+
}
|
|
1929
|
+
if (this.snap) {
|
|
1930
|
+
this.map.removeInteraction(this.snap);
|
|
1931
|
+
this.snap = void 0;
|
|
1932
|
+
}
|
|
1933
|
+
this.destroyVertexLayer();
|
|
1934
|
+
}
|
|
1935
|
+
clear() {
|
|
1936
|
+
const count = this.source?.getFeatures().length ?? 0;
|
|
1937
|
+
this.source?.clear(true);
|
|
1938
|
+
this.events.emit("draw:cleared", { count });
|
|
1939
|
+
if (this.vertexLayer) this.rebuildVertexOverlay();
|
|
1940
|
+
}
|
|
1941
|
+
exportGeoJSON(map, opts) {
|
|
1942
|
+
const json = this.fmt.writeFeatures(this.source?.getFeatures() ?? [], {
|
|
1943
|
+
featureProjection: String(map.getView().getProjection() ?? "EPSG:3857")
|
|
1944
|
+
});
|
|
1945
|
+
return opts?.pretty ? JSON.stringify(JSON.parse(json), null, 2) : json;
|
|
1946
|
+
}
|
|
1947
|
+
importGeoJSON(map, geojson, opts) {
|
|
1948
|
+
this.ensureLayer();
|
|
1949
|
+
const features = this.fmt.readFeatures(geojson, {
|
|
1950
|
+
featureProjection: String(map.getView().getProjection())
|
|
1951
|
+
});
|
|
1952
|
+
if (opts?.clearExisting) this.source.clear(true);
|
|
1953
|
+
for (const f of features) {
|
|
1954
|
+
const styleOpts = f.get("nbic:style");
|
|
1955
|
+
if (styleOpts) f.setStyle(makeDrawStyle(styleOpts));
|
|
1956
|
+
const label = f.get("label");
|
|
1957
|
+
if (label && styleOpts?.text) f.setStyle(makeDrawStyle({ ...styleOpts, text: { ...styleOpts.text, label } }));
|
|
1958
|
+
}
|
|
1959
|
+
this.source.addFeatures(features);
|
|
1960
|
+
this.events.emit("draw:imported", { count: features.length });
|
|
1961
|
+
}
|
|
1962
|
+
setFeatureStyle(feature, style) {
|
|
1963
|
+
if (!feature) return;
|
|
1964
|
+
feature.set("nbic:style", style);
|
|
1965
|
+
feature.setStyle(makeUpdatedDrawStyle(style));
|
|
1966
|
+
}
|
|
1967
|
+
clearFeatureStyle(feature) {
|
|
1968
|
+
feature.unset("nbic:style", true);
|
|
1969
|
+
feature.setStyle(void 0);
|
|
1970
|
+
}
|
|
1971
|
+
};
|
|
1972
|
+
|
|
1973
|
+
// src/core/ol/controls/ControlsController.ts
|
|
1974
|
+
var import_Zoom = __toESM(require("ol/control/Zoom"), 1);
|
|
1975
|
+
var import_ScaleLine = __toESM(require("ol/control/ScaleLine"), 1);
|
|
1976
|
+
var import_Attribution = __toESM(require("ol/control/Attribution"), 1);
|
|
1977
|
+
var import_FullScreen = __toESM(require("ol/control/FullScreen"), 1);
|
|
1978
|
+
var ControlsController = class {
|
|
1979
|
+
map;
|
|
1980
|
+
zoom;
|
|
1981
|
+
scale;
|
|
1982
|
+
attr;
|
|
1983
|
+
fs;
|
|
1984
|
+
custom = /* @__PURE__ */ new Map();
|
|
1985
|
+
attach(map, events, init) {
|
|
1986
|
+
this.map = map;
|
|
1987
|
+
if (init?.zoom) this.ensureZoom(map, events);
|
|
1988
|
+
if (init?.scaleLine) this.ensureScaleLine(map, events);
|
|
1989
|
+
if (init?.attribution) this.ensureAttribution(map, events);
|
|
1990
|
+
if (init?.fullscreen) this.ensureFullScreen(map);
|
|
1991
|
+
}
|
|
1992
|
+
detach() {
|
|
1993
|
+
if (!this.map) return;
|
|
1994
|
+
if (this.zoom) this.map.removeControl(this.zoom);
|
|
1995
|
+
if (this.scale) this.map.removeControl(this.scale);
|
|
1996
|
+
if (this.attr) this.map.removeControl(this.attr);
|
|
1997
|
+
if (this.fs) this.map.removeControl(this.fs);
|
|
1998
|
+
this.zoom = this.scale = this.attr = this.fs = void 0;
|
|
1999
|
+
this.map = void 0;
|
|
2000
|
+
}
|
|
2001
|
+
ensureZoom(map, events) {
|
|
2002
|
+
if (this.zoom) return;
|
|
2003
|
+
this.zoom = new import_Zoom.default();
|
|
2004
|
+
map.addControl(this.zoom);
|
|
2005
|
+
events?.emit("controls:zoom", { visible: true });
|
|
2006
|
+
}
|
|
2007
|
+
removeZoom(map, events) {
|
|
2008
|
+
if (!this.zoom) return;
|
|
2009
|
+
map.removeControl(this.zoom);
|
|
2010
|
+
this.zoom = void 0;
|
|
2011
|
+
events?.emit("controls:zoom", { visible: false });
|
|
2012
|
+
}
|
|
2013
|
+
ensureScaleLine(map, events) {
|
|
2014
|
+
if (this.scale) return;
|
|
2015
|
+
this.scale = new import_ScaleLine.default();
|
|
2016
|
+
map.addControl(this.scale);
|
|
2017
|
+
events?.emit("controls:scaleline", { visible: true });
|
|
2018
|
+
}
|
|
2019
|
+
removeScaleLine(map, events) {
|
|
2020
|
+
if (!this.scale) return;
|
|
2021
|
+
map.removeControl(this.scale);
|
|
2022
|
+
this.scale = void 0;
|
|
2023
|
+
events?.emit("controls:scaleline", { visible: false });
|
|
2024
|
+
}
|
|
2025
|
+
ensureAttribution(map, events) {
|
|
2026
|
+
if (this.attr) return;
|
|
2027
|
+
this.attr = new import_Attribution.default({ collapsible: true, collapsed: false });
|
|
2028
|
+
map.addControl(this.attr);
|
|
2029
|
+
events?.emit("controls:attribution", { visible: true });
|
|
2030
|
+
}
|
|
2031
|
+
removeAttribution(map, events) {
|
|
2032
|
+
if (!this.attr) return;
|
|
2033
|
+
map.removeControl(this.attr);
|
|
2034
|
+
this.attr = void 0;
|
|
2035
|
+
events?.emit("controls:attribution", { visible: false });
|
|
2036
|
+
}
|
|
2037
|
+
ensureFullScreen(map) {
|
|
2038
|
+
if (this.fs) return;
|
|
2039
|
+
this.fs = new import_FullScreen.default();
|
|
2040
|
+
map.addControl(this.fs);
|
|
2041
|
+
}
|
|
2042
|
+
removeFullScreen(map) {
|
|
2043
|
+
if (!this.fs) return;
|
|
2044
|
+
map.removeControl(this.fs);
|
|
2045
|
+
this.fs = void 0;
|
|
2046
|
+
}
|
|
2047
|
+
/** Add a custom OL control. Returns the resolved ID. */
|
|
2048
|
+
adopt(control, id) {
|
|
2049
|
+
if (!this.map) throw new Error("ControlsController: map not attached");
|
|
2050
|
+
const existingId = control.get?.("id");
|
|
2051
|
+
const resolved = id ?? existingId ?? `custom:${Math.random().toString(36).slice(2, 10)}`;
|
|
2052
|
+
const old = this.custom.get(resolved);
|
|
2053
|
+
if (old) this.map.removeControl(old);
|
|
2054
|
+
control.set?.("id", resolved);
|
|
2055
|
+
this.map.addControl(control);
|
|
2056
|
+
this.custom.set(resolved, control);
|
|
2057
|
+
return resolved;
|
|
2058
|
+
}
|
|
2059
|
+
/** Remove a custom control by id (no-op if not found). */
|
|
2060
|
+
remove(id) {
|
|
2061
|
+
if (!this.map) return false;
|
|
2062
|
+
const c = this.custom.get(id);
|
|
2063
|
+
if (!c) return false;
|
|
2064
|
+
this.map.removeControl(c);
|
|
2065
|
+
this.custom.delete(id);
|
|
2066
|
+
return true;
|
|
2067
|
+
}
|
|
2068
|
+
/** Get a custom control instance by id. */
|
|
2069
|
+
get(id) {
|
|
2070
|
+
return this.custom.get(id);
|
|
2071
|
+
}
|
|
2072
|
+
/** List all adopted custom control ids. */
|
|
2073
|
+
listIds() {
|
|
2074
|
+
return Array.from(this.custom.keys());
|
|
2075
|
+
}
|
|
2076
|
+
};
|
|
2077
|
+
|
|
2078
|
+
// src/core/ol/geo/GeoController.ts
|
|
2079
|
+
var import_Geolocation = __toESM(require("ol/Geolocation"), 1);
|
|
2080
|
+
var import_Vector9 = __toESM(require("ol/source/Vector"), 1);
|
|
2081
|
+
var import_Vector10 = __toESM(require("ol/layer/Vector"), 1);
|
|
2082
|
+
var import_Circle = __toESM(require("ol/geom/Circle"), 1);
|
|
2083
|
+
var import_Point3 = __toESM(require("ol/geom/Point"), 1);
|
|
2084
|
+
var import_style6 = require("ol/style");
|
|
2085
|
+
var import_ol2 = require("ol");
|
|
2086
|
+
var GeoController = class {
|
|
2087
|
+
constructor(events) {
|
|
2088
|
+
this.events = events;
|
|
2089
|
+
}
|
|
2090
|
+
map;
|
|
2091
|
+
geo;
|
|
2092
|
+
src;
|
|
2093
|
+
layer;
|
|
2094
|
+
follow = false;
|
|
2095
|
+
attach(map) {
|
|
2096
|
+
this.map = map;
|
|
2097
|
+
}
|
|
2098
|
+
detach() {
|
|
2099
|
+
this.deactivate();
|
|
2100
|
+
this.map = void 0;
|
|
2101
|
+
}
|
|
2102
|
+
ensureLayer() {
|
|
2103
|
+
if (!this.map || this.layer) return;
|
|
2104
|
+
this.src = new import_Vector9.default();
|
|
2105
|
+
this.layer = new import_Vector10.default({
|
|
2106
|
+
source: this.src,
|
|
2107
|
+
properties: { "nbic:role": "geolocation" },
|
|
2108
|
+
zIndex: 9998,
|
|
2109
|
+
style: (f) => {
|
|
2110
|
+
const g = f.getGeometry();
|
|
2111
|
+
if (g instanceof import_Circle.default) {
|
|
2112
|
+
return new import_style6.Style({
|
|
2113
|
+
fill: new import_style6.Fill({ color: "rgba(33,150,243,0.15)" }),
|
|
2114
|
+
stroke: new import_style6.Stroke({ color: "#2196f3", width: 1 })
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2117
|
+
return new import_style6.Style({
|
|
2118
|
+
image: new import_style6.Circle({
|
|
2119
|
+
radius: 6,
|
|
2120
|
+
fill: new import_style6.Fill({ color: "#2196f3" }),
|
|
2121
|
+
stroke: new import_style6.Stroke({ color: "#fff", width: 2 })
|
|
2122
|
+
})
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2125
|
+
});
|
|
2126
|
+
this.map.addLayer(this.layer);
|
|
2127
|
+
}
|
|
2128
|
+
activate(follow) {
|
|
2129
|
+
if (!this.map) return;
|
|
2130
|
+
if (!this.geo) {
|
|
2131
|
+
this.geo = new import_Geolocation.default({
|
|
2132
|
+
projection: this.map.getView().getProjection(),
|
|
2133
|
+
tracking: false,
|
|
2134
|
+
trackingOptions: { enableHighAccuracy: true, maximumAge: 1e4, timeout: 1e4 }
|
|
2135
|
+
});
|
|
2136
|
+
this.geo.on("change:position", () => {
|
|
2137
|
+
const p = this.geo.getPosition();
|
|
2138
|
+
const acc = this.geo.getAccuracy();
|
|
2139
|
+
if (!p) {
|
|
2140
|
+
this.events.emit("geo:position", null);
|
|
2141
|
+
return;
|
|
2142
|
+
}
|
|
2143
|
+
this.ensureLayer();
|
|
2144
|
+
this.src.clear();
|
|
2145
|
+
this.src.addFeatures([
|
|
2146
|
+
new import_ol2.Feature({ geometry: new import_Circle.default(p, acc || 0) }),
|
|
2147
|
+
new import_ol2.Feature({ geometry: new import_Point3.default(p) })
|
|
2148
|
+
]);
|
|
2149
|
+
if (this.follow) this.map.getView().setCenter(p);
|
|
2150
|
+
this.events.emit("geo:position", { coordinate: p, accuracy: acc ?? void 0 });
|
|
2151
|
+
});
|
|
2152
|
+
this.geo.on("error", (e) => {
|
|
2153
|
+
this.events.emit("geo:error", { message: e.message ?? "Geolocation error" });
|
|
2154
|
+
});
|
|
2155
|
+
}
|
|
2156
|
+
this.follow = !!follow;
|
|
2157
|
+
this.geo.setTracking(true);
|
|
2158
|
+
}
|
|
2159
|
+
deactivate() {
|
|
2160
|
+
if (this.geo) this.geo.setTracking(false);
|
|
2161
|
+
if (this.layer && this.map) this.map.removeLayer(this.layer);
|
|
2162
|
+
this.geo = void 0;
|
|
2163
|
+
this.src = void 0;
|
|
2164
|
+
this.layer = void 0;
|
|
2165
|
+
}
|
|
2166
|
+
async zoomToGeolocation(maxZoom = 14) {
|
|
2167
|
+
if (!this.map) return false;
|
|
2168
|
+
this.activate(true);
|
|
2169
|
+
return new Promise((resolve) => {
|
|
2170
|
+
const once = () => {
|
|
2171
|
+
const p = this.geo.getPosition();
|
|
2172
|
+
if (p) {
|
|
2173
|
+
this.ensureLayer();
|
|
2174
|
+
this.map.getView().animate({ center: p, zoom: Math.max(this.map.getView().getZoom() ?? 0, maxZoom), duration: 300 });
|
|
2175
|
+
resolve(true);
|
|
2176
|
+
} else resolve(false);
|
|
2177
|
+
this.geo.un("change:position", once);
|
|
2178
|
+
this.geo.setTracking(false);
|
|
2179
|
+
};
|
|
2180
|
+
this.geo.on("change:position", once);
|
|
2181
|
+
});
|
|
2182
|
+
}
|
|
2183
|
+
};
|
|
2184
|
+
|
|
2185
|
+
// src/core/ol/zoom/ZoomController.ts
|
|
2186
|
+
var import_Vector11 = __toESM(require("ol/source/Vector"), 1);
|
|
2187
|
+
var import_extent4 = require("ol/extent");
|
|
2188
|
+
var ZoomController = class {
|
|
2189
|
+
map = null;
|
|
2190
|
+
attach(map) {
|
|
2191
|
+
this.map = map;
|
|
2192
|
+
}
|
|
2193
|
+
detach() {
|
|
2194
|
+
this.map = null;
|
|
2195
|
+
}
|
|
2196
|
+
fitExtent(ext, opts) {
|
|
2197
|
+
if (!this.map) return false;
|
|
2198
|
+
const view = this.map.getView();
|
|
2199
|
+
const size = this.map.getSize();
|
|
2200
|
+
if (!view || !size) return false;
|
|
2201
|
+
const paddingPx = opts?.paddingPx ?? 40;
|
|
2202
|
+
const durationMs = opts?.durationMs ?? 250;
|
|
2203
|
+
const fitOpts = {
|
|
2204
|
+
padding: [paddingPx, paddingPx, paddingPx, paddingPx],
|
|
2205
|
+
duration: durationMs,
|
|
2206
|
+
maxZoom: Infinity
|
|
2207
|
+
};
|
|
2208
|
+
if (typeof opts?.maxZoom === "number") fitOpts.maxZoom = opts.maxZoom;
|
|
2209
|
+
const minSizePx = opts?.minSizePx ?? 64;
|
|
2210
|
+
const res = view.getResolution() ?? 1;
|
|
2211
|
+
const minMapUnits = res * minSizePx;
|
|
2212
|
+
const e = ext.slice();
|
|
2213
|
+
if (e[0] === e[2] || e[1] === e[3]) {
|
|
2214
|
+
const [cx, cy] = (0, import_extent4.getCenter)(e);
|
|
2215
|
+
e[0] = cx ?? 0 - minMapUnits / 2;
|
|
2216
|
+
e[2] = cx ?? 0 + minMapUnits / 2;
|
|
2217
|
+
e[1] = cy ?? 0 - minMapUnits / 2;
|
|
2218
|
+
e[3] = cy ?? 0 + minMapUnits / 2;
|
|
2219
|
+
}
|
|
2220
|
+
view.fit(e, fitOpts);
|
|
2221
|
+
return true;
|
|
2222
|
+
}
|
|
2223
|
+
fitGeometry(geom, opts) {
|
|
2224
|
+
return this.fitExtent(geom.getExtent(), opts);
|
|
2225
|
+
}
|
|
2226
|
+
zoomToFeature(layer, featureId, opts) {
|
|
2227
|
+
const src = layer.getSource();
|
|
2228
|
+
if (!src) return false;
|
|
2229
|
+
const feat = src.getFeatureById(featureId);
|
|
2230
|
+
const geom = feat?.getGeometry();
|
|
2231
|
+
return geom ? this.fitGeometry(geom, opts) : false;
|
|
2232
|
+
}
|
|
2233
|
+
zoomToLayer(layer, opts) {
|
|
2234
|
+
const src = layer.getSource();
|
|
2235
|
+
if (!src) return false;
|
|
2236
|
+
if (!(src instanceof import_Vector11.default)) return console.warn("zoomToLayer: not a vector source"), false;
|
|
2237
|
+
const feats = src.getFeatures();
|
|
2238
|
+
if (!feats.length) return false;
|
|
2239
|
+
let ext = feats[0]?.getGeometry()?.getExtent()?.slice();
|
|
2240
|
+
for (let i = 1; i < feats.length; i++) {
|
|
2241
|
+
const g = feats[i]?.getGeometry();
|
|
2242
|
+
if (!g) continue;
|
|
2243
|
+
const e = g.getExtent();
|
|
2244
|
+
if (!ext) ext = e.slice();
|
|
2245
|
+
else {
|
|
2246
|
+
ext[0] = Math.min(ext[0] ?? Infinity, e[0] ?? Infinity);
|
|
2247
|
+
ext[1] = Math.min(ext[1] ?? Infinity, e[1] ?? Infinity);
|
|
2248
|
+
ext[2] = Math.max(ext[2] ?? -Infinity, e[2] ?? -Infinity);
|
|
2249
|
+
ext[3] = Math.max(ext[3] ?? -Infinity, e[3] ?? -Infinity);
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
return ext ? this.fitExtent(ext, opts) : false;
|
|
2253
|
+
}
|
|
2254
|
+
// Convenience by id (uses your registry/lookup)
|
|
2255
|
+
zoomToFeatureById(reg, layerId, featureId, opts) {
|
|
2256
|
+
const l = reg.get(layerId);
|
|
2257
|
+
if (!l) return false;
|
|
2258
|
+
return this.zoomToFeature(l, featureId, opts);
|
|
2259
|
+
}
|
|
2260
|
+
zoomToLayerById(reg, layerId, opts) {
|
|
2261
|
+
const l = reg.get(layerId);
|
|
2262
|
+
if (!l) return false;
|
|
2263
|
+
return this.zoomToLayer(l, opts);
|
|
2264
|
+
}
|
|
2265
|
+
zoomToExtent(ext, opts) {
|
|
2266
|
+
return this.fitExtent(ext, opts);
|
|
2267
|
+
}
|
|
2268
|
+
};
|
|
2269
|
+
|
|
2270
|
+
// src/core/ol/OlMapEngine.ts
|
|
2271
|
+
var import_GeoJSON5 = __toESM(require("ol/format/GeoJSON"), 1);
|
|
2272
|
+
var import_Vector12 = __toESM(require("ol/source/Vector"), 1);
|
|
2273
|
+
var import_Cluster3 = __toESM(require("ol/source/Cluster"), 1);
|
|
2274
|
+
|
|
2275
|
+
// src/core/ol/utils/picking.ts
|
|
2276
|
+
function isPickableLayer(l) {
|
|
2277
|
+
if (!l || typeof l.get !== "function") return false;
|
|
2278
|
+
const get = l.get.bind(l);
|
|
2279
|
+
const role = get("nbic:role");
|
|
2280
|
+
if (role === "hover") return false;
|
|
2281
|
+
const vis = l.getVisible?.();
|
|
2282
|
+
if (vis === false) return false;
|
|
2283
|
+
if (get("nbic:pickable") === false) return false;
|
|
2284
|
+
return true;
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
// src/core/ol/utils/coords.ts
|
|
2288
|
+
var import_proj5 = require("ol/proj");
|
|
2289
|
+
var import_extent5 = require("ol/extent");
|
|
2290
|
+
function toViewCoord(map, coord, from) {
|
|
2291
|
+
const viewProj = String(map.getView().getProjection().getCode());
|
|
2292
|
+
if (!from || from === viewProj) return coord;
|
|
2293
|
+
return (0, import_proj5.transform)(coord, from, viewProj);
|
|
2294
|
+
}
|
|
2295
|
+
function toDataCoord(map, coord, to = "EPSG:4326") {
|
|
2296
|
+
const viewProj = String(map.getView().getProjection().getCode());
|
|
2297
|
+
if (to === viewProj) return coord;
|
|
2298
|
+
return (0, import_proj5.transform)(coord, viewProj, to);
|
|
2299
|
+
}
|
|
2300
|
+
function transformCoordsArrayFrom(coords, from, to) {
|
|
2301
|
+
return coords.map((c) => (0, import_proj5.transform)(c, from, to));
|
|
2302
|
+
}
|
|
2303
|
+
function transformCoordsFrom(coords, from, to) {
|
|
2304
|
+
return (0, import_proj5.transform)(coords, from, to);
|
|
2305
|
+
}
|
|
2306
|
+
function transformExtentFrom(extent, from, to) {
|
|
2307
|
+
return (0, import_proj5.transformExtent)(extent, from, to);
|
|
2308
|
+
}
|
|
2309
|
+
function getCenterFromExtent(extent) {
|
|
2310
|
+
return (0, import_extent5.getCenter)(extent);
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
// src/core/ol/OlMapEngine.ts
|
|
2314
|
+
var import_extent6 = require("ol/extent");
|
|
2315
|
+
|
|
2316
|
+
// src/core/ol/select/SelectController.ts
|
|
2317
|
+
var import_Cluster2 = __toESM(require("ol/source/Cluster"), 1);
|
|
2318
|
+
var SelectController = class {
|
|
2319
|
+
constructor(events) {
|
|
2320
|
+
this.events = events;
|
|
2321
|
+
}
|
|
2322
|
+
map;
|
|
2323
|
+
selected = null;
|
|
2324
|
+
selectedPrevStyle = void 0;
|
|
2325
|
+
selectedPrevNbicStyle = void 0;
|
|
2326
|
+
attach(map) {
|
|
2327
|
+
this.map = map;
|
|
2328
|
+
}
|
|
2329
|
+
detach() {
|
|
2330
|
+
this.clear();
|
|
2331
|
+
this.map = void 0;
|
|
2332
|
+
}
|
|
2333
|
+
getSelection() {
|
|
2334
|
+
return this.selected;
|
|
2335
|
+
}
|
|
2336
|
+
clear(registry) {
|
|
2337
|
+
if (!this.selected) return;
|
|
2338
|
+
if (registry) {
|
|
2339
|
+
const prev = this.findFeatureById(registry, this.selected.layerId, this.selected.featureId, this.selected.coordinates);
|
|
2340
|
+
if (prev) this.restoreFeature(prev);
|
|
2341
|
+
}
|
|
2342
|
+
this.selected = null;
|
|
2343
|
+
this.selectedPrevStyle = void 0;
|
|
2344
|
+
this.selectedPrevNbicStyle = void 0;
|
|
2345
|
+
this.events.emit("select:changed", null);
|
|
2346
|
+
}
|
|
2347
|
+
/** Select feature by its id (restores previous selection automatically). */
|
|
2348
|
+
selectById(registry, layerId, featureId, style, coordinates) {
|
|
2349
|
+
if (!this.map) return false;
|
|
2350
|
+
if (this.selected) {
|
|
2351
|
+
const prev = this.findFeatureById(registry, this.selected.layerId, this.selected.featureId, this.selected.coordinates);
|
|
2352
|
+
if (prev) this.restoreFeature(prev);
|
|
2353
|
+
this.selected = null;
|
|
2354
|
+
this.selectedPrevStyle = void 0;
|
|
2355
|
+
this.selectedPrevNbicStyle = void 0;
|
|
2356
|
+
}
|
|
2357
|
+
const f = this.findFeatureById(registry, layerId, featureId, coordinates);
|
|
2358
|
+
if (!f) return false;
|
|
2359
|
+
this.selected = { layerId, featureId, coordinates };
|
|
2360
|
+
this.selectedPrevStyle = f.getStyle();
|
|
2361
|
+
this.selectedPrevNbicStyle = f.get("nbic:style");
|
|
2362
|
+
if (style) {
|
|
2363
|
+
f.set("nbic:style", style);
|
|
2364
|
+
f.setStyle(makeDrawStyle(style));
|
|
2365
|
+
} else {
|
|
2366
|
+
f.setStyle(
|
|
2367
|
+
makeDrawStyle({
|
|
2368
|
+
strokeColor: "#00c853",
|
|
2369
|
+
strokeWidth: 3,
|
|
2370
|
+
fillColor: "rgba(0,200,83,0.15)"
|
|
2371
|
+
})
|
|
2372
|
+
);
|
|
2373
|
+
}
|
|
2374
|
+
this.events.emit("select:changed", { layerId, featureId });
|
|
2375
|
+
return true;
|
|
2376
|
+
}
|
|
2377
|
+
restoreFeature(feature) {
|
|
2378
|
+
if (this.selectedPrevNbicStyle === void 0) {
|
|
2379
|
+
feature.unset("nbic:style", true);
|
|
2380
|
+
} else {
|
|
2381
|
+
feature.set("nbic:style", this.selectedPrevNbicStyle);
|
|
2382
|
+
}
|
|
2383
|
+
feature.setStyle(this.selectedPrevStyle);
|
|
2384
|
+
}
|
|
2385
|
+
/**
|
|
2386
|
+
* If the clicked thing is a cluster output feature, unwrap single-member clusters
|
|
2387
|
+
* and return the inner feature id for selection.
|
|
2388
|
+
*/
|
|
2389
|
+
resolveSelectableIdFromClusterFeature(feature) {
|
|
2390
|
+
const members = feature.get("features");
|
|
2391
|
+
if (!members?.length) return null;
|
|
2392
|
+
if (members.length === 1) {
|
|
2393
|
+
const id = members[0]?.getId();
|
|
2394
|
+
return typeof id === "string" || typeof id === "number" ? id : null;
|
|
2395
|
+
}
|
|
2396
|
+
return null;
|
|
2397
|
+
}
|
|
2398
|
+
// ---- internals ----
|
|
2399
|
+
findFeatureById(registry, layerId, featureId, coordinates) {
|
|
2400
|
+
const src = registry.getVectorSource(layerId);
|
|
2401
|
+
if (!src) return null;
|
|
2402
|
+
if (src instanceof import_Cluster2.default) {
|
|
2403
|
+
const inner = src.getSource();
|
|
2404
|
+
const f2 = inner?.getFeatureById(featureId);
|
|
2405
|
+
return f2 ?? null;
|
|
2406
|
+
}
|
|
2407
|
+
let f = null;
|
|
2408
|
+
if (coordinates) {
|
|
2409
|
+
const features = src.getFeaturesAtCoordinate(coordinates);
|
|
2410
|
+
f = features ? features[0] : null;
|
|
2411
|
+
} else {
|
|
2412
|
+
f = src.getFeatureById(featureId);
|
|
2413
|
+
if (!f) {
|
|
2414
|
+
f = src.getFeatureByUid(featureId.toString());
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
return f ?? null;
|
|
2418
|
+
}
|
|
2419
|
+
};
|
|
2420
|
+
|
|
2421
|
+
// src/core/ol/OlMapEngine.ts
|
|
2422
|
+
function resolveVectorSource(layer) {
|
|
2423
|
+
const src = layer.getSource();
|
|
2424
|
+
if (!src) return null;
|
|
2425
|
+
if (src instanceof import_Cluster3.default) {
|
|
2426
|
+
const inner = src.getSource();
|
|
2427
|
+
return inner ?? null;
|
|
2428
|
+
}
|
|
2429
|
+
return src;
|
|
2430
|
+
}
|
|
2431
|
+
function createOlEngine(events) {
|
|
2432
|
+
let map;
|
|
2433
|
+
let hover = null;
|
|
2434
|
+
const registry = new LayerRegistry();
|
|
2435
|
+
const bases = new BaseLayersController(events);
|
|
2436
|
+
const draw = new DrawController(events);
|
|
2437
|
+
const controls = new ControlsController();
|
|
2438
|
+
const geo = new GeoController(events);
|
|
2439
|
+
const zoom = new ZoomController();
|
|
2440
|
+
const select = new SelectController(events);
|
|
2441
|
+
bases.bindFind((id) => registry.get(id) ?? null);
|
|
2442
|
+
return {
|
|
2443
|
+
async init(init) {
|
|
2444
|
+
const view = new import_ol3.View({
|
|
2445
|
+
projection: init.projection ?? "EPSG:3857",
|
|
2446
|
+
center: init.center ?? [0, 0],
|
|
2447
|
+
zoom: init.zoom ?? 2,
|
|
2448
|
+
minZoom: init.minZoom,
|
|
2449
|
+
maxZoom: init.maxZoom
|
|
2450
|
+
});
|
|
2451
|
+
map = new import_Map.default({
|
|
2452
|
+
target: init.target,
|
|
2453
|
+
view,
|
|
2454
|
+
controls: new import_Collection.default([])
|
|
2455
|
+
});
|
|
2456
|
+
controls.attach(map, events, init.controls);
|
|
2457
|
+
draw.attach(map);
|
|
2458
|
+
const dl = draw.getLayer();
|
|
2459
|
+
if (dl) registry.add("draw-layer", dl);
|
|
2460
|
+
geo.attach(map);
|
|
2461
|
+
zoom.attach(map);
|
|
2462
|
+
select.attach(map);
|
|
2463
|
+
map.on("moveend", () => {
|
|
2464
|
+
if (!map) return;
|
|
2465
|
+
const v = map.getView();
|
|
2466
|
+
const payload = {
|
|
2467
|
+
center: v.getCenter(),
|
|
2468
|
+
zoom: v.getZoom() ?? 0
|
|
2469
|
+
};
|
|
2470
|
+
events.emit("camera:changed", payload);
|
|
2471
|
+
const ex = v.calculateExtent(map.getSize() ?? void 0);
|
|
2472
|
+
events.emit("extent:changed", { extent: ex });
|
|
2473
|
+
});
|
|
2474
|
+
map.on("singleclick", (evt) => {
|
|
2475
|
+
if (!map) return;
|
|
2476
|
+
const features = [];
|
|
2477
|
+
map.forEachFeatureAtPixel(
|
|
2478
|
+
evt.pixel,
|
|
2479
|
+
(f, l) => {
|
|
2480
|
+
if (!f || !isPickableLayer(l)) return void 0;
|
|
2481
|
+
features.push({
|
|
2482
|
+
feature: f,
|
|
2483
|
+
layer: l,
|
|
2484
|
+
featureId: f.getId(),
|
|
2485
|
+
layerId: l.get("id"),
|
|
2486
|
+
properties: f.getProperties()
|
|
2487
|
+
});
|
|
2488
|
+
return void 0;
|
|
2489
|
+
},
|
|
2490
|
+
{ hitTolerance: 5, layerFilter: isPickableLayer }
|
|
2491
|
+
);
|
|
2492
|
+
const clickCoordinate = evt.coordinate;
|
|
2493
|
+
events.emit("pointer:click", features.length ? { features, clickCoordinate } : { clickCoordinate, features: null });
|
|
2494
|
+
});
|
|
2495
|
+
},
|
|
2496
|
+
destroy() {
|
|
2497
|
+
draw.detach();
|
|
2498
|
+
geo.detach();
|
|
2499
|
+
controls.detach();
|
|
2500
|
+
map?.setTarget(void 0);
|
|
2501
|
+
map = void 0;
|
|
2502
|
+
registry.clear();
|
|
2503
|
+
bases.clear();
|
|
2504
|
+
select.detach();
|
|
2505
|
+
},
|
|
2506
|
+
getCamera() {
|
|
2507
|
+
const v = map.getView();
|
|
2508
|
+
return {
|
|
2509
|
+
center: v.getCenter(),
|
|
2510
|
+
zoom: v.getZoom() ?? 0
|
|
2511
|
+
};
|
|
2512
|
+
},
|
|
2513
|
+
getExtent() {
|
|
2514
|
+
const v = map.getView();
|
|
2515
|
+
return v.calculateExtent(map.getSize() ?? void 0);
|
|
2516
|
+
},
|
|
2517
|
+
setCenter(center) {
|
|
2518
|
+
map.getView().setCenter(center);
|
|
2519
|
+
},
|
|
2520
|
+
setZoom(zoom2) {
|
|
2521
|
+
map.getView().setZoom(zoom2);
|
|
2522
|
+
},
|
|
2523
|
+
fitExtent(ext, padding) {
|
|
2524
|
+
map.getView().fit(ext, { padding: [padding, padding, padding, padding] });
|
|
2525
|
+
},
|
|
2526
|
+
// layers
|
|
2527
|
+
listLayerIds: () => registry.listIds(),
|
|
2528
|
+
listBaseLayerIds: () => registry.listIds().filter((id) => {
|
|
2529
|
+
const l = registry.get(id);
|
|
2530
|
+
return !!l && bases.isBase(l);
|
|
2531
|
+
}),
|
|
2532
|
+
listOverlayLayerIds: () => registry.listIds().filter((id) => {
|
|
2533
|
+
const l = registry.get(id);
|
|
2534
|
+
return !!l && !bases.isBase(l);
|
|
2535
|
+
}),
|
|
2536
|
+
getLayerById: (id) => registry.get(id) ?? null,
|
|
2537
|
+
addLayer(def) {
|
|
2538
|
+
const layer = toOlLayer(def);
|
|
2539
|
+
layer.set("id", def.id);
|
|
2540
|
+
layer.set("nbic:pickable", def?.pickable !== false);
|
|
2541
|
+
map.addLayer(layer);
|
|
2542
|
+
registry.add(def.id, layer);
|
|
2543
|
+
if (def.base) {
|
|
2544
|
+
const role = def.base === "super" ? "super" : "regional";
|
|
2545
|
+
bases.registerBase(layer, role, def);
|
|
2546
|
+
} else if (def.zIndexPinned && def.zIndex !== void 0) {
|
|
2547
|
+
layer.setZIndex(def.zIndex);
|
|
2548
|
+
}
|
|
2549
|
+
},
|
|
2550
|
+
removeLayer(id) {
|
|
2551
|
+
const l = registry.get(id);
|
|
2552
|
+
if (!l) return;
|
|
2553
|
+
map.removeLayer(l);
|
|
2554
|
+
registry.remove(id);
|
|
2555
|
+
bases.onRemoved(id);
|
|
2556
|
+
},
|
|
2557
|
+
adoptLayer(id, layer, opts) {
|
|
2558
|
+
if (!map || !layer) return;
|
|
2559
|
+
layer.set("id", id);
|
|
2560
|
+
layer.set("nbic:external", true);
|
|
2561
|
+
layer.set("nbic:role", opts?.role ?? "overlay");
|
|
2562
|
+
layer.set("nbic:pickable", opts?.pickable !== false);
|
|
2563
|
+
map.addLayer(layer);
|
|
2564
|
+
registry.add(id, layer);
|
|
2565
|
+
if (opts?.base) {
|
|
2566
|
+
const role = opts.base === "super" ? "super" : "regional";
|
|
2567
|
+
bases.registerBase(layer, role, { id, base: role });
|
|
2568
|
+
} else if (opts?.zIndex !== void 0) {
|
|
2569
|
+
layer.setZIndex(opts.zIndex);
|
|
2570
|
+
}
|
|
2571
|
+
},
|
|
2572
|
+
ejectLayer(id) {
|
|
2573
|
+
const l = registry.get(id);
|
|
2574
|
+
if (!l) return;
|
|
2575
|
+
map.removeLayer(l);
|
|
2576
|
+
registry.remove(id);
|
|
2577
|
+
bases.onRemoved(id);
|
|
2578
|
+
},
|
|
2579
|
+
getFeatureCount(layerId) {
|
|
2580
|
+
const src = registry.getVectorSource(layerId);
|
|
2581
|
+
if (!src) return 0;
|
|
2582
|
+
const feats = src.getFeatures();
|
|
2583
|
+
if (!feats.length) return 0;
|
|
2584
|
+
if (src instanceof import_Cluster3.default) {
|
|
2585
|
+
const maybeCluster = src.getSource?.();
|
|
2586
|
+
if (maybeCluster && typeof maybeCluster.getFeatures === "function") {
|
|
2587
|
+
return maybeCluster.getFeatures().length;
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
return feats.length;
|
|
2591
|
+
},
|
|
2592
|
+
getFeatures(layerId) {
|
|
2593
|
+
const src = registry.getVectorSource(layerId);
|
|
2594
|
+
if (!src) return [];
|
|
2595
|
+
if (src instanceof import_Cluster3.default) {
|
|
2596
|
+
const maybeCluster = src.getSource?.();
|
|
2597
|
+
if (maybeCluster && typeof maybeCluster.getFeatures === "function") {
|
|
2598
|
+
return maybeCluster.getFeatures();
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
return src.getFeatures();
|
|
2602
|
+
},
|
|
2603
|
+
adoptControl(control, id) {
|
|
2604
|
+
return controls.adopt(control, id);
|
|
2605
|
+
},
|
|
2606
|
+
removeControl(id) {
|
|
2607
|
+
return controls.remove(id);
|
|
2608
|
+
},
|
|
2609
|
+
listCustomControlIds() {
|
|
2610
|
+
return controls.listIds();
|
|
2611
|
+
},
|
|
2612
|
+
getCustomControl(id) {
|
|
2613
|
+
return controls.get(id);
|
|
2614
|
+
},
|
|
2615
|
+
updateGeoJSONLayer(layerId, geojson, opts) {
|
|
2616
|
+
const layer = registry.get(layerId);
|
|
2617
|
+
if (!map || !layer) return false;
|
|
2618
|
+
const viewProj = String(map.getView().getProjection().getCode());
|
|
2619
|
+
const dataProj = opts?.dataProjection ?? layer.get("nbic:dataProjection") ?? viewProj;
|
|
2620
|
+
const fmt2 = new import_GeoJSON5.default();
|
|
2621
|
+
const text = typeof geojson === "string" ? geojson : JSON.stringify(geojson);
|
|
2622
|
+
const incoming = fmt2.readFeatures(text, {
|
|
2623
|
+
dataProjection: dataProj,
|
|
2624
|
+
featureProjection: viewProj
|
|
2625
|
+
});
|
|
2626
|
+
const idProp = opts?.idProperty;
|
|
2627
|
+
if (idProp) {
|
|
2628
|
+
for (const f of incoming) {
|
|
2629
|
+
const id = f.get(idProp);
|
|
2630
|
+
if (id !== void 0) f.setId(id);
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
const currentSrc = layer.getSource();
|
|
2634
|
+
const clusterMeta = layer.get("nbic:cluster");
|
|
2635
|
+
const isClustered = currentSrc instanceof import_Cluster3.default;
|
|
2636
|
+
const curVector = isClustered ? currentSrc.getSource() : currentSrc;
|
|
2637
|
+
const mode = opts?.mode ?? "replace";
|
|
2638
|
+
let nextVector;
|
|
2639
|
+
if (mode === "replace") {
|
|
2640
|
+
nextVector = new import_Vector12.default({ features: incoming });
|
|
2641
|
+
} else {
|
|
2642
|
+
const existing = curVector?.getFeatures() ?? [];
|
|
2643
|
+
const byId = /* @__PURE__ */ new Map();
|
|
2644
|
+
for (const f of existing) {
|
|
2645
|
+
const id = f.getId();
|
|
2646
|
+
if (id !== void 0 && id !== null) byId.set(id, f);
|
|
2647
|
+
}
|
|
2648
|
+
const merged = [];
|
|
2649
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2650
|
+
for (const nf of incoming) {
|
|
2651
|
+
const id = nf.getId();
|
|
2652
|
+
if (id === void 0 || id === null) {
|
|
2653
|
+
merged.push(nf);
|
|
2654
|
+
continue;
|
|
2655
|
+
}
|
|
2656
|
+
seen.add(id);
|
|
2657
|
+
const cur = byId.get(id);
|
|
2658
|
+
if (!cur) {
|
|
2659
|
+
merged.push(nf);
|
|
2660
|
+
} else {
|
|
2661
|
+
const g = nf.getGeometry();
|
|
2662
|
+
if (g) cur.setGeometry(g);
|
|
2663
|
+
const props = nf.getProperties();
|
|
2664
|
+
delete props.geometry;
|
|
2665
|
+
cur.setProperties(props);
|
|
2666
|
+
if (!opts?.keepStyles) {
|
|
2667
|
+
const s = nf.get("nbic:style");
|
|
2668
|
+
if (s) cur.setStyle(cur.getStyle());
|
|
2669
|
+
}
|
|
2670
|
+
merged.push(cur);
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
for (const f of existing) {
|
|
2674
|
+
const id = f.getId();
|
|
2675
|
+
if (id === void 0 || id === null) merged.push(f);
|
|
2676
|
+
}
|
|
2677
|
+
nextVector = new import_Vector12.default({ features: merged });
|
|
2678
|
+
}
|
|
2679
|
+
let nextSource;
|
|
2680
|
+
if (isClustered || clusterMeta) {
|
|
2681
|
+
const distance = currentSrc instanceof import_Cluster3.default ? currentSrc.getDistance?.() ?? clusterMeta?.distance ?? 40 : clusterMeta?.distance ?? 40;
|
|
2682
|
+
const minDistance = currentSrc instanceof import_Cluster3.default ? currentSrc.getMinDistance?.() ?? clusterMeta?.minDistance ?? 0 : clusterMeta?.minDistance ?? 0;
|
|
2683
|
+
nextSource = new import_Cluster3.default({
|
|
2684
|
+
distance,
|
|
2685
|
+
minDistance,
|
|
2686
|
+
source: nextVector
|
|
2687
|
+
});
|
|
2688
|
+
} else {
|
|
2689
|
+
nextSource = nextVector;
|
|
2690
|
+
}
|
|
2691
|
+
layer.setSource(nextSource);
|
|
2692
|
+
return true;
|
|
2693
|
+
},
|
|
2694
|
+
setLayerVisibility(id, visible) {
|
|
2695
|
+
const l = registry.get(id);
|
|
2696
|
+
if (!l) return;
|
|
2697
|
+
if (!bases.isBase(l)) {
|
|
2698
|
+
l.setVisible(visible);
|
|
2699
|
+
return;
|
|
2700
|
+
}
|
|
2701
|
+
bases.setVisibility(l, visible);
|
|
2702
|
+
},
|
|
2703
|
+
setActiveBase(id) {
|
|
2704
|
+
bases.setActiveRegional(id);
|
|
2705
|
+
},
|
|
2706
|
+
reorderLayers(order) {
|
|
2707
|
+
registry.reorder(order, bases);
|
|
2708
|
+
},
|
|
2709
|
+
// hover info
|
|
2710
|
+
activateHoverInfo(options) {
|
|
2711
|
+
if (!map) return;
|
|
2712
|
+
if (!hover) hover = new HoverInfoController(map, events);
|
|
2713
|
+
hover.activate(options);
|
|
2714
|
+
},
|
|
2715
|
+
deactivateHoverInfo() {
|
|
2716
|
+
hover?.deactivate();
|
|
2717
|
+
},
|
|
2718
|
+
// picking
|
|
2719
|
+
pickAt(pixel) {
|
|
2720
|
+
if (!map) return null;
|
|
2721
|
+
const coordinate = map.getCoordinateFromPixel(pixel);
|
|
2722
|
+
if (!coordinate) return null;
|
|
2723
|
+
return map.forEachFeatureAtPixel(
|
|
2724
|
+
pixel,
|
|
2725
|
+
(feature, layer) => {
|
|
2726
|
+
if (!isPickableLayer(layer)) return void 0;
|
|
2727
|
+
return {
|
|
2728
|
+
layerId: layer.get("id"),
|
|
2729
|
+
featureId: feature.getId(),
|
|
2730
|
+
properties: feature.getProperties(),
|
|
2731
|
+
coordinate
|
|
2732
|
+
};
|
|
2733
|
+
},
|
|
2734
|
+
{ hitTolerance: 5, layerFilter: isPickableLayer }
|
|
2735
|
+
) ?? null;
|
|
2736
|
+
},
|
|
2737
|
+
// drawing
|
|
2738
|
+
startDrawing: (opts) => draw.start(opts),
|
|
2739
|
+
stopDrawing: () => draw.stop(),
|
|
2740
|
+
enableDrawEditing: (opts) => draw.enableEditing(opts),
|
|
2741
|
+
disableDrawEditing: () => draw.disableEditing(),
|
|
2742
|
+
undoLastPoint: () => draw.undoLastPoint(),
|
|
2743
|
+
undoEdit: () => draw.undoEdit(),
|
|
2744
|
+
finishCurrent: () => draw.finishCurrent(),
|
|
2745
|
+
abortCurrent: () => draw.abortCurrent(),
|
|
2746
|
+
clearDrawn: () => draw.clear(),
|
|
2747
|
+
exportDrawnGeoJSON: (opts) => draw.exportGeoJSON(map, opts),
|
|
2748
|
+
importDrawnGeoJSON: (geojson, opts) => draw.importGeoJSON(map, geojson, opts),
|
|
2749
|
+
setDrawnFeatureStyle: (feature, style) => draw.setFeatureStyle(feature, style),
|
|
2750
|
+
clearDrawnFeatureStyle: (feature) => draw.clearFeatureStyle(feature),
|
|
2751
|
+
setFeatureStyle(layerId, featureId, style) {
|
|
2752
|
+
const layer = registry.get(layerId);
|
|
2753
|
+
if (!layer) return false;
|
|
2754
|
+
const src = resolveVectorSource(layer);
|
|
2755
|
+
if (!src) return false;
|
|
2756
|
+
const feat = src.getFeatureById(featureId);
|
|
2757
|
+
if (!feat) return false;
|
|
2758
|
+
feat.set("nbic:style", style);
|
|
2759
|
+
feat.setStyle(makeDrawStyle(style));
|
|
2760
|
+
return true;
|
|
2761
|
+
},
|
|
2762
|
+
clearFeatureStyle(layerId, featureId) {
|
|
2763
|
+
const layer = registry.get(layerId);
|
|
2764
|
+
if (!layer) return false;
|
|
2765
|
+
const src = resolveVectorSource(layer);
|
|
2766
|
+
if (!src) return false;
|
|
2767
|
+
const feat = src.getFeatureById(featureId);
|
|
2768
|
+
if (!feat) return false;
|
|
2769
|
+
feat.unset("nbic:style", true);
|
|
2770
|
+
feat.setStyle(void 0);
|
|
2771
|
+
return true;
|
|
2772
|
+
},
|
|
2773
|
+
// geometry analysis
|
|
2774
|
+
analyzeSelfIntersections: async (feature) => {
|
|
2775
|
+
if (!map) return { valid: true };
|
|
2776
|
+
const { checkSelfIntersections: checkSelfIntersections2 } = await Promise.resolve().then(() => (init_geometry_analysis(), geometry_analysis_exports));
|
|
2777
|
+
return checkSelfIntersections2(map, feature);
|
|
2778
|
+
},
|
|
2779
|
+
// full-screen
|
|
2780
|
+
enterFullScreen() {
|
|
2781
|
+
if (!map) return;
|
|
2782
|
+
const el = map.getTargetElement();
|
|
2783
|
+
if (el && !document.fullscreenElement) {
|
|
2784
|
+
void el.requestFullscreen().catch(() => {
|
|
2785
|
+
});
|
|
2786
|
+
}
|
|
2787
|
+
},
|
|
2788
|
+
leaveFullScreen() {
|
|
2789
|
+
if (document.fullscreenElement) {
|
|
2790
|
+
void document.exitFullscreen().catch(() => {
|
|
2791
|
+
});
|
|
2792
|
+
}
|
|
2793
|
+
},
|
|
2794
|
+
// controls
|
|
2795
|
+
showScaleLine: () => controls.ensureScaleLine(map, events),
|
|
2796
|
+
hideScaleLine: () => controls.removeScaleLine(map, events),
|
|
2797
|
+
showZoomControl: () => controls.ensureZoom(map, events),
|
|
2798
|
+
hideZoomControl: () => controls.removeZoom(map, events),
|
|
2799
|
+
showAttribution: () => controls.ensureAttribution(map, events),
|
|
2800
|
+
hideAttribution: () => controls.removeAttribution(map, events),
|
|
2801
|
+
// geolocation
|
|
2802
|
+
activateGeolocation: (follow) => geo.activate(follow),
|
|
2803
|
+
deactivateGeolocation: () => geo.deactivate(),
|
|
2804
|
+
zoomToGeolocation: (maxZoom) => geo.zoomToGeolocation(maxZoom),
|
|
2805
|
+
// vector layers
|
|
2806
|
+
getVectorLayerSource: (layerId) => registry.getVectorSource(layerId),
|
|
2807
|
+
addPoint(layerId, coordinate, props, style, opts) {
|
|
2808
|
+
const src = registry.getVectorSource(layerId);
|
|
2809
|
+
if (!map || !src) return false;
|
|
2810
|
+
const viewCoord = toViewCoord(map, coordinate, opts?.dataProjection);
|
|
2811
|
+
const f = draw.createPointFeature(viewCoord, props, style);
|
|
2812
|
+
src.addFeature(f);
|
|
2813
|
+
return true;
|
|
2814
|
+
},
|
|
2815
|
+
removeAllFromLayer(layerId) {
|
|
2816
|
+
const src = registry.getVectorSource(layerId);
|
|
2817
|
+
if (!src) return false;
|
|
2818
|
+
src.clear(true);
|
|
2819
|
+
return true;
|
|
2820
|
+
},
|
|
2821
|
+
// Transform coordinates
|
|
2822
|
+
transformCoordsFrom: (coord, from, to) => {
|
|
2823
|
+
return transformCoordsFrom(coord, from, to);
|
|
2824
|
+
},
|
|
2825
|
+
transformCoordsArrayFrom: (coords, from, to) => {
|
|
2826
|
+
return transformCoordsArrayFrom(coords, from, to);
|
|
2827
|
+
},
|
|
2828
|
+
transformExtentFrom: (extent, from, to) => {
|
|
2829
|
+
return transformExtentFrom(extent, from, to);
|
|
2830
|
+
},
|
|
2831
|
+
// Zoom
|
|
2832
|
+
zoomToFeature: (layerId, featureId, opts) => zoom.zoomToFeatureById(registry, layerId, featureId, opts),
|
|
2833
|
+
zoomToLayer: (layerId, opts) => zoom.zoomToLayerById(registry, layerId, opts),
|
|
2834
|
+
zoomToExtent: (extent, opts) => zoom.zoomToExtent(extent, opts),
|
|
2835
|
+
fitGeometry: (geom, opts) => zoom.fitGeometry(geom, opts),
|
|
2836
|
+
// Select
|
|
2837
|
+
selectFeature: (layerId, featureId, style, coordinates) => select.selectById(registry, layerId, featureId, style, coordinates),
|
|
2838
|
+
clearSelection: () => select.clear(registry),
|
|
2839
|
+
getSelection: () => select.getSelection(),
|
|
2840
|
+
// Utils
|
|
2841
|
+
getTargetElement: () => {
|
|
2842
|
+
return map?.getTargetElement() ?? null;
|
|
2843
|
+
},
|
|
2844
|
+
getCenterFromExtent: (extent) => {
|
|
2845
|
+
return (0, import_extent6.getCenter)(extent);
|
|
2846
|
+
}
|
|
2847
|
+
};
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
// src/api/MapAPI.ts
|
|
2851
|
+
var MapAPI = class {
|
|
2852
|
+
engine;
|
|
2853
|
+
events;
|
|
2854
|
+
layerDefs = /* @__PURE__ */ new Map();
|
|
2855
|
+
constructor(init, engine) {
|
|
2856
|
+
this.events = createEmitter();
|
|
2857
|
+
this.engine = engine ?? createOlEngine(this.events);
|
|
2858
|
+
this.engine.init(init).then(() => {
|
|
2859
|
+
this.events.emit("ready", void 0);
|
|
2860
|
+
}).catch((err) => {
|
|
2861
|
+
this.events.emit("error", { scope: "init", message: String(err), detail: err });
|
|
2862
|
+
});
|
|
2863
|
+
}
|
|
2864
|
+
// Explicitly typed pass-throughs (avoid binding issues and keep types)
|
|
2865
|
+
on(type, cb) {
|
|
2866
|
+
return this.events.on(type, cb);
|
|
2867
|
+
}
|
|
2868
|
+
off(type, cb) {
|
|
2869
|
+
return this.events.off(type, cb);
|
|
2870
|
+
}
|
|
2871
|
+
once(type, cb) {
|
|
2872
|
+
return this.events.once(type, cb);
|
|
2873
|
+
}
|
|
2874
|
+
destroy() {
|
|
2875
|
+
this.engine.destroy();
|
|
2876
|
+
this.events.emit("destroy", void 0);
|
|
2877
|
+
}
|
|
2878
|
+
getCamera() {
|
|
2879
|
+
return this.engine.getCamera();
|
|
2880
|
+
}
|
|
2881
|
+
getExtent() {
|
|
2882
|
+
return this.engine.getExtent();
|
|
2883
|
+
}
|
|
2884
|
+
/** Return all layers marked as base (in insertion order). */
|
|
2885
|
+
getBaseLayerIds() {
|
|
2886
|
+
return this.engine.listBaseLayerIds();
|
|
2887
|
+
}
|
|
2888
|
+
getOverlayLayerIds() {
|
|
2889
|
+
return this.engine.listOverlayLayerIds();
|
|
2890
|
+
}
|
|
2891
|
+
/** If you still want full LayerDef[] for config-only layers, keep these: */
|
|
2892
|
+
getBaseLayers() {
|
|
2893
|
+
return Array.from(this.layerDefs.values()).filter((l) => !!l.base);
|
|
2894
|
+
}
|
|
2895
|
+
getOverlayLayers() {
|
|
2896
|
+
return Array.from(this.layerDefs.values()).filter((l) => !l.base);
|
|
2897
|
+
}
|
|
2898
|
+
/** Or add engine-backed variants that return live OL layers: */
|
|
2899
|
+
getLayerById(id) {
|
|
2900
|
+
return this.engine.getLayerById(id);
|
|
2901
|
+
}
|
|
2902
|
+
adoptControl(control, id) {
|
|
2903
|
+
return this.engine.adoptControl(control, id);
|
|
2904
|
+
}
|
|
2905
|
+
removeControl(id) {
|
|
2906
|
+
return this.engine.removeControl(id);
|
|
2907
|
+
}
|
|
2908
|
+
listCustomControlIds() {
|
|
2909
|
+
return this.engine.listCustomControlIds();
|
|
2910
|
+
}
|
|
2911
|
+
getCustomControl(id) {
|
|
2912
|
+
return this.engine.getCustomControl(id);
|
|
2913
|
+
}
|
|
2914
|
+
setCenter(center) {
|
|
2915
|
+
this.engine.setCenter(center);
|
|
2916
|
+
}
|
|
2917
|
+
setZoom(zoom) {
|
|
2918
|
+
this.engine.setZoom(zoom);
|
|
2919
|
+
}
|
|
2920
|
+
fitExtent(extent, padding = 16) {
|
|
2921
|
+
this.engine.fitExtent(extent, padding);
|
|
2922
|
+
}
|
|
2923
|
+
addLayer(layer) {
|
|
2924
|
+
this.layerDefs.set(layer.id, layer);
|
|
2925
|
+
this.engine.addLayer(layer);
|
|
2926
|
+
this.events.emit("layer:added", { layerId: layer.id });
|
|
2927
|
+
}
|
|
2928
|
+
removeLayer(layerId) {
|
|
2929
|
+
this.layerDefs.delete(layerId);
|
|
2930
|
+
this.engine.removeLayer(layerId);
|
|
2931
|
+
this.events.emit("layer:removed", { layerId });
|
|
2932
|
+
}
|
|
2933
|
+
updateGeoJSONLayer(layerId, geojson, opts) {
|
|
2934
|
+
this.engine.updateGeoJSONLayer(layerId, geojson, opts);
|
|
2935
|
+
}
|
|
2936
|
+
setLayerVisibility(layerId, visible) {
|
|
2937
|
+
this.engine.setLayerVisibility(layerId, visible);
|
|
2938
|
+
}
|
|
2939
|
+
reorderLayers(order) {
|
|
2940
|
+
this.engine.reorderLayers(order);
|
|
2941
|
+
}
|
|
2942
|
+
addPoint(layerId, coord, properties, style, opts) {
|
|
2943
|
+
return this.engine.addPoint(layerId, coord, properties, style, opts);
|
|
2944
|
+
}
|
|
2945
|
+
removeAllFromLayer(layerId) {
|
|
2946
|
+
return this.engine.removeAllFromLayer(layerId);
|
|
2947
|
+
}
|
|
2948
|
+
adoptLayer(id, layer, opts) {
|
|
2949
|
+
this.engine.adoptLayer(id, layer, opts);
|
|
2950
|
+
}
|
|
2951
|
+
ejectLayer(id) {
|
|
2952
|
+
this.engine.ejectLayer(id);
|
|
2953
|
+
this.layerDefs.delete(id);
|
|
2954
|
+
this.events.emit("layer:removed", { layerId: id });
|
|
2955
|
+
}
|
|
2956
|
+
getFeatureCount(layerId) {
|
|
2957
|
+
return this.engine.getFeatureCount(layerId);
|
|
2958
|
+
}
|
|
2959
|
+
getFeatures(layerId) {
|
|
2960
|
+
return this.engine.getFeatures(layerId);
|
|
2961
|
+
}
|
|
2962
|
+
activateHoverInfo(options) {
|
|
2963
|
+
this.engine.activateHoverInfo(options);
|
|
2964
|
+
}
|
|
2965
|
+
deactivateHoverInfo() {
|
|
2966
|
+
this.engine.deactivateHoverInfo();
|
|
2967
|
+
}
|
|
2968
|
+
pickAt(pixel) {
|
|
2969
|
+
return this.engine.pickAt(pixel);
|
|
2970
|
+
}
|
|
2971
|
+
startDrawing(opts) {
|
|
2972
|
+
this.engine.startDrawing(opts);
|
|
2973
|
+
}
|
|
2974
|
+
stopDrawing() {
|
|
2975
|
+
this.engine.stopDrawing();
|
|
2976
|
+
}
|
|
2977
|
+
enableDrawEditing(opts) {
|
|
2978
|
+
this.engine.enableDrawEditing(opts);
|
|
2979
|
+
}
|
|
2980
|
+
disableDrawEditing() {
|
|
2981
|
+
this.engine.disableDrawEditing();
|
|
2982
|
+
}
|
|
2983
|
+
setDrawnFeatureStyle(feature, style) {
|
|
2984
|
+
return this.engine.setDrawnFeatureStyle(feature, style);
|
|
2985
|
+
}
|
|
2986
|
+
clearDrawnFeatureStyle(feature) {
|
|
2987
|
+
return this.engine.clearDrawnFeatureStyle(feature);
|
|
2988
|
+
}
|
|
2989
|
+
setFeatureStyle(layerId, featureId, style) {
|
|
2990
|
+
return this.engine.setFeatureStyle(layerId, featureId, style);
|
|
2991
|
+
}
|
|
2992
|
+
clearFeatureStyle(layerId, featureId) {
|
|
2993
|
+
return this.engine.clearFeatureStyle(layerId, featureId);
|
|
2994
|
+
}
|
|
2995
|
+
undoLastPoint() {
|
|
2996
|
+
this.engine.undoLastPoint();
|
|
2997
|
+
}
|
|
2998
|
+
undoEdit() {
|
|
2999
|
+
this.engine.undoEdit();
|
|
3000
|
+
}
|
|
3001
|
+
finishCurrent() {
|
|
3002
|
+
this.engine.finishCurrent();
|
|
3003
|
+
}
|
|
3004
|
+
abortCurrent() {
|
|
3005
|
+
this.engine.abortCurrent();
|
|
3006
|
+
}
|
|
3007
|
+
clearDrawn() {
|
|
3008
|
+
this.engine.clearDrawn();
|
|
3009
|
+
}
|
|
3010
|
+
exportDrawnGeoJSON(opts) {
|
|
3011
|
+
return this.engine.exportDrawnGeoJSON(opts);
|
|
3012
|
+
}
|
|
3013
|
+
importDrawnGeoJSON(text, opts) {
|
|
3014
|
+
this.engine.importDrawnGeoJSON(text, opts);
|
|
3015
|
+
}
|
|
3016
|
+
// Geometry analysis
|
|
3017
|
+
analyzeSelfIntersections(feature) {
|
|
3018
|
+
return this.engine.analyzeSelfIntersections(feature);
|
|
3019
|
+
}
|
|
3020
|
+
enterFullScreen() {
|
|
3021
|
+
this.engine.enterFullScreen();
|
|
3022
|
+
}
|
|
3023
|
+
leaveFullScreen() {
|
|
3024
|
+
this.engine.leaveFullScreen();
|
|
3025
|
+
}
|
|
3026
|
+
showScaleLine() {
|
|
3027
|
+
this.engine.showScaleLine();
|
|
3028
|
+
}
|
|
3029
|
+
hideScaleLine() {
|
|
3030
|
+
this.engine.hideScaleLine();
|
|
3031
|
+
}
|
|
3032
|
+
showZoomControl() {
|
|
3033
|
+
this.engine.showZoomControl();
|
|
3034
|
+
}
|
|
3035
|
+
hideZoomControl() {
|
|
3036
|
+
this.engine.hideZoomControl();
|
|
3037
|
+
}
|
|
3038
|
+
showAttribution() {
|
|
3039
|
+
this.engine.showAttribution();
|
|
3040
|
+
}
|
|
3041
|
+
hideAttribution() {
|
|
3042
|
+
this.engine.hideAttribution();
|
|
3043
|
+
}
|
|
3044
|
+
activateGeolocation(follow) {
|
|
3045
|
+
this.engine.activateGeolocation(follow);
|
|
3046
|
+
}
|
|
3047
|
+
deactivateGeolocation() {
|
|
3048
|
+
this.engine.deactivateGeolocation();
|
|
3049
|
+
}
|
|
3050
|
+
zoomToGeolocation(maxZoom) {
|
|
3051
|
+
return this.engine.zoomToGeolocation(maxZoom);
|
|
3052
|
+
}
|
|
3053
|
+
// Transform coordinates
|
|
3054
|
+
transformCoordsFrom(coord, from, to) {
|
|
3055
|
+
return this.engine.transformCoordsFrom(coord, from, to);
|
|
3056
|
+
}
|
|
3057
|
+
transformCoordsArrayFrom(coords, from, to) {
|
|
3058
|
+
return this.engine.transformCoordsArrayFrom(coords, from, to);
|
|
3059
|
+
}
|
|
3060
|
+
transformExtentFrom(extent, from, to) {
|
|
3061
|
+
return this.engine.transformExtentFrom(extent, from, to);
|
|
3062
|
+
}
|
|
3063
|
+
// Zoom
|
|
3064
|
+
zoomToFeature(layerId, featureId, opts) {
|
|
3065
|
+
return this.engine.zoomToFeature(layerId, featureId, opts);
|
|
3066
|
+
}
|
|
3067
|
+
zoomToLayer(layerId, opts) {
|
|
3068
|
+
return this.engine.zoomToLayer(layerId, opts);
|
|
3069
|
+
}
|
|
3070
|
+
zoomToExtent(extent, opts) {
|
|
3071
|
+
return this.engine.zoomToExtent(extent, opts);
|
|
3072
|
+
}
|
|
3073
|
+
fitGeometry(geom, opts) {
|
|
3074
|
+
return this.engine.fitGeometry(geom, opts);
|
|
3075
|
+
}
|
|
3076
|
+
// Select
|
|
3077
|
+
selectFeature(layerId, featureId, style, coordinates) {
|
|
3078
|
+
return this.engine.selectFeature(layerId, featureId, style, coordinates);
|
|
3079
|
+
}
|
|
3080
|
+
clearSelection() {
|
|
3081
|
+
this.engine.clearSelection();
|
|
3082
|
+
}
|
|
3083
|
+
getSelection() {
|
|
3084
|
+
return this.engine.getSelection();
|
|
3085
|
+
}
|
|
3086
|
+
// Utils
|
|
3087
|
+
getTargetElement() {
|
|
3088
|
+
return this.engine.getTargetElement();
|
|
3089
|
+
}
|
|
3090
|
+
getCenterFromExtent(extent) {
|
|
3091
|
+
return this.engine.getCenterFromExtent(extent);
|
|
3092
|
+
}
|
|
3093
|
+
};
|
|
3094
|
+
|
|
3095
|
+
// src/api/config.schema.ts
|
|
3096
|
+
var import_zod = require("zod");
|
|
3097
|
+
var SourceRefSchema = import_zod.z.object({ ref: import_zod.z.string().min(1) });
|
|
3098
|
+
var InlineSourceSchema = import_zod.z.object({
|
|
3099
|
+
type: import_zod.z.enum(["wmts", "wfs", "osm", "xyz", "geojson"]),
|
|
3100
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
3101
|
+
});
|
|
3102
|
+
var MapConfigSchema = import_zod.z.object({
|
|
3103
|
+
version: import_zod.z.literal(1),
|
|
3104
|
+
id: import_zod.z.string().min(1),
|
|
3105
|
+
projection: import_zod.z.string().default("EPSG:3857"),
|
|
3106
|
+
center: import_zod.z.tuple([import_zod.z.number(), import_zod.z.number()]),
|
|
3107
|
+
zoom: import_zod.z.number(),
|
|
3108
|
+
minZoom: import_zod.z.number().optional(),
|
|
3109
|
+
maxZoom: import_zod.z.number(),
|
|
3110
|
+
size: import_zod.z.enum(["Large", "Small"]).optional(),
|
|
3111
|
+
tabIndex: import_zod.z.number().int().optional(),
|
|
3112
|
+
controls: import_zod.z.object({
|
|
3113
|
+
zoom: import_zod.z.boolean().optional(),
|
|
3114
|
+
scaleBar: import_zod.z.boolean().optional(),
|
|
3115
|
+
scaleLine: import_zod.z.boolean().optional(),
|
|
3116
|
+
geolocation: import_zod.z.boolean().optional(),
|
|
3117
|
+
attribution: import_zod.z.boolean().optional(),
|
|
3118
|
+
rotate: import_zod.z.boolean().optional(),
|
|
3119
|
+
fullscreen: import_zod.z.boolean().optional(),
|
|
3120
|
+
mousePosition: import_zod.z.boolean().optional(),
|
|
3121
|
+
draw: import_zod.z.object({
|
|
3122
|
+
enabled: import_zod.z.boolean(),
|
|
3123
|
+
polygonType: import_zod.z.enum(["box", "custom"]).optional()
|
|
3124
|
+
}).optional()
|
|
3125
|
+
}).default({}),
|
|
3126
|
+
wmts: import_zod.z.array(import_zod.z.object({
|
|
3127
|
+
id: import_zod.z.string(),
|
|
3128
|
+
title: import_zod.z.string().optional(),
|
|
3129
|
+
url: import_zod.z.string().url(),
|
|
3130
|
+
layer: import_zod.z.string(),
|
|
3131
|
+
matrixSet: import_zod.z.string(),
|
|
3132
|
+
format: import_zod.z.string().optional(),
|
|
3133
|
+
style: import_zod.z.string().optional(),
|
|
3134
|
+
tileSize: import_zod.z.number().optional(),
|
|
3135
|
+
origin: import_zod.z.tuple([import_zod.z.number(), import_zod.z.number()]).optional(),
|
|
3136
|
+
resolutions: import_zod.z.array(import_zod.z.number()).optional()
|
|
3137
|
+
})).optional(),
|
|
3138
|
+
wfs: import_zod.z.array(import_zod.z.object({
|
|
3139
|
+
id: import_zod.z.string(),
|
|
3140
|
+
title: import_zod.z.string().optional(),
|
|
3141
|
+
url: import_zod.z.string().url(),
|
|
3142
|
+
typeName: import_zod.z.string(),
|
|
3143
|
+
srsName: import_zod.z.string().optional(),
|
|
3144
|
+
outputFormat: import_zod.z.string().optional(),
|
|
3145
|
+
strategy: import_zod.z.enum(["all", "bbox"]).default("bbox").optional(),
|
|
3146
|
+
maxFeatures: import_zod.z.number().int().optional(),
|
|
3147
|
+
styleId: import_zod.z.string().optional()
|
|
3148
|
+
})).optional(),
|
|
3149
|
+
layers: import_zod.z.array(import_zod.z.object({
|
|
3150
|
+
id: import_zod.z.string(),
|
|
3151
|
+
kind: import_zod.z.enum(["tile", "vector", "raster"]),
|
|
3152
|
+
source: import_zod.z.union([SourceRefSchema, InlineSourceSchema]),
|
|
3153
|
+
// ← key bit
|
|
3154
|
+
style: import_zod.z.union([
|
|
3155
|
+
import_zod.z.string(),
|
|
3156
|
+
// style ref id
|
|
3157
|
+
import_zod.z.object({ type: import_zod.z.string(), options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional() })
|
|
3158
|
+
]).optional(),
|
|
3159
|
+
visible: import_zod.z.boolean().optional(),
|
|
3160
|
+
zIndex: import_zod.z.number().optional(),
|
|
3161
|
+
base: import_zod.z.boolean().optional()
|
|
3162
|
+
})).optional()
|
|
3163
|
+
}).refine((v) => v.minZoom === void 0 || v.minZoom <= v.maxZoom, {
|
|
3164
|
+
path: ["minZoom"],
|
|
3165
|
+
message: "minZoom must be \u2264 maxZoom"
|
|
3166
|
+
});
|
|
3167
|
+
|
|
3168
|
+
// src/core/registry/SourceRegistry.ts
|
|
3169
|
+
var SourceRegistry = class {
|
|
3170
|
+
static registry = /* @__PURE__ */ new Map();
|
|
3171
|
+
static register(id, factory) {
|
|
3172
|
+
this.registry.set(id, factory);
|
|
3173
|
+
}
|
|
3174
|
+
static get(id) {
|
|
3175
|
+
const factory = this.registry.get(id);
|
|
3176
|
+
if (!factory) {
|
|
3177
|
+
throw new Error(`Source '${id}' not found in SourceRegistry`);
|
|
3178
|
+
}
|
|
3179
|
+
return factory();
|
|
3180
|
+
}
|
|
3181
|
+
static clear() {
|
|
3182
|
+
this.registry.clear();
|
|
3183
|
+
}
|
|
3184
|
+
};
|
|
3185
|
+
|
|
3186
|
+
// src/api/config-loader.ts
|
|
3187
|
+
function initMapFromConfig(map, rawJson) {
|
|
3188
|
+
const cfg = MapConfigSchema.parse(rawJson);
|
|
3189
|
+
cfg.wmts?.forEach(
|
|
3190
|
+
(w) => SourceRegistry.register(w.id, () => ({ type: "wmts", options: w }))
|
|
3191
|
+
);
|
|
3192
|
+
cfg.wfs?.forEach(
|
|
3193
|
+
(w) => SourceRegistry.register(w.id, () => ({ type: "wfs", options: w }))
|
|
3194
|
+
);
|
|
3195
|
+
cfg.layers?.forEach((layer) => {
|
|
3196
|
+
const source = "ref" in layer.source ? { ref: layer.source.ref } : (() => {
|
|
3197
|
+
switch (layer.source.type) {
|
|
3198
|
+
case "osm":
|
|
3199
|
+
return { type: "osm" };
|
|
3200
|
+
case "xyz":
|
|
3201
|
+
return { type: "xyz", options: layer.source.options ?? {} };
|
|
3202
|
+
case "wmts":
|
|
3203
|
+
return { type: "wmts", options: layer.source.options ?? {} };
|
|
3204
|
+
case "wfs":
|
|
3205
|
+
return { type: "wfs", options: layer.source.options ?? {} };
|
|
3206
|
+
case "geojson":
|
|
3207
|
+
return { type: "geojson", options: layer.source.options ?? {} };
|
|
3208
|
+
default: {
|
|
3209
|
+
const src = layer.source;
|
|
3210
|
+
const type = typeof src === "object" && src !== null && "type" in src ? src.type : "undefined";
|
|
3211
|
+
throw new Error(`Unknown source type: ${type}`);
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
})();
|
|
3215
|
+
const style = typeof layer.style === "string" ? { ref: layer.style } : layer.style;
|
|
3216
|
+
const ld = {
|
|
3217
|
+
id: layer.id,
|
|
3218
|
+
kind: layer.kind,
|
|
3219
|
+
source,
|
|
3220
|
+
style,
|
|
3221
|
+
visible: layer.visible ?? true,
|
|
3222
|
+
zIndex: layer.zIndex,
|
|
3223
|
+
base: layer.base ?? false
|
|
3224
|
+
};
|
|
3225
|
+
map.addLayer(ld);
|
|
3226
|
+
});
|
|
3227
|
+
}
|
|
3228
|
+
|
|
3229
|
+
// src/factory.ts
|
|
3230
|
+
function createMap(target, rawConfig) {
|
|
3231
|
+
const cfg = MapConfigSchema.parse(rawConfig);
|
|
3232
|
+
const map = new MapAPI({
|
|
3233
|
+
target,
|
|
3234
|
+
projection: cfg.projection,
|
|
3235
|
+
center: cfg.center,
|
|
3236
|
+
zoom: cfg.zoom,
|
|
3237
|
+
minZoom: cfg.minZoom,
|
|
3238
|
+
maxZoom: cfg.maxZoom,
|
|
3239
|
+
controls: cfg.controls
|
|
3240
|
+
});
|
|
3241
|
+
initMapFromConfig(map, cfg);
|
|
3242
|
+
return map;
|
|
3243
|
+
}
|
|
3244
|
+
|
|
3245
|
+
// src/core/geojson/index.ts
|
|
3246
|
+
var geojson_exports = {};
|
|
3247
|
+
__export(geojson_exports, {
|
|
3248
|
+
sumProperty: () => sumProperty,
|
|
3249
|
+
toFeature: () => toFeature,
|
|
3250
|
+
toFeatureCollection: () => toFeatureCollection
|
|
3251
|
+
});
|
|
3252
|
+
|
|
3253
|
+
// src/core/geojson/builders.ts
|
|
3254
|
+
function isFiniteNumber(n) {
|
|
3255
|
+
return typeof n === "number" && Number.isFinite(n);
|
|
3256
|
+
}
|
|
3257
|
+
function validLonLat(lon, lat) {
|
|
3258
|
+
return isFiniteNumber(lon) && isFiniteNumber(lat) && lon >= -180 && lon <= 180 && lat >= -90 && lat <= 90;
|
|
3259
|
+
}
|
|
3260
|
+
function toFeature(item, index, opts) {
|
|
3261
|
+
const skipInvalid = opts.skipInvalid ?? true;
|
|
3262
|
+
let geometry = null;
|
|
3263
|
+
if (opts.kind === "Point") {
|
|
3264
|
+
const p = opts.getPoint(item, index);
|
|
3265
|
+
if (!p || !validLonLat(p.lon, p.lat)) {
|
|
3266
|
+
if (skipInvalid) return null;
|
|
3267
|
+
geometry = { type: "Point", coordinates: [0, 0] };
|
|
3268
|
+
} else {
|
|
3269
|
+
geometry = { type: "Point", coordinates: [p.lon, p.lat] };
|
|
3270
|
+
}
|
|
3271
|
+
} else if (opts.kind === "LineString") {
|
|
3272
|
+
const coords = opts.getLine(item, index);
|
|
3273
|
+
if (!coords || coords.length < 2) {
|
|
3274
|
+
if (skipInvalid) return null;
|
|
3275
|
+
geometry = { type: "LineString", coordinates: [] };
|
|
3276
|
+
} else {
|
|
3277
|
+
geometry = { type: "LineString", coordinates: coords };
|
|
3278
|
+
}
|
|
3279
|
+
} else {
|
|
3280
|
+
const rings = opts.getPolygon(item, index);
|
|
3281
|
+
if (!rings || !rings.length || !rings[0]?.length) {
|
|
3282
|
+
if (skipInvalid) return null;
|
|
3283
|
+
geometry = { type: "Polygon", coordinates: [] };
|
|
3284
|
+
} else {
|
|
3285
|
+
geometry = { type: "Polygon", coordinates: rings };
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
const properties = opts.props?.(item, index) ?? {};
|
|
3289
|
+
const f = { type: "Feature", properties, geometry };
|
|
3290
|
+
const id = opts.id?.(item, index);
|
|
3291
|
+
if (id !== void 0) f.id = id;
|
|
3292
|
+
return f;
|
|
3293
|
+
}
|
|
3294
|
+
function toFeatureCollection(items, opts) {
|
|
3295
|
+
const filterNull = opts.filterNull ?? true;
|
|
3296
|
+
const features = items.map((item, i) => toFeature(item, i, opts)).filter((f) => filterNull ? !!f : true);
|
|
3297
|
+
return { type: "FeatureCollection", features };
|
|
3298
|
+
}
|
|
3299
|
+
function sumProperty(features, field = "count") {
|
|
3300
|
+
let total = 0;
|
|
3301
|
+
for (const f of features) {
|
|
3302
|
+
const v = f.properties?.[field];
|
|
3303
|
+
const n = Number(v);
|
|
3304
|
+
total += Number.isFinite(n) ? n : 1;
|
|
3305
|
+
}
|
|
3306
|
+
return total;
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3309
|
+
// src/core/ol/utils/index.ts
|
|
3310
|
+
var utils_exports = {};
|
|
3311
|
+
__export(utils_exports, {
|
|
3312
|
+
areaFromLonLatRing: () => areaFromLonLatRing,
|
|
3313
|
+
areaToString: () => areaToString,
|
|
3314
|
+
ensureClosedRing: () => ensureClosedRing,
|
|
3315
|
+
getCenterFromExtent: () => getCenterFromExtent,
|
|
3316
|
+
getCenterFromPolygon: () => getCenterFromPolygon,
|
|
3317
|
+
getCentroid: () => getCentroid,
|
|
3318
|
+
getExtentFromPolygon: () => getExtentFromPolygon,
|
|
3319
|
+
isExtentInside: () => isExtentInside,
|
|
3320
|
+
isPointInsideExtent: () => isPointInsideExtent,
|
|
3321
|
+
toDataCoord: () => toDataCoord,
|
|
3322
|
+
toViewCoord: () => toViewCoord,
|
|
3323
|
+
transformCoordsArrayFrom: () => transformCoordsArrayFrom,
|
|
3324
|
+
transformCoordsFrom: () => transformCoordsFrom,
|
|
3325
|
+
transformExtentFrom: () => transformExtentFrom
|
|
3326
|
+
});
|
|
3327
|
+
|
|
3328
|
+
// src/core/ol/utils/area.ts
|
|
3329
|
+
var import_area = __toESM(require("@turf/area"), 1);
|
|
3330
|
+
var import_ol4 = require("ol");
|
|
3331
|
+
var import_geom3 = require("ol/geom");
|
|
3332
|
+
var import_extent7 = require("ol/extent");
|
|
3333
|
+
function ensureClosedRing(ring) {
|
|
3334
|
+
const first = ring[0];
|
|
3335
|
+
const last = ring[ring.length - 1];
|
|
3336
|
+
const isClosed = first[0] === last[0] && first[1] === last[1];
|
|
3337
|
+
return isClosed ? ring : [...ring, first];
|
|
3338
|
+
}
|
|
3339
|
+
function areaFromLonLatRing(ring) {
|
|
3340
|
+
if (!ring.length) return 0;
|
|
3341
|
+
const coords = ensureClosedRing(ring);
|
|
3342
|
+
const poly = {
|
|
3343
|
+
type: "Feature",
|
|
3344
|
+
properties: {},
|
|
3345
|
+
geometry: {
|
|
3346
|
+
type: "Polygon",
|
|
3347
|
+
coordinates: [coords]
|
|
3348
|
+
}
|
|
3349
|
+
};
|
|
3350
|
+
return (0, import_area.default)(poly);
|
|
3351
|
+
}
|
|
3352
|
+
function areaToString(m2) {
|
|
3353
|
+
if (m2 > 1e4) {
|
|
3354
|
+
return Math.round(m2 / 1e6 * 100) / 100 + " km\xB2";
|
|
3355
|
+
} else {
|
|
3356
|
+
return Math.round(m2 * 100) / 100 + " m\xB2";
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
function getCenterFromPolygon(polygonCoordinates) {
|
|
3360
|
+
const closedRing = ensureClosedRing(polygonCoordinates);
|
|
3361
|
+
const polygon = new import_ol4.Feature({
|
|
3362
|
+
geometry: new import_geom3.Polygon([closedRing])
|
|
3363
|
+
});
|
|
3364
|
+
if (polygon) {
|
|
3365
|
+
const extent = polygon.getGeometry()?.getExtent();
|
|
3366
|
+
if (extent) {
|
|
3367
|
+
const coordinate = (0, import_extent7.getCenter)(extent);
|
|
3368
|
+
return coordinate;
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
return null;
|
|
3372
|
+
}
|
|
3373
|
+
function getCentroid(coordinates) {
|
|
3374
|
+
if (!coordinates || coordinates.length === 0) {
|
|
3375
|
+
return null;
|
|
3376
|
+
}
|
|
3377
|
+
let sumX = 0;
|
|
3378
|
+
let sumY = 0;
|
|
3379
|
+
for (const point of coordinates) {
|
|
3380
|
+
sumX += point[0];
|
|
3381
|
+
sumY += point[1];
|
|
3382
|
+
}
|
|
3383
|
+
const centroidX = sumX / coordinates.length;
|
|
3384
|
+
const centroidY = sumY / coordinates.length;
|
|
3385
|
+
return [centroidX, centroidY];
|
|
3386
|
+
}
|
|
3387
|
+
function getExtentFromPolygon(polygonCoordinates) {
|
|
3388
|
+
if (!polygonCoordinates?.length) return null;
|
|
3389
|
+
const closedRing = ensureClosedRing(polygonCoordinates);
|
|
3390
|
+
const polygon = new import_ol4.Feature({
|
|
3391
|
+
geometry: new import_geom3.Polygon([closedRing])
|
|
3392
|
+
});
|
|
3393
|
+
return polygon.getGeometry()?.getExtent() ?? null;
|
|
3394
|
+
}
|
|
3395
|
+
function isExtentInside(inner, outer) {
|
|
3396
|
+
if (inner === void 0 || outer === void 0) return false;
|
|
3397
|
+
if (inner.length !== 4 || outer.length !== 4) return false;
|
|
3398
|
+
return inner[0] >= outer[0] && // minX
|
|
3399
|
+
inner[1] >= outer[1] && // minY
|
|
3400
|
+
inner[2] <= outer[2] && // maxX
|
|
3401
|
+
inner[3] <= outer[3];
|
|
3402
|
+
}
|
|
3403
|
+
function isPointInsideExtent(coord, extent) {
|
|
3404
|
+
if (coord === void 0 || extent === void 0) return false;
|
|
3405
|
+
if (extent.length !== 4) return false;
|
|
3406
|
+
const [x, y] = coord;
|
|
3407
|
+
return x >= extent[0] && // minX
|
|
3408
|
+
y >= extent[1] && // minY
|
|
3409
|
+
x <= extent[2] && // maxX
|
|
3410
|
+
y <= extent[3];
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3413
|
+
// src/presets/index.ts
|
|
3414
|
+
var presets_exports = {};
|
|
3415
|
+
__export(presets_exports, {
|
|
3416
|
+
administrativeEnheterFylke: () => administrativeEnheterFylke,
|
|
3417
|
+
administrativeEnheterKommune: () => administrativeEnheterKommune,
|
|
3418
|
+
janmayenBaseLayer: () => janmayenBaseLayer,
|
|
3419
|
+
nib: () => nib,
|
|
3420
|
+
norwayCoordinate: () => norwayCoordinate,
|
|
3421
|
+
osm: () => osm,
|
|
3422
|
+
svalbardBaseLayer: () => svalbardBaseLayer,
|
|
3423
|
+
topo4graatoneBaseLayer: () => topo4graatoneBaseLayer,
|
|
3424
|
+
topografiskBaseLayer: () => topografiskBaseLayer
|
|
3425
|
+
});
|
|
3426
|
+
|
|
3427
|
+
// src/presets/layers/base/baseLayers.ts
|
|
3428
|
+
var svalbardBaseLayer = {
|
|
3429
|
+
id: "svalbard",
|
|
3430
|
+
kind: "tile",
|
|
3431
|
+
base: "regional",
|
|
3432
|
+
visible: false,
|
|
3433
|
+
opacity: 1,
|
|
3434
|
+
source: {
|
|
3435
|
+
type: "wmts",
|
|
3436
|
+
options: {
|
|
3437
|
+
url: "https://geodata.npolar.no/arcgis/rest/services/Basisdata/NP_Basiskart_Svalbard_WMTS_32633/MapServer/WMTS?",
|
|
3438
|
+
layer: "Basisdata_NP_Basiskart_Svalbard_WMTS_32633",
|
|
3439
|
+
projection: "EPSG:32633",
|
|
3440
|
+
matrixSetId: "default028mm",
|
|
3441
|
+
// critical for ArcGIS service
|
|
3442
|
+
format: "image/jpgpng",
|
|
3443
|
+
style: "default",
|
|
3444
|
+
wrapX: true,
|
|
3445
|
+
// levels: 17,
|
|
3446
|
+
attribution: "\xA9 Kartverket / Norsk Polarinstitutt"
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
};
|
|
3450
|
+
var janmayenBaseLayer = {
|
|
3451
|
+
id: "janmayen",
|
|
3452
|
+
kind: "tile",
|
|
3453
|
+
base: "regional",
|
|
3454
|
+
visible: false,
|
|
3455
|
+
opacity: 1,
|
|
3456
|
+
source: {
|
|
3457
|
+
type: "wmts",
|
|
3458
|
+
options: {
|
|
3459
|
+
url: "https://geodata.npolar.no/arcgis/rest/services/Basisdata/NP_Basiskart_JanMayen_WMTS_25833/MapServer/WMTS?",
|
|
3460
|
+
layer: "NP_Basiskart_JanMayen_WMTS_25833",
|
|
3461
|
+
projection: "EPSG:25833",
|
|
3462
|
+
matrixSetId: "default028mm",
|
|
3463
|
+
// critical for ArcGIS service
|
|
3464
|
+
format: "image/jpgpng",
|
|
3465
|
+
style: "default",
|
|
3466
|
+
// levels: 17,
|
|
3467
|
+
wrapX: true,
|
|
3468
|
+
attribution: "\xA9 Kartverket / Norsk Polarinstitutt"
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
};
|
|
3472
|
+
var topografiskBaseLayer = {
|
|
3473
|
+
id: "topografisk",
|
|
3474
|
+
name: "Topografisk",
|
|
3475
|
+
kind: "tile",
|
|
3476
|
+
base: "regional",
|
|
3477
|
+
visible: true,
|
|
3478
|
+
source: {
|
|
3479
|
+
type: "wmts",
|
|
3480
|
+
options: {
|
|
3481
|
+
url: "https://cache.kartverket.no/v1/service",
|
|
3482
|
+
layer: "topo",
|
|
3483
|
+
format: "image/png",
|
|
3484
|
+
style: "default",
|
|
3485
|
+
matrixSet: "webmercator",
|
|
3486
|
+
//'utm33n',
|
|
3487
|
+
projection: "EPSG:3857",
|
|
3488
|
+
//'EPSG:25833',
|
|
3489
|
+
wrapX: true,
|
|
3490
|
+
opacity: 1,
|
|
3491
|
+
attribution: "Kartverket"
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
};
|
|
3495
|
+
var topo4graatoneBaseLayer = {
|
|
3496
|
+
id: "topo4graatone",
|
|
3497
|
+
name: "Topografisk Gr\xE5tone",
|
|
3498
|
+
kind: "tile",
|
|
3499
|
+
base: "regional",
|
|
3500
|
+
visible: false,
|
|
3501
|
+
source: {
|
|
3502
|
+
type: "wmts",
|
|
3503
|
+
options: {
|
|
3504
|
+
url: "https://cache.kartverket.no/v1/service",
|
|
3505
|
+
layer: "topograatone",
|
|
3506
|
+
format: "image/png",
|
|
3507
|
+
style: "default",
|
|
3508
|
+
matrixSet: "webmercator",
|
|
3509
|
+
//'utm33n',
|
|
3510
|
+
projection: "EPSG:3857",
|
|
3511
|
+
//'EPSG:25833',
|
|
3512
|
+
wrapX: true,
|
|
3513
|
+
opacity: 1,
|
|
3514
|
+
attribution: "Kartverket"
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
};
|
|
3518
|
+
var nib = {
|
|
3519
|
+
id: "nib",
|
|
3520
|
+
name: "Norge i Bilder",
|
|
3521
|
+
kind: "tile",
|
|
3522
|
+
base: "regional",
|
|
3523
|
+
visible: false,
|
|
3524
|
+
source: {
|
|
3525
|
+
type: "wmts",
|
|
3526
|
+
options: {
|
|
3527
|
+
url: "https://tilecache.norgeibilder.no/wmts/webmercator",
|
|
3528
|
+
layer: "Nibcache_web_mercator_v2",
|
|
3529
|
+
format: "image/png",
|
|
3530
|
+
style: "default",
|
|
3531
|
+
matrixSet: "GoogleMapsCompatible",
|
|
3532
|
+
projection: "EPSG:3857",
|
|
3533
|
+
wrapX: true,
|
|
3534
|
+
opacity: 1,
|
|
3535
|
+
attribution: "Kartverket"
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
};
|
|
3539
|
+
var osm = {
|
|
3540
|
+
id: "osm",
|
|
3541
|
+
name: "OSM",
|
|
3542
|
+
kind: "tile",
|
|
3543
|
+
base: "super",
|
|
3544
|
+
visible: true,
|
|
3545
|
+
source: {
|
|
3546
|
+
type: "osm"
|
|
3547
|
+
}
|
|
3548
|
+
};
|
|
3549
|
+
|
|
3550
|
+
// src/presets/layers/overlay/overlayLayers.ts
|
|
3551
|
+
var norwayCoordinate = [10.4, 63.44];
|
|
3552
|
+
var administrativeEnheterKommune = {
|
|
3553
|
+
id: "kommune",
|
|
3554
|
+
kind: "vector",
|
|
3555
|
+
visible: true,
|
|
3556
|
+
base: false,
|
|
3557
|
+
source: {
|
|
3558
|
+
type: "wfs",
|
|
3559
|
+
options: {
|
|
3560
|
+
url: "https://wfs.geonorge.no/skwms1/wfs.administrative_enheter",
|
|
3561
|
+
outputFormat: "application/gml+xml; version=3.2",
|
|
3562
|
+
// or 'text/xml; subtype=gml/3.1.1'
|
|
3563
|
+
typeName: "app:Kommune",
|
|
3564
|
+
srsName: "EPSG:3857",
|
|
3565
|
+
minZoomToLoad: 1,
|
|
3566
|
+
strategy: "bbox"
|
|
3567
|
+
}
|
|
3568
|
+
},
|
|
3569
|
+
pickable: false,
|
|
3570
|
+
maxZoom: 14,
|
|
3571
|
+
minZoom: 7,
|
|
3572
|
+
zIndex: -2,
|
|
3573
|
+
zIndexPinned: true,
|
|
3574
|
+
hover: {
|
|
3575
|
+
style: { strokeColor: "transparent", fillColor: "transparent" }
|
|
3576
|
+
},
|
|
3577
|
+
style: { type: "simple", options: { strokeColor: "#6d6099", strokeWidth: 0.5, fillColor: "transparent" } }
|
|
3578
|
+
};
|
|
3579
|
+
var administrativeEnheterFylke = {
|
|
3580
|
+
id: "fylker",
|
|
3581
|
+
kind: "vector",
|
|
3582
|
+
visible: true,
|
|
3583
|
+
base: false,
|
|
3584
|
+
source: {
|
|
3585
|
+
type: "wfs",
|
|
3586
|
+
options: {
|
|
3587
|
+
url: "https://wfs.geonorge.no/skwms1/wfs.administrative_enheter",
|
|
3588
|
+
outputFormat: "application/gml+xml; version=3.2",
|
|
3589
|
+
// or 'text/xml; subtype=gml/3.1.1'
|
|
3590
|
+
typeName: "app:Fylke",
|
|
3591
|
+
srsName: "EPSG:3857",
|
|
3592
|
+
minZoomToLoad: 1,
|
|
3593
|
+
strategy: "bbox"
|
|
3594
|
+
}
|
|
3595
|
+
},
|
|
3596
|
+
pickable: false,
|
|
3597
|
+
maxZoom: 14,
|
|
3598
|
+
minZoom: 1,
|
|
3599
|
+
zIndex: -1,
|
|
3600
|
+
zIndexPinned: true,
|
|
3601
|
+
hover: {
|
|
3602
|
+
style: { strokeColor: "transparent", fillColor: "transparent" }
|
|
3603
|
+
},
|
|
3604
|
+
style: { type: "simple", options: { strokeColor: "#9e7795", strokeWidth: 1.5, fillColor: "transparent" } }
|
|
3605
|
+
};
|
|
3606
|
+
|
|
3607
|
+
// src/tokens/nibToken.ts
|
|
3608
|
+
var DEFAULT_CONFIG = {
|
|
3609
|
+
tokenUrl: "https://artskart.artsdatabanken.no/appapi/api/token/gettoken2",
|
|
3610
|
+
storageKey: "nbic-map-nib-token",
|
|
3611
|
+
expiryMarginMinutes: 10,
|
|
3612
|
+
retryIntervalMs: 6e4
|
|
3613
|
+
};
|
|
3614
|
+
function createNibToken(config) {
|
|
3615
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
3616
|
+
let currentToken = null;
|
|
3617
|
+
let refreshTimer = null;
|
|
3618
|
+
let isRefreshing = false;
|
|
3619
|
+
let refreshPromise = null;
|
|
3620
|
+
let abortController = null;
|
|
3621
|
+
let initialized = false;
|
|
3622
|
+
const listeners = [];
|
|
3623
|
+
function loadStoredToken() {
|
|
3624
|
+
try {
|
|
3625
|
+
if (typeof localStorage === "undefined") return null;
|
|
3626
|
+
const stored = localStorage.getItem(cfg.storageKey);
|
|
3627
|
+
if (!stored) return null;
|
|
3628
|
+
const parsed = JSON.parse(stored);
|
|
3629
|
+
if (typeof parsed.value === "string" && typeof parsed.expires === "number" && typeof parsed.when === "number") {
|
|
3630
|
+
return parsed;
|
|
3631
|
+
}
|
|
3632
|
+
return null;
|
|
3633
|
+
} catch {
|
|
3634
|
+
return null;
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
function storeToken(token) {
|
|
3638
|
+
try {
|
|
3639
|
+
if (typeof localStorage === "undefined") return;
|
|
3640
|
+
localStorage.setItem(cfg.storageKey, JSON.stringify(token));
|
|
3641
|
+
} catch {
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
function isTokenValid(token) {
|
|
3645
|
+
if (!token?.value || !token.expires) return false;
|
|
3646
|
+
const marginMs = cfg.expiryMarginMinutes * 60 * 1e3;
|
|
3647
|
+
return token.expires > Date.now() + marginMs;
|
|
3648
|
+
}
|
|
3649
|
+
async function fetchToken() {
|
|
3650
|
+
abortController = new AbortController();
|
|
3651
|
+
const response = await fetch(cfg.tokenUrl, {
|
|
3652
|
+
method: "GET",
|
|
3653
|
+
headers: {
|
|
3654
|
+
"Accept": "application/json"
|
|
3655
|
+
},
|
|
3656
|
+
signal: abortController.signal
|
|
3657
|
+
});
|
|
3658
|
+
if (!response.ok) {
|
|
3659
|
+
throw new Error(`Token fetch failed: ${response.status} ${response.statusText}`);
|
|
3660
|
+
}
|
|
3661
|
+
const data = await response.json();
|
|
3662
|
+
if (!data.TokenValue) {
|
|
3663
|
+
throw new Error("Invalid token response: missing TokenValue");
|
|
3664
|
+
}
|
|
3665
|
+
if (data.ClientId === "Could Not Get Client IP address" || data.TokenValue === "Could Not Get Client IP address") {
|
|
3666
|
+
throw new Error("Server could not determine client IP address for token generation");
|
|
3667
|
+
}
|
|
3668
|
+
return {
|
|
3669
|
+
value: data.TokenValue,
|
|
3670
|
+
expires: new Date(data.ExpireTimestamp).getTime(),
|
|
3671
|
+
when: Date.now()
|
|
3672
|
+
};
|
|
3673
|
+
}
|
|
3674
|
+
function notifyListeners(token) {
|
|
3675
|
+
for (const cb of listeners) {
|
|
3676
|
+
try {
|
|
3677
|
+
cb(token);
|
|
3678
|
+
} catch {
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3682
|
+
function scheduleRefresh() {
|
|
3683
|
+
if (refreshTimer) {
|
|
3684
|
+
clearTimeout(refreshTimer);
|
|
3685
|
+
refreshTimer = null;
|
|
3686
|
+
}
|
|
3687
|
+
if (!currentToken?.expires) return;
|
|
3688
|
+
const marginMs = cfg.expiryMarginMinutes * 60 * 1e3;
|
|
3689
|
+
const refreshIn = Math.max(
|
|
3690
|
+
currentToken.expires - Date.now() - marginMs,
|
|
3691
|
+
cfg.retryIntervalMs
|
|
3692
|
+
);
|
|
3693
|
+
refreshTimer = setTimeout(() => {
|
|
3694
|
+
refreshToken().catch(() => {
|
|
3695
|
+
});
|
|
3696
|
+
}, refreshIn);
|
|
3697
|
+
}
|
|
3698
|
+
async function init() {
|
|
3699
|
+
if (initialized) return;
|
|
3700
|
+
currentToken = loadStoredToken();
|
|
3701
|
+
if (isTokenValid(currentToken)) {
|
|
3702
|
+
initialized = true;
|
|
3703
|
+
scheduleRefresh();
|
|
3704
|
+
return;
|
|
3705
|
+
}
|
|
3706
|
+
try {
|
|
3707
|
+
await refreshToken();
|
|
3708
|
+
} catch {
|
|
3709
|
+
}
|
|
3710
|
+
initialized = true;
|
|
3711
|
+
}
|
|
3712
|
+
function getToken() {
|
|
3713
|
+
return currentToken?.value ?? "";
|
|
3714
|
+
}
|
|
3715
|
+
function getTokenInfo() {
|
|
3716
|
+
return currentToken ? { ...currentToken } : null;
|
|
3717
|
+
}
|
|
3718
|
+
async function refreshToken(force = false) {
|
|
3719
|
+
if (isRefreshing && refreshPromise) {
|
|
3720
|
+
return refreshPromise;
|
|
3721
|
+
}
|
|
3722
|
+
if (!force && currentToken?.when && Date.now() - currentToken.when < cfg.retryIntervalMs) {
|
|
3723
|
+
return currentToken?.value ?? "";
|
|
3724
|
+
}
|
|
3725
|
+
isRefreshing = true;
|
|
3726
|
+
refreshPromise = (async () => {
|
|
3727
|
+
try {
|
|
3728
|
+
const newToken = await fetchToken();
|
|
3729
|
+
currentToken = newToken;
|
|
3730
|
+
storeToken(newToken);
|
|
3731
|
+
notifyListeners(newToken.value);
|
|
3732
|
+
config?.onTokenFetched?.(newToken.value);
|
|
3733
|
+
scheduleRefresh();
|
|
3734
|
+
return newToken.value;
|
|
3735
|
+
} catch (error) {
|
|
3736
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
3737
|
+
config?.onError?.(err);
|
|
3738
|
+
if (currentToken) {
|
|
3739
|
+
scheduleRefresh();
|
|
3740
|
+
}
|
|
3741
|
+
throw err;
|
|
3742
|
+
} finally {
|
|
3743
|
+
isRefreshing = false;
|
|
3744
|
+
refreshPromise = null;
|
|
3745
|
+
abortController = null;
|
|
3746
|
+
}
|
|
3747
|
+
})();
|
|
3748
|
+
return refreshPromise;
|
|
3749
|
+
}
|
|
3750
|
+
function onTokenChange(callback) {
|
|
3751
|
+
listeners.push(callback);
|
|
3752
|
+
return () => {
|
|
3753
|
+
const idx = listeners.indexOf(callback);
|
|
3754
|
+
if (idx >= 0) {
|
|
3755
|
+
listeners.splice(idx, 1);
|
|
3756
|
+
}
|
|
3757
|
+
};
|
|
3758
|
+
}
|
|
3759
|
+
function dispose() {
|
|
3760
|
+
if (refreshTimer) {
|
|
3761
|
+
clearTimeout(refreshTimer);
|
|
3762
|
+
refreshTimer = null;
|
|
3763
|
+
}
|
|
3764
|
+
if (abortController) {
|
|
3765
|
+
abortController.abort();
|
|
3766
|
+
abortController = null;
|
|
3767
|
+
}
|
|
3768
|
+
listeners.length = 0;
|
|
3769
|
+
currentToken = null;
|
|
3770
|
+
refreshPromise = null;
|
|
3771
|
+
isRefreshing = false;
|
|
3772
|
+
initialized = false;
|
|
3773
|
+
}
|
|
3774
|
+
function isInitialized() {
|
|
3775
|
+
return initialized;
|
|
3776
|
+
}
|
|
3777
|
+
return {
|
|
3778
|
+
init,
|
|
3779
|
+
getToken,
|
|
3780
|
+
getTokenInfo,
|
|
3781
|
+
refreshToken,
|
|
3782
|
+
onTokenChange,
|
|
3783
|
+
dispose,
|
|
3784
|
+
isInitialized
|
|
3785
|
+
};
|
|
3786
|
+
}
|
|
3787
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
3788
|
+
0 && (module.exports = {
|
|
3789
|
+
MapEvents,
|
|
3790
|
+
NbicMapComponent,
|
|
3791
|
+
createMap,
|
|
3792
|
+
createNibToken,
|
|
3793
|
+
nbicMapGeojson,
|
|
3794
|
+
nbicMapPresets,
|
|
3795
|
+
nbicMapUtils
|
|
3796
|
+
});
|