itowns 2.45.1-next.0 → 2.45.1-next.1
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/dist/455.js +2 -0
- package/dist/455.js.map +1 -0
- package/dist/debug.js +3 -0
- package/dist/debug.js.LICENSE.txt +13 -0
- package/dist/debug.js.map +1 -0
- package/dist/itowns.js +3 -0
- package/dist/itowns.js.LICENSE.txt +5 -0
- package/dist/itowns.js.map +1 -0
- package/dist/itowns_lasparser.js +2 -0
- package/dist/itowns_lasparser.js.map +1 -0
- package/dist/itowns_lasworker.js +2 -0
- package/dist/itowns_lasworker.js.map +1 -0
- package/dist/itowns_potree2worker.js +2 -0
- package/dist/itowns_potree2worker.js.map +1 -0
- package/dist/itowns_widgets.js +2 -0
- package/dist/itowns_widgets.js.map +1 -0
- package/lib/Controls/FirstPersonControls.js +308 -0
- package/lib/Controls/FlyControls.js +175 -0
- package/lib/Controls/GlobeControls.js +1178 -0
- package/lib/Controls/PlanarControls.js +1025 -0
- package/lib/Controls/StateControl.js +432 -0
- package/lib/Controls/StreetControls.js +392 -0
- package/lib/Converter/Feature2Mesh.js +612 -0
- package/lib/Converter/Feature2Texture.js +174 -0
- package/lib/Converter/convertToTile.js +70 -0
- package/lib/Converter/textureConverter.js +43 -0
- package/lib/Core/3DTiles/C3DTBatchTable.js +131 -0
- package/lib/Core/3DTiles/C3DTBatchTableHierarchyExtension.js +96 -0
- package/lib/Core/3DTiles/C3DTBoundingVolume.js +156 -0
- package/lib/Core/3DTiles/C3DTExtensions.js +97 -0
- package/lib/Core/3DTiles/C3DTFeature.js +110 -0
- package/lib/Core/3DTiles/C3DTilesEnums.js +20 -0
- package/lib/Core/3DTiles/C3DTileset.js +99 -0
- package/lib/Core/3DTiles/utils/BinaryPropertyAccessor.js +100 -0
- package/lib/Core/AnimationPlayer.js +142 -0
- package/lib/Core/CopcNode.js +174 -0
- package/lib/Core/Deprecated/Undeprecator.js +74 -0
- package/lib/Core/EntwinePointTileNode.js +126 -0
- package/lib/Core/Feature.js +488 -0
- package/lib/Core/Geographic/GeoidGrid.js +108 -0
- package/lib/Core/Label.js +222 -0
- package/lib/Core/MainLoop.js +209 -0
- package/lib/Core/Picking.js +255 -0
- package/lib/Core/PointCloudNode.js +42 -0
- package/lib/Core/Potree2Node.js +206 -0
- package/lib/Core/Potree2PointAttributes.js +139 -0
- package/lib/Core/PotreeNode.js +101 -0
- package/lib/Core/Prefab/Globe/Atmosphere.js +293 -0
- package/lib/Core/Prefab/Globe/GlobeLayer.js +152 -0
- package/lib/Core/Prefab/Globe/GlobeTileBuilder.js +110 -0
- package/lib/Core/Prefab/Globe/SkyShader.js +78 -0
- package/lib/Core/Prefab/GlobeView.js +155 -0
- package/lib/Core/Prefab/Planar/PlanarLayer.js +59 -0
- package/lib/Core/Prefab/Planar/PlanarTileBuilder.js +71 -0
- package/lib/Core/Prefab/PlanarView.js +62 -0
- package/lib/Core/Prefab/TileBuilder.js +82 -0
- package/lib/Core/Prefab/computeBufferTileGeometry.js +248 -0
- package/lib/Core/Scheduler/Cache.js +17 -0
- package/lib/Core/Scheduler/CancelledCommandException.js +15 -0
- package/lib/Core/Scheduler/Scheduler.js +294 -0
- package/lib/Core/Style.js +660 -0
- package/lib/Core/StyleOptions.js +486 -0
- package/lib/Core/System/Capabilities.js +63 -0
- package/lib/Core/Tile/Tile.js +205 -0
- package/lib/Core/Tile/TileGrid.js +49 -0
- package/lib/Core/TileGeometry.js +124 -0
- package/lib/Core/TileMesh.js +108 -0
- package/lib/Core/View.js +1115 -0
- package/lib/Layer/C3DTilesLayer.js +459 -0
- package/lib/Layer/ColorLayer.js +154 -0
- package/lib/Layer/CopcLayer.js +63 -0
- package/lib/Layer/ElevationLayer.js +139 -0
- package/lib/Layer/EntwinePointTileLayer.js +71 -0
- package/lib/Layer/FeatureGeometryLayer.js +77 -0
- package/lib/Layer/GeoidLayer.js +80 -0
- package/lib/Layer/GeometryLayer.js +233 -0
- package/lib/Layer/InfoLayer.js +64 -0
- package/lib/Layer/LabelLayer.js +469 -0
- package/lib/Layer/Layer.js +335 -0
- package/lib/Layer/LayerUpdateState.js +89 -0
- package/lib/Layer/LayerUpdateStrategy.js +80 -0
- package/lib/Layer/OGC3DTilesLayer.js +543 -0
- package/lib/Layer/OrientedImageLayer.js +227 -0
- package/lib/Layer/PointCloudLayer.js +405 -0
- package/lib/Layer/Potree2Layer.js +171 -0
- package/lib/Layer/PotreeLayer.js +72 -0
- package/lib/Layer/RasterLayer.js +37 -0
- package/lib/Layer/ReferencingLayerProperties.js +62 -0
- package/lib/Layer/TiledGeometryLayer.js +459 -0
- package/lib/Loader/LASLoader.js +193 -0
- package/lib/Loader/Potree2BrotliLoader.js +261 -0
- package/lib/Loader/Potree2Loader.js +207 -0
- package/lib/Main.js +113 -0
- package/lib/MainBundle.js +4 -0
- package/lib/Parser/B3dmParser.js +174 -0
- package/lib/Parser/CameraCalibrationParser.js +94 -0
- package/lib/Parser/GDFParser.js +72 -0
- package/lib/Parser/GTXParser.js +75 -0
- package/lib/Parser/GeoJsonParser.js +212 -0
- package/lib/Parser/GpxParser.js +25 -0
- package/lib/Parser/ISGParser.js +71 -0
- package/lib/Parser/KMLParser.js +25 -0
- package/lib/Parser/LASParser.js +137 -0
- package/lib/Parser/MapBoxUrlParser.js +83 -0
- package/lib/Parser/PntsParser.js +131 -0
- package/lib/Parser/Potree2BinParser.js +92 -0
- package/lib/Parser/PotreeBinParser.js +106 -0
- package/lib/Parser/PotreeCinParser.js +29 -0
- package/lib/Parser/ShapefileParser.js +78 -0
- package/lib/Parser/VectorTileParser.js +215 -0
- package/lib/Parser/XbilParser.js +120 -0
- package/lib/Parser/deprecated/LegacyGLTFLoader.js +1386 -0
- package/lib/Parser/iGLTFLoader.js +168 -0
- package/lib/Process/3dTilesProcessing.js +304 -0
- package/lib/Process/FeatureProcessing.js +76 -0
- package/lib/Process/LayeredMaterialNodeProcessing.js +229 -0
- package/lib/Process/ObjectRemovalHelper.js +97 -0
- package/lib/Process/handlerNodeError.js +23 -0
- package/lib/Provider/3dTilesProvider.js +149 -0
- package/lib/Provider/DataSourceProvider.js +24 -0
- package/lib/Provider/Fetcher.js +233 -0
- package/lib/Provider/PointCloudProvider.js +45 -0
- package/lib/Provider/TileProvider.js +16 -0
- package/lib/Provider/URLBuilder.js +116 -0
- package/lib/Renderer/Camera.js +281 -0
- package/lib/Renderer/Color.js +56 -0
- package/lib/Renderer/ColorLayersOrdering.js +115 -0
- package/lib/Renderer/CommonMaterial.js +31 -0
- package/lib/Renderer/Label2DRenderer.js +192 -0
- package/lib/Renderer/LayeredMaterial.js +243 -0
- package/lib/Renderer/OBB.js +150 -0
- package/lib/Renderer/OrientedImageCamera.js +118 -0
- package/lib/Renderer/OrientedImageMaterial.js +167 -0
- package/lib/Renderer/PointsMaterial.js +485 -0
- package/lib/Renderer/RasterTile.js +243 -0
- package/lib/Renderer/RenderMode.js +31 -0
- package/lib/Renderer/Shader/ShaderChunk.js +160 -0
- package/lib/Renderer/Shader/ShaderUtils.js +47 -0
- package/lib/Renderer/SphereHelper.js +17 -0
- package/lib/Renderer/WebXR.js +51 -0
- package/lib/Renderer/c3DEngine.js +214 -0
- package/lib/Source/C3DTilesGoogleSource.js +74 -0
- package/lib/Source/C3DTilesIonSource.js +54 -0
- package/lib/Source/C3DTilesSource.js +30 -0
- package/lib/Source/CopcSource.js +126 -0
- package/lib/Source/EntwinePointTileSource.js +72 -0
- package/lib/Source/FileSource.js +188 -0
- package/lib/Source/OGC3DTilesGoogleSource.js +29 -0
- package/lib/Source/OGC3DTilesIonSource.js +34 -0
- package/lib/Source/OGC3DTilesSource.js +21 -0
- package/lib/Source/OrientedImageSource.js +59 -0
- package/lib/Source/Potree2Source.js +167 -0
- package/lib/Source/PotreeSource.js +82 -0
- package/lib/Source/Source.js +202 -0
- package/lib/Source/TMSSource.js +144 -0
- package/lib/Source/VectorTilesSource.js +182 -0
- package/lib/Source/WFSSource.js +170 -0
- package/lib/Source/WMSSource.js +167 -0
- package/lib/Source/WMTSSource.js +92 -0
- package/lib/ThreeExtended/capabilities/WebGL.js +69 -0
- package/lib/ThreeExtended/libs/ktx-parse.module.js +506 -0
- package/lib/ThreeExtended/libs/zstddec.module.js +29 -0
- package/lib/ThreeExtended/loaders/DDSLoader.js +200 -0
- package/lib/ThreeExtended/loaders/DRACOLoader.js +400 -0
- package/lib/ThreeExtended/loaders/GLTFLoader.js +2879 -0
- package/lib/ThreeExtended/loaders/KTX2Loader.js +709 -0
- package/lib/ThreeExtended/math/ColorSpaces.js +59 -0
- package/lib/ThreeExtended/utils/BufferGeometryUtils.js +846 -0
- package/lib/ThreeExtended/utils/WorkerPool.js +70 -0
- package/lib/Utils/CameraUtils.js +554 -0
- package/lib/Utils/DEMUtils.js +350 -0
- package/lib/Utils/FeaturesUtils.js +156 -0
- package/lib/Utils/Gradients.js +16 -0
- package/lib/Utils/ThreeUtils.js +115 -0
- package/lib/Utils/gui/C3DTilesStyle.js +218 -0
- package/lib/Utils/gui/Main.js +7 -0
- package/lib/Utils/gui/Minimap.js +152 -0
- package/lib/Utils/gui/Navigation.js +245 -0
- package/lib/Utils/gui/Scale.js +104 -0
- package/lib/Utils/gui/Searchbar.js +234 -0
- package/lib/Utils/gui/Widget.js +80 -0
- package/lib/Utils/placeObjectOnGround.js +136 -0
- package/lib/Worker/LASLoaderWorker.js +19 -0
- package/lib/Worker/Potree2Worker.js +21 -0
- package/package.json +2 -2
|
@@ -0,0 +1,1178 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import AnimationPlayer from "../Core/AnimationPlayer.js";
|
|
3
|
+
import { Coordinates, ellipsoidSizes } from '@itowns/geographic';
|
|
4
|
+
import CameraUtils from "../Utils/CameraUtils.js";
|
|
5
|
+
import StateControl from "./StateControl.js";
|
|
6
|
+
import { VIEW_EVENTS } from "../Core/View.js";
|
|
7
|
+
|
|
8
|
+
// private members
|
|
9
|
+
const EPS = 0.000001;
|
|
10
|
+
const direction = {
|
|
11
|
+
up: new THREE.Vector2(0, 1),
|
|
12
|
+
bottom: new THREE.Vector2(0, -1),
|
|
13
|
+
left: new THREE.Vector2(1, 0),
|
|
14
|
+
right: new THREE.Vector2(-1, 0)
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Orbit
|
|
18
|
+
const rotateStart = new THREE.Vector2();
|
|
19
|
+
const rotateEnd = new THREE.Vector2();
|
|
20
|
+
const rotateDelta = new THREE.Vector2();
|
|
21
|
+
const spherical = new THREE.Spherical(1.0, 0.01, 0);
|
|
22
|
+
const sphericalDelta = new THREE.Spherical(1.0, 0, 0);
|
|
23
|
+
let orbitScale = 1.0;
|
|
24
|
+
|
|
25
|
+
// Pan
|
|
26
|
+
const panStart = new THREE.Vector2();
|
|
27
|
+
const panEnd = new THREE.Vector2();
|
|
28
|
+
const panDelta = new THREE.Vector2();
|
|
29
|
+
const panOffset = new THREE.Vector3();
|
|
30
|
+
|
|
31
|
+
// Dolly
|
|
32
|
+
const dollyStart = new THREE.Vector2();
|
|
33
|
+
const dollyEnd = new THREE.Vector2();
|
|
34
|
+
const dollyDelta = new THREE.Vector2();
|
|
35
|
+
let dollyScale;
|
|
36
|
+
|
|
37
|
+
// Globe move
|
|
38
|
+
const moveAroundGlobe = new THREE.Quaternion();
|
|
39
|
+
const cameraTarget = new THREE.Object3D();
|
|
40
|
+
const coordCameraTarget = new Coordinates('EPSG:4978');
|
|
41
|
+
cameraTarget.matrixWorldInverse = new THREE.Matrix4();
|
|
42
|
+
const xyz = new Coordinates('EPSG:4978', 0, 0, 0);
|
|
43
|
+
const c = new Coordinates('EPSG:4326', 0, 0, 0);
|
|
44
|
+
// Position object on globe
|
|
45
|
+
function positionObject(newPosition, object) {
|
|
46
|
+
xyz.setFromVector3(newPosition).as('EPSG:4326', c);
|
|
47
|
+
object.position.copy(newPosition);
|
|
48
|
+
object.lookAt(c.geodesicNormal.add(newPosition));
|
|
49
|
+
object.rotateX(Math.PI * 0.5);
|
|
50
|
+
object.updateMatrixWorld(true);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Save the last time of mouse move for damping
|
|
54
|
+
let lastTimeMouseMove = 0;
|
|
55
|
+
|
|
56
|
+
// Animations and damping
|
|
57
|
+
let enableAnimation = true;
|
|
58
|
+
const dampingFactorDefault = 0.25;
|
|
59
|
+
const dampingMove = new THREE.Quaternion(0, 0, 0, 1);
|
|
60
|
+
const durationDampingMove = 120;
|
|
61
|
+
const durationDampingOrbital = 60;
|
|
62
|
+
|
|
63
|
+
// Pan Move
|
|
64
|
+
const panVector = new THREE.Vector3();
|
|
65
|
+
|
|
66
|
+
// Save last transformation
|
|
67
|
+
const lastPosition = new THREE.Vector3();
|
|
68
|
+
const lastQuaternion = new THREE.Quaternion();
|
|
69
|
+
|
|
70
|
+
// Tangent sphere to ellipsoid
|
|
71
|
+
const pickSphere = new THREE.Sphere();
|
|
72
|
+
const pickingPoint = new THREE.Vector3();
|
|
73
|
+
|
|
74
|
+
// Sphere intersection
|
|
75
|
+
const intersection = new THREE.Vector3();
|
|
76
|
+
|
|
77
|
+
// Set to true to enable target helper
|
|
78
|
+
const enableTargetHelper = false;
|
|
79
|
+
const helpers = {};
|
|
80
|
+
/**
|
|
81
|
+
* Globe control pan event. Fires after camera pan
|
|
82
|
+
* @event GlobeControls#pan-changed
|
|
83
|
+
* @property target {GlobeControls} dispatched on controls
|
|
84
|
+
* @property type {string} orientation-changed
|
|
85
|
+
*/
|
|
86
|
+
/**
|
|
87
|
+
* Globe control orientation event. Fires when camera's orientation change
|
|
88
|
+
* @event GlobeControls#orientation-changed
|
|
89
|
+
* @property new {object}
|
|
90
|
+
* @property new.tilt {number} the new value of the tilt of the camera
|
|
91
|
+
* @property new.heading {number} the new value of the heading of the camera
|
|
92
|
+
* @property previous {object}
|
|
93
|
+
* @property previous.tilt {number} the previous value of the tilt of the camera
|
|
94
|
+
* @property previous.heading {number} the previous value of the heading of the camera
|
|
95
|
+
* @property target {GlobeControls} dispatched on controls
|
|
96
|
+
* @property type {string} orientation-changed
|
|
97
|
+
*/
|
|
98
|
+
/**
|
|
99
|
+
* Globe control range event. Fires when camera's range to target change
|
|
100
|
+
* @event GlobeControls#range-changed
|
|
101
|
+
* @property new {number} the new value of the range
|
|
102
|
+
* @property previous {number} the previous value of the range
|
|
103
|
+
* @property target {GlobeControls} dispatched on controls
|
|
104
|
+
* @property type {string} range-changed
|
|
105
|
+
*/
|
|
106
|
+
/**
|
|
107
|
+
* Globe control camera's target event. Fires when camera's target change
|
|
108
|
+
* @event GlobeControls#camera-target-changed
|
|
109
|
+
* @property new {object}
|
|
110
|
+
* @property new {Coordinates} the new camera's target coordinates
|
|
111
|
+
* @property previous {Coordinates} the previous camera's target coordinates
|
|
112
|
+
* @property target {GlobeControls} dispatched on controls
|
|
113
|
+
* @property type {string} camera-target-changed
|
|
114
|
+
*/
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* globe controls events
|
|
118
|
+
* @property PAN_CHANGED {string} Fires after camera pan
|
|
119
|
+
* @property ORIENTATION_CHANGED {string} Fires when camera's orientation change
|
|
120
|
+
* @property RANGE_CHANGED {string} Fires when camera's range to target change
|
|
121
|
+
* @property CAMERA_TARGET_CHANGED {string} Fires when camera's target change
|
|
122
|
+
*/
|
|
123
|
+
|
|
124
|
+
export const CONTROL_EVENTS = {
|
|
125
|
+
PAN_CHANGED: 'pan-changed',
|
|
126
|
+
ORIENTATION_CHANGED: 'orientation-changed',
|
|
127
|
+
RANGE_CHANGED: 'range-changed',
|
|
128
|
+
CAMERA_TARGET_CHANGED: 'camera-target-changed'
|
|
129
|
+
};
|
|
130
|
+
const quaterPano = new THREE.Quaternion();
|
|
131
|
+
const quaterAxis = new THREE.Quaternion();
|
|
132
|
+
const axisX = new THREE.Vector3(1, 0, 0);
|
|
133
|
+
let minDistanceZ = Infinity;
|
|
134
|
+
const lastNormalizedIntersection = new THREE.Vector3();
|
|
135
|
+
const normalizedIntersection = new THREE.Vector3();
|
|
136
|
+
const raycaster = new THREE.Raycaster();
|
|
137
|
+
const targetPosition = new THREE.Vector3();
|
|
138
|
+
const pickedPosition = new THREE.Vector3();
|
|
139
|
+
const sphereCamera = new THREE.Sphere();
|
|
140
|
+
let previous;
|
|
141
|
+
/**
|
|
142
|
+
* GlobeControls is a camera controller
|
|
143
|
+
*
|
|
144
|
+
* @class GlobeControls
|
|
145
|
+
* @param {GlobeView} view the view where the control will be used
|
|
146
|
+
* @param {CameraTransformOptions|Extent} placement the {@link CameraTransformOptions} to apply to view's camera
|
|
147
|
+
* or the extent it must display at initialisation, see {@link CameraTransformOptions} in {@link CameraUtils}.
|
|
148
|
+
* @param {object} [options] An object with one or more configuration properties. Any property of GlobeControls
|
|
149
|
+
* can be passed in this object.
|
|
150
|
+
* @property {number} zoomFactor The factor the scale is multiplied by when dollying (zooming) in or
|
|
151
|
+
* divided by when dollying out. Default is 1.1.
|
|
152
|
+
* @property {number} rotateSpeed Speed camera rotation in orbit and panoramic mode. Default is 0.25.
|
|
153
|
+
* @property {number} minDistance Minimum distance between ground and camera in meters (Perspective Camera only).
|
|
154
|
+
* Default is 250.
|
|
155
|
+
* @property {number} maxDistance Maximum distance between ground and camera in meters
|
|
156
|
+
* (Perspective Camera only). Default is ellipsoid radius * 8.
|
|
157
|
+
* @property {number} minZoom How far you can zoom in, in meters (Orthographic Camera only). Default is 0.
|
|
158
|
+
* @property {number} maxZoom How far you can zoom out, in meters (Orthographic Camera only). Default
|
|
159
|
+
* is Infinity.
|
|
160
|
+
* @property {number} keyPanSpeed Number of pixels moved per push on array key. Default is 7.
|
|
161
|
+
* @property {number} minPolarAngle Minimum vertical orbit angle (in degrees). Default is 0.5.
|
|
162
|
+
* @property {number} maxPolarAngle Maximum vertical orbit angle (in degrees). Default is 86.
|
|
163
|
+
* @property {number} minAzimuthAngle Minimum horizontal orbit angle (in degrees). If modified,
|
|
164
|
+
* should be in [-180,0]. Default is -Infinity.
|
|
165
|
+
* @property {number} maxAzimuthAngle Maximum horizontal orbit angle (in degrees). If modified,
|
|
166
|
+
* should be in [0,180]. Default is Infinity.
|
|
167
|
+
* @property {boolean} handleCollision Handle collision between camera and ground or not, i.e. whether
|
|
168
|
+
* you can zoom underground or not. Default is true.
|
|
169
|
+
* @property {boolean} enableDamping Enable damping or not (simulates the lag that a real camera
|
|
170
|
+
* operator introduces while operating a heavy physical camera). Default is true.
|
|
171
|
+
* @property {boolean} dampingMoveFactor the damping move factor. Default is 0.25.
|
|
172
|
+
* @property {StateControl~State} stateControl redefining which controls state is triggered by the keyboard/mouse
|
|
173
|
+
* event (For example, rewrite the PAN movement to be triggered with the 'left' mouseButton instead of 'right').
|
|
174
|
+
*/
|
|
175
|
+
class GlobeControls extends THREE.EventDispatcher {
|
|
176
|
+
constructor(view, placement) {
|
|
177
|
+
let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
178
|
+
super();
|
|
179
|
+
this.player = new AnimationPlayer();
|
|
180
|
+
this.view = view;
|
|
181
|
+
this.camera = view.camera3D;
|
|
182
|
+
|
|
183
|
+
// State control
|
|
184
|
+
this.states = new StateControl(this.view, options.stateControl);
|
|
185
|
+
|
|
186
|
+
// this.enabled property has moved to StateControl
|
|
187
|
+
Object.defineProperty(this, 'enabled', {
|
|
188
|
+
get: () => this.states.enabled,
|
|
189
|
+
set: value => {
|
|
190
|
+
console.warn('GlobeControls.enabled property is deprecated. Use StateControl.enabled instead ' + '- which you can access with GlobeControls.states.enabled.');
|
|
191
|
+
this.states.enabled = value;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// These options actually enables dollying in and out; left as "zoom" for
|
|
196
|
+
// backwards compatibility
|
|
197
|
+
if (options.zoomSpeed) {
|
|
198
|
+
console.warn('Controls zoomSpeed parameter is deprecated. Use zoomFactor instead.');
|
|
199
|
+
options.zoomFactor = options.zoomFactor || options.zoomSpeed;
|
|
200
|
+
}
|
|
201
|
+
this.zoomFactor = options.zoomFactor || 1.1;
|
|
202
|
+
|
|
203
|
+
// Limits to how far you can dolly in and out ( PerspectiveCamera only )
|
|
204
|
+
this.minDistance = options.minDistance || 250;
|
|
205
|
+
this.maxDistance = options.maxDistance || ellipsoidSizes.x * 8.0;
|
|
206
|
+
|
|
207
|
+
// Limits to how far you can zoom in and out ( OrthographicCamera only )
|
|
208
|
+
this.minZoom = options.minZoom || 0;
|
|
209
|
+
this.maxZoom = options.maxZoom || Infinity;
|
|
210
|
+
|
|
211
|
+
// Set to true to disable this control
|
|
212
|
+
this.rotateSpeed = options.rotateSpeed || 0.25;
|
|
213
|
+
|
|
214
|
+
// Set to true to disable this control
|
|
215
|
+
this.keyPanSpeed = options.keyPanSpeed || 7.0; // pixels moved per arrow key push
|
|
216
|
+
|
|
217
|
+
// How far you can orbit vertically, upper and lower limits.
|
|
218
|
+
// Range is 0 to Math.PI radians.
|
|
219
|
+
// TODO Warning minPolarAngle = 0.01 -> it isn't possible to be perpendicular on Globe
|
|
220
|
+
this.minPolarAngle = THREE.MathUtils.degToRad(options.minPolarAngle ?? 0.5);
|
|
221
|
+
this.maxPolarAngle = THREE.MathUtils.degToRad(options.minPolarAngle ?? 86);
|
|
222
|
+
|
|
223
|
+
// How far you can orbit horizontally, upper and lower limits.
|
|
224
|
+
// If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
|
|
225
|
+
this.minAzimuthAngle = options.minAzimuthAngle ? THREE.MathUtils.degToRad(options.minAzimuthAngle) : -Infinity; // radians
|
|
226
|
+
this.maxAzimuthAngle = options.maxAzimuthAngle ? THREE.MathUtils.degToRad(options.maxAzimuthAngle) : Infinity; // radians
|
|
227
|
+
|
|
228
|
+
// Set collision options
|
|
229
|
+
this.handleCollision = typeof options.handleCollision !== 'undefined' ? options.handleCollision : true;
|
|
230
|
+
this.minDistanceCollision = 60;
|
|
231
|
+
|
|
232
|
+
// this.enableKeys property has moved to StateControl
|
|
233
|
+
Object.defineProperty(this, 'enableKeys', {
|
|
234
|
+
get: () => this.states.enableKeys,
|
|
235
|
+
set: value => {
|
|
236
|
+
console.warn('GlobeControls.enableKeys property is deprecated. Use StateControl.enableKeys instead ' + '- which you can access with GlobeControls.states.enableKeys.');
|
|
237
|
+
this.states.enableKeys = value;
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Enable Damping
|
|
242
|
+
this.enableDamping = options.enableDamping !== false;
|
|
243
|
+
this.dampingMoveFactor = options.dampingMoveFactor != undefined ? options.dampingMoveFactor : dampingFactorDefault;
|
|
244
|
+
this.startEvent = {
|
|
245
|
+
type: 'start'
|
|
246
|
+
};
|
|
247
|
+
this.endEvent = {
|
|
248
|
+
type: 'end'
|
|
249
|
+
};
|
|
250
|
+
// Update helper
|
|
251
|
+
this.updateHelper = enableTargetHelper ? (position, helper) => {
|
|
252
|
+
positionObject(position, helper);
|
|
253
|
+
view.notifyChange(this.camera);
|
|
254
|
+
} : function () {};
|
|
255
|
+
this._onEndingMove = null;
|
|
256
|
+
this._onTravel = this.travel.bind(this);
|
|
257
|
+
this._onTouchStart = this.onTouchStart.bind(this);
|
|
258
|
+
this._onTouchEnd = this.onTouchEnd.bind(this);
|
|
259
|
+
this._onTouchMove = this.onTouchMove.bind(this);
|
|
260
|
+
this._onStateChange = this.onStateChange.bind(this);
|
|
261
|
+
this._onRotation = this.handleRotation.bind(this);
|
|
262
|
+
this._onDrag = this.handleDrag.bind(this);
|
|
263
|
+
this._onDolly = this.handleDolly.bind(this);
|
|
264
|
+
this._onPan = this.handlePan.bind(this);
|
|
265
|
+
this._onPanoramic = this.handlePanoramic.bind(this);
|
|
266
|
+
this._onZoom = this.handleZoom.bind(this);
|
|
267
|
+
this.states.addEventListener('state-changed', this._onStateChange, false);
|
|
268
|
+
this.states.addEventListener(this.states.ORBIT._event, this._onRotation, false);
|
|
269
|
+
this.states.addEventListener(this.states.MOVE_GLOBE._event, this._onDrag, false);
|
|
270
|
+
this.states.addEventListener(this.states.DOLLY._event, this._onDolly, false);
|
|
271
|
+
this.states.addEventListener(this.states.PAN._event, this._onPan, false);
|
|
272
|
+
this.states.addEventListener(this.states.PANORAMIC._event, this._onPanoramic, false);
|
|
273
|
+
this.states.addEventListener('zoom', this._onZoom, false);
|
|
274
|
+
this.view.domElement.addEventListener('touchstart', this._onTouchStart, false);
|
|
275
|
+
this.view.domElement.addEventListener('touchend', this._onTouchEnd, false);
|
|
276
|
+
this.view.domElement.addEventListener('touchmove', this._onTouchMove, false);
|
|
277
|
+
this.states.addEventListener(this.states.TRAVEL_IN._event, this._onTravel, false);
|
|
278
|
+
this.states.addEventListener(this.states.TRAVEL_OUT._event, this._onTravel, false);
|
|
279
|
+
view.scene.add(cameraTarget);
|
|
280
|
+
if (enableTargetHelper) {
|
|
281
|
+
cameraTarget.add(helpers.target);
|
|
282
|
+
view.scene.add(helpers.picking);
|
|
283
|
+
}
|
|
284
|
+
if (placement.isExtent) {
|
|
285
|
+
placement.center().as('EPSG:4978', xyz);
|
|
286
|
+
} else {
|
|
287
|
+
placement.coord.as('EPSG:4978', xyz);
|
|
288
|
+
placement.tilt = placement.tilt || 89.5;
|
|
289
|
+
placement.heading = placement.heading || 0;
|
|
290
|
+
}
|
|
291
|
+
positionObject(xyz, cameraTarget);
|
|
292
|
+
this.lookAtCoordinate(placement, false);
|
|
293
|
+
coordCameraTarget.crs = this.view.referenceCrs;
|
|
294
|
+
}
|
|
295
|
+
get zoomInScale() {
|
|
296
|
+
return this.zoomFactor;
|
|
297
|
+
}
|
|
298
|
+
get zoomOutScale() {
|
|
299
|
+
return 1 / this.zoomFactor;
|
|
300
|
+
}
|
|
301
|
+
get isPaused() {
|
|
302
|
+
// TODO : also check if CameraUtils is performing an animation
|
|
303
|
+
return this.states.currentState === this.states.NONE && !this.player.isPlaying();
|
|
304
|
+
}
|
|
305
|
+
onEndingMove(current) {
|
|
306
|
+
if (this._onEndingMove) {
|
|
307
|
+
this.player.removeEventListener('animation-stopped', this._onEndingMove);
|
|
308
|
+
this._onEndingMove = null;
|
|
309
|
+
}
|
|
310
|
+
this.handlingEvent(current);
|
|
311
|
+
}
|
|
312
|
+
rotateLeft() {
|
|
313
|
+
let angle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
|
314
|
+
sphericalDelta.theta -= angle;
|
|
315
|
+
}
|
|
316
|
+
rotateUp() {
|
|
317
|
+
let angle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
|
318
|
+
sphericalDelta.phi -= angle;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// pass in distance in world space to move left
|
|
322
|
+
panLeft(distance) {
|
|
323
|
+
const te = this.camera.matrix.elements;
|
|
324
|
+
// get X column of matrix
|
|
325
|
+
panOffset.fromArray(te);
|
|
326
|
+
panOffset.multiplyScalar(-distance);
|
|
327
|
+
panVector.add(panOffset);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// pass in distance in world space to move up
|
|
331
|
+
panUp(distance) {
|
|
332
|
+
const te = this.camera.matrix.elements;
|
|
333
|
+
// get Y column of matrix
|
|
334
|
+
panOffset.fromArray(te, 4);
|
|
335
|
+
panOffset.multiplyScalar(distance);
|
|
336
|
+
panVector.add(panOffset);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// pass in x,y of change desired in pixel space,
|
|
340
|
+
// right and down are positive
|
|
341
|
+
mouseToPan(deltaX, deltaY) {
|
|
342
|
+
const gfx = this.view.mainLoop.gfxEngine;
|
|
343
|
+
if (this.camera.isPerspectiveCamera) {
|
|
344
|
+
let targetDistance = this.camera.position.distanceTo(this.getCameraTargetPosition());
|
|
345
|
+
// half of the fov is center to top of screen
|
|
346
|
+
targetDistance *= 2 * Math.tan(THREE.MathUtils.degToRad(this.camera.fov * 0.5));
|
|
347
|
+
|
|
348
|
+
// we actually don't use screenWidth, since perspective camera is fixed to screen height
|
|
349
|
+
this.panLeft(deltaX * targetDistance / gfx.width * this.camera.aspect);
|
|
350
|
+
this.panUp(deltaY * targetDistance / gfx.height);
|
|
351
|
+
} else if (this.camera.isOrthographicCamera) {
|
|
352
|
+
// orthographic
|
|
353
|
+
this.panLeft(deltaX * (this.camera.right - this.camera.left) / gfx.width);
|
|
354
|
+
this.panUp(deltaY * (this.camera.top - this.camera.bottom) / gfx.height);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// For Mobile
|
|
359
|
+
dolly(delta) {
|
|
360
|
+
if (delta === 0) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
dollyScale = delta > 0 ? this.zoomInScale : this.zoomOutScale;
|
|
364
|
+
if (this.camera.isPerspectiveCamera) {
|
|
365
|
+
orbitScale /= dollyScale;
|
|
366
|
+
} else if (this.camera.isOrthographicCamera) {
|
|
367
|
+
this.camera.zoom = THREE.MathUtils.clamp(this.camera.zoom * dollyScale, this.minZoom, this.maxZoom);
|
|
368
|
+
this.camera.updateProjectionMatrix();
|
|
369
|
+
this.view.notifyChange(this.camera);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
getMinDistanceCameraBoundingSphereObbsUp(tile) {
|
|
373
|
+
if (tile.level > 10 && tile.children.length == 1 && tile.geometry) {
|
|
374
|
+
const obb = tile.obb;
|
|
375
|
+
sphereCamera.center.copy(this.camera.position);
|
|
376
|
+
sphereCamera.radius = this.minDistanceCollision;
|
|
377
|
+
if (obb.isSphereAboveXYBox(sphereCamera)) {
|
|
378
|
+
minDistanceZ = Math.min(sphereCamera.center.z - obb.box3D.max.z, minDistanceZ);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
update() {
|
|
383
|
+
let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.states.currentState;
|
|
384
|
+
// We compute distance between camera's bounding sphere and geometry's obb up face
|
|
385
|
+
minDistanceZ = Infinity;
|
|
386
|
+
if (this.handleCollision) {
|
|
387
|
+
// We check distance to the ground/surface geometry
|
|
388
|
+
// add minDistanceZ between camera's bounding and tiles's oriented bounding box (up face only)
|
|
389
|
+
// Depending on the distance of the camera with obbs, we add a slowdown or constrain to the movement.
|
|
390
|
+
// this constraint or deceleration is suitable for two types of movement MOVE_GLOBE and ORBIT.
|
|
391
|
+
// This constraint or deceleration inversely proportional to the camera/obb distance
|
|
392
|
+
if (this.view.tileLayer) {
|
|
393
|
+
for (const tile of this.view.tileLayer.level0Nodes) {
|
|
394
|
+
tile.traverse(this.getMinDistanceCameraBoundingSphereObbsUp.bind(this));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
switch (state) {
|
|
399
|
+
// MOVE_GLOBE Rotate globe with mouse
|
|
400
|
+
case this.states.MOVE_GLOBE:
|
|
401
|
+
if (minDistanceZ < 0) {
|
|
402
|
+
cameraTarget.translateY(-minDistanceZ);
|
|
403
|
+
this.camera.position.setLength(this.camera.position.length() - minDistanceZ);
|
|
404
|
+
} else if (minDistanceZ < this.minDistanceCollision) {
|
|
405
|
+
const translate = this.minDistanceCollision * (1.0 - minDistanceZ / this.minDistanceCollision);
|
|
406
|
+
cameraTarget.translateY(translate);
|
|
407
|
+
this.camera.position.setLength(this.camera.position.length() + translate);
|
|
408
|
+
}
|
|
409
|
+
lastNormalizedIntersection.copy(normalizedIntersection).applyQuaternion(moveAroundGlobe);
|
|
410
|
+
cameraTarget.position.applyQuaternion(moveAroundGlobe);
|
|
411
|
+
this.camera.position.applyQuaternion(moveAroundGlobe);
|
|
412
|
+
break;
|
|
413
|
+
// PAN Move camera in projection plan
|
|
414
|
+
case this.states.PAN:
|
|
415
|
+
this.camera.position.add(panVector);
|
|
416
|
+
cameraTarget.position.add(panVector);
|
|
417
|
+
break;
|
|
418
|
+
// PANORAMIC Move target camera
|
|
419
|
+
case this.states.PANORAMIC:
|
|
420
|
+
{
|
|
421
|
+
this.camera.worldToLocal(cameraTarget.position);
|
|
422
|
+
const normal = this.camera.position.clone().normalize().applyQuaternion(this.camera.quaternion.clone().invert());
|
|
423
|
+
quaterPano.setFromAxisAngle(normal, sphericalDelta.theta).multiply(quaterAxis.setFromAxisAngle(axisX, sphericalDelta.phi));
|
|
424
|
+
cameraTarget.position.applyQuaternion(quaterPano);
|
|
425
|
+
this.camera.localToWorld(cameraTarget.position);
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
// ZOOM/ORBIT Move Camera around the target camera
|
|
429
|
+
default:
|
|
430
|
+
{
|
|
431
|
+
// get camera position in local space of target
|
|
432
|
+
this.camera.position.applyMatrix4(cameraTarget.matrixWorldInverse);
|
|
433
|
+
|
|
434
|
+
// angle from z-axis around y-axis
|
|
435
|
+
if (sphericalDelta.theta || sphericalDelta.phi) {
|
|
436
|
+
spherical.setFromVector3(this.camera.position);
|
|
437
|
+
}
|
|
438
|
+
// far underground
|
|
439
|
+
const dynamicRadius = spherical.radius * Math.sin(this.minPolarAngle);
|
|
440
|
+
const slowdownLimit = dynamicRadius * 8;
|
|
441
|
+
const contraryLimit = dynamicRadius * 2;
|
|
442
|
+
const minContraintPhi = -0.01;
|
|
443
|
+
if (this.handleCollision) {
|
|
444
|
+
if (minDistanceZ < slowdownLimit && minDistanceZ > contraryLimit && sphericalDelta.phi > 0) {
|
|
445
|
+
// slowdown zone : slowdown sphericalDelta.phi
|
|
446
|
+
const slowdownZone = slowdownLimit - contraryLimit;
|
|
447
|
+
// the deeper the camera is in this zone, the bigger the factor is
|
|
448
|
+
const slowdownFactor = 1 - (slowdownZone - (minDistanceZ - contraryLimit)) / slowdownZone;
|
|
449
|
+
// apply slowdown factor on tilt mouvement
|
|
450
|
+
sphericalDelta.phi *= slowdownFactor * slowdownFactor;
|
|
451
|
+
} else if (minDistanceZ < contraryLimit && minDistanceZ > -contraryLimit && sphericalDelta.phi > minContraintPhi) {
|
|
452
|
+
// contraint zone : contraint sphericalDelta.phi
|
|
453
|
+
|
|
454
|
+
// calculation of the angle of rotation which allows to leave this zone
|
|
455
|
+
let contraryPhi = -Math.asin((contraryLimit - minDistanceZ) * 0.25 / spherical.radius);
|
|
456
|
+
// clamp contraryPhi to make a less brutal exit
|
|
457
|
+
contraryPhi = THREE.MathUtils.clamp(contraryPhi, minContraintPhi, 0);
|
|
458
|
+
// the deeper the camera is in this zone, the bigger the factor is
|
|
459
|
+
const contraryFactor = 1 - (contraryLimit - minDistanceZ) / (2 * contraryLimit);
|
|
460
|
+
sphericalDelta.phi = THREE.MathUtils.lerp(sphericalDelta.phi, contraryPhi, contraryFactor);
|
|
461
|
+
minDistanceZ -= Math.sin(sphericalDelta.phi) * spherical.radius;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
spherical.theta += sphericalDelta.theta;
|
|
465
|
+
spherical.phi += sphericalDelta.phi;
|
|
466
|
+
|
|
467
|
+
// restrict spherical.theta to be between desired limits
|
|
468
|
+
spherical.theta = Math.max(this.minAzimuthAngle, Math.min(this.maxAzimuthAngle, spherical.theta));
|
|
469
|
+
|
|
470
|
+
// restrict spherical.phi to be between desired limits
|
|
471
|
+
spherical.phi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, spherical.phi));
|
|
472
|
+
spherical.radius = this.camera.position.length() * orbitScale;
|
|
473
|
+
|
|
474
|
+
// restrict spherical.phi to be betwee EPS and PI-EPS
|
|
475
|
+
spherical.makeSafe();
|
|
476
|
+
|
|
477
|
+
// restrict radius to be between desired limits
|
|
478
|
+
spherical.radius = Math.max(this.minDistance, Math.min(this.maxDistance, spherical.radius));
|
|
479
|
+
this.camera.position.setFromSpherical(spherical);
|
|
480
|
+
|
|
481
|
+
// if camera is underground, so move up camera
|
|
482
|
+
if (minDistanceZ < 0) {
|
|
483
|
+
this.camera.position.y -= minDistanceZ;
|
|
484
|
+
spherical.setFromVector3(this.camera.position);
|
|
485
|
+
sphericalDelta.phi = 0;
|
|
486
|
+
}
|
|
487
|
+
cameraTarget.localToWorld(this.camera.position);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
this.camera.up.copy(cameraTarget.position).normalize();
|
|
491
|
+
this.camera.lookAt(cameraTarget.position);
|
|
492
|
+
if (!this.enableDamping) {
|
|
493
|
+
sphericalDelta.theta = 0;
|
|
494
|
+
sphericalDelta.phi = 0;
|
|
495
|
+
moveAroundGlobe.set(0, 0, 0, 1);
|
|
496
|
+
} else {
|
|
497
|
+
sphericalDelta.theta *= 1 - dampingFactorDefault;
|
|
498
|
+
sphericalDelta.phi *= 1 - dampingFactorDefault;
|
|
499
|
+
moveAroundGlobe.slerp(dampingMove, this.dampingMoveFactor * 0.2);
|
|
500
|
+
}
|
|
501
|
+
orbitScale = 1;
|
|
502
|
+
panVector.set(0, 0, 0);
|
|
503
|
+
|
|
504
|
+
// update condition is:
|
|
505
|
+
// min(camera displacement, camera rotation in radians)^2 > EPS
|
|
506
|
+
// using small-angle approximation cos(x/2) = 1 - x^2 / 8
|
|
507
|
+
if (lastPosition.distanceToSquared(this.camera.position) > EPS || 8 * (1 - lastQuaternion.dot(this.camera.quaternion)) > EPS) {
|
|
508
|
+
this.view.notifyChange(this.camera);
|
|
509
|
+
lastPosition.copy(this.camera.position);
|
|
510
|
+
lastQuaternion.copy(this.camera.quaternion);
|
|
511
|
+
}
|
|
512
|
+
// Launch animationdamping if mouse stops these movements
|
|
513
|
+
if (this.enableDamping && state === this.states.ORBIT && this.player.isStopped() && (sphericalDelta.theta > EPS || sphericalDelta.phi > EPS)) {
|
|
514
|
+
this.player.setCallback(() => {
|
|
515
|
+
this.update(this.states.ORBIT);
|
|
516
|
+
});
|
|
517
|
+
this.player.playLater(durationDampingOrbital, 2);
|
|
518
|
+
}
|
|
519
|
+
this.view.dispatchEvent({
|
|
520
|
+
type: VIEW_EVENTS.CAMERA_MOVED,
|
|
521
|
+
coord: coordCameraTarget.setFromVector3(cameraTarget.position),
|
|
522
|
+
range: spherical.radius,
|
|
523
|
+
heading: -THREE.MathUtils.radToDeg(spherical.theta),
|
|
524
|
+
tilt: 90 - THREE.MathUtils.radToDeg(spherical.phi)
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
onStateChange(event) {
|
|
528
|
+
// If the state changed to NONE, end the movement associated to the previous state.
|
|
529
|
+
if (this.states.currentState === this.states.NONE) {
|
|
530
|
+
this.handleEndMovement(event);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Stop CameraUtils ongoing animations, which can for instance be triggered with `this.travel` or
|
|
535
|
+
// `this.lookAtCoordinate` methods.
|
|
536
|
+
CameraUtils.stop(this.view, this.camera);
|
|
537
|
+
|
|
538
|
+
// Dispatch events which specify if changes occurred in camera transform options.
|
|
539
|
+
this.onEndingMove();
|
|
540
|
+
|
|
541
|
+
// Stop eventual damping movement.
|
|
542
|
+
this.player.stop();
|
|
543
|
+
|
|
544
|
+
// Update camera transform options.
|
|
545
|
+
this.updateTarget();
|
|
546
|
+
previous = CameraUtils.getTransformCameraLookingAtTarget(this.view, this.camera, pickedPosition);
|
|
547
|
+
|
|
548
|
+
// Initialize rotation and panoramic movements.
|
|
549
|
+
rotateStart.copy(event.viewCoords);
|
|
550
|
+
|
|
551
|
+
// Initialize drag movement.
|
|
552
|
+
if (this.view.getPickingPositionFromDepth(event.viewCoords, pickingPoint)) {
|
|
553
|
+
pickSphere.radius = pickingPoint.length();
|
|
554
|
+
lastNormalizedIntersection.copy(pickingPoint).normalize();
|
|
555
|
+
this.updateHelper(pickingPoint, helpers.picking);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Initialize dolly movement.
|
|
559
|
+
dollyStart.copy(event.viewCoords);
|
|
560
|
+
this.view.getPickingPositionFromDepth(event.viewCoords, pickedPosition); // mouse position
|
|
561
|
+
|
|
562
|
+
// Initialize pan movement.
|
|
563
|
+
panStart.copy(event.viewCoords);
|
|
564
|
+
}
|
|
565
|
+
handleRotation(event) {
|
|
566
|
+
// Stop player if needed. Player can be playing while moving mouse in the case of rotation. This is due to the
|
|
567
|
+
// fact that a damping move can occur while rotating (without the need of releasing the mouse button)
|
|
568
|
+
this.player.stop();
|
|
569
|
+
this.handlePanoramic(event);
|
|
570
|
+
}
|
|
571
|
+
handleDrag(event) {
|
|
572
|
+
const normalized = this.view.viewToNormalizedCoords(event.viewCoords);
|
|
573
|
+
|
|
574
|
+
// An updateMatrixWorld on the camera prevents camera jittering when moving globe on a zoomed out view, with
|
|
575
|
+
// devtools open in web browser.
|
|
576
|
+
this.camera.updateMatrixWorld();
|
|
577
|
+
raycaster.setFromCamera(normalized, this.camera);
|
|
578
|
+
|
|
579
|
+
// If there's intersection then move globe else we stop the move
|
|
580
|
+
if (raycaster.ray.intersectSphere(pickSphere, intersection)) {
|
|
581
|
+
normalizedIntersection.copy(intersection).normalize();
|
|
582
|
+
moveAroundGlobe.setFromUnitVectors(normalizedIntersection, lastNormalizedIntersection);
|
|
583
|
+
lastTimeMouseMove = Date.now();
|
|
584
|
+
this.update();
|
|
585
|
+
} else {
|
|
586
|
+
this.states.onPointerUp();
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
handleDolly(event) {
|
|
590
|
+
dollyEnd.copy(event.viewCoords);
|
|
591
|
+
dollyDelta.subVectors(dollyEnd, dollyStart);
|
|
592
|
+
dollyStart.copy(dollyEnd);
|
|
593
|
+
event.delta = dollyDelta.y;
|
|
594
|
+
if (event.delta != 0) {
|
|
595
|
+
this.handleZoom(event);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
handlePan(event) {
|
|
599
|
+
if (event.viewCoords) {
|
|
600
|
+
panEnd.copy(event.viewCoords);
|
|
601
|
+
panDelta.subVectors(panEnd, panStart);
|
|
602
|
+
panStart.copy(panEnd);
|
|
603
|
+
} else if (event.direction) {
|
|
604
|
+
panDelta.copy(direction[event.direction]).multiplyScalar(this.keyPanSpeed);
|
|
605
|
+
}
|
|
606
|
+
this.mouseToPan(panDelta.x, panDelta.y);
|
|
607
|
+
this.update(this.states.PAN);
|
|
608
|
+
}
|
|
609
|
+
handlePanoramic(event) {
|
|
610
|
+
rotateEnd.copy(event.viewCoords);
|
|
611
|
+
rotateDelta.subVectors(rotateEnd, rotateStart);
|
|
612
|
+
const gfx = this.view.mainLoop.gfxEngine;
|
|
613
|
+
sphericalDelta.theta -= 2 * Math.PI * rotateDelta.x / gfx.width * this.rotateSpeed;
|
|
614
|
+
// rotating up and down along whole screen attempts to go 360, but limited to 180
|
|
615
|
+
sphericalDelta.phi -= 2 * Math.PI * rotateDelta.y / gfx.height * this.rotateSpeed;
|
|
616
|
+
rotateStart.copy(rotateEnd);
|
|
617
|
+
this.update();
|
|
618
|
+
}
|
|
619
|
+
handleEndMovement() {
|
|
620
|
+
let event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
621
|
+
this.dispatchEvent(this.endEvent);
|
|
622
|
+
this.player.stop();
|
|
623
|
+
|
|
624
|
+
// Launch damping movement for :
|
|
625
|
+
// * this.states.ORBIT
|
|
626
|
+
// * this.states.MOVE_GLOBE
|
|
627
|
+
if (this.enableDamping) {
|
|
628
|
+
if (event.previous === this.states.ORBIT && (sphericalDelta.theta > EPS || sphericalDelta.phi > EPS)) {
|
|
629
|
+
this.player.setCallback(() => {
|
|
630
|
+
this.update(this.states.ORBIT);
|
|
631
|
+
});
|
|
632
|
+
this.player.play(durationDampingOrbital);
|
|
633
|
+
this._onEndingMove = () => this.onEndingMove();
|
|
634
|
+
this.player.addEventListener('animation-stopped', this._onEndingMove);
|
|
635
|
+
} else if (event.previous === this.states.MOVE_GLOBE && Date.now() - lastTimeMouseMove < 50) {
|
|
636
|
+
this.player.setCallback(() => {
|
|
637
|
+
this.update(this.states.MOVE_GLOBE);
|
|
638
|
+
});
|
|
639
|
+
// animation since mouse up event occurs less than 50ms after the last mouse move
|
|
640
|
+
this.player.play(durationDampingMove);
|
|
641
|
+
this._onEndingMove = () => this.onEndingMove();
|
|
642
|
+
this.player.addEventListener('animation-stopped', this._onEndingMove);
|
|
643
|
+
} else {
|
|
644
|
+
this.onEndingMove();
|
|
645
|
+
}
|
|
646
|
+
} else {
|
|
647
|
+
this.onEndingMove();
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
updateTarget() {
|
|
651
|
+
// Check if the middle of the screen is on the globe (to prevent having a dark-screen bug if outside the globe)
|
|
652
|
+
if (this.view.getPickingPositionFromDepth(null, pickedPosition)) {
|
|
653
|
+
// Update camera's target position
|
|
654
|
+
const distance = !isNaN(pickedPosition.x) ? this.camera.position.distanceTo(pickedPosition) : 100;
|
|
655
|
+
targetPosition.set(0, 0, -distance);
|
|
656
|
+
this.camera.localToWorld(targetPosition);
|
|
657
|
+
|
|
658
|
+
// set new camera target on globe
|
|
659
|
+
positionObject(targetPosition, cameraTarget);
|
|
660
|
+
cameraTarget.matrixWorldInverse.copy(cameraTarget.matrixWorld).invert();
|
|
661
|
+
targetPosition.copy(this.camera.position);
|
|
662
|
+
targetPosition.applyMatrix4(cameraTarget.matrixWorldInverse);
|
|
663
|
+
spherical.setFromVector3(targetPosition);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
handlingEvent(current) {
|
|
667
|
+
current = current || CameraUtils.getTransformCameraLookingAtTarget(this.view, this.camera);
|
|
668
|
+
const diff = CameraUtils.getDiffParams(previous, current);
|
|
669
|
+
if (diff) {
|
|
670
|
+
if (diff.range) {
|
|
671
|
+
this.dispatchEvent({
|
|
672
|
+
type: CONTROL_EVENTS.RANGE_CHANGED,
|
|
673
|
+
previous: diff.range.previous,
|
|
674
|
+
new: diff.range.new
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
if (diff.coord) {
|
|
678
|
+
this.dispatchEvent({
|
|
679
|
+
type: CONTROL_EVENTS.CAMERA_TARGET_CHANGED,
|
|
680
|
+
previous: diff.coord.previous,
|
|
681
|
+
new: diff.coord.new
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
if (diff.tilt || diff.heading) {
|
|
685
|
+
const event = {
|
|
686
|
+
type: CONTROL_EVENTS.ORIENTATION_CHANGED
|
|
687
|
+
};
|
|
688
|
+
if (diff.tilt) {
|
|
689
|
+
event.previous = {
|
|
690
|
+
tilt: diff.tilt.previous
|
|
691
|
+
};
|
|
692
|
+
event.new = {
|
|
693
|
+
tilt: diff.tilt.new
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
if (diff.heading) {
|
|
697
|
+
event.previous = event.previous || {};
|
|
698
|
+
event.new = event.new || {};
|
|
699
|
+
event.new.heading = diff.heading.new;
|
|
700
|
+
event.previous.heading = diff.heading.previous;
|
|
701
|
+
}
|
|
702
|
+
this.dispatchEvent(event);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
travel(event) {
|
|
707
|
+
this.player.stop();
|
|
708
|
+
const point = this.view.getPickingPositionFromDepth(event.viewCoords);
|
|
709
|
+
const range = this.getRange(point);
|
|
710
|
+
if (point && range > this.minDistance) {
|
|
711
|
+
return this.lookAtCoordinate({
|
|
712
|
+
coord: new Coordinates('EPSG:4978').setFromVector3(point),
|
|
713
|
+
range: range * (event.direction === 'out' ? 1 / 0.6 : 0.6),
|
|
714
|
+
time: 1500
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
handleZoom(event) {
|
|
719
|
+
this.player.stop();
|
|
720
|
+
CameraUtils.stop(this.view, this.camera);
|
|
721
|
+
const zoomScale = event.delta > 0 ? this.zoomInScale : this.zoomOutScale;
|
|
722
|
+
let point = event.type === 'dolly' ? pickedPosition : this.view.getPickingPositionFromDepth(event.viewCoords); // get cursor position
|
|
723
|
+
let range = this.getRange();
|
|
724
|
+
range *= zoomScale;
|
|
725
|
+
if (point && range > this.minDistance && range < this.maxDistance) {
|
|
726
|
+
// check if the zoom is in the allowed interval
|
|
727
|
+
const camPos = xyz.setFromVector3(cameraTarget.position).as('EPSG:4326', c).toVector3();
|
|
728
|
+
point = xyz.setFromVector3(point).as('EPSG:4326', c).toVector3();
|
|
729
|
+
if (camPos.x * point.x < 0) {
|
|
730
|
+
// Correct rotation at 180th meridian by using 0 <= longitude <=360 for interpolation purpose
|
|
731
|
+
if (camPos.x - point.x > 180) {
|
|
732
|
+
point.x += 360;
|
|
733
|
+
} else if (point.x - camPos.x > 180) {
|
|
734
|
+
camPos.x += 360;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
point.lerp(
|
|
738
|
+
// point interpol between mouse cursor and cam pos
|
|
739
|
+
camPos, zoomScale // interpol factor
|
|
740
|
+
);
|
|
741
|
+
point = c.setFromVector3(point).as('EPSG:4978', xyz);
|
|
742
|
+
return this.lookAtCoordinate({
|
|
743
|
+
// update view to the interpolate point
|
|
744
|
+
coord: point,
|
|
745
|
+
range
|
|
746
|
+
}, false);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
onTouchStart(event) {
|
|
750
|
+
// CameraUtils.stop(view);
|
|
751
|
+
this.player.stop();
|
|
752
|
+
// TODO : this.states.enabled check should be removed when moving touch events management to StateControl
|
|
753
|
+
if (this.states.enabled === false) {
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
this.state = this.states.touchToState(event.touches.length);
|
|
757
|
+
this.updateTarget();
|
|
758
|
+
if (this.state !== this.states.NONE) {
|
|
759
|
+
switch (this.state) {
|
|
760
|
+
case this.states.MOVE_GLOBE:
|
|
761
|
+
{
|
|
762
|
+
const coords = this.view.eventToViewCoords(event);
|
|
763
|
+
if (this.view.getPickingPositionFromDepth(coords, pickingPoint)) {
|
|
764
|
+
pickSphere.radius = pickingPoint.length();
|
|
765
|
+
lastNormalizedIntersection.copy(pickingPoint).normalize();
|
|
766
|
+
this.updateHelper(pickingPoint, helpers.picking);
|
|
767
|
+
} else {
|
|
768
|
+
this.state = this.states.NONE;
|
|
769
|
+
}
|
|
770
|
+
break;
|
|
771
|
+
}
|
|
772
|
+
case this.states.ORBIT:
|
|
773
|
+
case this.states.DOLLY:
|
|
774
|
+
{
|
|
775
|
+
const x = event.touches[0].pageX;
|
|
776
|
+
const y = event.touches[0].pageY;
|
|
777
|
+
const dx = x - event.touches[1].pageX;
|
|
778
|
+
const dy = y - event.touches[1].pageY;
|
|
779
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
780
|
+
dollyStart.set(0, distance);
|
|
781
|
+
rotateStart.set(x, y);
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
case this.states.PAN:
|
|
785
|
+
panStart.set(event.touches[0].pageX, event.touches[0].pageY);
|
|
786
|
+
break;
|
|
787
|
+
default:
|
|
788
|
+
}
|
|
789
|
+
this.dispatchEvent(this.startEvent);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
onTouchMove(event) {
|
|
793
|
+
if (this.player.isPlaying()) {
|
|
794
|
+
this.player.stop();
|
|
795
|
+
}
|
|
796
|
+
// TODO : this.states.enabled check should be removed when moving touch events management to StateControl
|
|
797
|
+
if (this.states.enabled === false) {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
event.preventDefault();
|
|
801
|
+
event.stopPropagation();
|
|
802
|
+
switch (event.touches.length) {
|
|
803
|
+
case this.states.MOVE_GLOBE.finger:
|
|
804
|
+
{
|
|
805
|
+
const coords = this.view.eventToViewCoords(event);
|
|
806
|
+
const normalized = this.view.viewToNormalizedCoords(coords);
|
|
807
|
+
// An updateMatrixWorld on the camera prevents camera jittering when moving globe on a zoomed out view, with
|
|
808
|
+
// devtools open in web browser.
|
|
809
|
+
this.camera.updateMatrixWorld();
|
|
810
|
+
raycaster.setFromCamera(normalized, this.camera);
|
|
811
|
+
// If there's intersection then move globe else we stop the move
|
|
812
|
+
if (raycaster.ray.intersectSphere(pickSphere, intersection)) {
|
|
813
|
+
normalizedIntersection.copy(intersection).normalize();
|
|
814
|
+
moveAroundGlobe.setFromUnitVectors(normalizedIntersection, lastNormalizedIntersection);
|
|
815
|
+
lastTimeMouseMove = Date.now();
|
|
816
|
+
} else {
|
|
817
|
+
this.onTouchEnd();
|
|
818
|
+
}
|
|
819
|
+
break;
|
|
820
|
+
}
|
|
821
|
+
case this.states.ORBIT.finger:
|
|
822
|
+
case this.states.DOLLY.finger:
|
|
823
|
+
{
|
|
824
|
+
const gfx = this.view.mainLoop.gfxEngine;
|
|
825
|
+
rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY);
|
|
826
|
+
rotateDelta.subVectors(rotateEnd, rotateStart);
|
|
827
|
+
|
|
828
|
+
// rotating across whole screen goes 360 degrees around
|
|
829
|
+
this.rotateLeft(2 * Math.PI * rotateDelta.x / gfx.width * this.rotateSpeed);
|
|
830
|
+
// rotating up and down along whole screen attempts to go 360, but limited to 180
|
|
831
|
+
this.rotateUp(2 * Math.PI * rotateDelta.y / gfx.height * this.rotateSpeed);
|
|
832
|
+
rotateStart.copy(rotateEnd);
|
|
833
|
+
const dx = event.touches[0].pageX - event.touches[1].pageX;
|
|
834
|
+
const dy = event.touches[0].pageY - event.touches[1].pageY;
|
|
835
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
836
|
+
dollyEnd.set(0, distance);
|
|
837
|
+
dollyDelta.subVectors(dollyEnd, dollyStart);
|
|
838
|
+
this.dolly(dollyDelta.y);
|
|
839
|
+
dollyStart.copy(dollyEnd);
|
|
840
|
+
break;
|
|
841
|
+
}
|
|
842
|
+
case this.states.PAN.finger:
|
|
843
|
+
panEnd.set(event.touches[0].pageX, event.touches[0].pageY);
|
|
844
|
+
panDelta.subVectors(panEnd, panStart);
|
|
845
|
+
this.mouseToPan(panDelta.x, panDelta.y);
|
|
846
|
+
panStart.copy(panEnd);
|
|
847
|
+
break;
|
|
848
|
+
default:
|
|
849
|
+
this.state = this.states.NONE;
|
|
850
|
+
}
|
|
851
|
+
if (this.state !== this.states.NONE) {
|
|
852
|
+
this.update(this.state);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
onTouchEnd() {
|
|
856
|
+
this.handleEndMovement({
|
|
857
|
+
previous: this.state
|
|
858
|
+
});
|
|
859
|
+
this.state = this.states.NONE;
|
|
860
|
+
}
|
|
861
|
+
dispose() {
|
|
862
|
+
this.view.domElement.removeEventListener('touchstart', this._onTouchStart, false);
|
|
863
|
+
this.view.domElement.removeEventListener('touchend', this._onTouchEnd, false);
|
|
864
|
+
this.view.domElement.removeEventListener('touchmove', this._onTouchMove, false);
|
|
865
|
+
this.states.dispose();
|
|
866
|
+
this.states.removeEventListener('state-changed', this._onStateChange, false);
|
|
867
|
+
this.states.removeEventListener(this.states.ORBIT._event, this._onRotation, false);
|
|
868
|
+
this.states.removeEventListener(this.states.MOVE_GLOBE._event, this._onDrag, false);
|
|
869
|
+
this.states.removeEventListener(this.states.DOLLY._event, this._onDolly, false);
|
|
870
|
+
this.states.removeEventListener(this.states.PAN._event, this._onPan, false);
|
|
871
|
+
this.states.removeEventListener(this.states.PANORAMIC._event, this._onPanoramic, false);
|
|
872
|
+
this.states.removeEventListener('zoom', this._onZoom, false);
|
|
873
|
+
this.states.removeEventListener(this.states.TRAVEL_IN._event, this._onTravel, false);
|
|
874
|
+
this.states.removeEventListener(this.states.TRAVEL_OUT._event, this._onTravel, false);
|
|
875
|
+
this.dispatchEvent({
|
|
876
|
+
type: 'dispose'
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Changes the tilt of the current camera, in degrees.
|
|
881
|
+
* @param {number} tilt
|
|
882
|
+
* @param {boolean} isAnimated
|
|
883
|
+
* @return {Promise<void>}
|
|
884
|
+
*/
|
|
885
|
+
setTilt(tilt, isAnimated) {
|
|
886
|
+
return this.lookAtCoordinate({
|
|
887
|
+
tilt
|
|
888
|
+
}, isAnimated);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* Changes the heading of the current camera, in degrees.
|
|
893
|
+
* @param {number} heading
|
|
894
|
+
* @param {boolean} isAnimated
|
|
895
|
+
* @return {Promise<void>}
|
|
896
|
+
*/
|
|
897
|
+
setHeading(heading, isAnimated) {
|
|
898
|
+
return this.lookAtCoordinate({
|
|
899
|
+
heading
|
|
900
|
+
}, isAnimated);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* Sets the "range": the distance in meters between the camera and the current central point on the screen.
|
|
905
|
+
* @param {number} range
|
|
906
|
+
* @param {boolean} isAnimated
|
|
907
|
+
* @return {Promise<void>}
|
|
908
|
+
*/
|
|
909
|
+
setRange(range, isAnimated) {
|
|
910
|
+
return this.lookAtCoordinate({
|
|
911
|
+
range
|
|
912
|
+
}, isAnimated);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Returns the {@linkcode Coordinates} of the globe point targeted by the camera in EPSG:4978 projection. See {@linkcode Coordinates} for conversion
|
|
917
|
+
* @return {THREE.Vector3} position
|
|
918
|
+
*/
|
|
919
|
+
getCameraTargetPosition() {
|
|
920
|
+
return cameraTarget.position;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* Returns the "range": the distance in meters between the camera and the current central point on the screen.
|
|
925
|
+
* @param {THREE.Vector3} [position] - The position to consider as picked on
|
|
926
|
+
* the ground.
|
|
927
|
+
* @return {number} number
|
|
928
|
+
*/
|
|
929
|
+
getRange(position) {
|
|
930
|
+
return CameraUtils.getTransformCameraLookingAtTarget(this.view, this.camera, position).range;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/**
|
|
934
|
+
* Returns the tilt of the current camera in degrees.
|
|
935
|
+
* @param {THREE.Vector3} [position] - The position to consider as picked on
|
|
936
|
+
* the ground.
|
|
937
|
+
* @return {number} The angle of the rotation in degrees.
|
|
938
|
+
*/
|
|
939
|
+
getTilt(position) {
|
|
940
|
+
return CameraUtils.getTransformCameraLookingAtTarget(this.view, this.camera, position).tilt;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* Returns the heading of the current camera in degrees.
|
|
945
|
+
* @param {THREE.Vector3} [position] - The position to consider as picked on
|
|
946
|
+
* the ground.
|
|
947
|
+
* @return {number} The angle of the rotation in degrees.
|
|
948
|
+
*/
|
|
949
|
+
getHeading(position) {
|
|
950
|
+
return CameraUtils.getTransformCameraLookingAtTarget(this.view, this.camera, position).heading;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* Displaces the central point to a specific amount of pixels from its current position.
|
|
955
|
+
* The view flies to the desired coordinate, i.e.is not teleported instantly. Note : The results can be strange in some cases, if ever possible, when e.g.the camera looks horizontally or if the displaced center would not pick the ground once displaced.
|
|
956
|
+
* @param {vector} pVector The vector
|
|
957
|
+
* @return {Promise}
|
|
958
|
+
*/
|
|
959
|
+
pan(pVector) {
|
|
960
|
+
this.mouseToPan(pVector.x, pVector.y);
|
|
961
|
+
this.update(this.states.PAN);
|
|
962
|
+
return Promise.resolve();
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* Returns the orientation angles of the current camera, in degrees.
|
|
967
|
+
* @return {Array<number>}
|
|
968
|
+
*/
|
|
969
|
+
getCameraOrientation() {
|
|
970
|
+
this.view.getPickingPositionFromDepth(null, pickedPosition);
|
|
971
|
+
return [this.getTilt(pickedPosition), this.getHeading(pickedPosition)];
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* Returns the camera location projected on the ground in lat,lon. See {@linkcode Coordinates} for conversion.
|
|
976
|
+
* @return {Coordinates} position
|
|
977
|
+
*/
|
|
978
|
+
|
|
979
|
+
getCameraCoordinate() {
|
|
980
|
+
return new Coordinates('EPSG:4978').setFromVector3(this.camera.position).as('EPSG:4326');
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Returns the {@linkcode Coordinates} of the central point on screen in lat,lon. See {@linkcode Coordinates} for conversion.
|
|
985
|
+
* @return {Coordinates} coordinate
|
|
986
|
+
*/
|
|
987
|
+
getLookAtCoordinate() {
|
|
988
|
+
return CameraUtils.getTransformCameraLookingAtTarget(this.view, this.camera).coord;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* Sets the animation enabled.
|
|
993
|
+
* @param {boolean} enable enable
|
|
994
|
+
*/
|
|
995
|
+
setAnimationEnabled(enable) {
|
|
996
|
+
enableAnimation = enable;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Determines if animation enabled.
|
|
1001
|
+
* @return {boolean} True if animation enabled, False otherwise.
|
|
1002
|
+
*/
|
|
1003
|
+
isAnimationEnabled() {
|
|
1004
|
+
return enableAnimation;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Returns the actual zoom. The zoom will always be between the [getMinZoom(), getMaxZoom()].
|
|
1009
|
+
* @return {number} The zoom .
|
|
1010
|
+
*/
|
|
1011
|
+
getZoom() {
|
|
1012
|
+
return this.view.tileLayer.computeTileZoomFromDistanceCamera(this.getRange(), this.view.camera);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Sets the current zoom, which is an index in the logical scales predefined for the application.
|
|
1017
|
+
* The higher the zoom, the closer to the ground.
|
|
1018
|
+
* The zoom is always in the [getMinZoom(), getMaxZoom()] range.
|
|
1019
|
+
* @param {number} zoom The zoom
|
|
1020
|
+
* @param {boolean} isAnimated Indicates if animated
|
|
1021
|
+
* @return {Promise}
|
|
1022
|
+
*/
|
|
1023
|
+
setZoom(zoom, isAnimated) {
|
|
1024
|
+
return this.lookAtCoordinate({
|
|
1025
|
+
zoom
|
|
1026
|
+
}, isAnimated);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Return the current zoom scale at the central point of the view.
|
|
1031
|
+
* This function compute the scale of a map
|
|
1032
|
+
* @param {number} pitch Screen pitch, in millimeters ; 0.28 by default
|
|
1033
|
+
* @return {number} The zoom scale.
|
|
1034
|
+
*
|
|
1035
|
+
* @deprecated Use View#getScale instead.
|
|
1036
|
+
*/
|
|
1037
|
+
getScale(pitch) {
|
|
1038
|
+
console.warn('Deprecated, use View#getScale instead.');
|
|
1039
|
+
return this.view.getScale(pitch);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
/**
|
|
1043
|
+
* To convert the projection in meters on the globe of a number of pixels of screen
|
|
1044
|
+
* @param {number} pixels count pixels to project
|
|
1045
|
+
* @param {number} pixelPitch Screen pixel pitch, in millimeters (default = 0.28 mm / standard pixel size of 0.28 millimeters as defined by the OGC)
|
|
1046
|
+
* @return {number} projection in meters on globe
|
|
1047
|
+
*
|
|
1048
|
+
* @deprecated Use `View#getPixelsToMeters` instead.
|
|
1049
|
+
*/
|
|
1050
|
+
pixelsToMeters(pixels) {
|
|
1051
|
+
let pixelPitch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.28;
|
|
1052
|
+
console.warn('Deprecated use View#getPixelsToMeters instead.');
|
|
1053
|
+
const scaled = this.getScale(pixelPitch);
|
|
1054
|
+
return pixels * pixelPitch / scaled / 1000;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* To convert the projection a number of horizontal pixels of screen to longitude degree WGS84 on the globe
|
|
1059
|
+
* @param {number} pixels count pixels to project
|
|
1060
|
+
* @param {number} pixelPitch Screen pixel pitch, in millimeters (default = 0.28 mm / standard pixel size of 0.28 millimeters as defined by the OGC)
|
|
1061
|
+
* @return {number} projection in degree on globe
|
|
1062
|
+
*
|
|
1063
|
+
* @deprecated Use `View#getPixelsToMeters` and `GlobeControls#metersToDegrees`
|
|
1064
|
+
* instead.
|
|
1065
|
+
*/
|
|
1066
|
+
pixelsToDegrees(pixels) {
|
|
1067
|
+
let pixelPitch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.28;
|
|
1068
|
+
console.warn('Deprecated, use View#getPixelsToMeters and GlobeControls#getMetersToDegrees instead.');
|
|
1069
|
+
const chord = this.pixelsToMeters(pixels, pixelPitch);
|
|
1070
|
+
return THREE.MathUtils.radToDeg(2 * Math.asin(chord / (2 * ellipsoidSizes.x)));
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* Projection on screen in pixels of length in meter on globe
|
|
1075
|
+
* @param {number} value Length in meter on globe
|
|
1076
|
+
* @param {number} pixelPitch Screen pixel pitch, in millimeters (default = 0.28 mm / standard pixel size of 0.28 millimeters as defined by the OGC)
|
|
1077
|
+
* @return {number} projection in pixels on screen
|
|
1078
|
+
*
|
|
1079
|
+
* @deprecated Use `View#getMetersToPixels` instead.
|
|
1080
|
+
*/
|
|
1081
|
+
metersToPixels(value) {
|
|
1082
|
+
let pixelPitch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.28;
|
|
1083
|
+
console.warn('Deprecated, use View#getMetersToPixels instead.');
|
|
1084
|
+
const scaled = this.getScale(pixelPitch);
|
|
1085
|
+
pixelPitch /= 1000;
|
|
1086
|
+
return value * scaled / pixelPitch;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
* Changes the zoom of the central point of screen so that screen acts as a map with a specified scale.
|
|
1091
|
+
* The view flies to the desired zoom scale;
|
|
1092
|
+
* @param {number} scale The scale
|
|
1093
|
+
* @param {number} pitch The pitch
|
|
1094
|
+
* @param {boolean} isAnimated Indicates if animated
|
|
1095
|
+
* @return {Promise}
|
|
1096
|
+
*/
|
|
1097
|
+
setScale(scale, pitch, isAnimated) {
|
|
1098
|
+
return this.lookAtCoordinate({
|
|
1099
|
+
scale,
|
|
1100
|
+
pitch
|
|
1101
|
+
}, isAnimated);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Changes the center of the scene on screen to the specified in lat, lon. See {@linkcode Coordinates} for conversion.
|
|
1106
|
+
* This function allows to change the central position, the zoom, the range, the scale and the camera orientation at the same time.
|
|
1107
|
+
* The zoom has to be between the [getMinZoom(), getMaxZoom()].
|
|
1108
|
+
* Zoom parameter is ignored if range is set
|
|
1109
|
+
* The tilt's interval is between 4 and 89.5 degree
|
|
1110
|
+
*
|
|
1111
|
+
* @param {CameraUtils~CameraTransformOptions|Extent} [params] - camera transformation to apply
|
|
1112
|
+
* @param {number} [params.zoom] - zoom
|
|
1113
|
+
* @param {number} [params.scale] - scale
|
|
1114
|
+
* @param {boolean} [isAnimated] - Indicates if animated
|
|
1115
|
+
* @return {Promise} A promise that resolves when transformation is complete
|
|
1116
|
+
*/
|
|
1117
|
+
lookAtCoordinate() {
|
|
1118
|
+
let params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
1119
|
+
let isAnimated = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.isAnimationEnabled();
|
|
1120
|
+
this.player.stop();
|
|
1121
|
+
if (!params.isExtent) {
|
|
1122
|
+
if (params.zoom) {
|
|
1123
|
+
params.range = this.view.tileLayer.computeDistanceCameraFromTileZoom(params.zoom, this.view.camera);
|
|
1124
|
+
} else if (params.scale) {
|
|
1125
|
+
params.range = this.view.getScaleFromDistance(params.pitch, params.scale);
|
|
1126
|
+
if (params.range < this.minDistance || params.range > this.maxDistance) {
|
|
1127
|
+
// eslint-disable-next-line no-console
|
|
1128
|
+
console.warn(`This scale ${params.scale} can not be reached`);
|
|
1129
|
+
params.range = THREE.MathUtils.clamp(params.range, this.minDistance, this.maxDistance);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
if (params.tilt !== undefined) {
|
|
1133
|
+
const minTilt = 90 - THREE.MathUtils.radToDeg(this.maxPolarAngle);
|
|
1134
|
+
const maxTilt = 90 - THREE.MathUtils.radToDeg(this.minPolarAngle);
|
|
1135
|
+
if (params.tilt < minTilt || params.tilt > maxTilt) {
|
|
1136
|
+
params.tilt = THREE.MathUtils.clamp(params.tilt, minTilt, maxTilt);
|
|
1137
|
+
// eslint-disable-next-line no-console
|
|
1138
|
+
console.warn('Tilt was clamped to ', params.tilt, ` the interval is between ${minTilt} and ${maxTilt} degree`);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
previous = CameraUtils.getTransformCameraLookingAtTarget(this.view, this.camera);
|
|
1143
|
+
if (isAnimated) {
|
|
1144
|
+
params.callback = r => cameraTarget.position.copy(r.targetWorldPosition);
|
|
1145
|
+
this.dispatchEvent({
|
|
1146
|
+
type: 'animation-started'
|
|
1147
|
+
});
|
|
1148
|
+
return CameraUtils.animateCameraToLookAtTarget(this.view, this.camera, params).then(result => {
|
|
1149
|
+
this.dispatchEvent({
|
|
1150
|
+
type: 'animation-ended'
|
|
1151
|
+
});
|
|
1152
|
+
this.handlingEvent(result);
|
|
1153
|
+
return result;
|
|
1154
|
+
});
|
|
1155
|
+
} else {
|
|
1156
|
+
return CameraUtils.transformCameraToLookAtTarget(this.view, this.camera, params).then(result => {
|
|
1157
|
+
cameraTarget.position.copy(result.targetWorldPosition);
|
|
1158
|
+
this.handlingEvent(result);
|
|
1159
|
+
return result;
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
* Pick a position on the globe at the given position in lat,lon. See {@linkcode Coordinates} for conversion.
|
|
1166
|
+
* @param {Vector2} windowCoords - window coordinates
|
|
1167
|
+
* @param {number=} y - The y-position inside the Globe element.
|
|
1168
|
+
* @return {Coordinates} position
|
|
1169
|
+
*/
|
|
1170
|
+
pickGeoPosition(windowCoords) {
|
|
1171
|
+
const pickedPosition = this.view.getPickingPositionFromDepth(windowCoords);
|
|
1172
|
+
if (!pickedPosition) {
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
return new Coordinates('EPSG:4978').setFromVector3(pickedPosition).as('EPSG:4326');
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
export default GlobeControls;
|