@angular-helpers/openlayers 0.4.0 → 0.5.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 +14 -6
- package/fesm2022/angular-helpers-openlayers-controls.mjs +289 -36
- package/fesm2022/angular-helpers-openlayers-core.mjs +197 -16
- package/fesm2022/angular-helpers-openlayers-interactions.mjs +414 -23
- package/fesm2022/angular-helpers-openlayers-layers.mjs +634 -83
- package/fesm2022/angular-helpers-openlayers-military.mjs +244 -144
- package/fesm2022/angular-helpers-openlayers-overlays.mjs +9 -9
- package/package.json +2 -2
- package/types/angular-helpers-openlayers-controls.d.ts +24 -4
- package/types/angular-helpers-openlayers-core.d.ts +126 -4
- package/types/angular-helpers-openlayers-interactions.d.ts +120 -23
- package/types/angular-helpers-openlayers-layers.d.ts +152 -7
- package/types/angular-helpers-openlayers-military.d.ts +84 -94
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, signal, computed, Injectable,
|
|
2
|
+
import { inject, signal, computed, Injectable, input, ChangeDetectionStrategy, Component, DestroyRef, contentChild, afterNextRender, effect } from '@angular/core';
|
|
3
3
|
import VectorLayer from 'ol/layer/Vector';
|
|
4
|
+
import HeatmapLayer from 'ol/layer/Heatmap';
|
|
4
5
|
import TileLayer from 'ol/layer/Tile';
|
|
5
6
|
import ImageLayer from 'ol/layer/Image';
|
|
6
7
|
import VectorSource from 'ol/source/Vector';
|
|
@@ -16,7 +17,15 @@ import XYZ from 'ol/source/XYZ';
|
|
|
16
17
|
import TileWMS from 'ol/source/TileWMS';
|
|
17
18
|
import ImageWMS from 'ol/source/ImageWMS';
|
|
18
19
|
import ImageStatic from 'ol/source/ImageStatic';
|
|
20
|
+
import GeoJSON from 'ol/format/GeoJSON';
|
|
21
|
+
import TopoJSON from 'ol/format/TopoJSON';
|
|
22
|
+
import KML from 'ol/format/KML';
|
|
19
23
|
import { OlMapService } from '@angular-helpers/openlayers/core';
|
|
24
|
+
import WebGLVectorLayer from 'ol/layer/WebGLVector';
|
|
25
|
+
import WebGLTileLayer from 'ol/layer/WebGLTile';
|
|
26
|
+
import WebGLVectorTileLayer from 'ol/layer/WebGLVectorTile';
|
|
27
|
+
import VectorTileSource from 'ol/source/VectorTile';
|
|
28
|
+
import MVT from 'ol/format/MVT';
|
|
20
29
|
|
|
21
30
|
// OlLayerService
|
|
22
31
|
/**
|
|
@@ -58,6 +67,8 @@ class OlLayerService {
|
|
|
58
67
|
switch (config.type) {
|
|
59
68
|
case 'vector':
|
|
60
69
|
return this.createVectorLayer(config, map);
|
|
70
|
+
case 'heatmap':
|
|
71
|
+
return this.createHeatmapLayer(config, map);
|
|
61
72
|
case 'tile':
|
|
62
73
|
return this.createTileLayer(config, map);
|
|
63
74
|
case 'image':
|
|
@@ -93,6 +104,12 @@ class OlLayerService {
|
|
|
93
104
|
layer.setVisible(visible);
|
|
94
105
|
this.updateLayerState();
|
|
95
106
|
}
|
|
107
|
+
else {
|
|
108
|
+
const pending = this.pendingConfigs.find((c) => c.id === id);
|
|
109
|
+
if (pending) {
|
|
110
|
+
pending.visible = visible;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
96
113
|
}
|
|
97
114
|
toggleVisibility(id) {
|
|
98
115
|
const layer = this.layerCache.get(id);
|
|
@@ -110,6 +127,12 @@ class OlLayerService {
|
|
|
110
127
|
layer.setOpacity(opacity);
|
|
111
128
|
this.updateLayerState();
|
|
112
129
|
}
|
|
130
|
+
else {
|
|
131
|
+
const pending = this.pendingConfigs.find((c) => c.id === id);
|
|
132
|
+
if (pending) {
|
|
133
|
+
pending.opacity = opacity;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
113
136
|
}
|
|
114
137
|
setZIndex(id, zIndex) {
|
|
115
138
|
const layer = this.layerCache.get(id);
|
|
@@ -117,6 +140,35 @@ class OlLayerService {
|
|
|
117
140
|
layer.setZIndex(zIndex);
|
|
118
141
|
this.updateLayerState();
|
|
119
142
|
}
|
|
143
|
+
else {
|
|
144
|
+
const pending = this.pendingConfigs.find((c) => c.id === id);
|
|
145
|
+
if (pending) {
|
|
146
|
+
pending.zIndex = zIndex;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
setHeatmapProperties(id, props) {
|
|
151
|
+
const layer = this.layerCache.get(id);
|
|
152
|
+
if (layer instanceof HeatmapLayer) {
|
|
153
|
+
if (props.blur !== undefined)
|
|
154
|
+
layer.setBlur(props.blur);
|
|
155
|
+
if (props.radius !== undefined)
|
|
156
|
+
layer.setRadius(props.radius);
|
|
157
|
+
if (props.weight !== undefined)
|
|
158
|
+
layer.setWeight(props.weight);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
const pending = this.pendingConfigs.find((c) => c.id === id);
|
|
162
|
+
if (pending && pending.type === 'heatmap') {
|
|
163
|
+
const heatmapConfig = pending;
|
|
164
|
+
if (props.blur !== undefined)
|
|
165
|
+
heatmapConfig.blur = props.blur;
|
|
166
|
+
if (props.radius !== undefined)
|
|
167
|
+
heatmapConfig.radius = props.radius;
|
|
168
|
+
if (props.weight !== undefined)
|
|
169
|
+
heatmapConfig.weight = props.weight;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
120
172
|
}
|
|
121
173
|
isVisible(id) {
|
|
122
174
|
return this.layerCache.get(id)?.getVisible() ?? false;
|
|
@@ -166,19 +218,27 @@ class OlLayerService {
|
|
|
166
218
|
: source;
|
|
167
219
|
if (!(vectorSource instanceof VectorSource))
|
|
168
220
|
return;
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
.
|
|
172
|
-
|
|
173
|
-
.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
221
|
+
// Sync features: remove old ones, update existing ones, add new ones
|
|
222
|
+
if (features) {
|
|
223
|
+
const newFeatureIds = new Set(features.map((f) => f.id));
|
|
224
|
+
const sourceFeatures = vectorSource.getFeatures();
|
|
225
|
+
// 1. Remove features that are no longer in the input
|
|
226
|
+
sourceFeatures.forEach((f) => {
|
|
227
|
+
const id = f.getId();
|
|
228
|
+
if (id !== undefined && !newFeatureIds.has(id)) {
|
|
229
|
+
vectorSource.removeFeature(f);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
// 2. Only add features that don't already exist in the source
|
|
233
|
+
const existingIds = new Set(vectorSource
|
|
234
|
+
.getFeatures()
|
|
235
|
+
.map((f) => f.getId())
|
|
236
|
+
.filter((id) => id !== undefined));
|
|
237
|
+
const featuresToAdd = features.filter((f) => !existingIds.has(f.id));
|
|
238
|
+
if (featuresToAdd.length > 0) {
|
|
239
|
+
const olFeatures = featuresToAdd.map((feature) => {
|
|
179
240
|
const geom = feature.geometry;
|
|
180
241
|
let geometry;
|
|
181
|
-
// Validate coordinates exist before processing
|
|
182
242
|
if (!geom.coordinates) {
|
|
183
243
|
geometry = new Point([0, 0]);
|
|
184
244
|
}
|
|
@@ -196,7 +256,6 @@ class OlLayerService {
|
|
|
196
256
|
}
|
|
197
257
|
else if (geom.type === 'Circle') {
|
|
198
258
|
const center = fromLonLat(geom.coordinates);
|
|
199
|
-
// Approximate radius in meters - use 1000m as default if not specified
|
|
200
259
|
geometry = new Circle(center, geom.radius ?? 1000);
|
|
201
260
|
}
|
|
202
261
|
else {
|
|
@@ -219,7 +278,13 @@ class OlLayerService {
|
|
|
219
278
|
updateLayerState() {
|
|
220
279
|
const layers = [];
|
|
221
280
|
this.layerCache.forEach((layer, id) => {
|
|
222
|
-
|
|
281
|
+
let type = 'vector';
|
|
282
|
+
if (layer instanceof HeatmapLayer)
|
|
283
|
+
type = 'heatmap';
|
|
284
|
+
else if (layer instanceof TileLayer)
|
|
285
|
+
type = 'tile';
|
|
286
|
+
else if (layer instanceof ImageLayer)
|
|
287
|
+
type = 'image';
|
|
223
288
|
layers.push({
|
|
224
289
|
id,
|
|
225
290
|
type: type,
|
|
@@ -231,7 +296,17 @@ class OlLayerService {
|
|
|
231
296
|
this.layerState.set(layers.sort((a, b) => a.zIndex - b.zIndex));
|
|
232
297
|
}
|
|
233
298
|
createVectorLayer(config, map) {
|
|
234
|
-
const
|
|
299
|
+
const sourceOptions = {};
|
|
300
|
+
if (config.url && config.format) {
|
|
301
|
+
sourceOptions.url = config.url;
|
|
302
|
+
if (config.format === 'geojson')
|
|
303
|
+
sourceOptions.format = new GeoJSON();
|
|
304
|
+
else if (config.format === 'topojson')
|
|
305
|
+
sourceOptions.format = new TopoJSON();
|
|
306
|
+
else if (config.format === 'kml')
|
|
307
|
+
sourceOptions.format = new KML();
|
|
308
|
+
}
|
|
309
|
+
const vectorSource = new VectorSource(sourceOptions);
|
|
235
310
|
// Add features if provided
|
|
236
311
|
if (config.features && config.features.length > 0) {
|
|
237
312
|
const olFeatures = config.features.map((feature) => {
|
|
@@ -305,58 +380,10 @@ class OlLayerService {
|
|
|
305
380
|
stroke: new Stroke({ color: '#d32f2f', width: 2 }),
|
|
306
381
|
}),
|
|
307
382
|
});
|
|
308
|
-
//
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (size > 1) {
|
|
313
|
-
const showCount = clusterCfg?.showCount ?? true;
|
|
314
|
-
return new Style({
|
|
315
|
-
image: new Circle$1({
|
|
316
|
-
radius: 15 + Math.min(size * 2, 15),
|
|
317
|
-
fill: new Fill({ color: 'rgba(255, 100, 100, 0.8)' }),
|
|
318
|
-
stroke: new Stroke({ color: '#fff', width: 2 }),
|
|
319
|
-
}),
|
|
320
|
-
text: showCount
|
|
321
|
-
? new Text({
|
|
322
|
-
text: String(size),
|
|
323
|
-
fill: new Fill({ color: '#fff' }),
|
|
324
|
-
})
|
|
325
|
-
: undefined,
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
// Single feature: get the original feature from the cluster and use its style
|
|
329
|
-
const originalFeatures = olFeature.get('features');
|
|
330
|
-
const originalFeature = originalFeatures?.[0];
|
|
331
|
-
if (originalFeature) {
|
|
332
|
-
const abstractStyle = originalFeature.get(STYLE_PROP);
|
|
333
|
-
if (abstractStyle) {
|
|
334
|
-
const style = new Style();
|
|
335
|
-
const { icon, fill, stroke } = abstractStyle;
|
|
336
|
-
if (icon?.src) {
|
|
337
|
-
style.setImage(new Icon({
|
|
338
|
-
src: icon.src,
|
|
339
|
-
...(icon.size ? { size: icon.size } : {}),
|
|
340
|
-
...(icon.anchor ? { anchor: icon.anchor } : {}),
|
|
341
|
-
}));
|
|
342
|
-
}
|
|
343
|
-
if (fill) {
|
|
344
|
-
style.setFill(new Fill({ color: fill.color }));
|
|
345
|
-
}
|
|
346
|
-
if (stroke) {
|
|
347
|
-
style.setStroke(new Stroke({ color: stroke.color, width: stroke.width }));
|
|
348
|
-
}
|
|
349
|
-
// If we mapped at least one property, return it, otherwise fallback
|
|
350
|
-
if (icon?.src || fill || stroke)
|
|
351
|
-
return style;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
return defaultStyle;
|
|
355
|
-
};
|
|
356
|
-
// Per-feature style resolver: features carrying `style` render it.
|
|
357
|
-
// Structural type avoids importing `FeatureLike` from `ol/Feature`;
|
|
358
|
-
// tooling has been observed to auto-remove the unused-looking import.
|
|
359
|
-
const styleFn = (olFeature) => {
|
|
383
|
+
// Resolved style: priority to stashed metadata, then config-level style, then default.
|
|
384
|
+
const userStyle = config.style;
|
|
385
|
+
const styleFn = (olFeature, resolution) => {
|
|
386
|
+
// 1. Per-feature style metadata (stashed via STYLE_PROP)
|
|
360
387
|
const abstractStyle = olFeature.get(STYLE_PROP);
|
|
361
388
|
if (abstractStyle) {
|
|
362
389
|
const style = new Style();
|
|
@@ -377,8 +404,59 @@ class OlLayerService {
|
|
|
377
404
|
if (icon?.src || fill || stroke)
|
|
378
405
|
return style;
|
|
379
406
|
}
|
|
407
|
+
// 2. Layer-level style from config (supports functions or static styles)
|
|
408
|
+
if (userStyle) {
|
|
409
|
+
if (typeof userStyle === 'function') {
|
|
410
|
+
// Check if it's already an OL native feature or wrap if needed
|
|
411
|
+
// For simplicity in the demo, we pass the feature as-is or mapped
|
|
412
|
+
const feature = {
|
|
413
|
+
id: String(olFeature.getId() ?? ''),
|
|
414
|
+
geometry: {
|
|
415
|
+
type: olFeature.getGeometry()?.getType(),
|
|
416
|
+
coordinates: [], // coordinates not easily reversible without extra work
|
|
417
|
+
},
|
|
418
|
+
properties: olFeature.getProperties(),
|
|
419
|
+
};
|
|
420
|
+
return userStyle(feature, resolution);
|
|
421
|
+
}
|
|
422
|
+
return userStyle;
|
|
423
|
+
}
|
|
380
424
|
return defaultStyle;
|
|
381
425
|
};
|
|
426
|
+
// Cluster style: shows count badge when features are clustered, else delegates to styleFn
|
|
427
|
+
const clusterStyleFn = (olFeature, resolution) => {
|
|
428
|
+
const features = olFeature.get('features');
|
|
429
|
+
const size = features?.length ?? 1;
|
|
430
|
+
if (size > 1) {
|
|
431
|
+
const showCount = clusterCfg?.showCount ?? true;
|
|
432
|
+
return new Style({
|
|
433
|
+
image: new Circle$1({
|
|
434
|
+
radius: 15 + Math.min(size * 2, 15),
|
|
435
|
+
fill: new Fill({ color: 'rgba(255, 100, 100, 0.8)' }),
|
|
436
|
+
stroke: new Stroke({ color: '#fff', width: 2 }),
|
|
437
|
+
}),
|
|
438
|
+
text: showCount
|
|
439
|
+
? new Text({
|
|
440
|
+
text: String(size),
|
|
441
|
+
fill: new Fill({ color: '#fff' }),
|
|
442
|
+
})
|
|
443
|
+
: undefined,
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
// Single feature in cluster: unwrap and call styleFn
|
|
447
|
+
const originalFeature = features?.[0];
|
|
448
|
+
if (originalFeature) {
|
|
449
|
+
// We MUST preserve the original geometry if it's a non-point ( spiderfication etc)
|
|
450
|
+
const style = styleFn(originalFeature, resolution);
|
|
451
|
+
if (style instanceof Style) {
|
|
452
|
+
const origGeom = originalFeature.getGeometry();
|
|
453
|
+
if (origGeom)
|
|
454
|
+
style.setGeometry(origGeom);
|
|
455
|
+
}
|
|
456
|
+
return style;
|
|
457
|
+
}
|
|
458
|
+
return styleFn(olFeature, resolution);
|
|
459
|
+
};
|
|
382
460
|
const layer = new VectorLayer({
|
|
383
461
|
source,
|
|
384
462
|
visible: config.visible ?? true,
|
|
@@ -392,6 +470,56 @@ class OlLayerService {
|
|
|
392
470
|
this.updateLayerState();
|
|
393
471
|
return { id: config.id };
|
|
394
472
|
}
|
|
473
|
+
createHeatmapLayer(config, map) {
|
|
474
|
+
const vectorSource = new VectorSource();
|
|
475
|
+
if (config.features && config.features.length > 0) {
|
|
476
|
+
const olFeatures = config.features.map((feature) => {
|
|
477
|
+
const geom = feature.geometry;
|
|
478
|
+
let geometry;
|
|
479
|
+
if (!geom.coordinates) {
|
|
480
|
+
geometry = new Point([0, 0]);
|
|
481
|
+
}
|
|
482
|
+
else if (geom.type === 'Point') {
|
|
483
|
+
const coords = geom.coordinates;
|
|
484
|
+
geometry = new Point(fromLonLat(coords));
|
|
485
|
+
}
|
|
486
|
+
else if (geom.type === 'LineString') {
|
|
487
|
+
const coords = geom.coordinates.map((c) => fromLonLat(c));
|
|
488
|
+
geometry = new LineString(coords);
|
|
489
|
+
}
|
|
490
|
+
else if (geom.type === 'Polygon') {
|
|
491
|
+
const rings = geom.coordinates.map((ring) => ring.map((c) => fromLonLat(c)));
|
|
492
|
+
geometry = new Polygon(rings);
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
geometry = new Point([0, 0]);
|
|
496
|
+
}
|
|
497
|
+
const olFeature = new OLFeature({
|
|
498
|
+
geometry,
|
|
499
|
+
...feature.properties,
|
|
500
|
+
});
|
|
501
|
+
olFeature.setId(feature.id);
|
|
502
|
+
return olFeature;
|
|
503
|
+
});
|
|
504
|
+
vectorSource.addFeatures(olFeatures);
|
|
505
|
+
}
|
|
506
|
+
const layer = new HeatmapLayer({
|
|
507
|
+
source: vectorSource,
|
|
508
|
+
visible: config.visible ?? true,
|
|
509
|
+
opacity: config.opacity ?? 1,
|
|
510
|
+
zIndex: config.zIndex,
|
|
511
|
+
...(config.blur !== undefined && { blur: config.blur }),
|
|
512
|
+
...(config.radius !== undefined && { radius: config.radius }),
|
|
513
|
+
...(config.weight !== undefined && {
|
|
514
|
+
weight: config.weight,
|
|
515
|
+
}),
|
|
516
|
+
});
|
|
517
|
+
layer.set('id', config.id);
|
|
518
|
+
map.addLayer(layer);
|
|
519
|
+
this.layerCache.set(config.id, layer);
|
|
520
|
+
this.updateLayerState();
|
|
521
|
+
return { id: config.id };
|
|
522
|
+
}
|
|
395
523
|
createTileLayer(config, map) {
|
|
396
524
|
let source;
|
|
397
525
|
switch (config.source.type) {
|
|
@@ -404,7 +532,7 @@ class OlLayerService {
|
|
|
404
532
|
case 'wms':
|
|
405
533
|
source = new TileWMS({
|
|
406
534
|
url: config.source.url,
|
|
407
|
-
params: config.source.params,
|
|
535
|
+
params: config.source.params ?? {},
|
|
408
536
|
attributions: config.source.attributions,
|
|
409
537
|
});
|
|
410
538
|
break;
|
|
@@ -446,36 +574,69 @@ class OlLayerService {
|
|
|
446
574
|
this.updateLayerState();
|
|
447
575
|
return { id: config.id };
|
|
448
576
|
}
|
|
449
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
450
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
577
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlLayerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
578
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlLayerService });
|
|
451
579
|
}
|
|
452
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
580
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlLayerService, decorators: [{
|
|
453
581
|
type: Injectable
|
|
454
582
|
}] });
|
|
455
583
|
|
|
584
|
+
class OlClusterComponent {
|
|
585
|
+
distance = input(40, ...(ngDevMode ? [{ debugName: "distance" }] : /* istanbul ignore next */ []));
|
|
586
|
+
minDistance = input(20, ...(ngDevMode ? [{ debugName: "minDistance" }] : /* istanbul ignore next */ []));
|
|
587
|
+
showCount = input(true, ...(ngDevMode ? [{ debugName: "showCount" }] : /* istanbul ignore next */ []));
|
|
588
|
+
featureStyle = input(...(ngDevMode ? [undefined, { debugName: "featureStyle" }] : /* istanbul ignore next */ []));
|
|
589
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlClusterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
590
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.13", type: OlClusterComponent, isStandalone: true, selector: "ol-cluster", inputs: { distance: { classPropertyName: "distance", publicName: "distance", isSignal: true, isRequired: false, transformFunction: null }, minDistance: { classPropertyName: "minDistance", publicName: "minDistance", isSignal: true, isRequired: false, transformFunction: null }, showCount: { classPropertyName: "showCount", publicName: "showCount", isSignal: true, isRequired: false, transformFunction: null }, featureStyle: { classPropertyName: "featureStyle", publicName: "featureStyle", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
591
|
+
}
|
|
592
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlClusterComponent, decorators: [{
|
|
593
|
+
type: Component,
|
|
594
|
+
args: [{
|
|
595
|
+
selector: 'ol-cluster',
|
|
596
|
+
template: '',
|
|
597
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
598
|
+
}]
|
|
599
|
+
}], propDecorators: { distance: [{ type: i0.Input, args: [{ isSignal: true, alias: "distance", required: false }] }], minDistance: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDistance", required: false }] }], showCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCount", required: false }] }], featureStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "featureStyle", required: false }] }] } });
|
|
600
|
+
|
|
456
601
|
// OlVectorLayerComponent
|
|
457
602
|
class OlVectorLayerComponent {
|
|
458
603
|
layerService = inject(OlLayerService);
|
|
459
604
|
destroyRef = inject(DestroyRef);
|
|
460
605
|
id = input.required(...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
461
606
|
features = input([], ...(ngDevMode ? [{ debugName: "features" }] : /* istanbul ignore next */ []));
|
|
607
|
+
url = input(...(ngDevMode ? [undefined, { debugName: "url" }] : /* istanbul ignore next */ []));
|
|
608
|
+
format = input(...(ngDevMode ? [undefined, { debugName: "format" }] : /* istanbul ignore next */ []));
|
|
462
609
|
zIndex = input(0, ...(ngDevMode ? [{ debugName: "zIndex" }] : /* istanbul ignore next */ []));
|
|
463
610
|
opacity = input(1, ...(ngDevMode ? [{ debugName: "opacity" }] : /* istanbul ignore next */ []));
|
|
464
611
|
visible = input(true, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
465
612
|
style = input(...(ngDevMode ? [undefined, { debugName: "style" }] : /* istanbul ignore next */ []));
|
|
466
613
|
cluster = input(...(ngDevMode ? [undefined, { debugName: "cluster" }] : /* istanbul ignore next */ []));
|
|
614
|
+
clusterComponent = contentChild(OlClusterComponent, ...(ngDevMode ? [{ debugName: "clusterComponent" }] : /* istanbul ignore next */ []));
|
|
467
615
|
constructor() {
|
|
468
616
|
// Initialize layer after DOM is ready
|
|
469
617
|
afterNextRender(() => {
|
|
618
|
+
const clusterCmp = this.clusterComponent();
|
|
619
|
+
const resolvedClusterConfig = this.cluster() ??
|
|
620
|
+
(clusterCmp
|
|
621
|
+
? {
|
|
622
|
+
enabled: true,
|
|
623
|
+
distance: clusterCmp.distance(),
|
|
624
|
+
minDistance: clusterCmp.minDistance(),
|
|
625
|
+
showCount: clusterCmp.showCount(),
|
|
626
|
+
featureStyle: clusterCmp.featureStyle(),
|
|
627
|
+
}
|
|
628
|
+
: undefined);
|
|
470
629
|
this.layerService.addLayer({
|
|
471
630
|
id: this.id(),
|
|
472
631
|
type: 'vector',
|
|
473
632
|
features: this.features(),
|
|
633
|
+
url: this.url(),
|
|
634
|
+
format: this.format(),
|
|
474
635
|
zIndex: this.zIndex(),
|
|
475
636
|
opacity: this.opacity(),
|
|
476
637
|
visible: this.visible(),
|
|
477
638
|
style: this.style(),
|
|
478
|
-
cluster:
|
|
639
|
+
cluster: resolvedClusterConfig,
|
|
479
640
|
});
|
|
480
641
|
});
|
|
481
642
|
// Effect to sync features when input changes
|
|
@@ -486,22 +647,31 @@ class OlVectorLayerComponent {
|
|
|
486
647
|
this.layerService.updateFeatures(this.id(), currentFeatures);
|
|
487
648
|
}
|
|
488
649
|
});
|
|
650
|
+
effect(() => {
|
|
651
|
+
this.layerService.setOpacity(this.id(), this.opacity());
|
|
652
|
+
});
|
|
653
|
+
effect(() => {
|
|
654
|
+
this.layerService.setVisibility(this.id(), this.visible());
|
|
655
|
+
});
|
|
656
|
+
effect(() => {
|
|
657
|
+
this.layerService.setZIndex(this.id(), this.zIndex());
|
|
658
|
+
});
|
|
489
659
|
// Cleanup when component is destroyed
|
|
490
660
|
this.destroyRef.onDestroy(() => {
|
|
491
661
|
this.layerService.removeLayer(this.id());
|
|
492
662
|
});
|
|
493
663
|
}
|
|
494
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
495
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.
|
|
664
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlVectorLayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
665
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.13", type: OlVectorLayerComponent, isStandalone: true, selector: "ol-vector-layer", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, features: { classPropertyName: "features", publicName: "features", isSignal: true, isRequired: false, transformFunction: null }, url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, zIndex: { classPropertyName: "zIndex", publicName: "zIndex", isSignal: true, isRequired: false, transformFunction: null }, opacity: { classPropertyName: "opacity", publicName: "opacity", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, style: { classPropertyName: "style", publicName: "style", isSignal: true, isRequired: false, transformFunction: null }, cluster: { classPropertyName: "cluster", publicName: "cluster", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "clusterComponent", first: true, predicate: OlClusterComponent, descendants: true, isSignal: true }], ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
496
666
|
}
|
|
497
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
667
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlVectorLayerComponent, decorators: [{
|
|
498
668
|
type: Component,
|
|
499
669
|
args: [{
|
|
500
670
|
selector: 'ol-vector-layer',
|
|
501
671
|
template: '',
|
|
502
672
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
503
673
|
}]
|
|
504
|
-
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], features: [{ type: i0.Input, args: [{ isSignal: true, alias: "features", required: false }] }], zIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "zIndex", required: false }] }], opacity: [{ type: i0.Input, args: [{ isSignal: true, alias: "opacity", required: false }] }], visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }], style: [{ type: i0.Input, args: [{ isSignal: true, alias: "style", required: false }] }], cluster: [{ type: i0.Input, args: [{ isSignal: true, alias: "cluster", required: false }] }] } });
|
|
674
|
+
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], features: [{ type: i0.Input, args: [{ isSignal: true, alias: "features", required: false }] }], url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], zIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "zIndex", required: false }] }], opacity: [{ type: i0.Input, args: [{ isSignal: true, alias: "opacity", required: false }] }], visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }], style: [{ type: i0.Input, args: [{ isSignal: true, alias: "style", required: false }] }], cluster: [{ type: i0.Input, args: [{ isSignal: true, alias: "cluster", required: false }] }], clusterComponent: [{ type: i0.ContentChild, args: [i0.forwardRef(() => OlClusterComponent), { isSignal: true }] }] } });
|
|
505
675
|
|
|
506
676
|
// OlTileLayerComponent
|
|
507
677
|
class OlTileLayerComponent {
|
|
@@ -532,15 +702,24 @@ class OlTileLayerComponent {
|
|
|
532
702
|
visible: this.visible(),
|
|
533
703
|
});
|
|
534
704
|
});
|
|
705
|
+
effect(() => {
|
|
706
|
+
this.layerService.setOpacity(this.id(), this.opacity());
|
|
707
|
+
});
|
|
708
|
+
effect(() => {
|
|
709
|
+
this.layerService.setVisibility(this.id(), this.visible());
|
|
710
|
+
});
|
|
711
|
+
effect(() => {
|
|
712
|
+
this.layerService.setZIndex(this.id(), this.zIndex());
|
|
713
|
+
});
|
|
535
714
|
// Cleanup when component is destroyed
|
|
536
715
|
this.destroyRef.onDestroy(() => {
|
|
537
716
|
this.layerService.removeLayer(this.id());
|
|
538
717
|
});
|
|
539
718
|
}
|
|
540
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
541
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.
|
|
719
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlTileLayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
720
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.13", type: OlTileLayerComponent, isStandalone: true, selector: "ol-tile-layer", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: true, transformFunction: null }, url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: false, transformFunction: null }, attributions: { classPropertyName: "attributions", publicName: "attributions", isSignal: true, isRequired: false, transformFunction: null }, params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: false, transformFunction: null }, zIndex: { classPropertyName: "zIndex", publicName: "zIndex", isSignal: true, isRequired: false, transformFunction: null }, opacity: { classPropertyName: "opacity", publicName: "opacity", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
542
721
|
}
|
|
543
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
722
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlTileLayerComponent, decorators: [{
|
|
544
723
|
type: Component,
|
|
545
724
|
args: [{
|
|
546
725
|
selector: 'ol-tile-layer',
|
|
@@ -583,10 +762,10 @@ class OlImageLayerComponent {
|
|
|
583
762
|
this.layerService.removeLayer(this.id());
|
|
584
763
|
});
|
|
585
764
|
}
|
|
586
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
587
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.
|
|
765
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlImageLayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
766
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.13", type: OlImageLayerComponent, isStandalone: true, selector: "ol-image-layer", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, sourceType: { classPropertyName: "sourceType", publicName: "sourceType", isSignal: true, isRequired: true, transformFunction: null }, url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: true, transformFunction: null }, params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: false, transformFunction: null }, imageExtent: { classPropertyName: "imageExtent", publicName: "imageExtent", isSignal: true, isRequired: false, transformFunction: null }, zIndex: { classPropertyName: "zIndex", publicName: "zIndex", isSignal: true, isRequired: false, transformFunction: null }, opacity: { classPropertyName: "opacity", publicName: "opacity", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
588
767
|
}
|
|
589
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
768
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlImageLayerComponent, decorators: [{
|
|
590
769
|
type: Component,
|
|
591
770
|
args: [{
|
|
592
771
|
selector: 'ol-image-layer',
|
|
@@ -595,6 +774,378 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
|
|
|
595
774
|
}]
|
|
596
775
|
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], sourceType: [{ type: i0.Input, args: [{ isSignal: true, alias: "sourceType", required: true }] }], url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: true }] }], params: [{ type: i0.Input, args: [{ isSignal: true, alias: "params", required: false }] }], imageExtent: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageExtent", required: false }] }], zIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "zIndex", required: false }] }], opacity: [{ type: i0.Input, args: [{ isSignal: true, alias: "opacity", required: false }] }], visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }] } });
|
|
597
776
|
|
|
777
|
+
class OlHeatmapLayerComponent {
|
|
778
|
+
layerService = inject(OlLayerService);
|
|
779
|
+
mapService = inject(OlMapService);
|
|
780
|
+
destroyRef = inject(DestroyRef);
|
|
781
|
+
id = input.required(...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
782
|
+
features = input([], ...(ngDevMode ? [{ debugName: "features" }] : /* istanbul ignore next */ []));
|
|
783
|
+
zIndex = input(0, ...(ngDevMode ? [{ debugName: "zIndex" }] : /* istanbul ignore next */ []));
|
|
784
|
+
opacity = input(1, ...(ngDevMode ? [{ debugName: "opacity" }] : /* istanbul ignore next */ []));
|
|
785
|
+
visible = input(true, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
786
|
+
blur = input(15, ...(ngDevMode ? [{ debugName: "blur" }] : /* istanbul ignore next */ []));
|
|
787
|
+
radius = input(8, ...(ngDevMode ? [{ debugName: "radius" }] : /* istanbul ignore next */ []));
|
|
788
|
+
/** Unit for radius and blur: 'pixels' (default) or 'meters' */
|
|
789
|
+
radiusUnit = input('pixels', ...(ngDevMode ? [{ debugName: "radiusUnit" }] : /* istanbul ignore next */ []));
|
|
790
|
+
weight = input(...(ngDevMode ? [undefined, { debugName: "weight" }] : /* istanbul ignore next */ []));
|
|
791
|
+
/** Computed radius in pixels based on current resolution if unit is 'meters' */
|
|
792
|
+
scaledRadius = computed(() => {
|
|
793
|
+
const r = this.radius();
|
|
794
|
+
if (this.radiusUnit() === 'pixels')
|
|
795
|
+
return r;
|
|
796
|
+
return r / this.mapService.resolution();
|
|
797
|
+
}, ...(ngDevMode ? [{ debugName: "scaledRadius" }] : /* istanbul ignore next */ []));
|
|
798
|
+
/** Computed blur in pixels based on current resolution if unit is 'meters' */
|
|
799
|
+
scaledBlur = computed(() => {
|
|
800
|
+
const b = this.blur();
|
|
801
|
+
if (this.radiusUnit() === 'pixels')
|
|
802
|
+
return b;
|
|
803
|
+
return b / this.mapService.resolution();
|
|
804
|
+
}, ...(ngDevMode ? [{ debugName: "scaledBlur" }] : /* istanbul ignore next */ []));
|
|
805
|
+
constructor() {
|
|
806
|
+
afterNextRender(() => {
|
|
807
|
+
this.layerService.addLayer({
|
|
808
|
+
id: this.id(),
|
|
809
|
+
type: 'heatmap',
|
|
810
|
+
features: this.features(),
|
|
811
|
+
zIndex: this.zIndex(),
|
|
812
|
+
opacity: this.opacity(),
|
|
813
|
+
visible: this.visible(),
|
|
814
|
+
blur: this.scaledBlur(),
|
|
815
|
+
radius: this.scaledRadius(),
|
|
816
|
+
weight: this.weight(),
|
|
817
|
+
});
|
|
818
|
+
});
|
|
819
|
+
effect(() => {
|
|
820
|
+
const currentFeatures = this.features();
|
|
821
|
+
if (this.layerService.getLayer(this.id())) {
|
|
822
|
+
this.layerService.updateFeatures(this.id(), currentFeatures);
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
effect(() => {
|
|
826
|
+
this.layerService.setOpacity(this.id(), this.opacity());
|
|
827
|
+
});
|
|
828
|
+
effect(() => {
|
|
829
|
+
this.layerService.setVisibility(this.id(), this.visible());
|
|
830
|
+
});
|
|
831
|
+
effect(() => {
|
|
832
|
+
this.layerService.setZIndex(this.id(), this.zIndex());
|
|
833
|
+
});
|
|
834
|
+
effect(() => {
|
|
835
|
+
this.layerService.setHeatmapProperties(this.id(), {
|
|
836
|
+
blur: this.scaledBlur(),
|
|
837
|
+
radius: this.scaledRadius(),
|
|
838
|
+
weight: this.weight(),
|
|
839
|
+
});
|
|
840
|
+
});
|
|
841
|
+
this.destroyRef.onDestroy(() => {
|
|
842
|
+
this.layerService.removeLayer(this.id());
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlHeatmapLayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
846
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.13", type: OlHeatmapLayerComponent, isStandalone: true, selector: "ol-heatmap-layer", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, features: { classPropertyName: "features", publicName: "features", isSignal: true, isRequired: false, transformFunction: null }, zIndex: { classPropertyName: "zIndex", publicName: "zIndex", isSignal: true, isRequired: false, transformFunction: null }, opacity: { classPropertyName: "opacity", publicName: "opacity", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, blur: { classPropertyName: "blur", publicName: "blur", isSignal: true, isRequired: false, transformFunction: null }, radius: { classPropertyName: "radius", publicName: "radius", isSignal: true, isRequired: false, transformFunction: null }, radiusUnit: { classPropertyName: "radiusUnit", publicName: "radiusUnit", isSignal: true, isRequired: false, transformFunction: null }, weight: { classPropertyName: "weight", publicName: "weight", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
847
|
+
}
|
|
848
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlHeatmapLayerComponent, decorators: [{
|
|
849
|
+
type: Component,
|
|
850
|
+
args: [{
|
|
851
|
+
selector: 'ol-heatmap-layer',
|
|
852
|
+
template: '',
|
|
853
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
854
|
+
}]
|
|
855
|
+
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], features: [{ type: i0.Input, args: [{ isSignal: true, alias: "features", required: false }] }], zIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "zIndex", required: false }] }], opacity: [{ type: i0.Input, args: [{ isSignal: true, alias: "opacity", required: false }] }], visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }], blur: [{ type: i0.Input, args: [{ isSignal: true, alias: "blur", required: false }] }], radius: [{ type: i0.Input, args: [{ isSignal: true, alias: "radius", required: false }] }], radiusUnit: [{ type: i0.Input, args: [{ isSignal: true, alias: "radiusUnit", required: false }] }], weight: [{ type: i0.Input, args: [{ isSignal: true, alias: "weight", required: false }] }] } });
|
|
856
|
+
|
|
857
|
+
// OlWebGLVectorLayerComponent
|
|
858
|
+
// GPU-accelerated vector layer for large datasets (10k+ features).
|
|
859
|
+
// Uses FlatStyleLike instead of ol/style/Style. Must be manually disposed.
|
|
860
|
+
/**
|
|
861
|
+
* GPU-accelerated vector layer for rendering large datasets.
|
|
862
|
+
*
|
|
863
|
+
* Important: Uses WebGL 2 for rendering. Styles must be provided as
|
|
864
|
+
* `FlatStyleLike` objects (not `ol/style/Style` instances).
|
|
865
|
+
* Hit detection is disabled by default for performance.
|
|
866
|
+
*
|
|
867
|
+
* @usageNotes
|
|
868
|
+
* ```html
|
|
869
|
+
* <ol-webgl-vector-layer
|
|
870
|
+
* id="big-dataset"
|
|
871
|
+
* [features]="largeDataset()"
|
|
872
|
+
* [flatStyle]="{
|
|
873
|
+
* 'circle-radius': 6,
|
|
874
|
+
* 'circle-fill-color': ['match', ['get', 'type'], 'alert', 'red', 'blue'],
|
|
875
|
+
* 'stroke-color': '#333',
|
|
876
|
+
* 'stroke-width': 1
|
|
877
|
+
* }"
|
|
878
|
+
* [disableHitDetection]="true">
|
|
879
|
+
* </ol-webgl-vector-layer>
|
|
880
|
+
* ```
|
|
881
|
+
*/
|
|
882
|
+
class OlWebGLVectorLayerComponent {
|
|
883
|
+
mapService = inject(OlMapService);
|
|
884
|
+
destroyRef = inject(DestroyRef);
|
|
885
|
+
/** Unique layer identifier */
|
|
886
|
+
id = input.required(...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
887
|
+
/** Features to render */
|
|
888
|
+
features = input([], ...(ngDevMode ? [{ debugName: "features" }] : /* istanbul ignore next */ []));
|
|
889
|
+
/** WebGL flat style (required — no default provided) */
|
|
890
|
+
flatStyle = input.required(...(ngDevMode ? [{ debugName: "flatStyle" }] : /* istanbul ignore next */ []));
|
|
891
|
+
/** Z-index for layer ordering */
|
|
892
|
+
zIndex = input(0, ...(ngDevMode ? [{ debugName: "zIndex" }] : /* istanbul ignore next */ []));
|
|
893
|
+
/** Opacity (0–1) */
|
|
894
|
+
opacity = input(1, ...(ngDevMode ? [{ debugName: "opacity" }] : /* istanbul ignore next */ []));
|
|
895
|
+
/** Layer visibility */
|
|
896
|
+
visible = input(true, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
897
|
+
/** Disable hit detection for better performance (default: true) */
|
|
898
|
+
disableHitDetection = input(true, ...(ngDevMode ? [{ debugName: "disableHitDetection" }] : /* istanbul ignore next */ []));
|
|
899
|
+
/** Style variables for dynamic expressions (e.g. `['var', 'threshold']`) */
|
|
900
|
+
variables = input(...(ngDevMode ? [undefined, { debugName: "variables" }] : /* istanbul ignore next */ []));
|
|
901
|
+
layer = null;
|
|
902
|
+
vectorSource = new VectorSource();
|
|
903
|
+
constructor() {
|
|
904
|
+
afterNextRender(() => {
|
|
905
|
+
const map = this.mapService.getMap();
|
|
906
|
+
if (!map)
|
|
907
|
+
return;
|
|
908
|
+
this.syncFeatures(this.features());
|
|
909
|
+
try {
|
|
910
|
+
this.layer = new WebGLVectorLayer({
|
|
911
|
+
source: this.vectorSource,
|
|
912
|
+
style: this.flatStyle(),
|
|
913
|
+
visible: this.visible(),
|
|
914
|
+
opacity: this.opacity(),
|
|
915
|
+
zIndex: this.zIndex(),
|
|
916
|
+
disableHitDetection: this.disableHitDetection(),
|
|
917
|
+
...(this.variables() ? { variables: this.variables() } : {}),
|
|
918
|
+
});
|
|
919
|
+
this.layer.set('id', this.id());
|
|
920
|
+
map.addLayer(this.layer);
|
|
921
|
+
}
|
|
922
|
+
catch (err) {
|
|
923
|
+
// WebGL Vector Layer failed to initialize (e.g., not supported by browser)
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
// Reactive feature sync
|
|
927
|
+
effect(() => {
|
|
928
|
+
const currentFeatures = this.features();
|
|
929
|
+
if (this.layer) {
|
|
930
|
+
this.syncFeatures(currentFeatures);
|
|
931
|
+
}
|
|
932
|
+
});
|
|
933
|
+
// Reactive style updates
|
|
934
|
+
effect(() => {
|
|
935
|
+
this.layer?.setStyle(this.flatStyle());
|
|
936
|
+
});
|
|
937
|
+
effect(() => {
|
|
938
|
+
this.layer?.setOpacity(this.opacity());
|
|
939
|
+
});
|
|
940
|
+
effect(() => {
|
|
941
|
+
this.layer?.setVisible(this.visible());
|
|
942
|
+
});
|
|
943
|
+
effect(() => {
|
|
944
|
+
this.layer?.setZIndex(this.zIndex());
|
|
945
|
+
});
|
|
946
|
+
effect(() => {
|
|
947
|
+
const vars = this.variables();
|
|
948
|
+
if (vars && this.layer) {
|
|
949
|
+
this.layer.updateStyleVariables(vars);
|
|
950
|
+
}
|
|
951
|
+
});
|
|
952
|
+
// CRITICAL: WebGL layers must be manually disposed
|
|
953
|
+
this.destroyRef.onDestroy(() => {
|
|
954
|
+
const map = this.mapService.getMap();
|
|
955
|
+
if (map && this.layer) {
|
|
956
|
+
map.removeLayer(this.layer);
|
|
957
|
+
try {
|
|
958
|
+
this.layer.dispose();
|
|
959
|
+
}
|
|
960
|
+
catch {
|
|
961
|
+
// Ignore WebGL layer disposal errors (e.g., reading 'deleteBuffer' if renderer not fully initialized)
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
syncFeatures(features) {
|
|
967
|
+
this.vectorSource.clear();
|
|
968
|
+
if (!features?.length)
|
|
969
|
+
return;
|
|
970
|
+
const olFeatures = features.map((feature) => {
|
|
971
|
+
const geom = feature.geometry;
|
|
972
|
+
let geometry;
|
|
973
|
+
if (!geom.coordinates) {
|
|
974
|
+
geometry = new Point([0, 0]);
|
|
975
|
+
}
|
|
976
|
+
else if (geom.type === 'Point') {
|
|
977
|
+
geometry = new Point(fromLonLat(geom.coordinates));
|
|
978
|
+
}
|
|
979
|
+
else if (geom.type === 'LineString') {
|
|
980
|
+
geometry = new LineString(geom.coordinates.map((c) => fromLonLat(c)));
|
|
981
|
+
}
|
|
982
|
+
else if (geom.type === 'Polygon') {
|
|
983
|
+
geometry = new Polygon(geom.coordinates.map((ring) => ring.map((c) => fromLonLat(c))));
|
|
984
|
+
}
|
|
985
|
+
else if (geom.type === 'Circle') {
|
|
986
|
+
const center = fromLonLat(geom.coordinates);
|
|
987
|
+
geometry = new Circle(center, geom.radius ?? 1000);
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
geometry = new Point([0, 0]);
|
|
991
|
+
}
|
|
992
|
+
const olFeature = new OLFeature({
|
|
993
|
+
geometry,
|
|
994
|
+
...feature.properties,
|
|
995
|
+
});
|
|
996
|
+
olFeature.setId(feature.id);
|
|
997
|
+
return olFeature;
|
|
998
|
+
});
|
|
999
|
+
this.vectorSource.addFeatures(olFeatures);
|
|
1000
|
+
}
|
|
1001
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlWebGLVectorLayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1002
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.13", type: OlWebGLVectorLayerComponent, isStandalone: true, selector: "ol-webgl-vector-layer", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, features: { classPropertyName: "features", publicName: "features", isSignal: true, isRequired: false, transformFunction: null }, flatStyle: { classPropertyName: "flatStyle", publicName: "flatStyle", isSignal: true, isRequired: true, transformFunction: null }, zIndex: { classPropertyName: "zIndex", publicName: "zIndex", isSignal: true, isRequired: false, transformFunction: null }, opacity: { classPropertyName: "opacity", publicName: "opacity", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, disableHitDetection: { classPropertyName: "disableHitDetection", publicName: "disableHitDetection", isSignal: true, isRequired: false, transformFunction: null }, variables: { classPropertyName: "variables", publicName: "variables", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1003
|
+
}
|
|
1004
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlWebGLVectorLayerComponent, decorators: [{
|
|
1005
|
+
type: Component,
|
|
1006
|
+
args: [{
|
|
1007
|
+
selector: 'ol-webgl-vector-layer',
|
|
1008
|
+
template: '',
|
|
1009
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1010
|
+
}]
|
|
1011
|
+
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], features: [{ type: i0.Input, args: [{ isSignal: true, alias: "features", required: false }] }], flatStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "flatStyle", required: true }] }], zIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "zIndex", required: false }] }], opacity: [{ type: i0.Input, args: [{ isSignal: true, alias: "opacity", required: false }] }], visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }], disableHitDetection: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableHitDetection", required: false }] }], variables: [{ type: i0.Input, args: [{ isSignal: true, alias: "variables", required: false }] }] } });
|
|
1012
|
+
|
|
1013
|
+
// OlWebGLTileLayerComponent
|
|
1014
|
+
// GPU-accelerated tile layer with style expressions for color manipulation.
|
|
1015
|
+
/**
|
|
1016
|
+
* GPU-accelerated tile layer with color/brightness/contrast expressions.
|
|
1017
|
+
*
|
|
1018
|
+
* Supports the same tile sources as `ol-tile-layer` but renders via WebGL,
|
|
1019
|
+
* enabling GPU-powered color manipulation (brightness, contrast, saturation, gamma).
|
|
1020
|
+
*
|
|
1021
|
+
* @usageNotes
|
|
1022
|
+
* ```html
|
|
1023
|
+
* <ol-webgl-tile-layer
|
|
1024
|
+
* id="satellite-webgl"
|
|
1025
|
+
* source="xyz"
|
|
1026
|
+
* [url]="'https://server.arcgisonline.com/.../{z}/{y}/{x}'"
|
|
1027
|
+
* [tileStyle]="{ brightness: 0.1, contrast: 0.2 }">
|
|
1028
|
+
* </ol-webgl-tile-layer>
|
|
1029
|
+
* ```
|
|
1030
|
+
*/
|
|
1031
|
+
class OlWebGLTileLayerComponent {
|
|
1032
|
+
mapService = inject(OlMapService);
|
|
1033
|
+
destroyRef = inject(DestroyRef);
|
|
1034
|
+
/** Unique layer identifier */
|
|
1035
|
+
id = input.required(...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
1036
|
+
/** Tile source type */
|
|
1037
|
+
source = input.required(...(ngDevMode ? [{ debugName: "source" }] : /* istanbul ignore next */ []));
|
|
1038
|
+
/** Tile URL template (required for 'xyz' and 'mvt') */
|
|
1039
|
+
url = input(...(ngDevMode ? [undefined, { debugName: "url" }] : /* istanbul ignore next */ []));
|
|
1040
|
+
/** Attribution text */
|
|
1041
|
+
attributions = input(...(ngDevMode ? [undefined, { debugName: "attributions" }] : /* istanbul ignore next */ []));
|
|
1042
|
+
/** WebGL tile style (raster expressions) or flat style (MVT) */
|
|
1043
|
+
tileStyle = input(...(ngDevMode ? [undefined, { debugName: "tileStyle" }] : /* istanbul ignore next */ []));
|
|
1044
|
+
/** Z-index for layer ordering */
|
|
1045
|
+
zIndex = input(0, ...(ngDevMode ? [{ debugName: "zIndex" }] : /* istanbul ignore next */ []));
|
|
1046
|
+
/** Opacity (0–1) */
|
|
1047
|
+
opacity = input(1, ...(ngDevMode ? [{ debugName: "opacity" }] : /* istanbul ignore next */ []));
|
|
1048
|
+
/** Layer visibility */
|
|
1049
|
+
visible = input(true, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
1050
|
+
/** Preload low-res tiles up to this many zoom levels */
|
|
1051
|
+
preload = input(0, ...(ngDevMode ? [{ debugName: "preload" }] : /* istanbul ignore next */ []));
|
|
1052
|
+
layer = null;
|
|
1053
|
+
constructor() {
|
|
1054
|
+
afterNextRender(() => {
|
|
1055
|
+
const map = this.mapService.getMap();
|
|
1056
|
+
if (!map)
|
|
1057
|
+
return;
|
|
1058
|
+
let tileSource;
|
|
1059
|
+
switch (this.source()) {
|
|
1060
|
+
case 'mvt':
|
|
1061
|
+
tileSource = new VectorTileSource({
|
|
1062
|
+
format: new MVT(),
|
|
1063
|
+
url: this.url(),
|
|
1064
|
+
attributions: this.attributions(),
|
|
1065
|
+
});
|
|
1066
|
+
this.layer = new WebGLVectorTileLayer({
|
|
1067
|
+
source: tileSource,
|
|
1068
|
+
visible: this.visible(),
|
|
1069
|
+
opacity: this.opacity(),
|
|
1070
|
+
zIndex: this.zIndex(),
|
|
1071
|
+
style: this.tileStyle() || {},
|
|
1072
|
+
});
|
|
1073
|
+
break;
|
|
1074
|
+
case 'xyz':
|
|
1075
|
+
tileSource = new XYZ({
|
|
1076
|
+
url: this.url(),
|
|
1077
|
+
attributions: this.attributions(),
|
|
1078
|
+
});
|
|
1079
|
+
this.layer = new WebGLTileLayer({
|
|
1080
|
+
source: tileSource,
|
|
1081
|
+
visible: this.visible(),
|
|
1082
|
+
opacity: this.opacity(),
|
|
1083
|
+
zIndex: this.zIndex(),
|
|
1084
|
+
preload: this.preload(),
|
|
1085
|
+
...(this.tileStyle() ? { style: this.tileStyle() } : {}),
|
|
1086
|
+
});
|
|
1087
|
+
break;
|
|
1088
|
+
case 'osm':
|
|
1089
|
+
default:
|
|
1090
|
+
tileSource = new OSM({
|
|
1091
|
+
attributions: this.attributions(),
|
|
1092
|
+
});
|
|
1093
|
+
this.layer = new WebGLTileLayer({
|
|
1094
|
+
source: tileSource,
|
|
1095
|
+
visible: this.visible(),
|
|
1096
|
+
opacity: this.opacity(),
|
|
1097
|
+
zIndex: this.zIndex(),
|
|
1098
|
+
preload: this.preload(),
|
|
1099
|
+
...(this.tileStyle() ? { style: this.tileStyle() } : {}),
|
|
1100
|
+
});
|
|
1101
|
+
break;
|
|
1102
|
+
}
|
|
1103
|
+
if (this.layer) {
|
|
1104
|
+
this.layer.set('id', this.id());
|
|
1105
|
+
map.addLayer(this.layer);
|
|
1106
|
+
}
|
|
1107
|
+
});
|
|
1108
|
+
effect(() => {
|
|
1109
|
+
this.layer?.setOpacity(this.opacity());
|
|
1110
|
+
});
|
|
1111
|
+
effect(() => {
|
|
1112
|
+
this.layer?.setVisible(this.visible());
|
|
1113
|
+
});
|
|
1114
|
+
effect(() => {
|
|
1115
|
+
this.layer?.setZIndex(this.zIndex());
|
|
1116
|
+
});
|
|
1117
|
+
effect(() => {
|
|
1118
|
+
const style = this.tileStyle();
|
|
1119
|
+
if (style && this.layer) {
|
|
1120
|
+
if (this.source() === 'mvt') {
|
|
1121
|
+
this.layer.setStyle(style);
|
|
1122
|
+
}
|
|
1123
|
+
else {
|
|
1124
|
+
this.layer.setStyle(style);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
});
|
|
1128
|
+
// WebGL tile layers also need manual dispose
|
|
1129
|
+
this.destroyRef.onDestroy(() => {
|
|
1130
|
+
const map = this.mapService.getMap();
|
|
1131
|
+
if (map && this.layer) {
|
|
1132
|
+
map.removeLayer(this.layer);
|
|
1133
|
+
this.layer.dispose();
|
|
1134
|
+
}
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlWebGLTileLayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1138
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.13", type: OlWebGLTileLayerComponent, isStandalone: true, selector: "ol-webgl-tile-layer", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: true, transformFunction: null }, url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: false, transformFunction: null }, attributions: { classPropertyName: "attributions", publicName: "attributions", isSignal: true, isRequired: false, transformFunction: null }, tileStyle: { classPropertyName: "tileStyle", publicName: "tileStyle", isSignal: true, isRequired: false, transformFunction: null }, zIndex: { classPropertyName: "zIndex", publicName: "zIndex", isSignal: true, isRequired: false, transformFunction: null }, opacity: { classPropertyName: "opacity", publicName: "opacity", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, preload: { classPropertyName: "preload", publicName: "preload", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1139
|
+
}
|
|
1140
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlWebGLTileLayerComponent, decorators: [{
|
|
1141
|
+
type: Component,
|
|
1142
|
+
args: [{
|
|
1143
|
+
selector: 'ol-webgl-tile-layer',
|
|
1144
|
+
template: '',
|
|
1145
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1146
|
+
}]
|
|
1147
|
+
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], source: [{ type: i0.Input, args: [{ isSignal: true, alias: "source", required: true }] }], url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: false }] }], attributions: [{ type: i0.Input, args: [{ isSignal: true, alias: "attributions", required: false }] }], tileStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "tileStyle", required: false }] }], zIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "zIndex", required: false }] }], opacity: [{ type: i0.Input, args: [{ isSignal: true, alias: "opacity", required: false }] }], visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }], preload: [{ type: i0.Input, args: [{ isSignal: true, alias: "preload", required: false }] }] } });
|
|
1148
|
+
|
|
598
1149
|
// Provider functions
|
|
599
1150
|
function withLayers() {
|
|
600
1151
|
return { kind: 'layers', providers: [OlLayerService] };
|
|
@@ -609,4 +1160,4 @@ function provideLayers() {
|
|
|
609
1160
|
* Generated bundle index. Do not edit.
|
|
610
1161
|
*/
|
|
611
1162
|
|
|
612
|
-
export { OlImageLayerComponent, OlLayerService, OlTileLayerComponent, OlVectorLayerComponent, provideLayers, withLayers };
|
|
1163
|
+
export { OlClusterComponent, OlHeatmapLayerComponent, OlImageLayerComponent, OlLayerService, OlTileLayerComponent, OlVectorLayerComponent, OlWebGLTileLayerComponent, OlWebGLVectorLayerComponent, provideLayers, withLayers };
|