@angular-helpers/openlayers 0.2.0 → 0.4.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
@@ -97,6 +97,173 @@ export class MapComponent {
97
97
  }
98
98
  ```
99
99
 
100
+ ## Overlays — popups and tooltips
101
+
102
+ Available since `0.3.0` from `@angular-helpers/openlayers/overlays`.
103
+
104
+ ### `<ol-popup>` — declarative popup with content projection
105
+
106
+ The popup's host element is used directly as the underlying `ol/Overlay` element, so projected children stay inside Angular's view tree and benefit from change detection without any extra plumbing.
107
+
108
+ ```typescript
109
+ import { OlPopupComponent } from '@angular-helpers/openlayers/overlays';
110
+
111
+ @Component({
112
+ imports: [OlMapComponent, OlVectorLayerComponent, OlPopupComponent],
113
+ template: `
114
+ <ol-map [center]="[2.17, 41.38]" [zoom]="12">
115
+ <ol-vector-layer id="cities" [features]="cities()" />
116
+
117
+ <ol-popup
118
+ [position]="selectedCoord()"
119
+ [closeButton]="true"
120
+ [autoPan]="true"
121
+ (closed)="clearSelection()"
122
+ >
123
+ <h3>{{ selected()?.name }}</h3>
124
+ <p>{{ selected()?.description }}</p>
125
+ </ol-popup>
126
+ </ol-map>
127
+ `,
128
+ })
129
+ export class MyMap {
130
+ // …
131
+ }
132
+ ```
133
+
134
+ Setting `[position]="null"` hides the popup and emits `closed`.
135
+
136
+ ### `[olTooltip]` — feature hover tooltip
137
+
138
+ ```html
139
+ <ol-vector-layer
140
+ id="cities"
141
+ [features]="cities()"
142
+ [olTooltip]="'name'"
143
+ [olTooltipLayer]="'cities'"
144
+ />
145
+ ```
146
+
147
+ Reads `feature.get('name')` for any feature on layer `cities` under the cursor and renders a styled `<div role="tooltip">` near the pointer. Use the `.ol-tooltip` class to override the default look.
148
+
149
+ ### `OlPopupService` — programmatic popups
150
+
151
+ Three content modes from a service:
152
+
153
+ ```typescript
154
+ const popups = inject(OlPopupService);
155
+
156
+ // 1) Plain text / HTMLElement
157
+ popups.open({
158
+ id: 'simple',
159
+ position: [2.17, 41.38],
160
+ content: 'Hello map',
161
+ positioning: 'bottom-center',
162
+ autoPan: true,
163
+ });
164
+
165
+ // 2) Dynamic Angular component (createComponent + hostElement)
166
+ const handle = popups.openComponent({
167
+ id: 'city-popup',
168
+ position: [2.17, 41.38],
169
+ component: CityCardComponent,
170
+ bindings: [
171
+ inputBinding('city', () => selected()),
172
+ outputBinding<void>('closed', () => handle.close()),
173
+ ],
174
+ });
175
+ ```
176
+
177
+ `open` is idempotent by `id` and updates the existing overlay in place. `openComponent` always recreates the `ComponentRef` on a repeated id and disposes the previous one (`appRef.detachView` + `ref.destroy`) to avoid CD leaks. Calls made before the map is ready are queued and replayed on `OlMapService.onReady`.
178
+
179
+ ## Military symbology
180
+
181
+ Available since `0.4.0` from `@angular-helpers/openlayers/military`.
182
+
183
+ Three pure-math geometry helpers (no extra deps) plus a NATO MIL-STD-2525 symbol helper backed by the optional [`milsymbol`](https://github.com/spatialillusions/milsymbol) peer dependency.
184
+
185
+ ```typescript
186
+ import { inject, signal } from '@angular/core';
187
+ import { OlMilitaryService } from '@angular-helpers/openlayers/military';
188
+ import type { Feature } from '@angular-helpers/openlayers/core';
189
+
190
+ @Component({
191
+ // …
192
+ imports: [OlMapComponent, OlVectorLayerComponent],
193
+ template: `
194
+ <ol-map [center]="[2.17, 41.38]" [zoom]="8">
195
+ <ol-tile-layer id="osm" source="osm" />
196
+ <ol-vector-layer id="military" [features]="features()" [zIndex]="10" />
197
+ </ol-map>
198
+ `,
199
+ })
200
+ export class MilDemo {
201
+ private ml = inject(OlMilitaryService);
202
+ features = signal<Feature[]>([]);
203
+
204
+ async ngOnInit() {
205
+ const ellipse = this.ml.createEllipse({
206
+ center: [2.17, 41.38],
207
+ semiMajor: 6_000,
208
+ semiMinor: 3_000,
209
+ rotation: Math.PI / 6,
210
+ });
211
+ const sector = this.ml.createSector({
212
+ center: [-0.38, 39.47],
213
+ radius: 8_000,
214
+ startAngle: Math.PI / 6,
215
+ endAngle: Math.PI / 2,
216
+ });
217
+ const donut = this.ml.createDonut({
218
+ center: [-5.99, 37.39],
219
+ innerRadius: 5_000,
220
+ outerRadius: 10_000,
221
+ });
222
+ const symbol = await this.ml.createMilSymbol({
223
+ sidc: 'SFGPUCI-----',
224
+ position: [-3.7, 40.42],
225
+ size: 36,
226
+ });
227
+ this.features.set([ellipse, sector, donut, symbol]);
228
+ }
229
+ }
230
+ ```
231
+
232
+ ### Geometry helpers
233
+
234
+ | Method | Output | Notes |
235
+ | ----------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------- |
236
+ | `createEllipse(config)` | `Feature<Polygon>` | Optional `rotation` in radians, configurable `segments` (default 64) |
237
+ | `createSector(config)` | `Feature<Polygon>` | Pie-slice (apex-arc-apex). `startAngle < endAngle ≤ start + 2π` |
238
+ | `createDonut(config)` | `Feature<Polygon>` | Two rings: outer CCW, inner CW (right-hand rule). Renders as an annular band with the basemap visible through the hole |
239
+
240
+ Coordinates are emitted in `EPSG:4326` (lon/lat) using a local tangent-plane projection. Accurate up to ~100 km from the center; for very large radii or polar regions, geodesic-correct math is on the Phase 3 roadmap.
241
+
242
+ ### MIL-STD-2525 symbols
243
+
244
+ `createMilSymbol` lazy-loads `milsymbol` on first use and returns a `Feature<Point>` with style metadata (`feature.style.icon`) so the vector layer renders it as an `ol/style/Icon`. The library is declared as an **optional peer dependency** — install it only if you use this helper:
245
+
246
+ ```bash
247
+ npm install milsymbol
248
+ ```
249
+
250
+ ```ts
251
+ const symbol = await ml.createMilSymbol({
252
+ sidc: 'SFGPUCI-----', // friendly infantry, ground unit
253
+ position: [-3.7, 40.42],
254
+ size: 36,
255
+ uniqueDesignation: 'A1',
256
+ });
257
+ ```
258
+
259
+ Three flavors:
260
+
261
+ - **`createMilSymbol(config)`** — async; lazy-loads on first call.
262
+ - **`createMilSymbolSync(config)`** — sync; throws if `milsymbol` is not loaded yet.
263
+ - **`preloadMilsymbol()`** — fire-and-forget on app init to make the first symbol render synchronous.
264
+
265
+ The service throws clearly on non-browser environments (`createMilSymbol` requires `window`).
266
+
100
267
  ## Architecture
101
268
 
102
269
  ### Data vs UI Separation
@@ -127,8 +294,8 @@ import {
127
294
  withInteractions,
128
295
  } from '@angular-helpers/openlayers/interactions';
129
296
 
130
- // Add military features (~10KB additional)
131
- import { OlEllipseFeatureComponent, withMilitary } from '@angular-helpers/openlayers/military';
297
+ // Add military features pure-math helpers + lazy-loaded milsymbol
298
+ import { OlMilitaryService, withMilitary } from '@angular-helpers/openlayers/military';
132
299
  ```
133
300
 
134
301
  ## API Reference
@@ -155,6 +155,7 @@ class OlMapComponent {
155
155
  mapDblClick = output();
156
156
  mapContainerRef = viewChild.required('mapContainer');
157
157
  map;
158
+ resizeObserver;
158
159
  constructor() {
159
160
  afterNextRender(() => this.initMap());
160
161
  effect(() => {
@@ -186,6 +187,19 @@ class OlMapComponent {
186
187
  });
187
188
  this.map = new OLMap({ target: container, view, layers: [] });
188
189
  this.mapService.setMap(this.map);
190
+ // Add ResizeObserver to handle container size changes (e.g. sidebars, window resize)
191
+ if (typeof ResizeObserver !== 'undefined') {
192
+ this.resizeObserver = new ResizeObserver(() => {
193
+ if (this.map) {
194
+ // Using requestAnimationFrame prevents "ResizeObserver loop limit exceeded" errors
195
+ requestAnimationFrame(() => {
196
+ if (this.map)
197
+ this.map.updateSize();
198
+ });
199
+ }
200
+ });
201
+ this.resizeObserver.observe(container);
202
+ }
189
203
  view.on('change:center', () => this.zoneHelper.runInsideAngular(() => this.emitViewChange()));
190
204
  view.on('change:resolution', () => this.zoneHelper.runInsideAngular(() => this.emitViewChange()));
191
205
  this.map.on('click', (e) => this.zoneHelper.runInsideAngular(() => this.mapClick.emit({
@@ -200,6 +214,10 @@ class OlMapComponent {
200
214
  this.emitViewChange();
201
215
  }
202
216
  destroyMap() {
217
+ if (this.resizeObserver) {
218
+ this.resizeObserver.disconnect();
219
+ this.resizeObserver = undefined;
220
+ }
203
221
  if (this.map) {
204
222
  this.zoneHelper.runOutsideAngular(() => {
205
223
  this.map.setTarget(undefined);
@@ -33,9 +33,20 @@ class InteractionStateService {
33
33
  modify$ = this.modifySubject.asObservable();
34
34
  /**
35
35
  * Adds a managed interaction to the state.
36
+ * If the interaction is marked as exclusive, it disables other exclusive interactions.
36
37
  * @param interaction - The interaction to add
37
38
  */
38
39
  addInteraction(interaction) {
40
+ if (interaction.config.exclusive !== false) {
41
+ // Disable other exclusive interactions to maintain mutual exclusivity
42
+ const currentInteractions = this.interactions();
43
+ for (const existing of currentInteractions) {
44
+ if (existing.id !== interaction.id && existing.config.exclusive !== false) {
45
+ existing.cleanup();
46
+ this.removeInteraction(existing.id);
47
+ }
48
+ }
49
+ }
39
50
  this.interactions.update((list) => [...list, interaction]);
40
51
  }
41
52
  /**
@@ -533,6 +544,7 @@ function withInteractions() {
533
544
  return {
534
545
  kind: 'interactions',
535
546
  providers: [
547
+ OlLayerService, // DX: interactions often need layers
536
548
  OlInteractionService,
537
549
  InteractionStateService,
538
550
  SelectInteractionService,
@@ -4,18 +4,27 @@ import VectorLayer from 'ol/layer/Vector';
4
4
  import TileLayer from 'ol/layer/Tile';
5
5
  import ImageLayer from 'ol/layer/Image';
6
6
  import VectorSource from 'ol/source/Vector';
7
- import { Feature } from 'ol';
7
+ import OLFeature from 'ol/Feature';
8
8
  import { Point, LineString, Polygon, Circle } from 'ol/geom';
9
9
  import { fromLonLat } from 'ol/proj';
10
- import { Style, Circle as Circle$1, Stroke, Fill } from 'ol/style';
10
+ import { getCenter } from 'ol/extent';
11
+ import { Style, Circle as Circle$1, Stroke, Fill, Icon } from 'ol/style';
12
+ import Text from 'ol/style/Text';
13
+ import ClusterSource from 'ol/source/Cluster';
11
14
  import OSM from 'ol/source/OSM';
12
15
  import XYZ from 'ol/source/XYZ';
13
16
  import TileWMS from 'ol/source/TileWMS';
14
17
  import ImageWMS from 'ol/source/ImageWMS';
15
- import { ImageStatic } from 'ol/source';
18
+ import ImageStatic from 'ol/source/ImageStatic';
16
19
  import { OlMapService } from '@angular-helpers/openlayers/core';
17
20
 
18
21
  // OlLayerService
22
+ /**
23
+ * Internal property key used to stash the abstract style metadata on the
24
+ * underlying `ol/Feature` so the layer style function can resolve a
25
+ * per-feature visual without colliding with user `properties`.
26
+ */
27
+ const STYLE_PROP = '__angular_helpers_style__';
19
28
  class OlLayerService {
20
29
  mapService = inject(OlMapService);
21
30
  layerCache = new Map();
@@ -127,7 +136,15 @@ class OlLayerService {
127
136
  const layer = this.layerCache.get(id);
128
137
  if (!(layer instanceof VectorLayer))
129
138
  return;
130
- layer.getSource()?.clear();
139
+ const source = layer.getSource();
140
+ if (!source)
141
+ return;
142
+ // Handle Cluster source: clear the underlying VectorSource
143
+ const clusterSource = source;
144
+ const vectorSource = clusterSource.getSource
145
+ ? clusterSource.getSource()
146
+ : source;
147
+ vectorSource?.clear();
131
148
  }
132
149
  /**
133
150
  * Updates the features of a vector layer.
@@ -142,8 +159,15 @@ class OlLayerService {
142
159
  const source = layer.getSource();
143
160
  if (!source)
144
161
  return;
162
+ // Handle Cluster source: get the underlying VectorSource
163
+ const clusterSource = source;
164
+ const vectorSource = clusterSource.getSource
165
+ ? clusterSource.getSource()
166
+ : source;
167
+ if (!(vectorSource instanceof VectorSource))
168
+ return;
145
169
  // Get existing feature IDs from source
146
- const existingIds = new Set(source
170
+ const existingIds = new Set(vectorSource
147
171
  .getFeatures()
148
172
  .map((f) => f.getId())
149
173
  .filter((id) => id !== undefined));
@@ -178,14 +202,17 @@ class OlLayerService {
178
202
  else {
179
203
  geometry = new Point([0, 0]);
180
204
  }
181
- const olFeature = new Feature({
205
+ const olFeature = new OLFeature({
182
206
  geometry,
183
207
  ...feature.properties,
184
208
  });
209
+ if (feature.style) {
210
+ olFeature.set(STYLE_PROP, feature.style);
211
+ }
185
212
  olFeature.setId(feature.id);
186
213
  return olFeature;
187
214
  });
188
- source.addFeatures(olFeatures);
215
+ vectorSource.addFeatures(olFeatures);
189
216
  }
190
217
  }
191
218
  }
@@ -204,7 +231,7 @@ class OlLayerService {
204
231
  this.layerState.set(layers.sort((a, b) => a.zIndex - b.zIndex));
205
232
  }
206
233
  createVectorLayer(config, map) {
207
- const source = new VectorSource();
234
+ const vectorSource = new VectorSource();
208
235
  // Add features if provided
209
236
  if (config.features && config.features.length > 0) {
210
237
  const olFeatures = config.features.map((feature) => {
@@ -234,15 +261,40 @@ class OlLayerService {
234
261
  else {
235
262
  geometry = new Point([0, 0]);
236
263
  }
237
- const olFeature = new Feature({
264
+ const olFeature = new OLFeature({
238
265
  geometry,
239
266
  ...feature.properties,
240
267
  });
268
+ if (feature.style) {
269
+ olFeature.set(STYLE_PROP, feature.style);
270
+ }
241
271
  olFeature.setId(feature.id);
242
272
  return olFeature;
243
273
  });
244
- source.addFeatures(olFeatures);
274
+ vectorSource.addFeatures(olFeatures);
245
275
  }
276
+ // Wrap in cluster source if enabled
277
+ const clusterCfg = config.cluster;
278
+ const source = clusterCfg?.enabled
279
+ ? new ClusterSource({
280
+ source: vectorSource,
281
+ distance: clusterCfg.distance ?? 40,
282
+ minDistance: clusterCfg.minDistance ?? 20,
283
+ geometryFunction: (feature) => {
284
+ const geometry = feature.getGeometry();
285
+ if (!geometry)
286
+ return null;
287
+ // For Point geometries, use as-is
288
+ if (geometry.getType() === 'Point') {
289
+ return geometry;
290
+ }
291
+ // For other geometries (Polygon, Circle, etc.), use center point
292
+ const extent = geometry.getExtent();
293
+ const center = getCenter(extent);
294
+ return new Point(center);
295
+ },
296
+ })
297
+ : vectorSource;
246
298
  // Default style for all geometry types (points, lines, polygons)
247
299
  const defaultStyle = new Style({
248
300
  fill: new Fill({ color: 'rgba(25, 118, 210, 0.3)' }),
@@ -253,12 +305,86 @@ class OlLayerService {
253
305
  stroke: new Stroke({ color: '#d32f2f', width: 2 }),
254
306
  }),
255
307
  });
308
+ // Cluster style: shows count badge when features are clustered
309
+ const clusterStyleFn = (olFeature) => {
310
+ const features = olFeature.get('features');
311
+ const size = features?.length ?? 1;
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) => {
360
+ const abstractStyle = olFeature.get(STYLE_PROP);
361
+ if (abstractStyle) {
362
+ const style = new Style();
363
+ const { icon, fill, stroke } = abstractStyle;
364
+ if (icon?.src) {
365
+ style.setImage(new Icon({
366
+ src: icon.src,
367
+ ...(icon.size ? { size: icon.size } : {}),
368
+ ...(icon.anchor ? { anchor: icon.anchor } : {}),
369
+ }));
370
+ }
371
+ if (fill) {
372
+ style.setFill(new Fill({ color: fill.color }));
373
+ }
374
+ if (stroke) {
375
+ style.setStroke(new Stroke({ color: stroke.color, width: stroke.width }));
376
+ }
377
+ if (icon?.src || fill || stroke)
378
+ return style;
379
+ }
380
+ return defaultStyle;
381
+ };
256
382
  const layer = new VectorLayer({
257
383
  source,
258
384
  visible: config.visible ?? true,
259
385
  opacity: config.opacity ?? 1,
260
386
  zIndex: config.zIndex,
261
- style: defaultStyle,
387
+ style: clusterCfg?.enabled ? clusterStyleFn : styleFn,
262
388
  });
263
389
  layer.set('id', config.id);
264
390
  map.addLayer(layer);
@@ -337,6 +463,7 @@ class OlVectorLayerComponent {
337
463
  opacity = input(1, ...(ngDevMode ? [{ debugName: "opacity" }] : /* istanbul ignore next */ []));
338
464
  visible = input(true, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
339
465
  style = input(...(ngDevMode ? [undefined, { debugName: "style" }] : /* istanbul ignore next */ []));
466
+ cluster = input(...(ngDevMode ? [undefined, { debugName: "cluster" }] : /* istanbul ignore next */ []));
340
467
  constructor() {
341
468
  // Initialize layer after DOM is ready
342
469
  afterNextRender(() => {
@@ -348,6 +475,7 @@ class OlVectorLayerComponent {
348
475
  opacity: this.opacity(),
349
476
  visible: this.visible(),
350
477
  style: this.style(),
478
+ cluster: this.cluster(),
351
479
  });
352
480
  });
353
481
  // Effect to sync features when input changes
@@ -364,7 +492,7 @@ class OlVectorLayerComponent {
364
492
  });
365
493
  }
366
494
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: OlVectorLayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
367
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.4", 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 }, 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 } }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
495
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.4", 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 }, 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 } }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
368
496
  }
369
497
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: OlVectorLayerComponent, decorators: [{
370
498
  type: Component,
@@ -373,7 +501,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
373
501
  template: '',
374
502
  changeDetection: ChangeDetectionStrategy.OnPush,
375
503
  }]
376
- }], 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 }] }] } });
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 }] }] } });
377
505
 
378
506
  // OlTileLayerComponent
379
507
  class OlTileLayerComponent {