@angular-helpers/openlayers 0.5.1 → 0.6.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
CHANGED
|
@@ -45,7 +45,7 @@ export const appConfig: ApplicationConfig = {
|
|
|
45
45
|
|
|
46
46
|
### 2. Use in your component
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
````typescript
|
|
49
49
|
// map.component.ts
|
|
50
50
|
import { Component, inject, signal } from '@angular/core';
|
|
51
51
|
import { OlMapComponent } from '@angular-helpers/openlayers/core';
|
|
@@ -103,8 +103,57 @@ export class MapComponent {
|
|
|
103
103
|
this.mapService.animateView({ center: [2.2945, 48.8584], zoom: 15, duration: 1000 });
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
+
## Custom Projections & Coordinate Systems
|
|
107
|
+
|
|
108
|
+
Available since `0.4.1` in `@angular-helpers/openlayers/core` and `@angular-helpers/openlayers/layers`.
|
|
109
|
+
|
|
110
|
+
When working with local reference systems (like UTM zones), you can register custom projections globally and pass coordinates in those reference systems directly to `[center]` or `[features]` without manual transforms:
|
|
111
|
+
|
|
112
|
+
### 1. Register custom projections globally
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
// app.config.ts
|
|
116
|
+
import { provideOpenLayers } from '@angular-helpers/openlayers/core';
|
|
117
|
+
import { withProjections } from '@angular-helpers/openlayers/core';
|
|
118
|
+
import proj4 from 'proj4';
|
|
119
|
+
|
|
120
|
+
export const appConfig = {
|
|
121
|
+
providers: [
|
|
122
|
+
provideOpenLayers(
|
|
123
|
+
withProjections(proj4, [
|
|
124
|
+
{
|
|
125
|
+
code: 'EPSG:25830', // UTM Zone 30N
|
|
126
|
+
def: '+proj=utm +zone=30 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs',
|
|
127
|
+
extent: [0, 0, 1000000, 10000000],
|
|
128
|
+
},
|
|
129
|
+
]),
|
|
130
|
+
),
|
|
131
|
+
],
|
|
132
|
+
};
|
|
133
|
+
````
|
|
134
|
+
|
|
135
|
+
### 2. Pass local coordinates natively to the map
|
|
136
|
+
|
|
137
|
+
By setting `[coordinateProjection]` to match the custom projection, the component automatically bypasses longitude/latitude conversion, feeding UTM coordinates directly into OpenLayers:
|
|
138
|
+
|
|
139
|
+
```html
|
|
140
|
+
<ol-map
|
|
141
|
+
[projection]="'EPSG:25830'"
|
|
142
|
+
[coordinateProjection]="'EPSG:25830'"
|
|
143
|
+
[center]="[440291, 4474255]" <!-- Madrid UTM Zone 30 coordinates -->
|
|
144
|
+
[zoom]="12"
|
|
145
|
+
(viewChange)="onViewChange($event)"
|
|
146
|
+
>
|
|
147
|
+
<ol-vector-layer
|
|
148
|
+
id="shapes"
|
|
149
|
+
[features]="utmFeatures()"
|
|
150
|
+
[coordinateProjection]="'EPSG:25830'"
|
|
151
|
+
/>
|
|
152
|
+
</ol-map>
|
|
106
153
|
```
|
|
107
154
|
|
|
155
|
+
---
|
|
156
|
+
|
|
108
157
|
## Overlays — popups and tooltips
|
|
109
158
|
|
|
110
159
|
Available since `0.3.0` from `@angular-helpers/openlayers/overlays`.
|
|
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { inject, NgZone, Injectable, signal, DestroyRef, input, output, viewChild, afterNextRender, effect, ChangeDetectionStrategy, Component, computed, resource, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
3
3
|
import OLMap from 'ol/Map';
|
|
4
4
|
import View from 'ol/View';
|
|
5
|
-
import {
|
|
5
|
+
import { transform, get } from 'ol/proj';
|
|
6
6
|
import { offset } from 'ol/sphere';
|
|
7
7
|
import GeoJSON from 'ol/format/GeoJSON';
|
|
8
8
|
import FeatureClass from 'ol/Feature';
|
|
@@ -164,6 +164,7 @@ class OlMapComponent {
|
|
|
164
164
|
zoom = input(0, ...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
|
|
165
165
|
rotation = input(0, ...(ngDevMode ? [{ debugName: "rotation" }] : /* istanbul ignore next */ []));
|
|
166
166
|
projection = input('EPSG:3857', ...(ngDevMode ? [{ debugName: "projection" }] : /* istanbul ignore next */ []));
|
|
167
|
+
coordinateProjection = input('EPSG:4326', ...(ngDevMode ? [{ debugName: "coordinateProjection" }] : /* istanbul ignore next */ [])); // Dynamic input for coordinate systems
|
|
167
168
|
viewChange = output();
|
|
168
169
|
mapClick = output();
|
|
169
170
|
mapDblClick = output();
|
|
@@ -190,11 +191,25 @@ class OlMapComponent {
|
|
|
190
191
|
// Cleanup when component is destroyed
|
|
191
192
|
this.destroyRef.onDestroy(() => this.destroyMap());
|
|
192
193
|
}
|
|
194
|
+
getProjectedCoordinate(coord) {
|
|
195
|
+
const coordProj = this.coordinateProjection();
|
|
196
|
+
const mapProj = this.projection();
|
|
197
|
+
if (coordProj === mapProj)
|
|
198
|
+
return coord;
|
|
199
|
+
return transform(coord, coordProj, mapProj);
|
|
200
|
+
}
|
|
201
|
+
getExternalCoordinate(coord) {
|
|
202
|
+
const coordProj = this.coordinateProjection();
|
|
203
|
+
const mapProj = this.projection();
|
|
204
|
+
if (coordProj === mapProj)
|
|
205
|
+
return coord;
|
|
206
|
+
return transform(coord, mapProj, coordProj);
|
|
207
|
+
}
|
|
193
208
|
initMap() {
|
|
194
209
|
const container = this.mapContainerRef().nativeElement;
|
|
195
210
|
this.zoneHelper.runOutsideAngular(() => {
|
|
196
211
|
const view = new View({
|
|
197
|
-
center:
|
|
212
|
+
center: this.getProjectedCoordinate(this.center()),
|
|
198
213
|
zoom: this.zoom(),
|
|
199
214
|
rotation: this.rotation(),
|
|
200
215
|
projection: this.projection(),
|
|
@@ -222,11 +237,11 @@ class OlMapComponent {
|
|
|
222
237
|
});
|
|
223
238
|
});
|
|
224
239
|
this.map.on('click', (e) => this.zoneHelper.runInsideAngular(() => this.mapClick.emit({
|
|
225
|
-
coordinate:
|
|
240
|
+
coordinate: this.getExternalCoordinate(e.coordinate),
|
|
226
241
|
pixel: e.pixel,
|
|
227
242
|
})));
|
|
228
243
|
this.map.on('dblclick', (e) => this.zoneHelper.runInsideAngular(() => this.mapDblClick.emit({
|
|
229
|
-
coordinate:
|
|
244
|
+
coordinate: this.getExternalCoordinate(e.coordinate),
|
|
230
245
|
pixel: e.pixel,
|
|
231
246
|
})));
|
|
232
247
|
});
|
|
@@ -250,7 +265,7 @@ class OlMapComponent {
|
|
|
250
265
|
if (!this.map)
|
|
251
266
|
return;
|
|
252
267
|
const view = this.map.getView();
|
|
253
|
-
const projectedCenter =
|
|
268
|
+
const projectedCenter = this.getProjectedCoordinate(center);
|
|
254
269
|
const currentCenter = view.getCenter();
|
|
255
270
|
// Only update if center is significantly different (prevents interfering with animations)
|
|
256
271
|
if (!currentCenter ||
|
|
@@ -283,23 +298,23 @@ class OlMapComponent {
|
|
|
283
298
|
const view = this.map?.getView();
|
|
284
299
|
if (view) {
|
|
285
300
|
const projectedCenter = view.getCenter() ?? [0, 0];
|
|
286
|
-
const
|
|
301
|
+
const externalCenter = this.getExternalCoordinate(projectedCenter);
|
|
287
302
|
this.viewChange.emit({
|
|
288
|
-
center:
|
|
303
|
+
center: externalCenter,
|
|
289
304
|
zoom: view.getZoom() ?? 0,
|
|
290
305
|
rotation: view.getRotation() ?? 0,
|
|
291
306
|
});
|
|
292
307
|
}
|
|
293
308
|
}
|
|
294
309
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
295
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.13", type: OlMapComponent, isStandalone: true, selector: "ol-map", inputs: { center: { classPropertyName: "center", publicName: "center", isSignal: true, isRequired: false, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, rotation: { classPropertyName: "rotation", publicName: "rotation", isSignal: true, isRequired: false, transformFunction: null }, projection: { classPropertyName: "projection", publicName: "projection", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { viewChange: "viewChange", mapClick: "mapClick", mapDblClick: "mapDblClick" }, viewQueries: [{ propertyName: "mapContainerRef", first: true, predicate: ["mapContainer"], descendants: true, isSignal: true }], ngImport: i0, template: `<div class="ol-map-container" #mapContainer></div>
|
|
310
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.13", type: OlMapComponent, isStandalone: true, selector: "ol-map", inputs: { center: { classPropertyName: "center", publicName: "center", isSignal: true, isRequired: false, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, rotation: { classPropertyName: "rotation", publicName: "rotation", isSignal: true, isRequired: false, transformFunction: null }, projection: { classPropertyName: "projection", publicName: "projection", isSignal: true, isRequired: false, transformFunction: null }, coordinateProjection: { classPropertyName: "coordinateProjection", publicName: "coordinateProjection", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { viewChange: "viewChange", mapClick: "mapClick", mapDblClick: "mapDblClick" }, viewQueries: [{ propertyName: "mapContainerRef", first: true, predicate: ["mapContainer"], descendants: true, isSignal: true }], ngImport: i0, template: `<div class="ol-map-container" #mapContainer></div>
|
|
296
311
|
<ng-content />`, isInline: true, styles: [":host{display:block;width:100%;height:100%;position:relative}.ol-map-container{width:100%;height:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
297
312
|
}
|
|
298
313
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlMapComponent, decorators: [{
|
|
299
314
|
type: Component,
|
|
300
315
|
args: [{ selector: 'ol-map', template: `<div class="ol-map-container" #mapContainer></div>
|
|
301
316
|
<ng-content />`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%;height:100%;position:relative}.ol-map-container{width:100%;height:100%}\n"] }]
|
|
302
|
-
}], ctorParameters: () => [], propDecorators: { center: [{ type: i0.Input, args: [{ isSignal: true, alias: "center", required: false }] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }], rotation: [{ type: i0.Input, args: [{ isSignal: true, alias: "rotation", required: false }] }], projection: [{ type: i0.Input, args: [{ isSignal: true, alias: "projection", required: false }] }], viewChange: [{ type: i0.Output, args: ["viewChange"] }], mapClick: [{ type: i0.Output, args: ["mapClick"] }], mapDblClick: [{ type: i0.Output, args: ["mapDblClick"] }], mapContainerRef: [{ type: i0.ViewChild, args: ['mapContainer', { isSignal: true }] }] } });
|
|
317
|
+
}], ctorParameters: () => [], propDecorators: { center: [{ type: i0.Input, args: [{ isSignal: true, alias: "center", required: false }] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }], rotation: [{ type: i0.Input, args: [{ isSignal: true, alias: "rotation", required: false }] }], projection: [{ type: i0.Input, args: [{ isSignal: true, alias: "projection", required: false }] }], coordinateProjection: [{ type: i0.Input, args: [{ isSignal: true, alias: "coordinateProjection", required: false }] }], viewChange: [{ type: i0.Output, args: ["viewChange"] }], mapClick: [{ type: i0.Output, args: ["mapClick"] }], mapDblClick: [{ type: i0.Output, args: ["mapDblClick"] }], mapContainerRef: [{ type: i0.ViewChild, args: ['mapContainer', { isSignal: true }] }] } });
|
|
303
318
|
|
|
304
319
|
// OlGeometryService — general purpose geometry helpers
|
|
305
320
|
/**
|
|
@@ -511,44 +526,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImpo
|
|
|
511
526
|
}] });
|
|
512
527
|
|
|
513
528
|
// Feature conversion utilities for OpenLayers interactions
|
|
529
|
+
function transformCoords(coords, sourceProj, targetProj) {
|
|
530
|
+
if (!sourceProj || !targetProj || sourceProj === targetProj)
|
|
531
|
+
return coords;
|
|
532
|
+
if (!Array.isArray(coords))
|
|
533
|
+
return coords;
|
|
534
|
+
if (typeof coords[0] === 'number') {
|
|
535
|
+
return transform(coords, sourceProj, targetProj);
|
|
536
|
+
}
|
|
537
|
+
return coords.map((c) => transformCoords(c, sourceProj, targetProj));
|
|
538
|
+
}
|
|
514
539
|
/**
|
|
515
540
|
* Converts an OpenLayers feature to the internal Feature format.
|
|
516
|
-
* Handles coordinate extraction and geometry type mapping.
|
|
541
|
+
* Handles coordinate extraction and geometry type mapping with custom projections.
|
|
517
542
|
*
|
|
518
543
|
* @param olFeature - The OpenLayers feature to convert
|
|
544
|
+
* @param options - Projection source and target codes
|
|
519
545
|
* @returns The converted Feature with normalized structure
|
|
520
546
|
*/
|
|
521
|
-
function olFeatureToFeature(olFeature) {
|
|
547
|
+
function olFeatureToFeature(olFeature, options) {
|
|
522
548
|
// Unwrap spider features
|
|
523
549
|
const spiderFeature = olFeature.get('spider-feature');
|
|
524
550
|
if (spiderFeature) {
|
|
525
|
-
return olFeatureToFeature(spiderFeature);
|
|
551
|
+
return olFeatureToFeature(spiderFeature, options);
|
|
526
552
|
}
|
|
527
553
|
// Unwrap single-item clusters
|
|
528
554
|
const clusterFeatures = olFeature.get('features');
|
|
529
555
|
if (Array.isArray(clusterFeatures) && clusterFeatures.length === 1) {
|
|
530
|
-
return olFeatureToFeature(clusterFeatures[0]);
|
|
556
|
+
return olFeatureToFeature(clusterFeatures[0], options);
|
|
531
557
|
}
|
|
532
558
|
const geometry = olFeature.getGeometry();
|
|
533
559
|
const geomType = geometry?.getType() ?? 'Point';
|
|
560
|
+
const sourceProj = options?.targetProjection;
|
|
561
|
+
const targetProj = options?.sourceProjection;
|
|
534
562
|
// Convert coordinates based on geometry type
|
|
535
563
|
let coordinates;
|
|
536
564
|
if (geomType === 'Circle') {
|
|
537
565
|
// ol/geom/Circle has no getCoordinates() — use getCenter() instead
|
|
538
566
|
const circle = geometry;
|
|
539
|
-
coordinates = circle.getCenter();
|
|
567
|
+
coordinates = transformCoords(circle.getCenter(), sourceProj, targetProj);
|
|
540
568
|
}
|
|
541
569
|
else {
|
|
542
570
|
// oxlint-disable-next-line no-explicit-any
|
|
543
571
|
const olCoords = geometry.getCoordinates();
|
|
544
|
-
|
|
545
|
-
// Multi-coordinate structures (LineString, Polygon, etc.)
|
|
546
|
-
coordinates = olCoords;
|
|
547
|
-
}
|
|
548
|
-
else {
|
|
549
|
-
// Single point
|
|
550
|
-
coordinates = olCoords;
|
|
551
|
-
}
|
|
572
|
+
coordinates = transformCoords(olCoords, sourceProj, targetProj);
|
|
552
573
|
}
|
|
553
574
|
return {
|
|
554
575
|
id: olFeature.getId()?.toString() ?? `feature-${Math.random().toString(36).slice(2)}`,
|
|
@@ -562,7 +583,9 @@ function olFeatureToFeature(olFeature) {
|
|
|
562
583
|
/**
|
|
563
584
|
* Converts an internal Feature to an OpenLayers feature.
|
|
564
585
|
*/
|
|
565
|
-
function featureToOlFeature(feature) {
|
|
586
|
+
function featureToOlFeature(feature, options) {
|
|
587
|
+
const sourceProj = options?.sourceProjection ?? 'EPSG:4326';
|
|
588
|
+
const targetProj = options?.targetProjection ?? 'EPSG:3857';
|
|
566
589
|
const geom = feature.geometry;
|
|
567
590
|
let geometry;
|
|
568
591
|
if (!geom.coordinates) {
|
|
@@ -570,14 +593,14 @@ function featureToOlFeature(feature) {
|
|
|
570
593
|
}
|
|
571
594
|
else if (geom.type === 'Point') {
|
|
572
595
|
const coords = geom.coordinates;
|
|
573
|
-
geometry = new Point(
|
|
596
|
+
geometry = new Point(transformCoords(coords, sourceProj, targetProj));
|
|
574
597
|
}
|
|
575
598
|
else if (geom.type === 'LineString') {
|
|
576
|
-
const coords = geom.coordinates
|
|
599
|
+
const coords = transformCoords(geom.coordinates, sourceProj, targetProj);
|
|
577
600
|
geometry = new LineString(coords);
|
|
578
601
|
}
|
|
579
602
|
else if (geom.type === 'Polygon') {
|
|
580
|
-
const rings = geom.coordinates
|
|
603
|
+
const rings = transformCoords(geom.coordinates, sourceProj, targetProj);
|
|
581
604
|
geometry = new Polygon(rings);
|
|
582
605
|
}
|
|
583
606
|
else {
|
|
@@ -166,12 +166,13 @@ function buildVectorLayer(config, source) {
|
|
|
166
166
|
layer.set('id', config.id);
|
|
167
167
|
layer.set('cluster-config', clusterCfg);
|
|
168
168
|
layer.set('style-fn', styleFn);
|
|
169
|
+
layer.set('coordinate-projection', config.coordinateProjection);
|
|
169
170
|
return layer;
|
|
170
171
|
}
|
|
171
172
|
function buildHeatmapLayer(config) {
|
|
172
173
|
const vectorSource = new VectorSource();
|
|
173
174
|
if (config.features && config.features.length > 0) {
|
|
174
|
-
const olFeatures = config.features.map(featureToOlFeature);
|
|
175
|
+
const olFeatures = config.features.map((f) => featureToOlFeature(f));
|
|
175
176
|
vectorSource.addFeatures(olFeatures);
|
|
176
177
|
}
|
|
177
178
|
const layer = new HeatmapLayer({
|
|
@@ -392,9 +393,16 @@ class OlLayerService {
|
|
|
392
393
|
sourceOptions.format = new KML();
|
|
393
394
|
}
|
|
394
395
|
const vectorSource = new VectorSource(sourceOptions);
|
|
396
|
+
const targetProj = (typeof map.getView === 'function'
|
|
397
|
+
? map.getView()?.getProjection()?.getCode()
|
|
398
|
+
: undefined) ?? 'EPSG:3857';
|
|
399
|
+
const sourceProj = vConfig.coordinateProjection ?? 'EPSG:4326';
|
|
395
400
|
if (vConfig.features && vConfig.features.length > 0) {
|
|
396
401
|
const olFeatures = vConfig.features.map((f) => {
|
|
397
|
-
const olf = featureToOlFeature(f
|
|
402
|
+
const olf = featureToOlFeature(f, {
|
|
403
|
+
sourceProjection: sourceProj,
|
|
404
|
+
targetProjection: targetProj,
|
|
405
|
+
});
|
|
398
406
|
if (f.style)
|
|
399
407
|
olf.set(STYLE_PROP, f.style);
|
|
400
408
|
return olf;
|
|
@@ -462,6 +470,28 @@ class OlLayerService {
|
|
|
462
470
|
const layer = this.layerCache.get(id);
|
|
463
471
|
if (map && layer) {
|
|
464
472
|
map.removeLayer(layer);
|
|
473
|
+
// Explicitly dispose sources to prevent memory leaks
|
|
474
|
+
if ('getSource' in layer) {
|
|
475
|
+
const source = layer.getSource();
|
|
476
|
+
if (source) {
|
|
477
|
+
// If it's a ClusterSource, dispose the underlying source first
|
|
478
|
+
if ('getSource' in source && typeof source.getSource === 'function') {
|
|
479
|
+
const underlyingSource = source.getSource();
|
|
480
|
+
if (underlyingSource && typeof underlyingSource.dispose === 'function') {
|
|
481
|
+
if (typeof underlyingSource.clear === 'function') {
|
|
482
|
+
underlyingSource.clear(true);
|
|
483
|
+
}
|
|
484
|
+
underlyingSource.dispose();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (typeof source.dispose === 'function') {
|
|
488
|
+
if (typeof source.clear === 'function') {
|
|
489
|
+
source.clear(true);
|
|
490
|
+
}
|
|
491
|
+
source.dispose();
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
465
495
|
layer.dispose();
|
|
466
496
|
this.layerCache.delete(id);
|
|
467
497
|
this.updateLayerState();
|
|
@@ -580,6 +610,11 @@ class OlLayerService {
|
|
|
580
610
|
const layer = this.layerCache.get(id);
|
|
581
611
|
if (!(layer instanceof VectorLayer))
|
|
582
612
|
return;
|
|
613
|
+
const sourceProj = layer.get('coordinate-projection') ?? 'EPSG:4326';
|
|
614
|
+
const map = this.mapService.getMap();
|
|
615
|
+
const targetProj = (map && typeof map.getView === 'function'
|
|
616
|
+
? map.getView()?.getProjection()?.getCode()
|
|
617
|
+
: undefined) ?? 'EPSG:3857';
|
|
583
618
|
const source = layer.getSource();
|
|
584
619
|
if (!source)
|
|
585
620
|
return;
|
|
@@ -605,7 +640,10 @@ class OlLayerService {
|
|
|
605
640
|
const featuresToAdd = features.filter((f) => !existingIds.has(f.id));
|
|
606
641
|
if (featuresToAdd.length > 0) {
|
|
607
642
|
const olFeatures = featuresToAdd.map((f) => {
|
|
608
|
-
const olf = featureToOlFeature(f
|
|
643
|
+
const olf = featureToOlFeature(f, {
|
|
644
|
+
sourceProjection: sourceProj,
|
|
645
|
+
targetProjection: targetProj,
|
|
646
|
+
});
|
|
609
647
|
if (f.style)
|
|
610
648
|
olf.set(STYLE_PROP, f.style);
|
|
611
649
|
return olf;
|
|
@@ -674,6 +712,7 @@ class OlVectorLayerComponent {
|
|
|
674
712
|
style = input(...(ngDevMode ? [undefined, { debugName: "style" }] : /* istanbul ignore next */ []));
|
|
675
713
|
cluster = input(...(ngDevMode ? [undefined, { debugName: "cluster" }] : /* istanbul ignore next */ []));
|
|
676
714
|
clusterComponent = contentChild(OlClusterComponent, ...(ngDevMode ? [{ debugName: "clusterComponent" }] : /* istanbul ignore next */ []));
|
|
715
|
+
coordinateProjection = input('EPSG:4326', ...(ngDevMode ? [{ debugName: "coordinateProjection" }] : /* istanbul ignore next */ []));
|
|
677
716
|
constructor() {
|
|
678
717
|
// Initialize layer after DOM is ready
|
|
679
718
|
afterNextRender(() => {
|
|
@@ -701,6 +740,7 @@ class OlVectorLayerComponent {
|
|
|
701
740
|
visible: this.visible(),
|
|
702
741
|
style: this.style(),
|
|
703
742
|
cluster: resolvedClusterConfig,
|
|
743
|
+
coordinateProjection: this.coordinateProjection(),
|
|
704
744
|
});
|
|
705
745
|
});
|
|
706
746
|
// Effect to sync features when input changes
|
|
@@ -737,7 +777,7 @@ class OlVectorLayerComponent {
|
|
|
737
777
|
});
|
|
738
778
|
}
|
|
739
779
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlVectorLayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
740
|
-
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 });
|
|
780
|
+
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 }, coordinateProjection: { classPropertyName: "coordinateProjection", publicName: "coordinateProjection", 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 });
|
|
741
781
|
}
|
|
742
782
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: OlVectorLayerComponent, decorators: [{
|
|
743
783
|
type: Component,
|
|
@@ -746,7 +786,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImpo
|
|
|
746
786
|
template: '',
|
|
747
787
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
748
788
|
}]
|
|
749
|
-
}], 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 }] }] } });
|
|
789
|
+
}], 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 }] }], coordinateProjection: [{ type: i0.Input, args: [{ isSignal: true, alias: "coordinateProjection", required: false }] }] } });
|
|
750
790
|
|
|
751
791
|
// OlTileLayerComponent
|
|
752
792
|
class OlTileLayerComponent {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular-helpers/openlayers",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Modern Angular wrapper for OpenLayers with modular architecture, standalone components, and hybrid template/programmatic API",
|
|
5
5
|
"homepage": "https://gaspar1992.github.io/angular-helpers/docs/openlayers",
|
|
6
6
|
"repository": {
|
|
@@ -94,6 +94,7 @@ declare class OlMapComponent {
|
|
|
94
94
|
zoom: _angular_core.InputSignal<number>;
|
|
95
95
|
rotation: _angular_core.InputSignal<number>;
|
|
96
96
|
projection: _angular_core.InputSignal<string>;
|
|
97
|
+
coordinateProjection: _angular_core.InputSignal<string>;
|
|
97
98
|
viewChange: _angular_core.OutputEmitterRef<ViewState>;
|
|
98
99
|
mapClick: _angular_core.OutputEmitterRef<MapClickEvent>;
|
|
99
100
|
mapDblClick: _angular_core.OutputEmitterRef<MapClickEvent>;
|
|
@@ -101,6 +102,8 @@ declare class OlMapComponent {
|
|
|
101
102
|
private map?;
|
|
102
103
|
private resizeObserver?;
|
|
103
104
|
constructor();
|
|
105
|
+
private getProjectedCoordinate;
|
|
106
|
+
private getExternalCoordinate;
|
|
104
107
|
private initMap;
|
|
105
108
|
private destroyMap;
|
|
106
109
|
private updateCenter;
|
|
@@ -108,7 +111,7 @@ declare class OlMapComponent {
|
|
|
108
111
|
private updateRotation;
|
|
109
112
|
private emitViewChange;
|
|
110
113
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlMapComponent, never>;
|
|
111
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlMapComponent, "ol-map", never, { "center": { "alias": "center"; "required": false; "isSignal": true; }; "zoom": { "alias": "zoom"; "required": false; "isSignal": true; }; "rotation": { "alias": "rotation"; "required": false; "isSignal": true; }; "projection": { "alias": "projection"; "required": false; "isSignal": true; }; }, { "viewChange": "viewChange"; "mapClick": "mapClick"; "mapDblClick": "mapDblClick"; }, never, ["*"], true, never>;
|
|
114
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlMapComponent, "ol-map", never, { "center": { "alias": "center"; "required": false; "isSignal": true; }; "zoom": { "alias": "zoom"; "required": false; "isSignal": true; }; "rotation": { "alias": "rotation"; "required": false; "isSignal": true; }; "projection": { "alias": "projection"; "required": false; "isSignal": true; }; "coordinateProjection": { "alias": "coordinateProjection"; "required": false; "isSignal": true; }; }, { "viewChange": "viewChange"; "mapClick": "mapClick"; "mapDblClick": "mapDblClick"; }, never, ["*"], true, never>;
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
/**
|
|
@@ -379,18 +382,23 @@ interface VectorResourceOptions {
|
|
|
379
382
|
*/
|
|
380
383
|
declare function createVectorResource(url: Signal<string | undefined>, options?: VectorResourceOptions): Resource<Feature[]>;
|
|
381
384
|
|
|
385
|
+
interface ProjectionOptions {
|
|
386
|
+
sourceProjection?: string;
|
|
387
|
+
targetProjection?: string;
|
|
388
|
+
}
|
|
382
389
|
/**
|
|
383
390
|
* Converts an OpenLayers feature to the internal Feature format.
|
|
384
|
-
* Handles coordinate extraction and geometry type mapping.
|
|
391
|
+
* Handles coordinate extraction and geometry type mapping with custom projections.
|
|
385
392
|
*
|
|
386
393
|
* @param olFeature - The OpenLayers feature to convert
|
|
394
|
+
* @param options - Projection source and target codes
|
|
387
395
|
* @returns The converted Feature with normalized structure
|
|
388
396
|
*/
|
|
389
|
-
declare function olFeatureToFeature(olFeature: Feature$1): Feature;
|
|
397
|
+
declare function olFeatureToFeature(olFeature: Feature$1, options?: ProjectionOptions): Feature;
|
|
390
398
|
/**
|
|
391
399
|
* Converts an internal Feature to an OpenLayers feature.
|
|
392
400
|
*/
|
|
393
|
-
declare function featureToOlFeature(feature: Feature): Feature$1;
|
|
401
|
+
declare function featureToOlFeature(feature: Feature, options?: ProjectionOptions): Feature$1;
|
|
394
402
|
|
|
395
403
|
type OlFeatureKind = 'layers' | 'controls' | 'interactions' | 'overlays' | 'military' | 'projections';
|
|
396
404
|
interface OlFeature<Kind extends OlFeatureKind> {
|
|
@@ -33,6 +33,7 @@ interface VectorLayerConfig extends LayerConfig {
|
|
|
33
33
|
format?: 'geojson' | 'topojson' | 'kml';
|
|
34
34
|
style?: Style | ((feature: Feature) => Style);
|
|
35
35
|
cluster?: ClusterConfig;
|
|
36
|
+
coordinateProjection?: string;
|
|
36
37
|
}
|
|
37
38
|
interface HeatmapLayerConfig extends LayerConfig {
|
|
38
39
|
type: 'heatmap';
|
|
@@ -87,9 +88,10 @@ declare class OlVectorLayerComponent {
|
|
|
87
88
|
style: _angular_core.InputSignal<any>;
|
|
88
89
|
cluster: _angular_core.InputSignal<ClusterConfig>;
|
|
89
90
|
clusterComponent: _angular_core.Signal<OlClusterComponent>;
|
|
91
|
+
coordinateProjection: _angular_core.InputSignal<string>;
|
|
90
92
|
constructor();
|
|
91
93
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlVectorLayerComponent, never>;
|
|
92
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlVectorLayerComponent, "ol-vector-layer", never, { "id": { "alias": "id"; "required": true; "isSignal": true; }; "features": { "alias": "features"; "required": false; "isSignal": true; }; "url": { "alias": "url"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "zIndex": { "alias": "zIndex"; "required": false; "isSignal": true; }; "opacity": { "alias": "opacity"; "required": false; "isSignal": true; }; "visible": { "alias": "visible"; "required": false; "isSignal": true; }; "style": { "alias": "style"; "required": false; "isSignal": true; }; "cluster": { "alias": "cluster"; "required": false; "isSignal": true; }; }, {}, ["clusterComponent"], never, true, never>;
|
|
94
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlVectorLayerComponent, "ol-vector-layer", never, { "id": { "alias": "id"; "required": true; "isSignal": true; }; "features": { "alias": "features"; "required": false; "isSignal": true; }; "url": { "alias": "url"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "zIndex": { "alias": "zIndex"; "required": false; "isSignal": true; }; "opacity": { "alias": "opacity"; "required": false; "isSignal": true; }; "visible": { "alias": "visible"; "required": false; "isSignal": true; }; "style": { "alias": "style"; "required": false; "isSignal": true; }; "cluster": { "alias": "cluster"; "required": false; "isSignal": true; }; "coordinateProjection": { "alias": "coordinateProjection"; "required": false; "isSignal": true; }; }, {}, ["clusterComponent"], never, true, never>;
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
declare class OlTileLayerComponent {
|