@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
|
@@ -19,8 +19,11 @@ var cesium_1 = require("cesium");
|
|
|
19
19
|
var lodash_1 = require("lodash");
|
|
20
20
|
var boolean_point_in_polygon_1 = __importDefault(require("@turf/boolean-point-in-polygon"));
|
|
21
21
|
var point_geojson_1 = require("./tools/geojson/point.geojson");
|
|
22
|
+
var customImageryProviders_1 = require("./helpers/customImageryProviders");
|
|
23
|
+
var utils_1 = require("./helpers/utils");
|
|
22
24
|
var INC = 1;
|
|
23
25
|
var DEC = -1;
|
|
26
|
+
var TRANSPARENT_LAYER_ID = 'TRANSPARENT_BASE_LAYER';
|
|
24
27
|
var LayerManager = /** @class */ (function () {
|
|
25
28
|
function LayerManager(mapViewer, legendsExtractor, onLayersUpdate) {
|
|
26
29
|
var _this = this;
|
|
@@ -33,18 +36,44 @@ var LayerManager = /** @class */ (function () {
|
|
|
33
36
|
if (onLayersUpdate) {
|
|
34
37
|
this.layerUpdated.addEventListener(onLayersUpdate, this);
|
|
35
38
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
// Binding layer's relevancy check to cesium lifecycle if optimized tile requests enabled.
|
|
40
|
+
if (this.mapViewer.shouldOptimizedTileRequests) {
|
|
41
|
+
this.layerUpdated.addEventListener(function (meta) {
|
|
42
|
+
var newMetaKeys = Object.keys(meta);
|
|
43
|
+
var shouldTriggerRelevancyCheck = newMetaKeys.length === 1 &&
|
|
44
|
+
newMetaKeys[0] === customImageryProviders_1.HAS_TRANSPARENCY_META_PROP;
|
|
45
|
+
if (shouldTriggerRelevancyCheck) {
|
|
46
|
+
_this.markRelevantLayersForExtent();
|
|
47
|
+
_this.hideNonRelevantLayers();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
this.mapViewer.imageryLayers.layerRemoved.addEventListener(function () {
|
|
51
|
+
_this.setLegends();
|
|
52
|
+
_this.markRelevantLayersForExtent();
|
|
53
|
+
_this.hideNonRelevantLayers();
|
|
54
|
+
});
|
|
55
|
+
this.mapViewer.imageryLayers.layerMoved.addEventListener(function () {
|
|
56
|
+
_this.markRelevantLayersForExtent();
|
|
57
|
+
_this.hideNonRelevantLayers();
|
|
58
|
+
});
|
|
59
|
+
this.mapViewer.imageryLayers.layerAdded.addEventListener(function () {
|
|
60
|
+
_this.markRelevantLayersForExtent();
|
|
61
|
+
_this.hideNonRelevantLayers();
|
|
62
|
+
});
|
|
63
|
+
this.mapViewer.camera.moveEnd.addEventListener(function () {
|
|
64
|
+
_this.markRelevantLayersForExtent();
|
|
65
|
+
_this.hideNonRelevantLayers();
|
|
66
|
+
});
|
|
67
|
+
}
|
|
40
68
|
}
|
|
41
69
|
/* eslint-disable */
|
|
42
70
|
LayerManager.prototype.addMetaToLayer = function (meta, layerPredicate) {
|
|
71
|
+
var _a;
|
|
43
72
|
var layer = this.layers.find(layerPredicate);
|
|
44
73
|
if (layer) {
|
|
45
|
-
layer.meta = meta;
|
|
74
|
+
layer.meta = __assign(__assign({}, ((_a = layer.meta) !== null && _a !== void 0 ? _a : {})), meta);
|
|
46
75
|
this.setLegends();
|
|
47
|
-
this.layerUpdated.raiseEvent();
|
|
76
|
+
this.layerUpdated.raiseEvent(meta);
|
|
48
77
|
}
|
|
49
78
|
};
|
|
50
79
|
/* eslint-enable */
|
|
@@ -54,19 +83,50 @@ var LayerManager = /** @class */ (function () {
|
|
|
54
83
|
sortedBaseMapLayers.forEach(function (layer, idx) {
|
|
55
84
|
_this.addRasterLayer(layer, idx, baseMap.id);
|
|
56
85
|
});
|
|
86
|
+
/**
|
|
87
|
+
* Set transparent layer as the first layer. if using optimized tile requests.
|
|
88
|
+
*
|
|
89
|
+
* Apparently, cesium layer's rectangle is not affective when:
|
|
90
|
+
* - There is only one active layer && The layer's rectangle contains the extent rectangle.
|
|
91
|
+
*
|
|
92
|
+
* As a result, when using optimized tile requesting and we zoom in a discrete layer,
|
|
93
|
+
* there are some visual artifacts due to tiles requesting outside of the layer's rectangle boundary.
|
|
94
|
+
*
|
|
95
|
+
* A simple workaround would be adding a transparent layer as the very first layer at all times,
|
|
96
|
+
* so that we ensure the rectangle will always be affective.
|
|
97
|
+
*/
|
|
98
|
+
if (this.mapViewer.shouldOptimizedTileRequests) {
|
|
99
|
+
this.removeLayer(TRANSPARENT_LAYER_ID);
|
|
100
|
+
this.addTransparentImageryProvider();
|
|
101
|
+
}
|
|
57
102
|
};
|
|
58
103
|
LayerManager.prototype.addRasterLayer = function (layer, index, parentId) {
|
|
59
104
|
var cesiumLayer;
|
|
60
105
|
switch (layer.type) {
|
|
61
|
-
case 'XYZ_LAYER':
|
|
62
|
-
|
|
106
|
+
case 'XYZ_LAYER': {
|
|
107
|
+
var options = layer.options;
|
|
108
|
+
var providerInstance = this.mapViewer.shouldOptimizedTileRequests
|
|
109
|
+
? new customImageryProviders_1.CustomUrlTemplateImageryProvider(options, this.mapViewer)
|
|
110
|
+
: new cesium_1.UrlTemplateImageryProvider(options);
|
|
111
|
+
cesiumLayer = this.mapViewer.imageryLayers.addImageryProvider(providerInstance, index);
|
|
63
112
|
break;
|
|
64
|
-
|
|
65
|
-
|
|
113
|
+
}
|
|
114
|
+
case 'WMS_LAYER': {
|
|
115
|
+
var options = layer.options;
|
|
116
|
+
var providerInstance = this.mapViewer.shouldOptimizedTileRequests
|
|
117
|
+
? new customImageryProviders_1.CustomWebMapServiceImageryProvider(options, this.mapViewer)
|
|
118
|
+
: new cesium_1.WebMapServiceImageryProvider(options);
|
|
119
|
+
cesiumLayer = this.mapViewer.imageryLayers.addImageryProvider(providerInstance, index);
|
|
66
120
|
break;
|
|
67
|
-
|
|
68
|
-
|
|
121
|
+
}
|
|
122
|
+
case 'WMTS_LAYER': {
|
|
123
|
+
var options = layer.options;
|
|
124
|
+
var providerInstance = this.mapViewer.shouldOptimizedTileRequests
|
|
125
|
+
? new customImageryProviders_1.CustomWebMapTileServiceImageryProvider(options, this.mapViewer)
|
|
126
|
+
: new cesium_1.WebMapTileServiceImageryProvider(options);
|
|
127
|
+
cesiumLayer = this.mapViewer.imageryLayers.addImageryProvider(providerInstance, index);
|
|
69
128
|
break;
|
|
129
|
+
}
|
|
70
130
|
case 'OSM_LAYER':
|
|
71
131
|
break;
|
|
72
132
|
}
|
|
@@ -205,6 +265,19 @@ var LayerManager = /** @class */ (function () {
|
|
|
205
265
|
return ((_a = layer2.meta) === null || _a === void 0 ? void 0 : _a.zIndex) - ((_b = layer1.meta) === null || _b === void 0 ? void 0 : _b.zIndex);
|
|
206
266
|
});
|
|
207
267
|
};
|
|
268
|
+
LayerManager.prototype.addTransparentImageryProvider = function () {
|
|
269
|
+
// Worldwide transparent layer
|
|
270
|
+
var transparentLayer = this.mapViewer.imageryLayers.addImageryProvider(new cesium_1.SingleTileImageryProvider({
|
|
271
|
+
url: './assets/img/transparent-tile.png',
|
|
272
|
+
/* eslint-disable @typescript-eslint/no-magic-numbers */
|
|
273
|
+
rectangle: new cesium_1.Rectangle(-3.141592653589793, -1.5707963267948966, 3.141592653589793, 1.5707963267948966),
|
|
274
|
+
/* eslint-enable @typescript-eslint/no-magic-numbers */
|
|
275
|
+
}), 0);
|
|
276
|
+
transparentLayer.meta = {
|
|
277
|
+
id: TRANSPARENT_LAYER_ID,
|
|
278
|
+
skipRelevancyCheck: true,
|
|
279
|
+
};
|
|
280
|
+
};
|
|
208
281
|
LayerManager.prototype.setLegends = function () {
|
|
209
282
|
if (typeof this.legendsExtractor !== 'undefined') {
|
|
210
283
|
this.legendsList = this.legendsExtractor(this.layers);
|
|
@@ -241,6 +314,90 @@ var LayerManager = /** @class */ (function () {
|
|
|
241
314
|
}
|
|
242
315
|
});
|
|
243
316
|
};
|
|
317
|
+
LayerManager.prototype.hideNonRelevantLayers = function () {
|
|
318
|
+
var _a, _b, _c;
|
|
319
|
+
for (var _i = 0, _d = this.layers; _i < _d.length; _i++) {
|
|
320
|
+
var layer = _d[_i];
|
|
321
|
+
if (((_a = layer.meta) === null || _a === void 0 ? void 0 : _a.relevantToExtent) !== layer.show &&
|
|
322
|
+
layer.imageryProvider.ready) {
|
|
323
|
+
//@ts-ignore
|
|
324
|
+
layer.show = (_c = (_b = layer.meta) === null || _b === void 0 ? void 0 : _b.relevantToExtent) !== null && _c !== void 0 ? _c : true;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
LayerManager.prototype.markRelevantLayersForExtent = function () {
|
|
329
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
330
|
+
try {
|
|
331
|
+
var extent = this.mapViewer.camera.computeViewRectangle();
|
|
332
|
+
// Iterating in reverse order so that top layer is first.
|
|
333
|
+
for (var i = this.layers.length - 1; i >= 0; i--) {
|
|
334
|
+
var layer = this.layers[i];
|
|
335
|
+
var intersectsExtent = !lodash_1.isEmpty(extent) &&
|
|
336
|
+
!lodash_1.isEmpty(layer.rectangle) &&
|
|
337
|
+
cesium_1.Rectangle.intersection(extent, layer.rectangle);
|
|
338
|
+
// Iterating from top layer until the current. (inclusive)
|
|
339
|
+
for (var j = this.layers.length - 1; j >= i; j--) {
|
|
340
|
+
if (((_a = layer.meta) === null || _a === void 0 ? void 0 : _a.skipRelevancyCheck) === true) {
|
|
341
|
+
layer.meta = __assign(__assign({}, layer.meta), { relevantToExtent: true });
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
var layerAbove = this.layers[j];
|
|
345
|
+
var layerAboveHasTransparency = ((_b = layerAbove.meta) === null || _b === void 0 ? void 0 : _b[customImageryProviders_1.HAS_TRANSPARENCY_META_PROP]) === true;
|
|
346
|
+
if (layer !== layerAbove) {
|
|
347
|
+
// Layer is relevant if in extent and there is no layer above it which is opaque and contains it.
|
|
348
|
+
if (intersectsExtent instanceof cesium_1.Rectangle) {
|
|
349
|
+
if (utils_1.cesiumRectangleContained(extent, layer.rectangle)) {
|
|
350
|
+
// Layer contains the extent.
|
|
351
|
+
if (utils_1.cesiumRectangleContained(extent, layerAbove.rectangle) &&
|
|
352
|
+
!layerAboveHasTransparency) {
|
|
353
|
+
layer.meta = __assign(__assign({}, ((_c = layer.meta) !== null && _c !== void 0 ? _c : {})), { relevantToExtent: false });
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
layer.meta = __assign(__assign({}, ((_d = layer.meta) !== null && _d !== void 0 ? _d : {})), { relevantToExtent: true });
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (utils_1.cesiumRectangleContained(extent, layerAbove.rectangle) &&
|
|
361
|
+
!layerAboveHasTransparency) {
|
|
362
|
+
layer.meta = __assign(__assign({}, ((_e = layer.meta) !== null && _e !== void 0 ? _e : {})), { relevantToExtent: false });
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
if (utils_1.cesiumRectangleContained(layer.rectangle, layerAbove.rectangle)) {
|
|
366
|
+
layer.meta = __assign(__assign({}, ((_f = layer.meta) !== null && _f !== void 0 ? _f : {})), { relevantToExtent: layerAboveHasTransparency });
|
|
367
|
+
// Once there is layer above that hides it, no need to continue to check.
|
|
368
|
+
if (!layerAboveHasTransparency) {
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
// Not contained by layer above it, and inside the extent.
|
|
374
|
+
layer.meta = __assign(__assign({}, ((_g = layer.meta) !== null && _g !== void 0 ? _g : {})), { relevantToExtent: true });
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
layer.meta = __assign(__assign({}, ((_h = layer.meta) !== null && _h !== void 0 ? _h : {})), { relevantToExtent: false });
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
// Handle top layer
|
|
383
|
+
if (i === this.layers.length - 1) {
|
|
384
|
+
layer.meta = __assign(__assign({}, ((_j = layer.meta) !== null && _j !== void 0 ? _j : {})), { relevantToExtent: intersectsExtent instanceof cesium_1.Rectangle });
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
catch (e) {
|
|
391
|
+
console.error(e);
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
Object.defineProperty(LayerManager.prototype, "layerList", {
|
|
395
|
+
get: function () {
|
|
396
|
+
return this.layers;
|
|
397
|
+
},
|
|
398
|
+
enumerable: false,
|
|
399
|
+
configurable: true
|
|
400
|
+
});
|
|
244
401
|
return LayerManager;
|
|
245
402
|
}());
|
|
246
403
|
exports.default = LayerManager;
|
package/dist/cesium-map/map.d.ts
CHANGED
|
@@ -9,7 +9,10 @@ import { CesiumSceneModeEnum } from './map.types';
|
|
|
9
9
|
import './map.css';
|
|
10
10
|
export declare class CesiumViewer extends CesiumViewerCls {
|
|
11
11
|
layersManager?: LayerManager;
|
|
12
|
+
private useOptimizedTileRequests?;
|
|
12
13
|
constructor(container: string | Element, options?: CesiumViewerCls.ConstructorOptions);
|
|
14
|
+
get shouldOptimizedTileRequests(): boolean;
|
|
15
|
+
set shouldOptimizedTileRequests(useOptimizedTileRequests: boolean);
|
|
13
16
|
}
|
|
14
17
|
export interface IContextMenuData {
|
|
15
18
|
data: Record<string, unknown>[];
|
|
@@ -49,6 +52,7 @@ export interface CesiumMapProps extends ViewerProps {
|
|
|
49
52
|
};
|
|
50
53
|
sceneModes?: CesiumSceneModeEnum[];
|
|
51
54
|
baseMaps?: IBaseMaps;
|
|
55
|
+
useOptimizedTileRequests?: boolean;
|
|
52
56
|
terrainProvider?: TerrainProvider;
|
|
53
57
|
imageryContextMenu?: React.ReactElement<IContextMenuData>;
|
|
54
58
|
imageryContextMenuSize?: {
|
package/dist/cesium-map/map.js
CHANGED
|
@@ -73,6 +73,17 @@ var CesiumViewer = /** @class */ (function (_super) {
|
|
|
73
73
|
function CesiumViewer(container, options) {
|
|
74
74
|
return _super.call(this, container, options) || this;
|
|
75
75
|
}
|
|
76
|
+
Object.defineProperty(CesiumViewer.prototype, "shouldOptimizedTileRequests", {
|
|
77
|
+
get: function () {
|
|
78
|
+
var _a;
|
|
79
|
+
return (_a = this.useOptimizedTileRequests) !== null && _a !== void 0 ? _a : false;
|
|
80
|
+
},
|
|
81
|
+
set: function (useOptimizedTileRequests) {
|
|
82
|
+
this.useOptimizedTileRequests = useOptimizedTileRequests;
|
|
83
|
+
},
|
|
84
|
+
enumerable: false,
|
|
85
|
+
configurable: true
|
|
86
|
+
});
|
|
76
87
|
return CesiumViewer;
|
|
77
88
|
}(cesium_1.Viewer));
|
|
78
89
|
exports.CesiumViewer = CesiumViewer;
|
|
@@ -134,14 +145,24 @@ var CesiumMap = function (props) {
|
|
|
134
145
|
setMapViewRef((_a = ref.current) === null || _a === void 0 ? void 0 : _a.cesiumElement);
|
|
135
146
|
}, [ref, props.imageryContextMenu]);
|
|
136
147
|
react_1.useEffect(function () {
|
|
137
|
-
var _a;
|
|
148
|
+
var _a, _b;
|
|
138
149
|
if (mapViewRef) {
|
|
139
|
-
mapViewRef.
|
|
150
|
+
mapViewRef.shouldOptimizedTileRequests =
|
|
151
|
+
(_a = props.useOptimizedTileRequests) !== null && _a !== void 0 ? _a : false;
|
|
152
|
+
mapViewRef.layersManager = new layers_manager_1.default(mapViewRef, (_b = props.legends) === null || _b === void 0 ? void 0 : _b.mapLegendsExtractor, function () {
|
|
140
153
|
var _a;
|
|
141
154
|
setLegendsList((_a = mapViewRef === null || mapViewRef === void 0 ? void 0 : mapViewRef.layersManager) === null || _a === void 0 ? void 0 : _a.legendsList);
|
|
142
155
|
});
|
|
143
156
|
}
|
|
157
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
144
158
|
}, [mapViewRef]);
|
|
159
|
+
react_1.useEffect(function () {
|
|
160
|
+
var _a;
|
|
161
|
+
if (mapViewRef) {
|
|
162
|
+
mapViewRef.shouldOptimizedTileRequests =
|
|
163
|
+
(_a = props.useOptimizedTileRequests) !== null && _a !== void 0 ? _a : false;
|
|
164
|
+
}
|
|
165
|
+
}, [props.useOptimizedTileRequests, mapViewRef]);
|
|
145
166
|
react_1.useEffect(function () {
|
|
146
167
|
var _a;
|
|
147
168
|
setSceneModes((_a = props.sceneModes) !== null && _a !== void 0 ? _a : [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@map-colonies/react-components",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.14.0",
|
|
4
4
|
"module": "dist/index.js",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"jest-enzyme": "^7.1.2",
|
|
94
94
|
"react-test-renderer": "^16.13.1"
|
|
95
95
|
},
|
|
96
|
-
"gitHead": "
|
|
96
|
+
"gitHead": "71cc65e8ccd3f45ab1b0eb9de2bd3aab10735654",
|
|
97
97
|
"jest": {
|
|
98
98
|
"coverageReporters": [
|
|
99
99
|
"text",
|
|
Binary file
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Request,
|
|
3
|
+
ImageryProvider,
|
|
4
|
+
UrlTemplateImageryProvider,
|
|
5
|
+
WebMapServiceImageryProvider,
|
|
6
|
+
WebMapTileServiceImageryProvider,
|
|
7
|
+
ImageryLayer,
|
|
8
|
+
} from 'cesium';
|
|
9
|
+
import { get, isEmpty } from 'lodash';
|
|
10
|
+
import { ICesiumImageryLayer } from '../layers-manager';
|
|
11
|
+
import { CesiumViewer } from '../map';
|
|
12
|
+
import { imageHasTransparency } from './utils';
|
|
13
|
+
|
|
14
|
+
export interface CustomImageryProvider extends ImageryProvider {
|
|
15
|
+
readonly layerListInstance: ICesiumImageryLayer[];
|
|
16
|
+
tileTransparencyCheckedCounter: number;
|
|
17
|
+
mapViewer: CesiumViewer;
|
|
18
|
+
readonly maxTilesForTransparencyCheck: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const NUMBER_OF_TILES_TO_CHECK = 3;
|
|
22
|
+
export const HAS_TRANSPARENCY_META_PROP = 'hasTransparency';
|
|
23
|
+
|
|
24
|
+
function customCommonRequestImage(
|
|
25
|
+
this: CustomImageryProvider,
|
|
26
|
+
requestImageFn: ImageryProvider['requestImage'],
|
|
27
|
+
x: number,
|
|
28
|
+
y: number,
|
|
29
|
+
level: number,
|
|
30
|
+
request?: Request | undefined
|
|
31
|
+
): Promise<HTMLImageElement | HTMLCanvasElement> | undefined {
|
|
32
|
+
// custom Logic
|
|
33
|
+
setTimeout(() => {
|
|
34
|
+
const requestedLayerMeta = this.layerListInstance.find(
|
|
35
|
+
/* eslint-disable */
|
|
36
|
+
(layer: ImageryLayer): boolean => {
|
|
37
|
+
return (
|
|
38
|
+
(layer as any)._imageryProvider._resource._url ===
|
|
39
|
+
(this as any)._resource._url
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
/* eslint-enable */
|
|
43
|
+
)?.meta;
|
|
44
|
+
|
|
45
|
+
const layerHasTransparency =
|
|
46
|
+
get(requestedLayerMeta, HAS_TRANSPARENCY_META_PROP) === true;
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
this.tileTransparencyCheckedCounter < NUMBER_OF_TILES_TO_CHECK &&
|
|
50
|
+
!layerHasTransparency
|
|
51
|
+
) {
|
|
52
|
+
void imageHasTransparency(request?.url as string, this).then(
|
|
53
|
+
(hasTransparency) => {
|
|
54
|
+
this.mapViewer.layersManager?.addMetaToLayer(
|
|
55
|
+
{ [HAS_TRANSPARENCY_META_PROP]: hasTransparency },
|
|
56
|
+
/* eslint-disable */
|
|
57
|
+
(layer: ImageryLayer): boolean => {
|
|
58
|
+
return (
|
|
59
|
+
(layer as any)._imageryProvider._resource._url ===
|
|
60
|
+
(this as any)._resource._url
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
/* eslint-enable */
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}, 0);
|
|
69
|
+
|
|
70
|
+
return requestImageFn(x, y, level, request);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class CustomUrlTemplateImageryProvider extends UrlTemplateImageryProvider {
|
|
74
|
+
public readonly layerListInstance: ICesiumImageryLayer[];
|
|
75
|
+
public readonly mapViewer: CesiumViewer;
|
|
76
|
+
public readonly maxTilesForTransparencyCheck = NUMBER_OF_TILES_TO_CHECK;
|
|
77
|
+
|
|
78
|
+
public tileTransparencyCheckedCounter = 0;
|
|
79
|
+
|
|
80
|
+
public constructor(
|
|
81
|
+
opts: UrlTemplateImageryProvider.ConstructorOptions,
|
|
82
|
+
mapViewer: CesiumViewer
|
|
83
|
+
) {
|
|
84
|
+
super(opts);
|
|
85
|
+
this.layerListInstance = mapViewer.layersManager
|
|
86
|
+
?.layerList as ICesiumImageryLayer[];
|
|
87
|
+
this.mapViewer = mapViewer;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public requestImage(
|
|
91
|
+
x: number,
|
|
92
|
+
y: number,
|
|
93
|
+
level: number,
|
|
94
|
+
request?: Request | undefined
|
|
95
|
+
): Promise<HTMLImageElement | HTMLCanvasElement> | undefined {
|
|
96
|
+
return customCommonRequestImage.call(
|
|
97
|
+
this,
|
|
98
|
+
super.requestImage.bind(this),
|
|
99
|
+
x,
|
|
100
|
+
y,
|
|
101
|
+
level,
|
|
102
|
+
request
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export class CustomWebMapServiceImageryProvider extends WebMapServiceImageryProvider {
|
|
108
|
+
public readonly layerListInstance: ICesiumImageryLayer[];
|
|
109
|
+
public readonly mapViewer: CesiumViewer;
|
|
110
|
+
public readonly maxTilesForTransparencyCheck = NUMBER_OF_TILES_TO_CHECK;
|
|
111
|
+
|
|
112
|
+
public tileTransparencyCheckedCounter = 0;
|
|
113
|
+
|
|
114
|
+
public constructor(
|
|
115
|
+
opts: WebMapServiceImageryProvider.ConstructorOptions,
|
|
116
|
+
mapViewer: CesiumViewer
|
|
117
|
+
) {
|
|
118
|
+
super(opts);
|
|
119
|
+
this.layerListInstance = mapViewer.layersManager
|
|
120
|
+
?.layerList as ICesiumImageryLayer[];
|
|
121
|
+
this.mapViewer = mapViewer;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public requestImage(
|
|
125
|
+
x: number,
|
|
126
|
+
y: number,
|
|
127
|
+
level: number,
|
|
128
|
+
request?: Request | undefined
|
|
129
|
+
): Promise<HTMLImageElement | HTMLCanvasElement> | undefined {
|
|
130
|
+
return customCommonRequestImage.call(
|
|
131
|
+
this,
|
|
132
|
+
super.requestImage.bind(this),
|
|
133
|
+
x,
|
|
134
|
+
y,
|
|
135
|
+
level,
|
|
136
|
+
request
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export class CustomWebMapTileServiceImageryProvider extends WebMapTileServiceImageryProvider {
|
|
142
|
+
public readonly layerListInstance: ICesiumImageryLayer[];
|
|
143
|
+
public readonly mapViewer: CesiumViewer;
|
|
144
|
+
public readonly maxTilesForTransparencyCheck = NUMBER_OF_TILES_TO_CHECK;
|
|
145
|
+
|
|
146
|
+
public tileTransparencyCheckedCounter = 0;
|
|
147
|
+
|
|
148
|
+
public constructor(
|
|
149
|
+
opts: WebMapTileServiceImageryProvider.ConstructorOptions,
|
|
150
|
+
mapViewer: CesiumViewer
|
|
151
|
+
) {
|
|
152
|
+
super(opts);
|
|
153
|
+
this.layerListInstance = mapViewer.layersManager
|
|
154
|
+
?.layerList as ICesiumImageryLayer[];
|
|
155
|
+
this.mapViewer = mapViewer;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public requestImage(
|
|
159
|
+
x: number,
|
|
160
|
+
y: number,
|
|
161
|
+
level: number,
|
|
162
|
+
request?: Request | undefined
|
|
163
|
+
): Promise<HTMLImageElement | HTMLCanvasElement> | undefined {
|
|
164
|
+
return customCommonRequestImage.call(
|
|
165
|
+
this,
|
|
166
|
+
super.requestImage.bind(this),
|
|
167
|
+
x,
|
|
168
|
+
y,
|
|
169
|
+
level,
|
|
170
|
+
request
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Rectangle } from 'cesium';
|
|
2
|
+
import { CustomImageryProvider } from './customImageryProviders';
|
|
3
|
+
|
|
4
|
+
const canvasElem = document.createElement('canvas');
|
|
5
|
+
const canvasCtx = canvasElem.getContext('2d');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param image Image data to check
|
|
9
|
+
* @returns `true` if image data has at least one transparent pixel, `false` otherwise.
|
|
10
|
+
*/
|
|
11
|
+
const imageDataHasTransparency = (image: ImageData | undefined): boolean => {
|
|
12
|
+
const ALPHA_CHANNEL_OFFSET = 4; // [R,G,B,A, R,G,B,A] => FLAT ARRAY OF THIS SHAPE; (Uint8ClampedArray)
|
|
13
|
+
const OPAQUE_PIXEL_ALPHA_VALUE = 255;
|
|
14
|
+
const imgData = image?.data ?? [];
|
|
15
|
+
|
|
16
|
+
// Iterate through alpha channels only.
|
|
17
|
+
for (let i = 3; i < imgData?.length; i += ALPHA_CHANNEL_OFFSET) {
|
|
18
|
+
if (imgData[i] < OPAQUE_PIXEL_ALPHA_VALUE) {
|
|
19
|
+
// Transparent pixel found.
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* An async function to detect images with transparency.
|
|
28
|
+
* @param image The image to resolve. if value is `string (url)` it tries to fetch the image data first.
|
|
29
|
+
* Could also be `HTMLImageElement` or `ImageBitmap`
|
|
30
|
+
* @param context `optional` `CustomImageryProvider` context in which the function will automatically increase
|
|
31
|
+
* the `tileTransparencyCheckedCounter`. Sets to `maxTilesForTransparencyCheck` when layer detected as transparent.
|
|
32
|
+
* @returns
|
|
33
|
+
*/
|
|
34
|
+
export const imageHasTransparency = async (
|
|
35
|
+
image: string | HTMLImageElement | ImageBitmap,
|
|
36
|
+
context?: CustomImageryProvider
|
|
37
|
+
): Promise<boolean> => {
|
|
38
|
+
if (context) {
|
|
39
|
+
context.tileTransparencyCheckedCounter++;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return new Promise<boolean>((resolve, reject) => {
|
|
43
|
+
try {
|
|
44
|
+
canvasCtx?.clearRect(0, 0, canvasElem.width, canvasElem.height);
|
|
45
|
+
let imageElement: HTMLImageElement;
|
|
46
|
+
|
|
47
|
+
// Init Image instance.
|
|
48
|
+
if (image instanceof HTMLImageElement) {
|
|
49
|
+
imageElement = image;
|
|
50
|
+
} else if (image instanceof ImageBitmap) {
|
|
51
|
+
canvasElem.width = image.width;
|
|
52
|
+
canvasElem.height = image.height;
|
|
53
|
+
canvasCtx?.drawImage(image, 0, 0);
|
|
54
|
+
|
|
55
|
+
const canvasImg = canvasCtx?.getImageData(
|
|
56
|
+
0,
|
|
57
|
+
0,
|
|
58
|
+
canvasElem.width,
|
|
59
|
+
canvasElem.height
|
|
60
|
+
);
|
|
61
|
+
const hasTransparency = imageDataHasTransparency(canvasImg);
|
|
62
|
+
if (hasTransparency) {
|
|
63
|
+
if (context) {
|
|
64
|
+
context.tileTransparencyCheckedCounter =
|
|
65
|
+
context.maxTilesForTransparencyCheck;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
resolve(hasTransparency);
|
|
70
|
+
return;
|
|
71
|
+
} else {
|
|
72
|
+
imageElement = new Image();
|
|
73
|
+
imageElement.crossOrigin = 'anonymous'; // Disable CORS errors on canvas image load.
|
|
74
|
+
imageElement.src = image;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
imageElement.onload = (): void => {
|
|
78
|
+
// Image loaded, set canvas size to image size.
|
|
79
|
+
canvasElem.width = imageElement.width;
|
|
80
|
+
canvasElem.height = imageElement.height;
|
|
81
|
+
|
|
82
|
+
canvasCtx?.drawImage(imageElement, 0, 0);
|
|
83
|
+
|
|
84
|
+
const canvasImg = canvasCtx?.getImageData(
|
|
85
|
+
0,
|
|
86
|
+
0,
|
|
87
|
+
canvasElem.width,
|
|
88
|
+
canvasElem.height
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const hasTransparency = imageDataHasTransparency(canvasImg);
|
|
92
|
+
|
|
93
|
+
if (hasTransparency) {
|
|
94
|
+
if (context) {
|
|
95
|
+
context.tileTransparencyCheckedCounter =
|
|
96
|
+
context.maxTilesForTransparencyCheck;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
resolve(true);
|
|
100
|
+
} else {
|
|
101
|
+
resolve(false);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
} catch (e) {
|
|
105
|
+
console.error('Could not determine image transparency. Error => ', e);
|
|
106
|
+
reject(e);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Checks if `rect` is contained inside `anotherRect`
|
|
113
|
+
* @param rect
|
|
114
|
+
* @param anotherRect
|
|
115
|
+
*/
|
|
116
|
+
export const cesiumRectangleContained = (
|
|
117
|
+
rect: Rectangle,
|
|
118
|
+
anotherRect: Rectangle
|
|
119
|
+
): boolean => {
|
|
120
|
+
const { west, east, north, south } = rect;
|
|
121
|
+
const {
|
|
122
|
+
west: anotherWest,
|
|
123
|
+
east: anotherEast,
|
|
124
|
+
north: anotherNorth,
|
|
125
|
+
south: anotherSouth,
|
|
126
|
+
} = anotherRect;
|
|
127
|
+
|
|
128
|
+
const isRectInsideAnother =
|
|
129
|
+
west >= anotherWest &&
|
|
130
|
+
east <= anotherEast &&
|
|
131
|
+
north <= anotherNorth &&
|
|
132
|
+
south >= anotherSouth;
|
|
133
|
+
|
|
134
|
+
return isRectInsideAnother;
|
|
135
|
+
};
|