@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.
- package/CHANGELOG.md +41 -17
- package/dist/assets/img/transparent-tile.png +0 -0
- package/dist/cesium-map/helpers/customImageryProviders.d.ts +34 -0
- package/dist/cesium-map/helpers/customImageryProviders.js +106 -0
- package/dist/cesium-map/helpers/utils.d.ts +17 -0
- package/dist/cesium-map/helpers/utils.js +142 -0
- package/dist/cesium-map/layers/wms.layer.js +7 -1
- package/dist/cesium-map/layers/wmts.layer.js +7 -1
- package/dist/cesium-map/layers/xyz.layer.js +7 -1
- package/dist/cesium-map/layers-manager.d.ts +4 -0
- package/dist/cesium-map/layers-manager.js +169 -12
- package/dist/cesium-map/map.d.ts +4 -0
- package/dist/cesium-map/map.js +23 -2
- package/package.json +2 -2
- package/public/assets/img/transparent-tile.png +0 -0
- package/src/lib/cesium-map/helpers/customImageryProviders.ts +173 -0
- package/src/lib/cesium-map/helpers/utils.ts +135 -0
- package/src/lib/cesium-map/layers/optimized-tile-requests.stories.tsx +279 -0
- package/src/lib/cesium-map/layers/wms.layer.tsx +10 -4
- package/src/lib/cesium-map/layers/wmts.layer.tsx +9 -4
- package/src/lib/cesium-map/layers/xyz.layer.tsx +9 -4
- package/src/lib/cesium-map/layers-manager.ts +223 -19
- package/src/lib/cesium-map/map.tsx +21 -0
- package/src/lib/cesium-map/tools/coordinates-tracker.tool.tsx +6 -1
- package/src/lib/date-range-picker/date-range-picker.spec.tsx +81 -81
- package/jest_html_reporters.html +0 -78
|
@@ -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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
116
|
-
layer.options as UrlTemplateImageryProvider.ConstructorOptions
|
|
117
|
-
),
|
|
181
|
+
providerInstance,
|
|
118
182
|
index
|
|
119
183
|
);
|
|
184
|
+
|
|
120
185
|
break;
|
|
121
|
-
|
|
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
|
-
|
|
124
|
-
layer.options as WebMapServiceImageryProvider.ConstructorOptions
|
|
125
|
-
),
|
|
195
|
+
providerInstance,
|
|
126
196
|
index
|
|
127
197
|
);
|
|
128
198
|
break;
|
|
129
|
-
|
|
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
|
-
|
|
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;
|