@map-colonies/react-components 3.13.1 → 3.14.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.
@@ -0,0 +1,279 @@
1
+ import React, { ReactNode, useEffect, useState } from 'react';
2
+ import bbox from '@turf/bbox';
3
+ import { Story, Meta } from '@storybook/react/types-6-0';
4
+ import { Rectangle } from 'cesium';
5
+ import { CesiumMap, CesiumMapProps, useCesiumMap } from '../map';
6
+ import { CesiumSceneMode } from '../map.types';
7
+ import { IBaseMaps } from '../settings/settings';
8
+ import { CesiumXYZLayer } from './xyz.layer';
9
+
10
+ export default {
11
+ title: 'Cesium Map/Map Optimizations',
12
+ component: CesiumMap,
13
+ parameters: {
14
+ layout: 'fullscreen',
15
+ },
16
+ args: {
17
+ useOptimizedTileRequests: true,
18
+ },
19
+ argTypes: {
20
+ useOptimizedTileRequests: {
21
+ description:
22
+ 'Should the viewer determine layer relevancy based on its visibility and presence in scene. (Improves bandwidth usage and overall performance)',
23
+ table: {
24
+ defaultValue: { summary: 'false' },
25
+ },
26
+ control: 'boolean',
27
+ },
28
+ },
29
+ } as Meta;
30
+
31
+ const BASE_MAPS = {
32
+ maps: [
33
+ {
34
+ id: '3rd',
35
+ title: '3rd Map Title',
36
+ isCurrent: true,
37
+ thumbnail:
38
+ 'https://nsw.digitaltwin.terria.io/build/d8b97d3e38a0d43e5a06dea9aae17a3e.png',
39
+ baseRasteLayers: [
40
+ {
41
+ id: 'Opaque Base world wide layer',
42
+ type: 'XYZ_LAYER',
43
+ opacity: 1,
44
+ zIndex: 0,
45
+ options: {
46
+ url:
47
+ 'https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey=6170aad10dfd42a38d4d8c709a536f38',
48
+ layers: '',
49
+ credit: 'thunderforest',
50
+ },
51
+ },
52
+ {
53
+ id: 'Transparent Base Roads world wide layer ',
54
+ type: 'XYZ_LAYER',
55
+ opacity: 1,
56
+ zIndex: 1,
57
+ options: {
58
+ url: 'https://gps.tile.openstreetmap.org/lines/{z}/{x}/{y}.png',
59
+ layers: '',
60
+ credit: 'openstreetmap',
61
+ },
62
+ },
63
+ ],
64
+ baseVectorLayers: [],
65
+ },
66
+ ],
67
+ };
68
+
69
+ const mapDivStyle = {
70
+ height: '100%',
71
+ width: '100%',
72
+ position: 'absolute' as const,
73
+ };
74
+
75
+ const mapViewProps: CesiumMapProps = {
76
+ center: [-117.30644008676421, 33.117098433617564],
77
+ zoom: 14,
78
+ imageryProvider: false,
79
+ sceneModes: [CesiumSceneMode.SCENE3D, CesiumSceneMode.COLUMBUS_VIEW],
80
+ baseMaps: BASE_MAPS as IBaseMaps,
81
+ };
82
+
83
+ interface OptimizedTileRequestingMapStoryProps {
84
+ useOptimizedTileRequests: boolean;
85
+ }
86
+
87
+ interface LayerRelevancy {
88
+ layerId?: string;
89
+ isRelevant?: boolean;
90
+ }
91
+
92
+ const RelevancyPresentor: React.FC<OptimizedTileRequestingMapStoryProps> = ({
93
+ useOptimizedTileRequests,
94
+ }) => {
95
+ const viewer = useCesiumMap();
96
+ const [layersRelevancy, setLayersRelevancy] = useState<LayerRelevancy[]>([]);
97
+
98
+ const updateLayerRelevancy = (): void => {
99
+ if (viewer.layersManager?.layerList) {
100
+ setLayersRelevancy(
101
+ viewer.layersManager?.layerList
102
+ .filter(
103
+ (layer): boolean => layer.meta?.id !== 'TRANSPARENT_BASE_LAYER'
104
+ )
105
+ .map(
106
+ (layer): LayerRelevancy => ({
107
+ layerId: layer.meta?.id as string | undefined,
108
+ isRelevant: layer.meta?.relevantToExtent as boolean,
109
+ })
110
+ )
111
+ );
112
+ }
113
+ };
114
+
115
+ useEffect(() => {
116
+ const removeTileLoad = viewer.scene.globe.tileLoadProgressEvent.addEventListener(
117
+ (tilesLoadingCount) => {
118
+ if (tilesLoadingCount === 0) {
119
+ updateLayerRelevancy();
120
+ removeTileLoad();
121
+ }
122
+ }
123
+ );
124
+
125
+ const removeMoveEnd = viewer.camera.moveEnd.addEventListener(() => {
126
+ updateLayerRelevancy();
127
+ });
128
+
129
+ return (): void => {
130
+ removeMoveEnd();
131
+ };
132
+
133
+ // eslint-disable-next-line react-hooks/exhaustive-deps
134
+ }, []);
135
+
136
+ return (
137
+ <>
138
+ <div
139
+ style={{
140
+ position: 'absolute',
141
+ left: 0,
142
+ top: 0,
143
+ zIndex: 999,
144
+ background: 'white',
145
+ padding: '20px',
146
+ fontFamily: 'Helvetica',
147
+ minWidth: '200px',
148
+ minHeight: '200px',
149
+ }}
150
+ >
151
+ <h3>
152
+ {`Optimized Tile Requesting: ${
153
+ useOptimizedTileRequests ? 'enabled' : 'disabled'
154
+ }`}
155
+ </h3>
156
+ {layersRelevancy.map((layer) => {
157
+ return (
158
+ <div>
159
+ <p>Layer Id: {layer.layerId}</p>
160
+ <p>Requesting tiles: {layer.isRelevant?.toString()}</p>
161
+ </div>
162
+ );
163
+ })}
164
+ </div>
165
+ </>
166
+ );
167
+ };
168
+
169
+ const LayersContainer: React.FC = () => {
170
+ const [layer, setLayer] = useState<ReactNode>(null);
171
+ const btnStyle = {
172
+ position: 'absolute',
173
+ top: 50,
174
+ left: '50%',
175
+ zIndex: 1000,
176
+ transform: 'translate(0, -50%)',
177
+ } as React.CSSProperties;
178
+
179
+ const optionsXYZTransparency = {
180
+ url:
181
+ 'https://tiles.openaerialmap.org/5d73614588556200055f10d6/0/5d73614588556200055f10d7/{z}/{x}/{y}',
182
+ footprint: {
183
+ coordinates: [
184
+ [
185
+ [-117.30976118375267, 33.116454006568205],
186
+ [-117.30976118375267, 33.11330462707964],
187
+ [-117.30513526140776, 33.11330462707964],
188
+ [-117.30513526140776, 33.116454006568205],
189
+ [-117.30976118375267, 33.116454006568205],
190
+ ],
191
+ ],
192
+ type: 'Polygon',
193
+ },
194
+ };
195
+
196
+ const optionsXYZOpaque = {
197
+ url: 'http://stamen-tiles-b.a.ssl.fastly.net/toner/{z}/{x}/{y}.png',
198
+ footprint: {
199
+ coordinates: [
200
+ [
201
+ [-117.31921599064628, 33.1210849388296],
202
+ [-117.31921599064628, 33.1094152732627],
203
+ [-117.29986251692546, 33.1094152732627],
204
+ [-117.29986251692546, 33.1210849388296],
205
+ [-117.31921599064628, 33.1210849388296],
206
+ ],
207
+ ],
208
+ type: 'Polygon',
209
+ },
210
+ };
211
+
212
+ return (
213
+ <>
214
+ <div
215
+ className="buttonsContainer"
216
+ style={{ display: 'flex', gap: '10px', ...btnStyle }}
217
+ >
218
+ <button
219
+ onClick={(): void =>
220
+ setLayer(
221
+ <CesiumXYZLayer
222
+ key="Transparent"
223
+ meta={{
224
+ id: 'Transparent Layer',
225
+ options: { ...optionsXYZTransparency },
226
+ }}
227
+ rectangle={Rectangle.fromDegrees(
228
+ ...bbox(optionsXYZTransparency.footprint)
229
+ )}
230
+ options={optionsXYZTransparency}
231
+ />
232
+ )
233
+ }
234
+ >
235
+ Layer With Transparency
236
+ </button>
237
+
238
+ <button
239
+ onClick={(): void =>
240
+ setLayer(
241
+ <CesiumXYZLayer
242
+ key="Opaque"
243
+ meta={{ id: 'Opaque Layer', options: { ...optionsXYZOpaque } }}
244
+ rectangle={Rectangle.fromDegrees(
245
+ ...bbox(optionsXYZOpaque.footprint)
246
+ )}
247
+ options={optionsXYZOpaque}
248
+ />
249
+ )
250
+ }
251
+ >
252
+ Opaque layer
253
+ </button>
254
+ </div>
255
+ {layer}
256
+ </>
257
+ );
258
+ };
259
+
260
+ export const OptimizedTileRequestingMap: Story<OptimizedTileRequestingMapStoryProps> = (
261
+ args
262
+ ) => {
263
+ return (
264
+ <div style={mapDivStyle}>
265
+ <CesiumMap
266
+ {...mapViewProps}
267
+ useOptimizedTileRequests={args.useOptimizedTileRequests}
268
+ key={args.useOptimizedTileRequests ? 'OPTIMIZED_MAP' : 'REGULAR_MAP'}
269
+ >
270
+ <LayersContainer />
271
+ <RelevancyPresentor
272
+ useOptimizedTileRequests={args.useOptimizedTileRequests}
273
+ />
274
+ </CesiumMap>
275
+ </div>
276
+ );
277
+ };
278
+
279
+ OptimizedTileRequestingMap.storyName = 'Optimized Tile Requesting';
@@ -1,6 +1,9 @@
1
1
  import React from 'react';
2
2
 
3
3
  import { WebMapServiceImageryProvider } from 'cesium';
4
+ import { CustomWebMapServiceImageryProvider } from '../helpers/customImageryProviders';
5
+ import { useCesiumMap } from '../map';
6
+ import { ICesiumImageryLayer } from '../layers-manager';
4
7
  import { CesiumImageryLayer, RCesiumImageryLayerProps } from './imagery.layer';
5
8
 
6
9
  export interface RCesiumWMSLayerOptions
@@ -13,10 +16,13 @@ export interface RCesiumWMSLayerProps
13
16
 
14
17
  export const CesiumWMSLayer: React.FC<RCesiumWMSLayerProps> = (props) => {
15
18
  const { options, ...restProps } = props;
19
+ const mapViewer = useCesiumMap();
20
+
21
+ const providerInstance = mapViewer.shouldOptimizedTileRequests
22
+ ? new CustomWebMapServiceImageryProvider(options, mapViewer)
23
+ : new WebMapServiceImageryProvider(options);
24
+
16
25
  return (
17
- <CesiumImageryLayer
18
- {...restProps}
19
- imageryProvider={new WebMapServiceImageryProvider(options)}
20
- />
26
+ <CesiumImageryLayer {...restProps} imageryProvider={providerInstance} />
21
27
  );
22
28
  };
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
2
 
3
3
  import { WebMapTileServiceImageryProvider } from 'cesium';
4
+ import { CustomWebMapTileServiceImageryProvider } from '../helpers/customImageryProviders';
5
+ import { useCesiumMap } from '../map';
4
6
  import { CesiumImageryLayer, RCesiumImageryLayerProps } from './imagery.layer';
5
7
 
6
8
  export interface RCesiumWMTSLayerOptions
@@ -13,10 +15,13 @@ export interface RCesiumWMTSLayerProps
13
15
 
14
16
  export const CesiumWMTSLayer: React.FC<RCesiumWMTSLayerProps> = (props) => {
15
17
  const { options, ...restProps } = props;
18
+ const mapViewer = useCesiumMap();
19
+
20
+ const providerInstance = mapViewer.shouldOptimizedTileRequests
21
+ ? new CustomWebMapTileServiceImageryProvider(options, mapViewer)
22
+ : new WebMapTileServiceImageryProvider(options);
23
+
16
24
  return (
17
- <CesiumImageryLayer
18
- {...restProps}
19
- imageryProvider={new WebMapTileServiceImageryProvider(options)}
20
- />
25
+ <CesiumImageryLayer {...restProps} imageryProvider={providerInstance} />
21
26
  );
22
27
  };
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
2
 
3
3
  import { UrlTemplateImageryProvider } from 'cesium';
4
+ import { CustomUrlTemplateImageryProvider } from '../helpers/customImageryProviders';
5
+ import { useCesiumMap } from '../map';
4
6
  import { CesiumImageryLayer, RCesiumImageryLayerProps } from './imagery.layer';
5
7
 
6
8
  export interface RCesiumXYZLayerOptions
@@ -13,10 +15,13 @@ export interface RCesiumXYZLayerProps
13
15
 
14
16
  export const CesiumXYZLayer: React.FC<RCesiumXYZLayerProps> = (props) => {
15
17
  const { options, ...restProps } = props;
18
+ const mapViewer = useCesiumMap();
19
+
20
+ const providerInstance = mapViewer.shouldOptimizedTileRequests
21
+ ? new CustomUrlTemplateImageryProvider(options, mapViewer)
22
+ : new UrlTemplateImageryProvider(options);
23
+
16
24
  return (
17
- <CesiumImageryLayer
18
- {...restProps}
19
- imageryProvider={new UrlTemplateImageryProvider(options)}
20
- />
25
+ <CesiumImageryLayer {...restProps} imageryProvider={providerInstance} />
21
26
  );
22
27
  };
@@ -5,8 +5,10 @@ import {
5
5
  WebMapServiceImageryProvider,
6
6
  WebMapTileServiceImageryProvider,
7
7
  Event,
8
+ Rectangle,
9
+ SingleTileImageryProvider,
8
10
  } from 'cesium';
9
- import { get } from 'lodash';
11
+ import { get, isEmpty } from 'lodash';
10
12
  import { Feature, Point, Polygon } from 'geojson';
11
13
  import booleanPointInPolygon from '@turf/boolean-point-in-polygon';
12
14
  import {
@@ -19,6 +21,13 @@ import { CesiumViewer } from './map';
19
21
  import { IBaseMap } from './settings/settings';
20
22
  import { pointToGeoJSON } from './tools/geojson/point.geojson';
21
23
  import { IMapLegend } from './map-legend';
24
+ import {
25
+ CustomUrlTemplateImageryProvider,
26
+ CustomWebMapServiceImageryProvider,
27
+ CustomWebMapTileServiceImageryProvider,
28
+ HAS_TRANSPARENCY_META_PROP,
29
+ } from './helpers/customImageryProviders';
30
+ import { cesiumRectangleContained } from './helpers/utils';
22
31
 
23
32
  const INC = 1;
24
33
  const DEC = -1;
@@ -52,6 +61,8 @@ export interface IVectorLayer {
52
61
 
53
62
  export type LegendExtractor = (layers: (any & { meta: any })[]) => IMapLegend[];
54
63
 
64
+ const TRANSPARENT_LAYER_ID = 'TRANSPARENT_BASE_LAYER';
65
+
55
66
  class LayerManager {
56
67
  public mapViewer: CesiumViewer;
57
68
 
@@ -74,10 +85,41 @@ class LayerManager {
74
85
  if (onLayersUpdate) {
75
86
  this.layerUpdated.addEventListener(onLayersUpdate, this);
76
87
  }
77
- this.mapViewer.imageryLayers.layerRemoved.addEventListener(() => {
78
- this.setLegends();
79
- this.layerUpdated.raiseEvent();
80
- });
88
+
89
+ // Binding layer's relevancy check to cesium lifecycle if optimized tile requests enabled.
90
+ if (this.mapViewer.shouldOptimizedTileRequests) {
91
+ this.layerUpdated.addEventListener((meta: Record<string, unknown>) => {
92
+ const newMetaKeys = Object.keys(meta);
93
+ const shouldTriggerRelevancyCheck =
94
+ newMetaKeys.length === 1 &&
95
+ newMetaKeys[0] === HAS_TRANSPARENCY_META_PROP;
96
+ if (shouldTriggerRelevancyCheck) {
97
+ this.markRelevantLayersForExtent();
98
+ this.hideNonRelevantLayers();
99
+ }
100
+ });
101
+
102
+ this.mapViewer.imageryLayers.layerRemoved.addEventListener(() => {
103
+ this.setLegends();
104
+ this.markRelevantLayersForExtent();
105
+ this.hideNonRelevantLayers();
106
+ });
107
+
108
+ this.mapViewer.imageryLayers.layerMoved.addEventListener(() => {
109
+ this.markRelevantLayersForExtent();
110
+ this.hideNonRelevantLayers();
111
+ });
112
+
113
+ this.mapViewer.imageryLayers.layerAdded.addEventListener(() => {
114
+ this.markRelevantLayersForExtent();
115
+ this.hideNonRelevantLayers();
116
+ });
117
+
118
+ this.mapViewer.camera.moveEnd.addEventListener(() => {
119
+ this.markRelevantLayersForExtent();
120
+ this.hideNonRelevantLayers();
121
+ });
122
+ }
81
123
  }
82
124
 
83
125
  /* eslint-disable */
@@ -87,9 +129,9 @@ class LayerManager {
87
129
  ): void {
88
130
  const layer = this.layers.find(layerPredicate);
89
131
  if (layer) {
90
- layer.meta = meta;
132
+ layer.meta = { ...(layer.meta ?? {}), ...meta };
91
133
  this.setLegends();
92
- this.layerUpdated.raiseEvent();
134
+ this.layerUpdated.raiseEvent(meta);
93
135
  }
94
136
  }
95
137
  /* eslint-enable */
@@ -101,6 +143,24 @@ class LayerManager {
101
143
  sortedBaseMapLayers.forEach((layer, idx) => {
102
144
  this.addRasterLayer(layer, idx, baseMap.id);
103
145
  });
146
+
147
+ /**
148
+ * Set transparent layer as the first layer. if using optimized tile requests.
149
+ *
150
+ * Apparently, cesium layer's rectangle is not affective when:
151
+ * - There is only one active layer && The layer's rectangle contains the extent rectangle.
152
+ *
153
+ * As a result, when using optimized tile requesting and we zoom in a discrete layer,
154
+ * there are some visual artifacts due to tiles requesting outside of the layer's rectangle boundary.
155
+ *
156
+ * A simple workaround would be adding a transparent layer as the very first layer at all times,
157
+ * so that we ensure the rectangle will always be affective.
158
+ */
159
+
160
+ if (this.mapViewer.shouldOptimizedTileRequests) {
161
+ this.removeLayer(TRANSPARENT_LAYER_ID);
162
+ this.addTransparentImageryProvider();
163
+ }
104
164
  }
105
165
 
106
166
  public addRasterLayer(
@@ -110,30 +170,47 @@ class LayerManager {
110
170
  ): void {
111
171
  let cesiumLayer: ICesiumImageryLayer | undefined;
112
172
  switch (layer.type) {
113
- case 'XYZ_LAYER':
173
+ case 'XYZ_LAYER': {
174
+ const options = layer.options as UrlTemplateImageryProvider.ConstructorOptions;
175
+
176
+ const providerInstance = this.mapViewer.shouldOptimizedTileRequests
177
+ ? new CustomUrlTemplateImageryProvider(options, this.mapViewer)
178
+ : new UrlTemplateImageryProvider(options);
179
+
114
180
  cesiumLayer = this.mapViewer.imageryLayers.addImageryProvider(
115
- new UrlTemplateImageryProvider(
116
- layer.options as UrlTemplateImageryProvider.ConstructorOptions
117
- ),
181
+ providerInstance,
118
182
  index
119
183
  );
184
+
120
185
  break;
121
- case 'WMS_LAYER':
186
+ }
187
+ case 'WMS_LAYER': {
188
+ const options = layer.options as WebMapServiceImageryProvider.ConstructorOptions;
189
+
190
+ const providerInstance = this.mapViewer.shouldOptimizedTileRequests
191
+ ? new CustomWebMapServiceImageryProvider(options, this.mapViewer)
192
+ : new WebMapServiceImageryProvider(options);
193
+
122
194
  cesiumLayer = this.mapViewer.imageryLayers.addImageryProvider(
123
- new WebMapServiceImageryProvider(
124
- layer.options as WebMapServiceImageryProvider.ConstructorOptions
125
- ),
195
+ providerInstance,
126
196
  index
127
197
  );
128
198
  break;
129
- case 'WMTS_LAYER':
199
+ }
200
+ case 'WMTS_LAYER': {
201
+ const options = layer.options as WebMapTileServiceImageryProvider.ConstructorOptions;
202
+
203
+ const providerInstance = this.mapViewer.shouldOptimizedTileRequests
204
+ ? new CustomWebMapTileServiceImageryProvider(options, this.mapViewer)
205
+ : new WebMapTileServiceImageryProvider(options);
206
+
130
207
  cesiumLayer = this.mapViewer.imageryLayers.addImageryProvider(
131
- new WebMapTileServiceImageryProvider(
132
- layer.options as WebMapTileServiceImageryProvider.ConstructorOptions
133
- ),
208
+ providerInstance,
134
209
  index
135
210
  );
211
+
136
212
  break;
213
+ }
137
214
  case 'OSM_LAYER':
138
215
  break;
139
216
  }
@@ -310,6 +387,29 @@ class LayerManager {
310
387
  });
311
388
  }
312
389
 
390
+ public addTransparentImageryProvider(): void {
391
+ // Worldwide transparent layer
392
+ const transparentLayer = this.mapViewer.imageryLayers.addImageryProvider(
393
+ new SingleTileImageryProvider({
394
+ url: './assets/img/transparent-tile.png',
395
+ /* eslint-disable @typescript-eslint/no-magic-numbers */
396
+ rectangle: new Rectangle(
397
+ -3.141592653589793,
398
+ -1.5707963267948966,
399
+ 3.141592653589793,
400
+ 1.5707963267948966
401
+ ),
402
+ /* eslint-enable @typescript-eslint/no-magic-numbers */
403
+ }),
404
+ 0
405
+ );
406
+
407
+ (transparentLayer as ICesiumImageryLayer).meta = {
408
+ id: TRANSPARENT_LAYER_ID,
409
+ skipRelevancyCheck: true,
410
+ };
411
+ }
412
+
313
413
  private setLegends(): void {
314
414
  if (typeof this.legendsExtractor !== 'undefined') {
315
415
  this.legendsList = this.legendsExtractor(this.layers);
@@ -349,6 +449,110 @@ class LayerManager {
349
449
  }
350
450
  });
351
451
  }
452
+
453
+ private hideNonRelevantLayers(): void {
454
+ for (const layer of this.layers) {
455
+ if (
456
+ layer.meta?.relevantToExtent !== layer.show &&
457
+ layer.imageryProvider.ready
458
+ ) {
459
+ //@ts-ignore
460
+ layer.show = layer.meta?.relevantToExtent ?? true;
461
+ }
462
+ }
463
+ }
464
+
465
+ private markRelevantLayersForExtent(): void {
466
+ try {
467
+ const extent = this.mapViewer.camera.computeViewRectangle() as Rectangle;
468
+
469
+ // Iterating in reverse order so that top layer is first.
470
+ for (let i = this.layers.length - 1; i >= 0; i--) {
471
+ const layer = this.layers[i];
472
+ const intersectsExtent =
473
+ !isEmpty(extent) &&
474
+ !isEmpty(layer.rectangle) &&
475
+ Rectangle.intersection(extent, layer.rectangle);
476
+
477
+ // Iterating from top layer until the current. (inclusive)
478
+ for (let j = this.layers.length - 1; j >= i; j--) {
479
+ if (layer.meta?.skipRelevancyCheck === true) {
480
+ layer.meta = { ...layer.meta, relevantToExtent: true };
481
+ continue;
482
+ }
483
+
484
+ const layerAbove = this.layers[j];
485
+ const layerAboveHasTransparency =
486
+ layerAbove.meta?.[HAS_TRANSPARENCY_META_PROP] === true;
487
+
488
+ if (layer !== layerAbove) {
489
+ // Layer is relevant if in extent and there is no layer above it which is opaque and contains it.
490
+ if (intersectsExtent instanceof Rectangle) {
491
+ if (cesiumRectangleContained(extent, layer.rectangle)) {
492
+ // Layer contains the extent.
493
+ if (
494
+ cesiumRectangleContained(extent, layerAbove.rectangle) &&
495
+ !layerAboveHasTransparency
496
+ ) {
497
+ layer.meta = {
498
+ ...(layer.meta ?? {}),
499
+ relevantToExtent: false,
500
+ };
501
+ break;
502
+ } else {
503
+ layer.meta = {
504
+ ...(layer.meta ?? {}),
505
+ relevantToExtent: true,
506
+ };
507
+ }
508
+ }
509
+
510
+ if (
511
+ cesiumRectangleContained(extent, layerAbove.rectangle) &&
512
+ !layerAboveHasTransparency
513
+ ) {
514
+ layer.meta = { ...(layer.meta ?? {}), relevantToExtent: false };
515
+ break;
516
+ }
517
+
518
+ if (
519
+ cesiumRectangleContained(layer.rectangle, layerAbove.rectangle)
520
+ ) {
521
+ layer.meta = {
522
+ ...(layer.meta ?? {}),
523
+ relevantToExtent: layerAboveHasTransparency,
524
+ };
525
+
526
+ // Once there is layer above that hides it, no need to continue to check.
527
+ if (!layerAboveHasTransparency) {
528
+ break;
529
+ }
530
+ } else {
531
+ // Not contained by layer above it, and inside the extent.
532
+ layer.meta = { ...(layer.meta ?? {}), relevantToExtent: true };
533
+ }
534
+ } else {
535
+ layer.meta = { ...(layer.meta ?? {}), relevantToExtent: false };
536
+ }
537
+ } else {
538
+ // Handle top layer
539
+ if (i === this.layers.length - 1) {
540
+ layer.meta = {
541
+ ...(layer.meta ?? {}),
542
+ relevantToExtent: intersectsExtent instanceof Rectangle,
543
+ };
544
+ }
545
+ }
546
+ }
547
+ }
548
+ } catch (e) {
549
+ console.error(e);
550
+ }
551
+ }
552
+
553
+ public get layerList(): ICesiumImageryLayer[] {
554
+ return this.layers;
555
+ }
352
556
  }
353
557
 
354
558
  export default LayerManager;