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.
Files changed (185) hide show
  1. package/dist/455.js +2 -0
  2. package/dist/455.js.map +1 -0
  3. package/dist/debug.js +3 -0
  4. package/dist/debug.js.LICENSE.txt +13 -0
  5. package/dist/debug.js.map +1 -0
  6. package/dist/itowns.js +3 -0
  7. package/dist/itowns.js.LICENSE.txt +5 -0
  8. package/dist/itowns.js.map +1 -0
  9. package/dist/itowns_lasparser.js +2 -0
  10. package/dist/itowns_lasparser.js.map +1 -0
  11. package/dist/itowns_lasworker.js +2 -0
  12. package/dist/itowns_lasworker.js.map +1 -0
  13. package/dist/itowns_potree2worker.js +2 -0
  14. package/dist/itowns_potree2worker.js.map +1 -0
  15. package/dist/itowns_widgets.js +2 -0
  16. package/dist/itowns_widgets.js.map +1 -0
  17. package/lib/Controls/FirstPersonControls.js +308 -0
  18. package/lib/Controls/FlyControls.js +175 -0
  19. package/lib/Controls/GlobeControls.js +1178 -0
  20. package/lib/Controls/PlanarControls.js +1025 -0
  21. package/lib/Controls/StateControl.js +432 -0
  22. package/lib/Controls/StreetControls.js +392 -0
  23. package/lib/Converter/Feature2Mesh.js +612 -0
  24. package/lib/Converter/Feature2Texture.js +174 -0
  25. package/lib/Converter/convertToTile.js +70 -0
  26. package/lib/Converter/textureConverter.js +43 -0
  27. package/lib/Core/3DTiles/C3DTBatchTable.js +131 -0
  28. package/lib/Core/3DTiles/C3DTBatchTableHierarchyExtension.js +96 -0
  29. package/lib/Core/3DTiles/C3DTBoundingVolume.js +156 -0
  30. package/lib/Core/3DTiles/C3DTExtensions.js +97 -0
  31. package/lib/Core/3DTiles/C3DTFeature.js +110 -0
  32. package/lib/Core/3DTiles/C3DTilesEnums.js +20 -0
  33. package/lib/Core/3DTiles/C3DTileset.js +99 -0
  34. package/lib/Core/3DTiles/utils/BinaryPropertyAccessor.js +100 -0
  35. package/lib/Core/AnimationPlayer.js +142 -0
  36. package/lib/Core/CopcNode.js +174 -0
  37. package/lib/Core/Deprecated/Undeprecator.js +74 -0
  38. package/lib/Core/EntwinePointTileNode.js +126 -0
  39. package/lib/Core/Feature.js +488 -0
  40. package/lib/Core/Geographic/GeoidGrid.js +108 -0
  41. package/lib/Core/Label.js +222 -0
  42. package/lib/Core/MainLoop.js +209 -0
  43. package/lib/Core/Picking.js +255 -0
  44. package/lib/Core/PointCloudNode.js +42 -0
  45. package/lib/Core/Potree2Node.js +206 -0
  46. package/lib/Core/Potree2PointAttributes.js +139 -0
  47. package/lib/Core/PotreeNode.js +101 -0
  48. package/lib/Core/Prefab/Globe/Atmosphere.js +293 -0
  49. package/lib/Core/Prefab/Globe/GlobeLayer.js +152 -0
  50. package/lib/Core/Prefab/Globe/GlobeTileBuilder.js +110 -0
  51. package/lib/Core/Prefab/Globe/SkyShader.js +78 -0
  52. package/lib/Core/Prefab/GlobeView.js +155 -0
  53. package/lib/Core/Prefab/Planar/PlanarLayer.js +59 -0
  54. package/lib/Core/Prefab/Planar/PlanarTileBuilder.js +71 -0
  55. package/lib/Core/Prefab/PlanarView.js +62 -0
  56. package/lib/Core/Prefab/TileBuilder.js +82 -0
  57. package/lib/Core/Prefab/computeBufferTileGeometry.js +248 -0
  58. package/lib/Core/Scheduler/Cache.js +17 -0
  59. package/lib/Core/Scheduler/CancelledCommandException.js +15 -0
  60. package/lib/Core/Scheduler/Scheduler.js +294 -0
  61. package/lib/Core/Style.js +660 -0
  62. package/lib/Core/StyleOptions.js +486 -0
  63. package/lib/Core/System/Capabilities.js +63 -0
  64. package/lib/Core/Tile/Tile.js +205 -0
  65. package/lib/Core/Tile/TileGrid.js +49 -0
  66. package/lib/Core/TileGeometry.js +124 -0
  67. package/lib/Core/TileMesh.js +108 -0
  68. package/lib/Core/View.js +1115 -0
  69. package/lib/Layer/C3DTilesLayer.js +459 -0
  70. package/lib/Layer/ColorLayer.js +154 -0
  71. package/lib/Layer/CopcLayer.js +63 -0
  72. package/lib/Layer/ElevationLayer.js +139 -0
  73. package/lib/Layer/EntwinePointTileLayer.js +71 -0
  74. package/lib/Layer/FeatureGeometryLayer.js +77 -0
  75. package/lib/Layer/GeoidLayer.js +80 -0
  76. package/lib/Layer/GeometryLayer.js +233 -0
  77. package/lib/Layer/InfoLayer.js +64 -0
  78. package/lib/Layer/LabelLayer.js +469 -0
  79. package/lib/Layer/Layer.js +335 -0
  80. package/lib/Layer/LayerUpdateState.js +89 -0
  81. package/lib/Layer/LayerUpdateStrategy.js +80 -0
  82. package/lib/Layer/OGC3DTilesLayer.js +543 -0
  83. package/lib/Layer/OrientedImageLayer.js +227 -0
  84. package/lib/Layer/PointCloudLayer.js +405 -0
  85. package/lib/Layer/Potree2Layer.js +171 -0
  86. package/lib/Layer/PotreeLayer.js +72 -0
  87. package/lib/Layer/RasterLayer.js +37 -0
  88. package/lib/Layer/ReferencingLayerProperties.js +62 -0
  89. package/lib/Layer/TiledGeometryLayer.js +459 -0
  90. package/lib/Loader/LASLoader.js +193 -0
  91. package/lib/Loader/Potree2BrotliLoader.js +261 -0
  92. package/lib/Loader/Potree2Loader.js +207 -0
  93. package/lib/Main.js +113 -0
  94. package/lib/MainBundle.js +4 -0
  95. package/lib/Parser/B3dmParser.js +174 -0
  96. package/lib/Parser/CameraCalibrationParser.js +94 -0
  97. package/lib/Parser/GDFParser.js +72 -0
  98. package/lib/Parser/GTXParser.js +75 -0
  99. package/lib/Parser/GeoJsonParser.js +212 -0
  100. package/lib/Parser/GpxParser.js +25 -0
  101. package/lib/Parser/ISGParser.js +71 -0
  102. package/lib/Parser/KMLParser.js +25 -0
  103. package/lib/Parser/LASParser.js +137 -0
  104. package/lib/Parser/MapBoxUrlParser.js +83 -0
  105. package/lib/Parser/PntsParser.js +131 -0
  106. package/lib/Parser/Potree2BinParser.js +92 -0
  107. package/lib/Parser/PotreeBinParser.js +106 -0
  108. package/lib/Parser/PotreeCinParser.js +29 -0
  109. package/lib/Parser/ShapefileParser.js +78 -0
  110. package/lib/Parser/VectorTileParser.js +215 -0
  111. package/lib/Parser/XbilParser.js +120 -0
  112. package/lib/Parser/deprecated/LegacyGLTFLoader.js +1386 -0
  113. package/lib/Parser/iGLTFLoader.js +168 -0
  114. package/lib/Process/3dTilesProcessing.js +304 -0
  115. package/lib/Process/FeatureProcessing.js +76 -0
  116. package/lib/Process/LayeredMaterialNodeProcessing.js +229 -0
  117. package/lib/Process/ObjectRemovalHelper.js +97 -0
  118. package/lib/Process/handlerNodeError.js +23 -0
  119. package/lib/Provider/3dTilesProvider.js +149 -0
  120. package/lib/Provider/DataSourceProvider.js +24 -0
  121. package/lib/Provider/Fetcher.js +233 -0
  122. package/lib/Provider/PointCloudProvider.js +45 -0
  123. package/lib/Provider/TileProvider.js +16 -0
  124. package/lib/Provider/URLBuilder.js +116 -0
  125. package/lib/Renderer/Camera.js +281 -0
  126. package/lib/Renderer/Color.js +56 -0
  127. package/lib/Renderer/ColorLayersOrdering.js +115 -0
  128. package/lib/Renderer/CommonMaterial.js +31 -0
  129. package/lib/Renderer/Label2DRenderer.js +192 -0
  130. package/lib/Renderer/LayeredMaterial.js +243 -0
  131. package/lib/Renderer/OBB.js +150 -0
  132. package/lib/Renderer/OrientedImageCamera.js +118 -0
  133. package/lib/Renderer/OrientedImageMaterial.js +167 -0
  134. package/lib/Renderer/PointsMaterial.js +485 -0
  135. package/lib/Renderer/RasterTile.js +243 -0
  136. package/lib/Renderer/RenderMode.js +31 -0
  137. package/lib/Renderer/Shader/ShaderChunk.js +160 -0
  138. package/lib/Renderer/Shader/ShaderUtils.js +47 -0
  139. package/lib/Renderer/SphereHelper.js +17 -0
  140. package/lib/Renderer/WebXR.js +51 -0
  141. package/lib/Renderer/c3DEngine.js +214 -0
  142. package/lib/Source/C3DTilesGoogleSource.js +74 -0
  143. package/lib/Source/C3DTilesIonSource.js +54 -0
  144. package/lib/Source/C3DTilesSource.js +30 -0
  145. package/lib/Source/CopcSource.js +126 -0
  146. package/lib/Source/EntwinePointTileSource.js +72 -0
  147. package/lib/Source/FileSource.js +188 -0
  148. package/lib/Source/OGC3DTilesGoogleSource.js +29 -0
  149. package/lib/Source/OGC3DTilesIonSource.js +34 -0
  150. package/lib/Source/OGC3DTilesSource.js +21 -0
  151. package/lib/Source/OrientedImageSource.js +59 -0
  152. package/lib/Source/Potree2Source.js +167 -0
  153. package/lib/Source/PotreeSource.js +82 -0
  154. package/lib/Source/Source.js +202 -0
  155. package/lib/Source/TMSSource.js +144 -0
  156. package/lib/Source/VectorTilesSource.js +182 -0
  157. package/lib/Source/WFSSource.js +170 -0
  158. package/lib/Source/WMSSource.js +167 -0
  159. package/lib/Source/WMTSSource.js +92 -0
  160. package/lib/ThreeExtended/capabilities/WebGL.js +69 -0
  161. package/lib/ThreeExtended/libs/ktx-parse.module.js +506 -0
  162. package/lib/ThreeExtended/libs/zstddec.module.js +29 -0
  163. package/lib/ThreeExtended/loaders/DDSLoader.js +200 -0
  164. package/lib/ThreeExtended/loaders/DRACOLoader.js +400 -0
  165. package/lib/ThreeExtended/loaders/GLTFLoader.js +2879 -0
  166. package/lib/ThreeExtended/loaders/KTX2Loader.js +709 -0
  167. package/lib/ThreeExtended/math/ColorSpaces.js +59 -0
  168. package/lib/ThreeExtended/utils/BufferGeometryUtils.js +846 -0
  169. package/lib/ThreeExtended/utils/WorkerPool.js +70 -0
  170. package/lib/Utils/CameraUtils.js +554 -0
  171. package/lib/Utils/DEMUtils.js +350 -0
  172. package/lib/Utils/FeaturesUtils.js +156 -0
  173. package/lib/Utils/Gradients.js +16 -0
  174. package/lib/Utils/ThreeUtils.js +115 -0
  175. package/lib/Utils/gui/C3DTilesStyle.js +218 -0
  176. package/lib/Utils/gui/Main.js +7 -0
  177. package/lib/Utils/gui/Minimap.js +152 -0
  178. package/lib/Utils/gui/Navigation.js +245 -0
  179. package/lib/Utils/gui/Scale.js +104 -0
  180. package/lib/Utils/gui/Searchbar.js +234 -0
  181. package/lib/Utils/gui/Widget.js +80 -0
  182. package/lib/Utils/placeObjectOnGround.js +136 -0
  183. package/lib/Worker/LASLoaderWorker.js +19 -0
  184. package/lib/Worker/Potree2Worker.js +21 -0
  185. 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;