@angular-helpers/openlayers 0.4.0 → 0.5.1

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
@@ -10,12 +10,14 @@ A modern Angular wrapper for OpenLayers with modular architecture, standalone co
10
10
  - **Dual API** - Template for UI, Inputs for data, Services for operations
11
11
  - **Tree-shaking** - Unused OpenLayers code is eliminated from your bundle
12
12
  - **TypeScript First** - Full type safety with strict mode support
13
- - **Military Features** - Ellipses, sectors, NATO symbology, and MGRS coordinates
13
+ - **Military Features** - True geodesic precision for ellipses and sectors, NATO symbology, and MGRS coordinates
14
+ - **Proj4 Integration** - Declarative registration of local coordinate systems (like UTM)
15
+ - **WebGL Accelerated** - Mapbox Vector Tiles (MVT) and GPU raster expressions
14
16
 
15
17
  ## Installation
16
18
 
17
19
  ```bash
18
- npm install @angular-helpers/openlayers ol
20
+ pnpm add @angular-helpers/openlayers ol
19
21
  ```
20
22
 
21
23
  ## Quick Start
@@ -30,7 +32,14 @@ import { withLayers } from '@angular-helpers/openlayers/layers';
30
32
  import { withControls } from '@angular-helpers/openlayers/controls';
31
33
 
32
34
  export const appConfig: ApplicationConfig = {
33
- providers: [provideOpenLayers(withLayers(), withControls())],
35
+ providers: [
36
+ provideOpenLayers(
37
+ withLayers(),
38
+ withControls(),
39
+ // Optional: Register custom coordinate systems with proj4
40
+ // withProjections(proj4, [{ code: 'EPSG:32630', proj4def: '...', extent: [...] }])
41
+ ),
42
+ ],
34
43
  };
35
44
  ```
36
45
 
@@ -49,7 +58,6 @@ import { OlMapService } from '@angular-helpers/openlayers/core';
49
58
 
50
59
  @Component({
51
60
  selector: 'app-map',
52
- standalone: true,
53
61
  imports: [
54
62
  OlMapComponent,
55
63
  OlTileLayerComponent,
@@ -176,87 +184,150 @@ const handle = popups.openComponent({
176
184
 
177
185
  `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
186
 
179
- ## Military symbology
187
+ ## WebGL Layers — GPU-accelerated rendering
180
188
 
181
- Available since `0.4.0` from `@angular-helpers/openlayers/military`.
189
+ Available since `0.3.0` from `@angular-helpers/openlayers/layers`.
190
+
191
+ WebGL layers render directly on the GPU, making them perfect for extremely heavy tile configurations (with real-time styling expressions) and massive coordinate datasets (10,000+ vector points).
192
+
193
+ ### `<ol-webgl-tile-layer>` — Raster style manipulation
194
+
195
+ Renders tile layers (OSM, XYZ, MVT) via WebGL. Supports the dynamic application of WebGL tile styles (raster expressions) for dynamic, GPU-powered adjustments like brightness, contrast, saturation, and gamma.
196
+
197
+ ```html
198
+ <ol-webgl-tile-layer
199
+ id="satellite-webgl"
200
+ source="xyz"
201
+ [url]="'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'"
202
+ [tileStyle]="{
203
+ brightness: 0.1,
204
+ contrast: 0.2,
205
+ saturation: -0.5
206
+ }"
207
+ />
208
+ ```
209
+
210
+ ### `<ol-webgl-vector-layer>` — Smooth massive datasets (10k+ features)
211
+
212
+ Renders points, lines, and polygons using WebGL 2. For peak performance, hit detection is disabled by default, and styling must be declared using `FlatStyleLike` expressions rather than standard `ol/style/Style` instances.
213
+
214
+ ```html
215
+ <ol-webgl-vector-layer
216
+ id="massive-points"
217
+ [features]="densePoints()"
218
+ [flatStyle]="{
219
+ 'circle-radius': 6,
220
+ 'circle-fill-color': '#10b981',
221
+ 'stroke-color': '#334155',
222
+ 'stroke-width': 1
223
+ }"
224
+ [disableHitDetection]="true"
225
+ />
226
+ ```
227
+
228
+ Rigorous cleanup guarantees that WebGL contexts, framebuffers, and active buffers are fully released on destroy (`layer.dispose()`), preventing GPU leaks.
182
229
 
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.
230
+ ## Geodesic Geometry Helpers
231
+
232
+ Available from `@angular-helpers/openlayers/core` via `OlGeometryService`.
233
+
234
+ Approximates standard shapes in metric space using true geodesic calculations (`Vincenty`'s formulae via `ol/sphere`). This means your shapes remain mathematically accurate and visually consistent (without map projection scale distortion) across massive global distances.
184
235
 
185
236
  ```typescript
186
- import { inject, signal } from '@angular/core';
187
- import { OlMilitaryService } from '@angular-helpers/openlayers/military';
237
+ import { inject, Component, signal } from '@angular/core';
238
+ import { OlGeometryService } from '@angular-helpers/openlayers/core';
188
239
  import type { Feature } from '@angular-helpers/openlayers/core';
189
240
 
190
241
  @Component({
191
- // …
192
242
  imports: [OlMapComponent, OlVectorLayerComponent],
193
243
  template: `
194
244
  <ol-map [center]="[2.17, 41.38]" [zoom]="8">
195
245
  <ol-tile-layer id="osm" source="osm" />
196
- <ol-vector-layer id="military" [features]="features()" [zIndex]="10" />
246
+ <ol-vector-layer id="shapes" [features]="features()" />
197
247
  </ol-map>
198
248
  `,
199
249
  })
200
- export class MilDemo {
201
- private ml = inject(OlMilitaryService);
250
+ export class GeodesicDemo {
251
+ private geomSvc = inject(OlGeometryService);
202
252
  features = signal<Feature[]>([]);
203
253
 
204
- async ngOnInit() {
205
- const ellipse = this.ml.createEllipse({
254
+ ngOnInit() {
255
+ const ellipse = this.geomSvc.createEllipse({
206
256
  center: [2.17, 41.38],
207
257
  semiMajor: 6_000,
208
258
  semiMinor: 3_000,
209
259
  rotation: Math.PI / 6,
210
260
  });
211
- const sector = this.ml.createSector({
261
+ const sector = this.geomSvc.createSector({
212
262
  center: [-0.38, 39.47],
213
263
  radius: 8_000,
214
264
  startAngle: Math.PI / 6,
215
265
  endAngle: Math.PI / 2,
216
266
  });
217
- const donut = this.ml.createDonut({
267
+ const donut = this.geomSvc.createDonut({
218
268
  center: [-5.99, 37.39],
219
269
  innerRadius: 5_000,
220
270
  outerRadius: 10_000,
221
271
  });
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]);
272
+ this.features.set([ellipse, sector, donut]);
228
273
  }
229
274
  }
230
275
  ```
231
276
 
232
- ### Geometry helpers
233
-
234
277
  | Method | Output | Notes |
235
278
  | ----------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------- |
236
279
  | `createEllipse(config)` | `Feature<Polygon>` | Optional `rotation` in radians, configurable `segments` (default 64) |
237
280
  | `createSector(config)` | `Feature<Polygon>` | Pie-slice (apex-arc-apex). `startAngle < endAngle ≤ start + 2π` |
238
281
  | `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
282
 
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.
283
+ ---
241
284
 
242
- ### MIL-STD-2525 symbols
285
+ ## Military Symbology & Tactical Graphics
243
286
 
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:
287
+ Available since `0.4.0` from `@angular-helpers/openlayers/military`.
245
288
 
246
- ```bash
247
- npm install milsymbol
289
+ Exposes NATO MIL-STD-2525 symbol rendering backed by the optional [`milsymbol`](https://github.com/spatialillusions/milsymbol) peer dependency, plus tactical military graphic components (frontlines, attack vectors).
290
+
291
+ ### MIL-STD-2525 Point Symbology (`OlMilitaryService`)
292
+
293
+ Lazy-loads the heavy `milsymbol` package dynamically on demand, returning a styled `Feature<Point>` so the vector layer renders it natively.
294
+
295
+ ```typescript
296
+ import { inject, Component, signal } from '@angular/core';
297
+ import { OlMilitaryService } from '@angular-helpers/openlayers/military';
298
+ import type { Feature } from '@angular-helpers/openlayers/core';
299
+
300
+ @Component({
301
+ imports: [OlMapComponent, OlVectorLayerComponent],
302
+ providers: [OlMilitaryService],
303
+ template: `
304
+ <ol-map [center]="[-3.7, 40.42]" [zoom]="8">
305
+ <ol-vector-layer id="military" [features]="features()" />
306
+ </ol-map>
307
+ `,
308
+ })
309
+ export class MilDemo {
310
+ private milSvc = inject(OlMilitaryService);
311
+ features = signal<Feature[]>([]);
312
+
313
+ async ngOnInit() {
314
+ const symbol = await this.milSvc.createMilSymbol({
315
+ sidc: 'SFGPUCI-----', // Friendly Infantry Unit
316
+ position: [-3.7, 40.42],
317
+ size: 36,
318
+ });
319
+ this.features.set([symbol]);
320
+ }
321
+ }
248
322
  ```
249
323
 
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
- });
324
+ Install the optional peer dependency if utilizing NATO symbology:
325
+
326
+ ```bash
327
+ pnpm add milsymbol
257
328
  ```
258
329
 
259
- Three flavors:
330
+ Three execution strategies:
260
331
 
261
332
  - **`createMilSymbol(config)`** — async; lazy-loads on first call.
262
333
  - **`createMilSymbolSync(config)`** — sync; throws if `milsymbol` is not loaded yet.
@@ -264,6 +335,28 @@ Three flavors:
264
335
 
265
336
  The service throws clearly on non-browser environments (`createMilSymbol` requires `window`).
266
337
 
338
+ ### Tactical Graphics (`OlTacticalGraphicsService`)
339
+
340
+ Builds advanced multi-point military tactical graphic features (frontlines with directional teeth, attack arrow coordinates) and provides custom styles.
341
+
342
+ ```typescript
343
+ import { OlTacticalGraphicsService } from '@angular-helpers/openlayers/military';
344
+
345
+ const tacticalSvc = inject(OlTacticalGraphicsService);
346
+
347
+ // Create a frontline graphic
348
+ const frontline = tacticalSvc.createFrontLine(
349
+ [
350
+ [2.1, 41.3],
351
+ [2.2, 41.4],
352
+ ],
353
+ 'friendly',
354
+ );
355
+
356
+ // Apply specialized style for frontline teeth
357
+ const frontlineStyle = tacticalSvc.createFrontLineStyle('#4f46e5', 'friendly');
358
+ ```
359
+
267
360
  ## Architecture
268
361
 
269
362
  ### Data vs UI Separation
package/core/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # @angular-helpers/openlayers/core
2
+
3
+ Core library for Angular bindings to OpenLayers.
4
+ Provides essential models, services, and the base `<ol-map>` component.
5
+
6
+ ## Installation
7
+
8
+ \`\`\`bash
9
+ npm install @angular-helpers/openlayers
10
+ \`\`\`
11
+
12
+ ## Core Services & Components
13
+
14
+ - \`OlMapComponent\`: The root map component that manages the core OpenLayers `Map` instance.
15
+ - \`OlMapService\`: A service to retrieve and manage the map instance across child components.
16
+ - \`OlLayerService\`: Manages map layers dynamically.
17
+ - \`OlZoneHelper\`: Optimizes Angular change detection around OpenLayers events.
18
+
19
+ ## Usage
20
+
21
+ \`\`\`typescript
22
+ import { Component } from '@angular/core';
23
+ import { OlMapComponent } from '@angular-helpers/openlayers';
24
+ import { OlTileLayerComponent } from '@angular-helpers/openlayers/layers';
25
+
26
+ @Component({
27
+ selector: 'app-map-demo',
28
+ imports: [OlMapComponent, OlTileLayerComponent],
29
+ template: \`
30
+ <ol-map [center]="[0, 0]" [zoom]="4" class="map-container">
31
+ <ol-tile-layer source="osm"></ol-tile-layer>
32
+ </ol-map>
33
+ \`,
34
+ styles: [\`
35
+ .map-container {
36
+ width: 100%;
37
+ height: 400px;
38
+ display: block;
39
+ }
40
+ \`]
41
+ })
42
+ export class MapDemoComponent {}
43
+ \`\`\`