aur-openlayers 19.6.3 → 19.6.5
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/fesm2022/aur-openlayers.mjs +160 -10
- package/fesm2022/aur-openlayers.mjs.map +1 -1
- package/lib/map-framework/public/types.d.ts +41 -0
- package/lib/map-framework/runtime/decorations/arrow-decoration-manager.d.ts +27 -0
- package/lib/map-framework/runtime/fit-layer.utils.d.ts +2 -1
- package/lib/map-framework/runtime/layer-manager.d.ts +1 -0
- package/package.json +1 -1
|
@@ -9,6 +9,8 @@ import Collection from 'ol/Collection';
|
|
|
9
9
|
import DragPan from 'ol/interaction/DragPan';
|
|
10
10
|
import Modify from 'ol/interaction/Modify';
|
|
11
11
|
import * as Observable from 'ol/Observable';
|
|
12
|
+
import { unByKey } from 'ol/Observable';
|
|
13
|
+
import { LineString, MultiLineString, Point } from 'ol/geom';
|
|
12
14
|
|
|
13
15
|
class LibService {
|
|
14
16
|
constructor() { }
|
|
@@ -180,12 +182,15 @@ function toOlPadding(p, fallback = DEFAULT_OL_PADDING) {
|
|
|
180
182
|
}
|
|
181
183
|
return [p.top, p.right, p.bottom, p.left];
|
|
182
184
|
}
|
|
183
|
-
function toOlFitOptions(opts) {
|
|
185
|
+
function toOlFitOptions(opts, map) {
|
|
184
186
|
const padding = toOlPadding(opts?.padding) ?? DEFAULT_OL_PADDING;
|
|
185
187
|
const duration = opts?.duration ?? DEFAULT_FIT_DURATION;
|
|
186
188
|
// OL View#fit options are a plain object; keep it minimal & typed-friendly
|
|
187
189
|
const fitOpts = { padding, duration };
|
|
188
|
-
if (opts?.
|
|
190
|
+
if (opts?.keepZoom && map) {
|
|
191
|
+
fitOpts.maxZoom = map.getView().getZoom();
|
|
192
|
+
}
|
|
193
|
+
else if (opts?.maxZoom != null) {
|
|
189
194
|
fitOpts.maxZoom = opts.maxZoom;
|
|
190
195
|
}
|
|
191
196
|
return fitOpts;
|
|
@@ -214,12 +219,12 @@ const createMapContext = (map, layers, popupHost, scheduler = new FlushScheduler
|
|
|
214
219
|
centerOnAllLayers: (opts) => {
|
|
215
220
|
const extent = collectLayersExtent(layers);
|
|
216
221
|
if (extent)
|
|
217
|
-
map.getView().fit(extent, toOlFitOptions(opts));
|
|
222
|
+
map.getView().fit(extent, toOlFitOptions(opts, map));
|
|
218
223
|
},
|
|
219
224
|
centerOnLayers: (layerIds, opts) => {
|
|
220
225
|
const extent = collectLayersExtent(layers, layerIds);
|
|
221
226
|
if (extent)
|
|
222
|
-
map.getView().fit(extent, toOlFitOptions(opts));
|
|
227
|
+
map.getView().fit(extent, toOlFitOptions(opts, map));
|
|
223
228
|
},
|
|
224
229
|
};
|
|
225
230
|
};
|
|
@@ -1582,7 +1587,7 @@ class StyleCache {
|
|
|
1582
1587
|
}
|
|
1583
1588
|
}
|
|
1584
1589
|
|
|
1585
|
-
const resolveMaybeFn = (value, args) => {
|
|
1590
|
+
const resolveMaybeFn$1 = (value, args) => {
|
|
1586
1591
|
if (typeof value === 'function') {
|
|
1587
1592
|
return value(...args);
|
|
1588
1593
|
}
|
|
@@ -1617,7 +1622,7 @@ const createStyleFunction = (options) => {
|
|
|
1617
1622
|
resolution,
|
|
1618
1623
|
zoom: map?.getView().getZoom(),
|
|
1619
1624
|
};
|
|
1620
|
-
let opts = resolveMaybeFn(style.base, [model, view]);
|
|
1625
|
+
let opts = resolveMaybeFn$1(style.base, [model, view]);
|
|
1621
1626
|
if (style.states) {
|
|
1622
1627
|
const featureStates = orderStates(getFeatureStates(feature), style.statePriority);
|
|
1623
1628
|
featureStates.forEach((state) => {
|
|
@@ -1625,7 +1630,7 @@ const createStyleFunction = (options) => {
|
|
|
1625
1630
|
if (!patchFactory) {
|
|
1626
1631
|
return;
|
|
1627
1632
|
}
|
|
1628
|
-
const patch = resolveMaybeFn(patchFactory, [model, view]);
|
|
1633
|
+
const patch = resolveMaybeFn$1(patchFactory, [model, view]);
|
|
1629
1634
|
opts = applyPatch(opts, patch);
|
|
1630
1635
|
});
|
|
1631
1636
|
}
|
|
@@ -1886,7 +1891,7 @@ class VectorLayerBase {
|
|
|
1886
1891
|
if (!extent || isEmpty(extent)) {
|
|
1887
1892
|
return;
|
|
1888
1893
|
}
|
|
1889
|
-
this.ctx.map.getView().fit(extent, toOlFitOptions(opts));
|
|
1894
|
+
this.ctx.map.getView().fit(extent, toOlFitOptions(opts, this.ctx.map));
|
|
1890
1895
|
}
|
|
1891
1896
|
/** Fit view to a single feature by id. No-op if feature/geometry is missing. */
|
|
1892
1897
|
centerOnModel(id, opts) {
|
|
@@ -1895,7 +1900,7 @@ class VectorLayerBase {
|
|
|
1895
1900
|
if (!geom) {
|
|
1896
1901
|
return;
|
|
1897
1902
|
}
|
|
1898
|
-
this.ctx.map.getView().fit(geom.getExtent(), toOlFitOptions(opts));
|
|
1903
|
+
this.ctx.map.getView().fit(geom.getExtent(), toOlFitOptions(opts, this.ctx.map));
|
|
1899
1904
|
}
|
|
1900
1905
|
/**
|
|
1901
1906
|
* Fit view to a subset of features by ids (combined extent).
|
|
@@ -1915,7 +1920,7 @@ class VectorLayerBase {
|
|
|
1915
1920
|
if (!found || isEmpty(extent)) {
|
|
1916
1921
|
return;
|
|
1917
1922
|
}
|
|
1918
|
-
this.ctx.map.getView().fit(extent, toOlFitOptions(opts));
|
|
1923
|
+
this.ctx.map.getView().fit(extent, toOlFitOptions(opts, this.ctx.map));
|
|
1919
1924
|
}
|
|
1920
1925
|
getExtent() {
|
|
1921
1926
|
const extent = this.source.getExtent();
|
|
@@ -2289,6 +2294,140 @@ class PopupHost {
|
|
|
2289
2294
|
}
|
|
2290
2295
|
}
|
|
2291
2296
|
|
|
2297
|
+
const resolveMaybeFn = (value, args) => typeof value === 'function' ? value(...args) : value;
|
|
2298
|
+
/**
|
|
2299
|
+
* Extracts flat coordinate arrays from a feature's geometry.
|
|
2300
|
+
* Supports LineString and MultiLineString; returns empty array for others.
|
|
2301
|
+
*/
|
|
2302
|
+
function extractLineCoords(geometry) {
|
|
2303
|
+
if (geometry instanceof LineString) {
|
|
2304
|
+
return [geometry.getCoordinates()];
|
|
2305
|
+
}
|
|
2306
|
+
if (geometry instanceof MultiLineString) {
|
|
2307
|
+
return geometry.getCoordinates();
|
|
2308
|
+
}
|
|
2309
|
+
return [];
|
|
2310
|
+
}
|
|
2311
|
+
/**
|
|
2312
|
+
* Generate arrow points along a polyline at a given interval (meters in EPSG:3857).
|
|
2313
|
+
*/
|
|
2314
|
+
function generateArrowPoints(coords, intervalMeters, offsetRatio) {
|
|
2315
|
+
if (coords.length < 2 || intervalMeters <= 0)
|
|
2316
|
+
return [];
|
|
2317
|
+
const arrows = [];
|
|
2318
|
+
let accumulated = intervalMeters * offsetRatio;
|
|
2319
|
+
for (let i = 0; i < coords.length - 1; i++) {
|
|
2320
|
+
const ax = coords[i][0], ay = coords[i][1];
|
|
2321
|
+
const bx = coords[i + 1][0], by = coords[i + 1][1];
|
|
2322
|
+
const segLen = Math.hypot(bx - ax, by - ay);
|
|
2323
|
+
const rotation = Math.atan2(bx - ax, by - ay);
|
|
2324
|
+
while (accumulated <= segLen) {
|
|
2325
|
+
const t = accumulated / segLen;
|
|
2326
|
+
arrows.push({
|
|
2327
|
+
coordinate: [ax + t * (bx - ax), ay + t * (by - ay)],
|
|
2328
|
+
rotation,
|
|
2329
|
+
});
|
|
2330
|
+
accumulated += intervalMeters;
|
|
2331
|
+
}
|
|
2332
|
+
accumulated -= segLen;
|
|
2333
|
+
}
|
|
2334
|
+
return arrows;
|
|
2335
|
+
}
|
|
2336
|
+
class ArrowDecorationManager {
|
|
2337
|
+
source = new VectorSource();
|
|
2338
|
+
layer;
|
|
2339
|
+
config;
|
|
2340
|
+
map;
|
|
2341
|
+
parentLayer;
|
|
2342
|
+
parentApi;
|
|
2343
|
+
moveEndKey;
|
|
2344
|
+
unsubCollection;
|
|
2345
|
+
unsubChanges;
|
|
2346
|
+
rafId = null;
|
|
2347
|
+
constructor(options) {
|
|
2348
|
+
this.config = options.config;
|
|
2349
|
+
this.map = options.map;
|
|
2350
|
+
this.parentLayer = options.parentLayer;
|
|
2351
|
+
this.parentApi = options.parentApi;
|
|
2352
|
+
const parentZ = this.parentLayer.getZIndex() ?? 0;
|
|
2353
|
+
this.layer = new VectorLayer({
|
|
2354
|
+
source: this.source,
|
|
2355
|
+
zIndex: parentZ + 1,
|
|
2356
|
+
});
|
|
2357
|
+
this.layer.set('id', `__decoration_arrows`);
|
|
2358
|
+
this.map.addLayer(this.layer);
|
|
2359
|
+
this.syncVisibility();
|
|
2360
|
+
this.syncOpacity();
|
|
2361
|
+
this.moveEndKey = this.map.on('moveend', () => this.scheduleUpdate());
|
|
2362
|
+
this.unsubCollection = this.parentApi.onModelsCollectionChanged(() => this.scheduleUpdate());
|
|
2363
|
+
this.unsubChanges = this.parentApi.onModelsChanged?.(() => this.scheduleUpdate());
|
|
2364
|
+
}
|
|
2365
|
+
scheduleUpdate() {
|
|
2366
|
+
if (this.rafId !== null)
|
|
2367
|
+
return;
|
|
2368
|
+
this.rafId = requestAnimationFrame(() => {
|
|
2369
|
+
this.rafId = null;
|
|
2370
|
+
this.rebuild();
|
|
2371
|
+
});
|
|
2372
|
+
}
|
|
2373
|
+
rebuild() {
|
|
2374
|
+
this.syncVisibility();
|
|
2375
|
+
this.syncOpacity();
|
|
2376
|
+
if (!this.parentLayer.getVisible()) {
|
|
2377
|
+
this.source.clear();
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
const view = this.map.getView();
|
|
2381
|
+
const resolution = view.getResolution() ?? 1;
|
|
2382
|
+
const styleView = { resolution, zoom: view.getZoom() };
|
|
2383
|
+
const interval = resolveMaybeFn(this.config.interval, [styleView]);
|
|
2384
|
+
const offsetRatio = this.config.offsetRatio ?? 0.5;
|
|
2385
|
+
const parentSource = this.parentLayer.getSource();
|
|
2386
|
+
if (!parentSource) {
|
|
2387
|
+
this.source.clear();
|
|
2388
|
+
return;
|
|
2389
|
+
}
|
|
2390
|
+
const allArrows = [];
|
|
2391
|
+
parentSource.getFeatures().forEach((feature) => {
|
|
2392
|
+
const geom = feature.getGeometry();
|
|
2393
|
+
if (!geom)
|
|
2394
|
+
return;
|
|
2395
|
+
const lines = extractLineCoords(geom);
|
|
2396
|
+
for (const coords of lines) {
|
|
2397
|
+
const arrows = generateArrowPoints(coords, interval, offsetRatio);
|
|
2398
|
+
allArrows.push(...arrows);
|
|
2399
|
+
}
|
|
2400
|
+
});
|
|
2401
|
+
const features = allArrows.map((arrow, i) => {
|
|
2402
|
+
const f = new Feature({ geometry: new Point(arrow.coordinate) });
|
|
2403
|
+
f.setId(`__arrow_${i}`);
|
|
2404
|
+
const styleResult = this.config.style({ rotation: arrow.rotation, view: styleView });
|
|
2405
|
+
f.setStyle(Array.isArray(styleResult) ? styleResult : [styleResult]);
|
|
2406
|
+
return f;
|
|
2407
|
+
});
|
|
2408
|
+
this.source.clear();
|
|
2409
|
+
if (features.length > 0) {
|
|
2410
|
+
this.source.addFeatures(features);
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
syncVisibility() {
|
|
2414
|
+
this.layer.setVisible(this.parentLayer.getVisible());
|
|
2415
|
+
}
|
|
2416
|
+
syncOpacity() {
|
|
2417
|
+
this.layer.setOpacity(this.parentLayer.getOpacity());
|
|
2418
|
+
}
|
|
2419
|
+
dispose() {
|
|
2420
|
+
if (this.rafId !== null) {
|
|
2421
|
+
cancelAnimationFrame(this.rafId);
|
|
2422
|
+
this.rafId = null;
|
|
2423
|
+
}
|
|
2424
|
+
unByKey(this.moveEndKey);
|
|
2425
|
+
this.unsubCollection();
|
|
2426
|
+
this.unsubChanges?.();
|
|
2427
|
+
this.map.removeLayer(this.layer);
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2292
2431
|
class LayerManager {
|
|
2293
2432
|
map;
|
|
2294
2433
|
layers = {};
|
|
@@ -2297,6 +2436,7 @@ class LayerManager {
|
|
|
2297
2436
|
scheduler;
|
|
2298
2437
|
popupHost;
|
|
2299
2438
|
ctx;
|
|
2439
|
+
decorationManagers = [];
|
|
2300
2440
|
constructor(map, schema) {
|
|
2301
2441
|
this.map = map;
|
|
2302
2442
|
const popupHost = schema.options?.popupHost ? new PopupHost(schema.options.popupHost) : undefined;
|
|
@@ -2344,6 +2484,15 @@ class LayerManager {
|
|
|
2344
2484
|
this.layers[descriptor.id] = layer;
|
|
2345
2485
|
this.apis[descriptor.id] = api;
|
|
2346
2486
|
this.map.addLayer(layer);
|
|
2487
|
+
if (descriptor.feature.decorations?.arrows) {
|
|
2488
|
+
const decorationManager = new ArrowDecorationManager({
|
|
2489
|
+
map: this.map,
|
|
2490
|
+
parentLayer: layer,
|
|
2491
|
+
parentApi: api,
|
|
2492
|
+
config: descriptor.feature.decorations.arrows,
|
|
2493
|
+
});
|
|
2494
|
+
this.decorationManagers.push(decorationManager);
|
|
2495
|
+
}
|
|
2347
2496
|
});
|
|
2348
2497
|
this.interactions = new InteractionManager({
|
|
2349
2498
|
ctx,
|
|
@@ -2375,6 +2524,7 @@ class LayerManager {
|
|
|
2375
2524
|
this.interactions.dispose();
|
|
2376
2525
|
this.popupHost?.dispose();
|
|
2377
2526
|
this.scheduler.dispose();
|
|
2527
|
+
this.decorationManagers.forEach((dm) => dm.dispose());
|
|
2378
2528
|
Object.values(this.layers).forEach((layer) => this.map.removeLayer(layer));
|
|
2379
2529
|
}
|
|
2380
2530
|
}
|