@map-colonies/react-components 3.14.0 → 3.15.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 +11 -0
- package/dist/cesium-map/map.d.ts +1 -0
- package/dist/cesium-map/map.js +17 -10
- package/dist/cesium-map/tools/scale-tracker.tool.js +4 -1
- package/dist/cesium-map/tools/zoom_level-tracker.tool.css +19 -0
- package/dist/cesium-map/tools/zoom_level-tracker.tool.d.ts +8 -0
- package/dist/cesium-map/tools/zoom_level-tracker.tool.js +131 -0
- package/package.json +2 -2
- package/src/lib/cesium-map/map.stories.tsx +1 -0
- package/src/lib/cesium-map/map.tsx +12 -0
- package/src/lib/cesium-map/settings/settings.stories.tsx +5 -1
- package/src/lib/cesium-map/tools/scale-tracker.tool.tsx +4 -1
- package/src/lib/cesium-map/tools/zoom_level-tracker.tool.css +19 -0
- package/src/lib/cesium-map/tools/zoom_level-tracker.tool.tsx +142 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [3.15.0](https://github.com/MapColonies/shared-components/compare/@map-colonies/react-components@3.14.0...@map-colonies/react-components@3.15.0) (2023-05-28)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* zoom level tracker ([#138](https://github.com/MapColonies/shared-components/issues/138)) ([b3d04ef](https://github.com/MapColonies/shared-components/commit/b3d04efe107b7c79595bd5bdb3229bafe31891df))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
# [3.14.0](https://github.com/MapColonies/shared-components/compare/@map-colonies/react-components@3.13.1...@map-colonies/react-components@3.14.0) (2023-04-13)
|
|
7
18
|
|
|
8
19
|
|
package/dist/cesium-map/map.d.ts
CHANGED
package/dist/cesium-map/map.js
CHANGED
|
@@ -59,6 +59,7 @@ var box_1 = require("../box");
|
|
|
59
59
|
var projections_1 = require("../utils/projections");
|
|
60
60
|
var coordinates_tracker_tool_1 = require("./tools/coordinates-tracker.tool");
|
|
61
61
|
var scale_tracker_tool_1 = require("./tools/scale-tracker.tool");
|
|
62
|
+
var zoom_level_tracker_tool_1 = require("./tools/zoom_level-tracker.tool");
|
|
62
63
|
var settings_1 = require("./settings/settings");
|
|
63
64
|
var map_legend_1 = require("./map-legend");
|
|
64
65
|
var layers_manager_1 = __importDefault(require("./layers-manager"));
|
|
@@ -104,16 +105,17 @@ var CesiumMap = function (props) {
|
|
|
104
105
|
var _p = react_1.useState(), mapViewRef = _p[0], setMapViewRef = _p[1];
|
|
105
106
|
var _q = react_1.useState(), projection = _q[0], setProjection = _q[1];
|
|
106
107
|
var _r = react_1.useState(), showMousePosition = _r[0], setShowMousePosition = _r[1];
|
|
107
|
-
var _s = react_1.useState(),
|
|
108
|
-
var _t = react_1.useState(),
|
|
109
|
-
var _u = react_1.useState(),
|
|
110
|
-
var _v = react_1.useState(),
|
|
111
|
-
var _w = react_1.useState(
|
|
112
|
-
var _x = react_1.useState(),
|
|
113
|
-
var _y = react_1.useState(
|
|
114
|
-
var _z = react_1.useState(
|
|
115
|
-
var _0 = react_1.useState(
|
|
116
|
-
var _1 = react_1.useState(),
|
|
108
|
+
var _s = react_1.useState(), showZoomLevel = _s[0], setShowZoomLevel = _s[1];
|
|
109
|
+
var _t = react_1.useState(), showScale = _t[0], setShowScale = _t[1];
|
|
110
|
+
var _u = react_1.useState(), locale = _u[0], setLocale = _u[1];
|
|
111
|
+
var _v = react_1.useState(), cameraState = _v[0], setCameraState = _v[1];
|
|
112
|
+
var _w = react_1.useState(), sceneModes = _w[0], setSceneModes = _w[1];
|
|
113
|
+
var _x = react_1.useState([]), legendsList = _x[0], setLegendsList = _x[1];
|
|
114
|
+
var _y = react_1.useState(), baseMaps = _y[0], setBaseMaps = _y[1];
|
|
115
|
+
var _z = react_1.useState(false), showImageryMenu = _z[0], setShowImageryMenu = _z[1];
|
|
116
|
+
var _0 = react_1.useState(undefined), imageryMenuPosition = _0[0], setImageryMenuPosition = _0[1];
|
|
117
|
+
var _1 = react_1.useState(false), isLegendsSidebarOpen = _1[0], setIsLegendsSidebarOpen = _1[1];
|
|
118
|
+
var _2 = react_1.useState(), rightClickCoordinates = _2[0], setRightClickCoordinates = _2[1];
|
|
117
119
|
var viewerProps = __assign({ fullscreenButton: true, timeline: false, animation: false, baseLayerPicker: false, geocoder: false, navigationHelpButton: false, homeButton: false, sceneModePicker: false }, props);
|
|
118
120
|
var getImageryMenuStyle = function (x, y, menuWidth, menuHeight, menuDynamicHeightIncrement) {
|
|
119
121
|
var container = mapViewRef.container;
|
|
@@ -190,6 +192,10 @@ var CesiumMap = function (props) {
|
|
|
190
192
|
var _a;
|
|
191
193
|
setShowMousePosition((_a = props.showMousePosition) !== null && _a !== void 0 ? _a : true);
|
|
192
194
|
}, [props.showMousePosition]);
|
|
195
|
+
react_1.useEffect(function () {
|
|
196
|
+
var _a;
|
|
197
|
+
setShowZoomLevel((_a = props.showZoomLevel) !== null && _a !== void 0 ? _a : true);
|
|
198
|
+
}, [props.showZoomLevel]);
|
|
193
199
|
react_1.useEffect(function () {
|
|
194
200
|
var _a;
|
|
195
201
|
setShowScale((_a = props.showScale) !== null && _a !== void 0 ? _a : true);
|
|
@@ -290,6 +296,7 @@ var CesiumMap = function (props) {
|
|
|
290
296
|
} })),
|
|
291
297
|
react_1.default.createElement(box_1.Box, { className: "toolsContainer" },
|
|
292
298
|
showMousePosition === true ? (react_1.default.createElement(coordinates_tracker_tool_1.CoordinatesTrackerTool, { projection: projection })) : (react_1.default.createElement(react_1.default.Fragment, null)),
|
|
299
|
+
showZoomLevel === true ? (react_1.default.createElement(zoom_level_tracker_tool_1.ZoomLevelTrackerTool, { locale: locale })) : (react_1.default.createElement(react_1.default.Fragment, null)),
|
|
293
300
|
showScale === true ? react_1.default.createElement(scale_tracker_tool_1.ScaleTrackerTool, { locale: locale }) : react_1.default.createElement(react_1.default.Fragment, null))), document.querySelector('.cesium-viewer')));
|
|
294
301
|
}, [
|
|
295
302
|
baseMaps,
|
|
@@ -136,7 +136,10 @@ var ScaleTrackerTool = function (props) {
|
|
|
136
136
|
mapViewer.camera.moveEnd.addEventListener(setFromEvent);
|
|
137
137
|
return function () {
|
|
138
138
|
try {
|
|
139
|
-
|
|
139
|
+
/* eslint-disable @typescript-eslint/no-unnecessary-condition*/
|
|
140
|
+
if (lodash_1.get(mapViewer, '_cesiumWidget') != undefined) {
|
|
141
|
+
mapViewer.camera.moveEnd.removeEventListener(setFromEvent);
|
|
142
|
+
}
|
|
140
143
|
}
|
|
141
144
|
catch (e) {
|
|
142
145
|
console.log('CESIUM camera "moveEnd" remove listener failed', e);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.zoomLevel {
|
|
2
|
+
z-index: 2000;
|
|
3
|
+
background-color: var(--mdc-theme-surface);
|
|
4
|
+
border-radius: 2px;
|
|
5
|
+
padding: 2px;
|
|
6
|
+
height: 24px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.zoomLevelValue {
|
|
10
|
+
margin: 2px;
|
|
11
|
+
font-size: 14px;
|
|
12
|
+
line-height: 10px;
|
|
13
|
+
text-align: center;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.zoomLevelLabel {
|
|
17
|
+
font-size: 8px;
|
|
18
|
+
line-height: 8px;
|
|
19
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.ZoomLevelTrackerTool = void 0;
|
|
23
|
+
var react_1 = __importStar(require("react"));
|
|
24
|
+
var lodash_1 = require("lodash");
|
|
25
|
+
var map_1 = require("../map");
|
|
26
|
+
var map_types_1 = require("../map.types");
|
|
27
|
+
require("./zoom_level-tracker.tool.css");
|
|
28
|
+
/* eslint-disable @typescript-eslint/no-magic-numbers, @typescript-eslint/naming-convention, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */
|
|
29
|
+
var detectZoomLevel = function (distance, viewer) {
|
|
30
|
+
var MAX_ZOOM_LEVEL = 19;
|
|
31
|
+
var tileProvider = lodash_1.get(viewer.scene.globe, '_surface.tileProvider');
|
|
32
|
+
var quadtree = tileProvider._quadtree;
|
|
33
|
+
var drawingBufferHeight = viewer.canvas.height;
|
|
34
|
+
var sseDenominator = lodash_1.get(viewer.camera.frustum, 'sseDenominator');
|
|
35
|
+
for (var level = 0; level <= MAX_ZOOM_LEVEL; level++) {
|
|
36
|
+
var maxGeometricError = tileProvider.getLevelMaximumGeometricError(level);
|
|
37
|
+
var error = (maxGeometricError * drawingBufferHeight) / (distance * sseDenominator);
|
|
38
|
+
if (error < quadtree.maximumScreenSpaceError) {
|
|
39
|
+
return level;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
};
|
|
44
|
+
var getZoomLevelHeights = function (precision, viewer) {
|
|
45
|
+
precision = precision || 10;
|
|
46
|
+
var result = [];
|
|
47
|
+
var step = 100000.0;
|
|
48
|
+
var currentZoomLevel = 0;
|
|
49
|
+
for (var height = 100000000.0; height > step; height = height - step) {
|
|
50
|
+
var level = detectZoomLevel(height, viewer);
|
|
51
|
+
if (level === null) {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
if (level !== currentZoomLevel) {
|
|
55
|
+
var minHeight = height;
|
|
56
|
+
var maxHeight = height + step;
|
|
57
|
+
while (maxHeight - minHeight > precision) {
|
|
58
|
+
height = minHeight + (maxHeight - minHeight) / 2;
|
|
59
|
+
if (detectZoomLevel(height, viewer) === level) {
|
|
60
|
+
minHeight = height;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
maxHeight = height;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
result.push({
|
|
67
|
+
level: level,
|
|
68
|
+
height: Math.round(height),
|
|
69
|
+
});
|
|
70
|
+
currentZoomLevel = level;
|
|
71
|
+
if (result.length >= 2) {
|
|
72
|
+
step = (result[result.length - 2].height - height) / 1000.0;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
};
|
|
78
|
+
/* eslint-enable @typescript-eslint/no-magic-numbers */
|
|
79
|
+
var ZoomLevelTrackerTool = function (props) {
|
|
80
|
+
var _a;
|
|
81
|
+
var mapViewer = map_1.useCesiumMap();
|
|
82
|
+
var _b = react_1.useState(1), zoomLevel = _b[0], setZoomLevel = _b[1];
|
|
83
|
+
var zoomLevelHeights = getZoomLevelHeights(1, mapViewer);
|
|
84
|
+
react_1.useEffect(function () {
|
|
85
|
+
var calculateZoomLevel = function () {
|
|
86
|
+
var camera = mapViewer.camera;
|
|
87
|
+
var ORTHOPHOTO_HEIGHT_FRUSTRUM_FACTOR = 0.5;
|
|
88
|
+
var cameraHeight = 0;
|
|
89
|
+
switch (mapViewer.scene.mode) {
|
|
90
|
+
case map_types_1.CesiumSceneMode.SCENE3D:
|
|
91
|
+
cameraHeight = mapViewer.scene.mapProjection.ellipsoid.cartesianToCartographic(camera.positionWC).height;
|
|
92
|
+
break;
|
|
93
|
+
case map_types_1.CesiumSceneMode.SCENE2D:
|
|
94
|
+
cameraHeight =
|
|
95
|
+
(camera.frustum.right -
|
|
96
|
+
camera.frustum.left) *
|
|
97
|
+
ORTHOPHOTO_HEIGHT_FRUSTRUM_FACTOR;
|
|
98
|
+
break;
|
|
99
|
+
case map_types_1.CesiumSceneMode.COLUMBUS_VIEW:
|
|
100
|
+
cameraHeight = camera.position.z;
|
|
101
|
+
break;
|
|
102
|
+
default:
|
|
103
|
+
cameraHeight = 0;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
var closestZoom = zoomLevelHeights.reduce(function (a, b) {
|
|
107
|
+
return Math.abs(b.height - cameraHeight) <
|
|
108
|
+
Math.abs(a.height - cameraHeight)
|
|
109
|
+
? b
|
|
110
|
+
: a;
|
|
111
|
+
});
|
|
112
|
+
setZoomLevel(closestZoom.level);
|
|
113
|
+
};
|
|
114
|
+
mapViewer.camera.moveEnd.addEventListener(calculateZoomLevel);
|
|
115
|
+
return function () {
|
|
116
|
+
try {
|
|
117
|
+
/* eslint-disable @typescript-eslint/no-unnecessary-condition*/
|
|
118
|
+
if (lodash_1.get(mapViewer, '_cesiumWidget') != undefined) {
|
|
119
|
+
mapViewer.camera.moveEnd.removeEventListener(calculateZoomLevel);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
console.log('CESIUM camera "moveEnd"(from zoom tracker) remove listener failed', e);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}, [mapViewer]);
|
|
127
|
+
return (react_1.default.createElement("div", { className: "zoomLevel" },
|
|
128
|
+
react_1.default.createElement("div", { className: "zoomLevelValue" }, zoomLevel),
|
|
129
|
+
react_1.default.createElement("div", { className: "zoomLevelLabel" }, (_a = lodash_1.get(props.locale, 'ZOOM_LABEL')) !== null && _a !== void 0 ? _a : 'zoom')));
|
|
130
|
+
};
|
|
131
|
+
exports.ZoomLevelTrackerTool = ZoomLevelTrackerTool;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@map-colonies/react-components",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.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": "6b136929ab3c4ba3489825ee9be301eb0b968a1e",
|
|
97
97
|
"jest": {
|
|
98
98
|
"coverageReporters": [
|
|
99
99
|
"text",
|
|
@@ -121,6 +121,7 @@ LocalizedMap.argTypes = {
|
|
|
121
121
|
MAP_SETTINGS_SCENE_MODE_TITLE: 'תצורה',
|
|
122
122
|
MAP_SETTINGS_BASE_MAP_TITLE: 'מפות בסיס',
|
|
123
123
|
MAP_SETTINGS_OK_BUTTON_TEXT: 'אישור',
|
|
124
|
+
ZOOM_LABEL: 'זום',
|
|
124
125
|
},
|
|
125
126
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
126
127
|
},
|
|
@@ -27,6 +27,7 @@ import { Box } from '../box';
|
|
|
27
27
|
import { Proj } from '../utils/projections';
|
|
28
28
|
import { CoordinatesTrackerTool } from './tools/coordinates-tracker.tool';
|
|
29
29
|
import { ScaleTrackerTool } from './tools/scale-tracker.tool';
|
|
30
|
+
import { ZoomLevelTrackerTool } from './tools/zoom_level-tracker.tool';
|
|
30
31
|
import { CesiumSettings, IBaseMap, IBaseMaps } from './settings/settings';
|
|
31
32
|
import { IMapLegend, MapLegendSidebar, MapLegendToggle } from './map-legend';
|
|
32
33
|
import LayerManager, { LegendExtractor } from './layers-manager';
|
|
@@ -105,6 +106,7 @@ interface ILegends {
|
|
|
105
106
|
|
|
106
107
|
export interface CesiumMapProps extends ViewerProps {
|
|
107
108
|
showMousePosition?: boolean;
|
|
109
|
+
showZoomLevel?: boolean;
|
|
108
110
|
showScale?: boolean;
|
|
109
111
|
projection?: Proj;
|
|
110
112
|
center?: [number, number];
|
|
@@ -138,6 +140,7 @@ export const CesiumMap: React.FC<CesiumMapProps> = (props) => {
|
|
|
138
140
|
const [mapViewRef, setMapViewRef] = useState<CesiumViewer>();
|
|
139
141
|
const [projection, setProjection] = useState<Proj>();
|
|
140
142
|
const [showMousePosition, setShowMousePosition] = useState<boolean>();
|
|
143
|
+
const [showZoomLevel, setShowZoomLevel] = useState<boolean>();
|
|
141
144
|
const [showScale, setShowScale] = useState<boolean>();
|
|
142
145
|
const [locale, setLocale] = useState<{ [key: string]: string }>();
|
|
143
146
|
const [cameraState, setCameraState] = useState<ICameraState | undefined>();
|
|
@@ -275,6 +278,10 @@ export const CesiumMap: React.FC<CesiumMapProps> = (props) => {
|
|
|
275
278
|
setShowMousePosition(props.showMousePosition ?? true);
|
|
276
279
|
}, [props.showMousePosition]);
|
|
277
280
|
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
setShowZoomLevel(props.showZoomLevel ?? true);
|
|
283
|
+
}, [props.showZoomLevel]);
|
|
284
|
+
|
|
278
285
|
useEffect(() => {
|
|
279
286
|
setShowScale(props.showScale ?? true);
|
|
280
287
|
}, [props.showScale]);
|
|
@@ -412,6 +419,11 @@ export const CesiumMap: React.FC<CesiumMapProps> = (props) => {
|
|
|
412
419
|
) : (
|
|
413
420
|
<></>
|
|
414
421
|
)}
|
|
422
|
+
{showZoomLevel === true ? (
|
|
423
|
+
<ZoomLevelTrackerTool locale={locale} />
|
|
424
|
+
) : (
|
|
425
|
+
<></>
|
|
426
|
+
)}
|
|
415
427
|
{showScale === true ? <ScaleTrackerTool locale={locale} /> : <></>}
|
|
416
428
|
</Box>
|
|
417
429
|
</>,
|
|
@@ -171,7 +171,11 @@ export const MapWithSettings: Story = () => {
|
|
|
171
171
|
center={center}
|
|
172
172
|
zoom={14}
|
|
173
173
|
imageryProvider={false}
|
|
174
|
-
sceneModes={[
|
|
174
|
+
sceneModes={[
|
|
175
|
+
CesiumSceneMode.SCENE3D,
|
|
176
|
+
CesiumSceneMode.SCENE2D,
|
|
177
|
+
CesiumSceneMode.COLUMBUS_VIEW,
|
|
178
|
+
]}
|
|
175
179
|
baseMaps={BASE_MAPS}
|
|
176
180
|
>
|
|
177
181
|
<CesiumXYZLayer options={optionsXYZSanDiego} />
|
|
@@ -160,7 +160,10 @@ export const ScaleTrackerTool: React.FC<RScaleTrackerToolProps> = (props) => {
|
|
|
160
160
|
|
|
161
161
|
return (): void => {
|
|
162
162
|
try {
|
|
163
|
-
|
|
163
|
+
/* eslint-disable @typescript-eslint/no-unnecessary-condition*/
|
|
164
|
+
if (get(mapViewer, '_cesiumWidget') != undefined) {
|
|
165
|
+
mapViewer.camera.moveEnd.removeEventListener(setFromEvent);
|
|
166
|
+
}
|
|
164
167
|
} catch (e) {
|
|
165
168
|
console.log('CESIUM camera "moveEnd" remove listener failed', e);
|
|
166
169
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.zoomLevel {
|
|
2
|
+
z-index: 2000;
|
|
3
|
+
background-color: var(--mdc-theme-surface);
|
|
4
|
+
border-radius: 2px;
|
|
5
|
+
padding: 2px;
|
|
6
|
+
height: 24px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.zoomLevelValue {
|
|
10
|
+
margin: 2px;
|
|
11
|
+
font-size: 14px;
|
|
12
|
+
line-height: 10px;
|
|
13
|
+
text-align: center;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.zoomLevelLabel {
|
|
17
|
+
font-size: 8px;
|
|
18
|
+
line-height: 8px;
|
|
19
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { get } from 'lodash';
|
|
3
|
+
import { PerspectiveOffCenterFrustum } from 'cesium';
|
|
4
|
+
import { CesiumViewer, useCesiumMap } from '../map';
|
|
5
|
+
import { CesiumSceneMode } from '../map.types';
|
|
6
|
+
|
|
7
|
+
import './zoom_level-tracker.tool.css';
|
|
8
|
+
|
|
9
|
+
export interface RZoomLevelTrackerToolProps {
|
|
10
|
+
locale?: { [key: string]: string };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-magic-numbers, @typescript-eslint/naming-convention, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */
|
|
14
|
+
const detectZoomLevel = (distance: number, viewer: CesiumViewer) => {
|
|
15
|
+
const MAX_ZOOM_LEVEL = 19;
|
|
16
|
+
const tileProvider = get(viewer.scene.globe, '_surface.tileProvider') as any;
|
|
17
|
+
const quadtree = tileProvider._quadtree;
|
|
18
|
+
const drawingBufferHeight = viewer.canvas.height;
|
|
19
|
+
const sseDenominator = get(viewer.camera.frustum, 'sseDenominator');
|
|
20
|
+
|
|
21
|
+
for (let level = 0; level <= MAX_ZOOM_LEVEL; level++) {
|
|
22
|
+
const maxGeometricError = tileProvider.getLevelMaximumGeometricError(level);
|
|
23
|
+
const error =
|
|
24
|
+
(maxGeometricError * drawingBufferHeight) / (distance * sseDenominator);
|
|
25
|
+
if (error < quadtree.maximumScreenSpaceError) {
|
|
26
|
+
return level;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getZoomLevelHeights = (precision: number, viewer: CesiumViewer) => {
|
|
34
|
+
precision = precision || 10;
|
|
35
|
+
|
|
36
|
+
const result = [];
|
|
37
|
+
let step = 100000.0;
|
|
38
|
+
let currentZoomLevel = 0;
|
|
39
|
+
for (let height = 100000000.0; height > step; height = height - step) {
|
|
40
|
+
const level = detectZoomLevel(height, viewer);
|
|
41
|
+
if (level === null) {
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (level !== currentZoomLevel) {
|
|
46
|
+
let minHeight = height;
|
|
47
|
+
let maxHeight = height + step;
|
|
48
|
+
while (maxHeight - minHeight > precision) {
|
|
49
|
+
height = minHeight + (maxHeight - minHeight) / 2;
|
|
50
|
+
if (detectZoomLevel(height, viewer) === level) {
|
|
51
|
+
minHeight = height;
|
|
52
|
+
} else {
|
|
53
|
+
maxHeight = height;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
result.push({
|
|
58
|
+
level: level,
|
|
59
|
+
height: Math.round(height),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
currentZoomLevel = level;
|
|
63
|
+
|
|
64
|
+
if (result.length >= 2) {
|
|
65
|
+
step = (result[result.length - 2].height - height) / 1000.0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return result;
|
|
71
|
+
};
|
|
72
|
+
/* eslint-enable @typescript-eslint/no-magic-numbers */
|
|
73
|
+
|
|
74
|
+
export const ZoomLevelTrackerTool: React.FC<RZoomLevelTrackerToolProps> = (
|
|
75
|
+
props
|
|
76
|
+
) => {
|
|
77
|
+
const mapViewer: CesiumViewer = useCesiumMap();
|
|
78
|
+
const [zoomLevel, setZoomLevel] = useState(1);
|
|
79
|
+
const zoomLevelHeights = getZoomLevelHeights(1, mapViewer);
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
const calculateZoomLevel = () => {
|
|
83
|
+
const camera = mapViewer.camera;
|
|
84
|
+
const ORTHOPHOTO_HEIGHT_FRUSTRUM_FACTOR = 0.5;
|
|
85
|
+
let cameraHeight = 0;
|
|
86
|
+
|
|
87
|
+
switch (mapViewer.scene.mode) {
|
|
88
|
+
case CesiumSceneMode.SCENE3D:
|
|
89
|
+
cameraHeight = mapViewer.scene.mapProjection.ellipsoid.cartesianToCartographic(
|
|
90
|
+
camera.positionWC
|
|
91
|
+
).height;
|
|
92
|
+
break;
|
|
93
|
+
case CesiumSceneMode.SCENE2D:
|
|
94
|
+
cameraHeight =
|
|
95
|
+
((camera.frustum as PerspectiveOffCenterFrustum).right -
|
|
96
|
+
(camera.frustum as PerspectiveOffCenterFrustum).left) *
|
|
97
|
+
ORTHOPHOTO_HEIGHT_FRUSTRUM_FACTOR;
|
|
98
|
+
break;
|
|
99
|
+
case CesiumSceneMode.COLUMBUS_VIEW:
|
|
100
|
+
cameraHeight = camera.position.z;
|
|
101
|
+
break;
|
|
102
|
+
default:
|
|
103
|
+
cameraHeight = 0;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const closestZoom = zoomLevelHeights.reduce((a, b) => {
|
|
108
|
+
return Math.abs(b.height - cameraHeight) <
|
|
109
|
+
Math.abs(a.height - cameraHeight)
|
|
110
|
+
? b
|
|
111
|
+
: a;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
setZoomLevel(closestZoom.level);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
mapViewer.camera.moveEnd.addEventListener(calculateZoomLevel);
|
|
118
|
+
|
|
119
|
+
return (): void => {
|
|
120
|
+
try {
|
|
121
|
+
/* eslint-disable @typescript-eslint/no-unnecessary-condition*/
|
|
122
|
+
if (get(mapViewer, '_cesiumWidget') != undefined) {
|
|
123
|
+
mapViewer.camera.moveEnd.removeEventListener(calculateZoomLevel);
|
|
124
|
+
}
|
|
125
|
+
} catch (e) {
|
|
126
|
+
console.log(
|
|
127
|
+
'CESIUM camera "moveEnd"(from zoom tracker) remove listener failed',
|
|
128
|
+
e
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}, [mapViewer]);
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<div className="zoomLevel">
|
|
136
|
+
<div className="zoomLevelValue">{zoomLevel}</div>
|
|
137
|
+
<div className="zoomLevelLabel">
|
|
138
|
+
{get(props.locale, 'ZOOM_LABEL') ?? 'zoom'}
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
};
|