@vcmap/core 6.3.0-rc.3 → 6.3.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.
Files changed (77) hide show
  1. package/build/postinstall.js +16 -2
  2. package/dist/index.d.ts +2 -1
  3. package/dist/index.js +2 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/ol.d.ts +10 -1
  6. package/dist/src/featureProvider/abstractFeatureProvider.d.ts +8 -0
  7. package/dist/src/featureProvider/abstractFeatureProvider.js +10 -0
  8. package/dist/src/featureProvider/abstractFeatureProvider.js.map +1 -1
  9. package/dist/src/featureProvider/jsonAttributeProvider.d.ts +2 -1
  10. package/dist/src/featureProvider/jsonAttributeProvider.js +5 -2
  11. package/dist/src/featureProvider/jsonAttributeProvider.js.map +1 -1
  12. package/dist/src/featureProvider/mapboxFeatureProvider.d.ts +18 -0
  13. package/dist/src/featureProvider/mapboxFeatureProvider.js +49 -0
  14. package/dist/src/featureProvider/mapboxFeatureProvider.js.map +1 -0
  15. package/dist/src/featureProvider/wmsFeatureProvider.d.ts +11 -0
  16. package/dist/src/featureProvider/wmsFeatureProvider.js +19 -8
  17. package/dist/src/featureProvider/wmsFeatureProvider.js.map +1 -1
  18. package/dist/src/interaction/eventHandler.js +3 -1
  19. package/dist/src/interaction/eventHandler.js.map +1 -1
  20. package/dist/src/interaction/featureAtPixelInteraction.js +5 -0
  21. package/dist/src/interaction/featureAtPixelInteraction.js.map +1 -1
  22. package/dist/src/interaction/featureProviderInteraction.d.ts +10 -1
  23. package/dist/src/interaction/featureProviderInteraction.js +19 -3
  24. package/dist/src/interaction/featureProviderInteraction.js.map +1 -1
  25. package/dist/src/layer/cesium/mapboxStyleCesiumImpl.d.ts +25 -0
  26. package/dist/src/layer/cesium/mapboxStyleCesiumImpl.js +58 -0
  27. package/dist/src/layer/cesium/mapboxStyleCesiumImpl.js.map +1 -0
  28. package/dist/src/layer/cesium/mapboxStyleImageryProvider.d.ts +26 -0
  29. package/dist/src/layer/cesium/mapboxStyleImageryProvider.js +147 -0
  30. package/dist/src/layer/cesium/mapboxStyleImageryProvider.js.map +1 -0
  31. package/dist/src/layer/cesium/vectorTileImageryProvider.d.ts +1 -1
  32. package/dist/src/layer/cesium/vectorTileImageryProvider.js.map +1 -1
  33. package/dist/src/layer/mapboxStyleLayer.d.ts +55 -0
  34. package/dist/src/layer/mapboxStyleLayer.js +176 -0
  35. package/dist/src/layer/mapboxStyleLayer.js.map +1 -0
  36. package/dist/src/layer/openlayers/layerOpenlayersImpl.d.ts +3 -4
  37. package/dist/src/layer/openlayers/layerOpenlayersImpl.js +11 -3
  38. package/dist/src/layer/openlayers/layerOpenlayersImpl.js.map +1 -1
  39. package/dist/src/layer/openlayers/mapboxStyleOpenlayersImpl.d.ts +15 -0
  40. package/dist/src/layer/openlayers/mapboxStyleOpenlayersImpl.js +49 -0
  41. package/dist/src/layer/openlayers/mapboxStyleOpenlayersImpl.js.map +1 -0
  42. package/dist/src/map/baseCesiumMap.d.ts +17 -0
  43. package/dist/src/map/baseCesiumMap.js +36 -0
  44. package/dist/src/map/baseCesiumMap.js.map +1 -1
  45. package/dist/src/map/baseOLMap.d.ts +6 -4
  46. package/dist/src/map/baseOLMap.js +3 -2
  47. package/dist/src/map/baseOLMap.js.map +1 -1
  48. package/dist/src/map/cesiumMap.d.ts +3 -3
  49. package/dist/src/map/cesiumMap.js +2 -3
  50. package/dist/src/map/cesiumMap.js.map +1 -1
  51. package/dist/src/map/panoramaMap.d.ts +2 -2
  52. package/dist/src/map/panoramaMap.js +1 -2
  53. package/dist/src/map/panoramaMap.js.map +1 -1
  54. package/dist/src/map/vcsMap.d.ts +3 -3
  55. package/dist/src/map/vcsMap.js +11 -3
  56. package/dist/src/map/vcsMap.js.map +1 -1
  57. package/index.ts +2 -0
  58. package/package.json +4 -3
  59. package/src/featureProvider/abstractFeatureProvider.ts +12 -0
  60. package/src/featureProvider/jsonAttributeProvider.ts +10 -2
  61. package/src/featureProvider/mapboxFeatureProvider.ts +74 -0
  62. package/src/featureProvider/wmsFeatureProvider.ts +29 -8
  63. package/src/interaction/eventHandler.ts +3 -1
  64. package/src/interaction/featureAtPixelInteraction.ts +5 -0
  65. package/src/interaction/featureProviderInteraction.ts +37 -8
  66. package/src/layer/cesium/mapboxStyleCesiumImpl.ts +95 -0
  67. package/src/layer/cesium/mapboxStyleImageryProvider.ts +214 -0
  68. package/src/layer/cesium/vectorTileImageryProvider.ts +2 -1
  69. package/src/layer/mapboxStyleLayer.ts +244 -0
  70. package/src/layer/openlayers/layerOpenlayersImpl.ts +21 -10
  71. package/src/layer/openlayers/mapboxStyleOpenlayersImpl.ts +68 -0
  72. package/src/map/baseCesiumMap.ts +57 -0
  73. package/src/map/baseOLMap.ts +16 -14
  74. package/src/map/cesiumMap.ts +7 -6
  75. package/src/map/panoramaMap.ts +3 -3
  76. package/src/map/vcsMap.ts +16 -10
  77. package/src/ol/ol.d.ts +10 -1
@@ -1,3 +1,4 @@
1
+ import { parseBoolean } from '@vcsuite/parsers';
1
2
  import type { Coordinate } from 'ol/coordinate.js';
2
3
  import Point from 'ol/geom/Point.js';
3
4
  import Feature from 'ol/Feature.js';
@@ -19,17 +20,38 @@ import { vcsLayerName } from '../layer/layerSymbols.js';
19
20
  import CompositeFeatureProvider from '../featureProvider/compositeFeatureProvider.js';
20
21
  import AbstractAttributeProvider from '../featureProvider/abstractAttributeProvider.js';
21
22
 
23
+ type FeatureProviderInteractionOptions = {
24
+ /**
25
+ * Whether to respect the rendering order of features. Defaults to false, but when set to true and `inRenderingOrder` is set to true on the FeatureProvider as well, only the first feature will be returned.
26
+ * Used for MapboxFeatureProvider, to return only the top most feature on picking (respectRenderingOrder set to true in EventHandler), but return all the features for deep picking (respectRenderingOrder set to false in DeepPicking action)
27
+ */
28
+ respectRenderingOrder?: boolean;
29
+ };
30
+
22
31
  /**
23
32
  * @group Interaction
24
33
  */
25
34
  class FeatureProviderInteraction extends AbstractInteraction {
26
- constructor() {
35
+ static getDefaultOptions(): FeatureProviderInteractionOptions {
36
+ return {
37
+ respectRenderingOrder: false,
38
+ };
39
+ }
40
+
41
+ private _respectRenderingOrder: boolean;
42
+ constructor(options?: FeatureProviderInteractionOptions) {
27
43
  super(EventType.CLICK, ModificationKeyType.ALL, PointerKeyType.ALL);
28
44
 
45
+ const defaultOptions = FeatureProviderInteraction.getDefaultOptions();
46
+
47
+ this._respectRenderingOrder = parseBoolean(
48
+ options?.respectRenderingOrder,
49
+ defaultOptions.respectRenderingOrder,
50
+ );
51
+
29
52
  this.setActive();
30
53
  }
31
54
 
32
- // eslint-disable-next-line class-methods-use-this
33
55
  async pipe(event: InteractionEvent): Promise<InteractionEvent> {
34
56
  if (!event.feature) {
35
57
  const layersWithProvider = [...event.map.layerCollection]
@@ -50,15 +72,22 @@ class FeatureProviderInteraction extends AbstractInteraction {
50
72
  // TODO make sure the layers are rendered, check min/max RenderingResolution
51
73
  const features = (
52
74
  await Promise.all(
53
- layersWithProvider.map((l) =>
54
- (
55
- l.featureProvider as AbstractFeatureProvider
56
- ).getFeaturesByCoordinate?.(
75
+ layersWithProvider.map(async (l) => {
76
+ const featureProvider =
77
+ l.featureProvider as AbstractFeatureProvider;
78
+ const f = await featureProvider.getFeaturesByCoordinate?.(
57
79
  event.position as Coordinate,
58
80
  resolution,
59
81
  l,
60
- ),
61
- ),
82
+ );
83
+ if (
84
+ this._respectRenderingOrder &&
85
+ featureProvider.inRenderingOrder
86
+ ) {
87
+ return f.slice(0, 1);
88
+ }
89
+ return f;
90
+ }),
62
91
  )
63
92
  )
64
93
  .filter((f) => !!f)
@@ -0,0 +1,95 @@
1
+ import {
2
+ ImageryLayer as CesiumImageryLayer,
3
+ Rectangle,
4
+ } from '@vcmap-cesium/engine';
5
+ import type { SplitDirection } from '@vcmap-cesium/engine';
6
+ import type LayerGroup from 'ol/layer/Group.js';
7
+ import type BaseCesiumMap from '../../map/baseCesiumMap.js';
8
+ import { wgs84Projection } from '../../util/projection.js';
9
+ import { TilingScheme } from '../rasterLayer.js';
10
+ import type { LayerImplementationOptions } from '../layer.js';
11
+ import TileProvider from '../tileProvider/tileProvider.js';
12
+ import MapboxStyleImageryProvider, {
13
+ type MapboxStyleImageryProviderOptions,
14
+ } from './mapboxStyleImageryProvider.js';
15
+ import RasterLayerCesiumImpl from './rasterLayerCesiumImpl.js';
16
+
17
+ export type MapboxStyleLayerImplementationOptions =
18
+ LayerImplementationOptions & {
19
+ styledMapboxLayerGroup: LayerGroup;
20
+ splitDirection: SplitDirection;
21
+ minRenderingLevel?: number;
22
+ maxRenderingLevel?: number;
23
+ };
24
+
25
+ class MapboxStyleCesiumImpl extends RasterLayerCesiumImpl {
26
+ static get className(): string {
27
+ return 'MapboxStyleCesiumImpl';
28
+ }
29
+
30
+ private _styledMapboxLayerGroup: LayerGroup;
31
+
32
+ minRenderingLevel: number | undefined;
33
+
34
+ maxRenderingLevel: number | undefined;
35
+
36
+ imageryProvider: MapboxStyleImageryProvider | undefined = undefined;
37
+
38
+ constructor(
39
+ map: BaseCesiumMap,
40
+ options: MapboxStyleLayerImplementationOptions,
41
+ ) {
42
+ super(map, {
43
+ ...options,
44
+ minLevel: 0,
45
+ maxLevel: 25,
46
+ tilingSchema: TilingScheme.MERCATOR,
47
+ opacity: 1,
48
+ });
49
+ this._styledMapboxLayerGroup = options.styledMapboxLayerGroup;
50
+ this.minRenderingLevel = options.minRenderingLevel;
51
+ this.maxRenderingLevel = options.maxRenderingLevel;
52
+ }
53
+
54
+ async activate(): Promise<void> {
55
+ await super.activate();
56
+ if (this.active) {
57
+ this._styledMapboxLayerGroup.setVisible(true);
58
+ }
59
+ }
60
+
61
+ getCesiumLayer(): Promise<CesiumImageryLayer> {
62
+ const options: MapboxStyleImageryProviderOptions = {
63
+ headers: this.headers,
64
+ styledMapboxLayerGroup: this._styledMapboxLayerGroup,
65
+ minimumTerrainLevel: this.minRenderingLevel,
66
+ maximumTerrainLevel: this.maxRenderingLevel,
67
+ tileProvider: new TileProvider({}),
68
+ tileSize: [256, 256],
69
+ };
70
+
71
+ this.imageryProvider = new MapboxStyleImageryProvider(options);
72
+
73
+ const layerOptions = this.getCesiumLayerOptions();
74
+ if (this.extent && this.extent.isValid()) {
75
+ const extent = this.extent.getCoordinatesInProjection(wgs84Projection);
76
+ layerOptions.rectangle = Rectangle.fromDegrees(
77
+ extent[0],
78
+ extent[1],
79
+ extent[2],
80
+ extent[3],
81
+ );
82
+ }
83
+ return Promise.resolve(
84
+ // @ts-expect-error mistyped
85
+ new CesiumImageryLayer(this.imageryProvider, layerOptions),
86
+ );
87
+ }
88
+
89
+ destroy(): void {
90
+ this.imageryProvider?.destroy();
91
+ super.destroy();
92
+ }
93
+ }
94
+
95
+ export default MapboxStyleCesiumImpl;
@@ -0,0 +1,214 @@
1
+ import { parseInteger } from '@vcsuite/parsers';
2
+ import { getLogger } from '@vcsuite/logger';
3
+ import type LayerGroup from 'ol/layer/Group.js';
4
+ import { getCenter, buffer } from 'ol/extent.js';
5
+ import LRUCache from 'ol/structs/LRUCache.js';
6
+ import OLMap from 'ol/Map.js';
7
+ import { rectangleToMercatorExtent } from '../../util/math.js';
8
+ import VectorTileImageryProvider from './vectorTileImageryProvider.js';
9
+ import type { VectorTileImageryProviderOptions } from './vectorTileImageryProvider.js';
10
+
11
+ export type MapboxStyleImageryProviderOptions =
12
+ VectorTileImageryProviderOptions & {
13
+ styledMapboxLayerGroup: LayerGroup;
14
+ minimumTerrainLevel?: number;
15
+ maximumTerrainLevel?: number;
16
+ tileCacheSize?: number;
17
+ };
18
+
19
+ function getTileCacheKey(x: number, y: number, level: number): string {
20
+ return `${x}-${y}-${level}`;
21
+ }
22
+
23
+ function getGutterSize(level: number): number {
24
+ if (level < 12) {
25
+ return 32;
26
+ }
27
+ if (level < 15) {
28
+ return 48;
29
+ }
30
+ return 64;
31
+ }
32
+
33
+ /**
34
+ * Implementation of Cesium ImageryProvider Interface for Mapbox Style Tiles
35
+ */
36
+ class MapboxStyleImageryProvider extends VectorTileImageryProvider {
37
+ static get className(): string {
38
+ return 'MapboxStyleImageryProvider';
39
+ }
40
+
41
+ private _tileCacheByLevel: Map<number, LRUCache<HTMLCanvasElement>>;
42
+
43
+ private _maxCacheSize: number;
44
+
45
+ private _isRendering = false;
46
+
47
+ private _renderMap = new OLMap({
48
+ target: document.createElement('div'),
49
+ });
50
+
51
+ constructor(options: MapboxStyleImageryProviderOptions) {
52
+ super(options);
53
+
54
+ this._tileCacheByLevel = new Map();
55
+ this._maxCacheSize = parseInteger(options.tileCacheSize, 100);
56
+ this._renderMap.addLayer(options.styledMapboxLayerGroup);
57
+ }
58
+
59
+ requestImage(
60
+ x: number,
61
+ y: number,
62
+ level: number,
63
+ ): Promise<HTMLImageElement | HTMLCanvasElement> | undefined {
64
+ const cacheKey = getTileCacheKey(x, y, level);
65
+ const levelCache = this._getLevelCache(level);
66
+ if (levelCache.containsKey(cacheKey)) {
67
+ return Promise.resolve(levelCache.get(cacheKey));
68
+ }
69
+ if (this._isRendering) {
70
+ return undefined;
71
+ }
72
+ return this._doRequestImage(x, y, level, cacheKey);
73
+ }
74
+
75
+ private async _doRequestImage(
76
+ x: number,
77
+ y: number,
78
+ level: number,
79
+ cacheKey: string,
80
+ ): Promise<HTMLImageElement | HTMLCanvasElement> {
81
+ const { tilingScheme } = this.tileProvider;
82
+ const rectangle = tilingScheme.tileXYToRectangle(x, y, level);
83
+ const extent = rectangleToMercatorExtent(rectangle);
84
+
85
+ // Add gutters to prevent labels from being cut off
86
+ const gutterSize = getGutterSize(level);
87
+ const gutter = ((extent[2] - extent[0]) / this.tileWidth) * gutterSize;
88
+ const expandedExtent = buffer(extent, gutter);
89
+
90
+ const renderedTile = await this._renderTile(
91
+ level,
92
+ expandedExtent,
93
+ gutterSize,
94
+ );
95
+ const levelCache = this._getLevelCache(level);
96
+ levelCache.set(cacheKey, renderedTile);
97
+ levelCache.expireCache();
98
+ return renderedTile;
99
+ }
100
+
101
+ private _getLevelCache(level: number): LRUCache<HTMLCanvasElement> {
102
+ const existing = this._tileCacheByLevel.get(level);
103
+ if (existing) {
104
+ return existing;
105
+ }
106
+ const cache = new LRUCache<HTMLCanvasElement>(this._maxCacheSize);
107
+ this._tileCacheByLevel.set(level, cache);
108
+ return cache;
109
+ }
110
+
111
+ private _renderTile(
112
+ level: number,
113
+ extent: number[],
114
+ gutterSize: number,
115
+ ): Promise<HTMLCanvasElement> {
116
+ const TIMEOUT_MS = 2500;
117
+ return new Promise((resolve) => {
118
+ this._isRendering = true;
119
+ let isFinished = false;
120
+ const finish = (canvas: HTMLCanvasElement): void => {
121
+ if (isFinished) {
122
+ return;
123
+ }
124
+ isFinished = true;
125
+ this._isRendering = false;
126
+ resolve(canvas);
127
+ };
128
+
129
+ try {
130
+ if (!this._renderMap) {
131
+ finish(this.emptyCanvas);
132
+ return;
133
+ }
134
+
135
+ const renderWidth = this.tileWidth + 2 * gutterSize;
136
+ const renderHeight = this.tileHeight + 2 * gutterSize;
137
+
138
+ const view = this._renderMap.getView();
139
+ view.setCenter(getCenter(extent));
140
+ view.setZoom(level);
141
+ this._renderMap.setSize([renderWidth, renderHeight]);
142
+
143
+ const handleRenderComplete = (): void => {
144
+ this._renderMap?.un('rendercomplete', handleRenderComplete);
145
+
146
+ const renderedCanvas = this._renderMap
147
+ ?.getViewport()
148
+ .querySelector('canvas');
149
+
150
+ if (!renderedCanvas) {
151
+ finish(this.emptyCanvas);
152
+ return;
153
+ }
154
+
155
+ // Crop the canvas to extract the center portion (original tile size)
156
+ const tileCanvas = document.createElement('canvas');
157
+ tileCanvas.width = this.tileWidth;
158
+ tileCanvas.height = this.tileHeight;
159
+ const ctx = tileCanvas.getContext('2d');
160
+ if (ctx) {
161
+ const scaleX = renderedCanvas.width / renderWidth;
162
+ const scaleY = renderedCanvas.height / renderHeight;
163
+ ctx.drawImage(
164
+ renderedCanvas,
165
+ gutterSize * scaleX,
166
+ gutterSize * scaleY,
167
+ this.tileWidth * scaleX,
168
+ this.tileHeight * scaleY,
169
+ 0,
170
+ 0,
171
+ this.tileWidth,
172
+ this.tileHeight,
173
+ );
174
+ }
175
+
176
+ finish(tileCanvas);
177
+ };
178
+
179
+ const timeoutId = window.setTimeout(() => {
180
+ this._renderMap?.un('rendercomplete', handleRenderComplete);
181
+ getLogger('MapboxVectorTileImageryProvider').warning(
182
+ 'Tile render timed out; returning empty tile.',
183
+ );
184
+ finish(this.emptyCanvas);
185
+ }, TIMEOUT_MS);
186
+
187
+ const wrappedHandleRenderComplete = (): void => {
188
+ window.clearTimeout(timeoutId);
189
+ handleRenderComplete();
190
+ };
191
+
192
+ this._renderMap.once('rendercomplete', wrappedHandleRenderComplete);
193
+ this._renderMap.render();
194
+ } catch (e: unknown) {
195
+ getLogger('MapboxVectorTileImageryProvider').error(
196
+ `Error rendering tile: ${(e as Error).message}`,
197
+ );
198
+ finish(this.emptyCanvas);
199
+ }
200
+ });
201
+ }
202
+
203
+ destroy(): void {
204
+ for (const cache of this._tileCacheByLevel.values()) {
205
+ cache.clear();
206
+ }
207
+ this._tileCacheByLevel.clear();
208
+ this._isRendering = false;
209
+ this._renderMap?.setTarget(undefined);
210
+ this._renderMap?.dispose();
211
+ }
212
+ }
213
+
214
+ export default MapboxStyleImageryProvider;
@@ -207,7 +207,8 @@ class VectorTileImageryProvider {
207
207
  x: number,
208
208
  y: number,
209
209
  level: number,
210
- ): Promise<HTMLImageElement | HTMLCanvasElement> {
210
+ // @ts-expect-error returns undefined if there are too many active requests
211
+ ): Promise<HTMLImageElement | HTMLCanvasElement> | undefined {
211
212
  const features = await this.tileProvider.getFeaturesForTile(
212
213
  x,
213
214
  y,
@@ -0,0 +1,244 @@
1
+ import { SplitDirection } from '@vcmap-cesium/engine';
2
+ import { parseInteger } from '@vcsuite/parsers';
3
+ import { is } from '@vcsuite/check';
4
+ import Collection from 'ol/Collection.js';
5
+ import LayerGroup from 'ol/layer/Group.js';
6
+ import type BaseLayer from 'ol/layer/Base.js';
7
+ import { apply } from 'ol-mapbox-style';
8
+ import { layerClassRegistry } from '../classRegistry.js';
9
+ import AbstractAttributeProvider from '../featureProvider/abstractAttributeProvider.js';
10
+ import CompositeFeatureProvider from '../featureProvider/compositeFeatureProvider.js';
11
+ import MapboxFeatureProvider from '../featureProvider/mapboxFeatureProvider.js';
12
+ import type VcsMap from '../map/vcsMap.js';
13
+ import BaseCesiumMap from '../map/baseCesiumMap.js';
14
+ import CesiumMap from '../map/cesiumMap.js';
15
+ import ObliqueMap from '../map/obliqueMap.js';
16
+ import OpenlayersMap from '../map/openlayersMap.js';
17
+ import PanoramaMap from '../map/panoramaMap.js';
18
+ import VcsEvent from '../vcsEvent.js';
19
+ import MapboxVectorRasterTileCesiumImpl, {
20
+ type MapboxStyleLayerImplementationOptions,
21
+ } from './cesium/mapboxStyleCesiumImpl.js';
22
+ import Layer from './layer.js';
23
+ import type { LayerOptions, SplitLayer } from './layer.js';
24
+ import type LayerImplementation from './layerImplementation.js';
25
+ import MapboxVectorTileOpenlayersImpl from './openlayers/mapboxStyleOpenlayersImpl.js';
26
+ import { allowPicking, vcsLayerName } from './layerSymbols.js';
27
+
28
+ export type MapboxStyleOptions = LayerOptions & {
29
+ /** The sources from the Mapbox Style to use. If not provided, all sources from the style will be used. */
30
+ sources?: string[];
31
+ /** The Mapbox layers to exclude from picking. */
32
+ excludeLayerFromPicking?: string[];
33
+ /** either 'left' or 'right', none if omitted */
34
+ splitDirection?: string;
35
+ /** configures the visible level in the rendered map. Maps to Openlayers `minZoom` and Cesium `minimiumTerrainLevel` */
36
+ minRenderingLevel?: number;
37
+ /** configures the visible level in the rendered map. Maps to Openlayers `maxZoom` and Cesium `maximumTerrainLevel` */
38
+ maxRenderingLevel?: number;
39
+ };
40
+
41
+ export interface MaboxStyleImplementation extends LayerImplementation<VcsMap> {
42
+ updateSplitDirection(direction: SplitDirection): void;
43
+ }
44
+
45
+ /**
46
+ * Layer class for Mapbox Style layers.
47
+ * @group Layer
48
+ */
49
+ class MapboxStyleLayer
50
+ extends Layer<MaboxStyleImplementation>
51
+ implements SplitLayer
52
+ {
53
+ static get className(): string {
54
+ return 'MapboxStyleLayer';
55
+ }
56
+
57
+ static getDefaultOptions(): MapboxStyleOptions {
58
+ return {
59
+ ...Layer.getDefaultOptions(),
60
+ minRenderingLevel: undefined,
61
+ maxRenderingLevel: undefined,
62
+ };
63
+ }
64
+
65
+ protected _supportedMaps = [
66
+ CesiumMap.className,
67
+ ObliqueMap.className,
68
+ OpenlayersMap.className,
69
+ PanoramaMap.className,
70
+ ];
71
+ private _mapboxLayerGroup: LayerGroup;
72
+ private _sources?: string[];
73
+ private _excludeLayerFromPicking?: string[];
74
+ private _splitDirection: SplitDirection = SplitDirection.NONE;
75
+ splitDirectionChanged = new VcsEvent<SplitDirection>();
76
+ /**
77
+ * defines the visible level in the rendered map, maps to Openlayers `minZoom` and Cesium `minimiumTerrainLevel`.
78
+ * Changes requires calling layer.redraw() to take effect.
79
+ */
80
+ minRenderingLevel: number | undefined;
81
+ /**
82
+ * defines the visible level in the rendered map, maps to Openlayers `minZoom` and Cesium `minimiumTerrainLevel`.
83
+ * Changes requires calling layer.redraw() to take effect.
84
+ */
85
+ maxRenderingLevel: number | undefined;
86
+
87
+ constructor(options: MapboxStyleOptions) {
88
+ const defaultOptions = MapboxStyleLayer.getDefaultOptions();
89
+ super({ ...defaultOptions, ...options });
90
+
91
+ this._sources = options.sources;
92
+ this._excludeLayerFromPicking = options.excludeLayerFromPicking;
93
+ if (options.splitDirection) {
94
+ this._splitDirection =
95
+ options.splitDirection === 'left'
96
+ ? SplitDirection.LEFT
97
+ : SplitDirection.RIGHT;
98
+ }
99
+ this.minRenderingLevel = parseInteger(
100
+ options.minRenderingLevel,
101
+ defaultOptions.minRenderingLevel,
102
+ );
103
+ this.maxRenderingLevel = parseInteger(
104
+ options.maxRenderingLevel,
105
+ defaultOptions.maxRenderingLevel,
106
+ );
107
+
108
+ this._mapboxLayerGroup = new LayerGroup({
109
+ minZoom: this.minRenderingLevel,
110
+ maxZoom: this.maxRenderingLevel,
111
+ });
112
+ }
113
+
114
+ async initialize(): Promise<void> {
115
+ if (!this.initialized) {
116
+ await apply(this._mapboxLayerGroup, this.url);
117
+
118
+ const layers = this._mapboxLayerGroup.getLayersArray();
119
+ layers.forEach((layer) => {
120
+ layer[vcsLayerName] = this.name;
121
+ layer[allowPicking] = super.allowPicking;
122
+ });
123
+
124
+ if (this._sources && this._sources.length > 0) {
125
+ const filteredCollection = new Collection<BaseLayer>();
126
+ layers
127
+ .filter((layer) =>
128
+ this._sources!.includes(layer.get('mapbox-source') as string),
129
+ )
130
+ .forEach((layer) => {
131
+ filteredCollection.push(layer);
132
+ });
133
+
134
+ this._mapboxLayerGroup.setLayers(filteredCollection);
135
+ }
136
+ }
137
+
138
+ if (!this.featureProvider) {
139
+ this.featureProvider = new MapboxFeatureProvider({
140
+ styledMapboxLayerGroup: this._mapboxLayerGroup,
141
+ excludeLayerFromPicking: this._excludeLayerFromPicking,
142
+ });
143
+ } else if (is(this.featureProvider, AbstractAttributeProvider)) {
144
+ this.featureProvider = new CompositeFeatureProvider({
145
+ attributeProviders: [this.featureProvider],
146
+ featureProviders: [
147
+ new MapboxFeatureProvider({
148
+ styledMapboxLayerGroup: this._mapboxLayerGroup,
149
+ excludeLayerFromPicking: this._excludeLayerFromPicking,
150
+ }),
151
+ ],
152
+ });
153
+ }
154
+
155
+ await super.initialize();
156
+ }
157
+
158
+ get splitDirection(): SplitDirection {
159
+ return this._splitDirection;
160
+ }
161
+
162
+ set splitDirection(direction: SplitDirection) {
163
+ if (direction !== this._splitDirection) {
164
+ this._splitDirection = direction;
165
+ this.getImplementations().forEach((impl) => {
166
+ impl.updateSplitDirection(direction);
167
+ });
168
+ this.splitDirectionChanged.raiseEvent(this._splitDirection);
169
+ }
170
+ }
171
+
172
+ getImplementationOptions(): MapboxStyleLayerImplementationOptions {
173
+ return {
174
+ ...super.getImplementationOptions(),
175
+ styledMapboxLayerGroup: this._mapboxLayerGroup,
176
+ splitDirection: this._splitDirection,
177
+ minRenderingLevel: this.minRenderingLevel,
178
+ maxRenderingLevel: this.maxRenderingLevel,
179
+ };
180
+ }
181
+
182
+ createImplementationsForMap(map: VcsMap): MaboxStyleImplementation[] {
183
+ if (map instanceof BaseCesiumMap) {
184
+ return [
185
+ new MapboxVectorRasterTileCesiumImpl(
186
+ map,
187
+ this.getImplementationOptions(),
188
+ ),
189
+ ];
190
+ }
191
+
192
+ if (map instanceof OpenlayersMap) {
193
+ return [
194
+ new MapboxVectorTileOpenlayersImpl(
195
+ map,
196
+ this.getImplementationOptions(),
197
+ ),
198
+ ];
199
+ }
200
+ return super.createImplementationsForMap(map);
201
+ }
202
+
203
+ toJSON(
204
+ defaultOptions = MapboxStyleLayer.getDefaultOptions(),
205
+ ): MapboxStyleOptions {
206
+ const config: MapboxStyleOptions = { ...super.toJSON() };
207
+
208
+ if (this._sources && this._sources.length > 0) {
209
+ config.sources = this._sources.slice();
210
+ }
211
+
212
+ if (
213
+ this._excludeLayerFromPicking &&
214
+ this._excludeLayerFromPicking.length > 0
215
+ ) {
216
+ config.excludeLayerFromPicking = this._excludeLayerFromPicking.slice();
217
+ }
218
+
219
+ if (this._splitDirection !== SplitDirection.NONE) {
220
+ config.splitDirection =
221
+ this._splitDirection === SplitDirection.LEFT ? 'left' : 'right';
222
+ }
223
+
224
+ if (this.minRenderingLevel !== defaultOptions.minRenderingLevel) {
225
+ config.minRenderingLevel = this.minRenderingLevel;
226
+ }
227
+
228
+ if (this.maxRenderingLevel !== defaultOptions.maxRenderingLevel) {
229
+ config.maxRenderingLevel = this.maxRenderingLevel;
230
+ }
231
+
232
+ return config;
233
+ }
234
+
235
+ destroy(): void {
236
+ this.splitDirectionChanged.destroy();
237
+ this._mapboxLayerGroup.dispose();
238
+ this.featureProvider?.destroy();
239
+ super.destroy();
240
+ }
241
+ }
242
+
243
+ layerClassRegistry.registerClass(MapboxStyleLayer.className, MapboxStyleLayer);
244
+ export default MapboxStyleLayer;
@@ -1,10 +1,11 @@
1
1
  import { SplitDirection } from '@vcmap-cesium/engine';
2
2
  import { unByKey } from 'ol/Observable.js';
3
3
  import type { EventsKey } from 'ol/events.js';
4
- import type OLLayer from 'ol/layer/Layer.js';
4
+ import OLLayer from 'ol/layer/Layer.js';
5
5
  import type RenderEvent from 'ol/render/Event.js';
6
6
  import { vcsLayerName } from '../layerSymbols.js';
7
7
  import LayerImplementation from '../layerImplementation.js';
8
+ import type { OLLayerLike } from '../../map/baseOLMap.js';
8
9
  import type OpenlayersMap from '../../map/openlayersMap.js';
9
10
  import type { LayerImplementationOptions } from '../layer.js';
10
11
 
@@ -22,7 +23,7 @@ class LayerOpenlayersImpl extends LayerImplementation<OpenlayersMap> {
22
23
  return 'LayerOpenlayersImpl';
23
24
  }
24
25
 
25
- olLayer: OLLayer | null = null;
26
+ olLayer: OLLayerLike | null = null;
26
27
 
27
28
  splitDirection: SplitDirection;
28
29
 
@@ -62,10 +63,9 @@ class LayerOpenlayersImpl extends LayerImplementation<OpenlayersMap> {
62
63
 
63
64
  /**
64
65
  * returns the ol Layer
65
- * @returns {import("ol/layer").Layer<import("ol/source/Source").default>}
66
66
  */
67
67
  // eslint-disable-next-line class-methods-use-this
68
- getOLLayer(): OLLayer {
68
+ getOLLayer(): OLLayerLike {
69
69
  throw new Error();
70
70
  }
71
71
 
@@ -84,12 +84,23 @@ class LayerOpenlayersImpl extends LayerImplementation<OpenlayersMap> {
84
84
  !this._splitDirectionRenderListeners
85
85
  ) {
86
86
  this._splitDirectionRenderListeners = [];
87
- this._splitDirectionRenderListeners.push(
88
- this.olLayer!.on('prerender', this._splitPreRender.bind(this)),
89
- );
90
- this._splitDirectionRenderListeners.push(
91
- this.olLayer!.on('postrender', this._splitPostReder.bind(this)),
92
- );
87
+ if (this.olLayer instanceof OLLayer) {
88
+ this._splitDirectionRenderListeners.push(
89
+ this.olLayer.on('prerender', this._splitPreRender.bind(this)),
90
+ );
91
+ this._splitDirectionRenderListeners.push(
92
+ this.olLayer.on('postrender', this._splitPostReder.bind(this)),
93
+ );
94
+ } else {
95
+ this.olLayer!.getLayersArray().forEach((layer) => {
96
+ this._splitDirectionRenderListeners!.push(
97
+ layer.on('prerender', this._splitPreRender.bind(this)),
98
+ );
99
+ this._splitDirectionRenderListeners!.push(
100
+ layer.on('postrender', this._splitPostReder.bind(this)),
101
+ );
102
+ });
103
+ }
93
104
  this.olLayer!.changed();
94
105
  }
95
106
  }