@openglobus/og 0.12.4 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +0 -2
  2. package/dist/@openglobus/og.esm.js +2 -2
  3. package/dist/@openglobus/og.esm.js.map +1 -1
  4. package/dist/@openglobus/og.umd.js +2 -2
  5. package/dist/@openglobus/og.umd.js.map +1 -1
  6. package/package.json +7 -7
  7. package/src/og/Extent.js +3 -3
  8. package/src/og/Globe.js +277 -275
  9. package/src/og/ImageCanvas.js +3 -3
  10. package/src/og/LonLat.js +9 -9
  11. package/src/og/QueueArray.js +2 -2
  12. package/src/og/Rectangle.js +5 -5
  13. package/src/og/bv/Box.js +1 -1
  14. package/src/og/bv/Sphere.js +2 -2
  15. package/src/og/camera/Camera.js +1 -1
  16. package/src/og/camera/PlanetCamera.js +649 -646
  17. package/src/og/control/LayerSwitcher.js +1 -1
  18. package/src/og/control/MouseNavigation.js +22 -13
  19. package/src/og/control/MouseWheelZoomControl.js +2 -2
  20. package/src/og/control/ScaleControl.js +1 -1
  21. package/src/og/control/TouchNavigation.js +319 -319
  22. package/src/og/control/ZoomControl.js +132 -132
  23. package/src/og/control/visibleExtent/Segment.js +12 -12
  24. package/src/og/ellipsoid/Ellipsoid.js +577 -567
  25. package/src/og/entity/BillboardHandler.js +878 -1051
  26. package/src/og/entity/Entity.js +1 -1
  27. package/src/og/entity/EntityCollection.js +847 -857
  28. package/src/og/entity/GeoObjectHandler.js +2 -2
  29. package/src/og/entity/LabelHandler.js +888 -1040
  30. package/src/og/entity/LabelWorker.js +0 -6
  31. package/src/og/entity/PointCloud.js +495 -495
  32. package/src/og/entity/Polyline.js +2218 -2222
  33. package/src/og/entity/RayHandler.js +1 -1
  34. package/src/og/layer/CanvasTiles.js +6 -3
  35. package/src/og/layer/GeoImage.js +1 -1
  36. package/src/og/layer/KML.js +5 -5
  37. package/src/og/layer/Layer.js +800 -752
  38. package/src/og/layer/Vector.js +997 -1010
  39. package/src/og/layer/XYZ.js +359 -357
  40. package/src/og/math/Line2.js +4 -4
  41. package/src/og/math/Quat.js +2 -2
  42. package/src/og/math/Ray.js +2 -2
  43. package/src/og/math/Vec2.js +524 -524
  44. package/src/og/math/Vec3.js +900 -900
  45. package/src/og/math/Vec4.js +261 -261
  46. package/src/og/math.js +397 -398
  47. package/src/og/quadTree/EntityCollectionNode.js +389 -387
  48. package/src/og/renderer/Renderer.js +14 -20
  49. package/src/og/renderer/RendererEvents.js +1034 -1045
  50. package/src/og/scene/Planet.js +12 -9
  51. package/src/og/scene/RenderNode.js +347 -354
  52. package/src/og/segment/Segment.js +3 -1
  53. package/src/og/segment/SegmentLonLat.js +1 -4
  54. package/src/og/shaders/billboard.js +220 -204
  55. package/src/og/shaders/drawnode.js +603 -603
  56. package/src/og/shaders/label.js +596 -456
  57. package/src/og/shaders/polyline.js +365 -361
  58. package/src/og/terrain/MapboxTerrain.js +1 -1
  59. package/src/og/utils/FontAtlas.js +209 -200
  60. package/src/og/utils/GeoImageCreator.js +4 -5
  61. package/src/og/utils/TextureAtlas.js +4 -4
  62. package/src/og/utils/VectorTileCreator.js +414 -414
  63. package/src/og/utils/shared.js +1 -1
  64. package/src/og/webgl/Handler.js +28 -60
  65. package/src/og/webgl/Multisample.js +260 -260
  66. package/src/og/webgl/Program.js +411 -396
  67. package/types/bv/Box.d.ts +1 -1
  68. package/types/bv/Sphere.d.ts +1 -1
  69. package/types/camera/PlanetCamera.d.ts +3 -3
  70. package/types/control/LayerSwitcher.d.ts +1 -1
  71. package/types/ellipsoid/Ellipsoid.d.ts +9 -2
  72. package/types/entity/BillboardHandler.d.ts +0 -4
  73. package/types/entity/EntityCollection.d.ts +0 -8
  74. package/types/entity/LabelHandler.d.ts +0 -2
  75. package/types/entity/Polyline.d.ts +1 -1
  76. package/types/layer/KML.d.ts +11 -11
  77. package/types/layer/Layer.d.ts +4 -0
  78. package/types/layer/Vector.d.ts +1 -8
  79. package/types/math/Line2.d.ts +4 -4
  80. package/types/math/Ray.d.ts +2 -2
  81. package/types/math/Vec2.d.ts +1 -1
  82. package/types/math/Vec3.d.ts +1 -1
  83. package/types/math/Vec4.d.ts +2 -2
  84. package/types/math.d.ts +1 -1
  85. package/types/renderer/Renderer.d.ts +1 -1
  86. package/types/renderer/RendererEvents.d.ts +1 -1
  87. package/types/shaders/label.d.ts +1 -1
  88. package/types/utils/FontAtlas.d.ts +3 -2
  89. package/types/utils/TextureAtlas.d.ts +3 -3
  90. package/types/utils/VectorTileCreator.d.ts +4 -4
  91. package/types/utils/shared.d.ts +1 -1
  92. package/types/webgl/Handler.d.ts +6 -31
  93. package/types/webgl/Program.d.ts +2 -0
@@ -1,568 +1,578 @@
1
- /**
2
- * @module og/ellipsoid/Ellipsoid
3
- */
4
-
5
- "use strict";
6
-
7
- import * as math from "../math.js";
8
- import { Vec3 } from "../math/Vec3.js";
9
- import { LonLat } from "../LonLat.js";
10
-
11
- /**
12
- * Class represents a plant ellipsoid.
13
- * @class
14
- * @param {number} equatorialSize - Equatorial ellipsoid size.
15
- * @param {number} polarSize - Polar ellipsoid size.
16
- */
17
- class Ellipsoid {
18
- /**
19
- * @param {number} equatorialSize - Equatorial ellipsoid size.
20
- * @param {number} polarSize - Polar ellipsoid size.
21
- */
22
- constructor(equatorialSize, polarSize) {
23
- this._a = equatorialSize;
24
- this._b = polarSize;
25
- this._flattening = (equatorialSize - polarSize) / equatorialSize;
26
- this._f = 1 / this._flattening;
27
-
28
- this._a2 = equatorialSize * equatorialSize;
29
- this._b2 = polarSize * polarSize;
30
-
31
- var qa2b2 = Math.sqrt(this._a2 - this._b2);
32
-
33
- this._e = qa2b2 / equatorialSize;
34
- this._e2 = this._e * this._e;
35
- this._e22 = this._e2 * this._e2;
36
-
37
- this._k = qa2b2 / polarSize;
38
- this._k2 = this._k * this._k;
39
-
40
- this._radii = new Vec3(equatorialSize, polarSize, equatorialSize);
41
- this._radii2 = new Vec3(this._a2, this._b2, this._a2);
42
- this._invRadii = new Vec3(1.0 / equatorialSize, 1.0 / polarSize, 1.0 / equatorialSize);
43
- this._invRadii2 = new Vec3(1.0 / this._a2, 1.0 / this._b2, 1.0 / this._a2);
44
- }
45
-
46
- static getRelativeBearing(a, b) {
47
- let a_y = Math.cos(a),
48
- a_x = Math.sin(a),
49
- b_y = Math.cos(b),
50
- b_x = Math.sin(b);
51
- let c = a_y * b_x - b_y * a_x,
52
- d = a_x * b_x + a_y * b_y;
53
- if (c > 0.0) {
54
- return Math.acos(d);
55
- }
56
- return -Math.acos(d);
57
- }
58
-
59
- /**
60
- * Returns the midpoint between two points on the great circle.
61
- * @param {LonLat} lonLat1 - Longitude/latitude of first point.
62
- * @param {LonLat} lonLat2 - Longitude/latitude of second point.
63
- * @return {LonLat} Midpoint between points.
64
- */
65
- static getMiddlePointOnGreatCircle(lonLat1, lonLat2) {
66
- var f1 = lonLat1.lat * math.RADIANS,
67
- l1 = lonLat1.lon * math.RADIANS;
68
- var f2 = lonLat2.lat * math.RADIANS;
69
- var dl = (lonLat2.lon - lonLat1.lon) * math.RADIANS;
70
-
71
- var Bx = Math.cos(f2) * Math.cos(dl);
72
- var By = Math.cos(f2) * Math.sin(dl);
73
-
74
- var x = Math.sqrt((Math.cos(f1) + Bx) * (Math.cos(f1) + Bx) + By * By);
75
- var y = Math.sin(f1) + Math.sin(f2);
76
- var f3 = Math.atan2(y, x);
77
-
78
- var l3 = l1 + Math.atan2(By, Math.cos(f1) + Bx);
79
-
80
- return new LonLat(((l3 * math.DEGREES + 540) % 360) - 180, f3 * math.DEGREES);
81
- }
82
-
83
- /**
84
- * Returns the point at given fraction between two points on the great circle.
85
- * @param {LonLat} lonLat1 - Longitude/Latitude of source point.
86
- * @param {LonLat} lonLat2 - Longitude/Latitude of destination point.
87
- * @param {number} fraction - Fraction between the two points (0 = source point, 1 = destination point).
88
- * @returns {LonLat} Intermediate point between points.
89
- */
90
- static getIntermediatePointOnGreatCircle(lonLat1, lonLat2, fraction) {
91
- var f1 = lonLat1.lat * math.RADIANS,
92
- l1 = lonLat1.lon * math.RADIANS;
93
- var f2 = lonLat2.lat * math.RADIANS,
94
- l2 = lonLat2.lon * math.RADIANS;
95
-
96
- var sinf1 = Math.sin(f1),
97
- cosf1 = Math.cos(f1),
98
- sinl1 = Math.sin(l1),
99
- cosl1 = Math.cos(l1);
100
- var sinf2 = Math.sin(f2),
101
- cosf2 = Math.cos(f2),
102
- sinl2 = Math.sin(l2),
103
- cosl2 = Math.cos(l2);
104
-
105
- var df = f2 - f1,
106
- dl = l2 - l1;
107
- var a =
108
- Math.sin(df / 2) * Math.sin(df / 2) +
109
- Math.cos(f1) * Math.cos(f2) * Math.sin(dl / 2) * Math.sin(dl / 2);
110
- var d = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
111
-
112
- var A = Math.sin((1 - fraction) * d) / Math.sin(d);
113
- var B = Math.sin(fraction * d) / Math.sin(d);
114
-
115
- var x = A * cosf1 * cosl1 + B * cosf2 * cosl2;
116
- var y = A * cosf1 * sinl1 + B * cosf2 * sinl2;
117
- var z = A * sinf1 + B * sinf2;
118
-
119
- var f3 = Math.atan2(z, Math.sqrt(x * x + y * y));
120
- var l3 = Math.atan2(y, x);
121
-
122
- return new LonLat(((l3 * math.DEGREES + 540) % 360) - 180, f3 * math.DEGREES);
123
- }
124
-
125
- static getRhumbBearing(lonLat1, lonLat2) {
126
- var dLon = (lonLat2.lon - lonLat1.lon) * math.RADIANS;
127
- var dPhi = Math.log(
128
- Math.tan((lonLat2.lat * math.RADIANS) / 2 + Math.PI / 4) /
129
- Math.tan((lonLat1.lat * math.RADIANS) / 2 + Math.PI / 4)
130
- );
131
- if (Math.abs(dLon) > Math.PI) {
132
- if (dLon > 0) {
133
- dLon = (2 * Math.PI - dLon) * -1;
134
- } else {
135
- dLon = 2 * Math.PI + dLon;
136
- }
137
- }
138
- return (Math.atan2(dLon, dPhi) * math.DEGREES + 360) % 360;
139
- }
140
-
141
- static getBearing(lonLat1, lonLat2) {
142
- var f1 = lonLat1.lat * math.RADIANS,
143
- l1 = lonLat1.lon * math.RADIANS;
144
- var f2 = lonLat2.lat * math.RADIANS,
145
- l2 = lonLat2.lon * math.RADIANS;
146
- var y = Math.sin(l2 - l1) * Math.cos(f2);
147
- var x = Math.cos(f1) * Math.sin(f2) - Math.sin(f1) * Math.cos(f2) * Math.cos(l2 - l1);
148
- return Math.atan2(y, x) * math.DEGREES;
149
- }
150
-
151
- /**
152
- * Returns the (initial) bearing from source to destination point on the great circle.
153
- * @param {LonLat} lonLat1 - Longitude/latitude of source point.
154
- * @param {LonLat} lonLat2 - Longitude/latitude of destination point.
155
- * @return {number} Initial bearing in degrees from north.
156
- */
157
- static getInitialBearing(lonLat1, lonLat2) {
158
- var f1 = lonLat1.lat * math.RADIANS,
159
- f2 = lonLat2.lat * math.RADIANS;
160
- var dl = (lonLat2.lon - lonLat1.lon) * math.RADIANS;
161
- var y = Math.sin(dl) * Math.cos(f2);
162
- var x = Math.cos(f1) * Math.sin(f2) - Math.sin(f1) * Math.cos(f2) * Math.cos(dl);
163
- var D = Math.atan2(y, x);
164
- return (D * math.DEGREES + 360) % 360;
165
- }
166
-
167
- /**
168
- * Returns the point of intersection of two paths defined by point and bearing.
169
- * @param {LonLat} p1 - First point.
170
- * @param {number} brng1 - Initial bearing from first point.
171
- * @param {LonLat} p2 - Second point.
172
- * @param {number} brng2 - Initial bearing from second point.
173
- * @return {LonLat|null} Destination point (null if no unique intersection defined).
174
- */
175
- static intersection(p1, brng1, p2, brng2) {
176
- var f1 = p1.lat * math.RADIANS,
177
- l1 = p1.lon * math.RADIANS;
178
- var f2 = p2.lat * math.RADIANS,
179
- l2 = p2.lon * math.RADIANS;
180
- var D13 = brng1 * math.RADIANS,
181
- D23 = brng2 * math.RADIANS;
182
- var df = f2 - f1,
183
- dl = l2 - l1;
184
-
185
- var d12 =
186
- 2 *
187
- Math.asin(
188
- Math.sqrt(
189
- Math.sin(df / 2) * Math.sin(df / 2) +
190
- Math.cos(f1) * Math.cos(f2) * Math.sin(dl / 2) * Math.sin(dl / 2)
191
- )
192
- );
193
- if (d12 == 0) return null;
194
-
195
- // initial/final bearings between points
196
- var Da = Math.acos(
197
- (Math.sin(f2) - Math.sin(f1) * Math.cos(d12)) / (Math.sin(d12) * Math.cos(f1))
198
- );
199
- if (isNaN(Da)) Da = 0; // protect against rounding
200
- var Db = Math.acos(
201
- (Math.sin(f1) - Math.sin(f2) * Math.cos(d12)) / (Math.sin(d12) * Math.cos(f2))
202
- );
203
-
204
- var D12 = Math.sin(l2 - l1) > 0 ? Da : 2 * Math.PI - Da;
205
- var D21 = Math.sin(l2 - l1) > 0 ? 2 * Math.PI - Db : Db;
206
-
207
- var a1 = ((D13 - D12 + Math.PI) % (2 * Math.PI)) - Math.PI;
208
- var a2 = ((D21 - D23 + Math.PI) % (2 * Math.PI)) - Math.PI;
209
-
210
- if (Math.sin(a1) == 0 && Math.sin(a2) == 0) return null; // infinite intersections
211
- if (Math.sin(a1) * Math.sin(a2) < 0) return null; // ambiguous intersection
212
-
213
- // a1 = Math.abs(a1);
214
- // a2 = Math.abs(a2);
215
- // ... Ed Williams takes abs of a1/a2, but seems to break calculation?
216
-
217
- var a3 = Math.acos(
218
- -Math.cos(a1) * Math.cos(a2) + Math.sin(a1) * Math.sin(a2) * Math.cos(d12)
219
- );
220
- var d13 = Math.atan2(
221
- Math.sin(d12) * Math.sin(a1) * Math.sin(a2),
222
- Math.cos(a2) + Math.cos(a1) * Math.cos(a3)
223
- );
224
- var f3 = Math.asin(
225
- Math.sin(f1) * Math.cos(d13) + Math.cos(f1) * Math.sin(d13) * Math.cos(D13)
226
- );
227
- var dl13 = Math.atan2(
228
- Math.sin(D13) * Math.sin(d13) * Math.cos(f1),
229
- Math.cos(d13) - Math.sin(f1) * Math.sin(f3)
230
- );
231
- var l3 = l1 + dl13;
232
-
233
- return new LonLat(((l3 * math.DEGREES + 540) % 360) - 180, f3 * math.DEGREES);
234
- }
235
-
236
- /**
237
- * Returns final bearing arriving at destination destination point from lonLat1 point; the final bearing
238
- * will differ from the initial bearing by varying degrees according to distance and latitude.
239
- * @param {LonLat} lonLat1 - Longitude/latitude of source point.
240
- * @param {LonLat} lonLat2 - Longitude/latitude of destination point.
241
- * @return {number} Final bearing in degrees from north.
242
- */
243
- static getFinalBearing(lonLat1, lonLat2) {
244
- // get initial bearing from destination lonLat2 to lonLat1 & reverse it by adding 180°
245
- return (Ellipsoid.getInitialBearing(lonLat2, lonLat1) + 180) % 360;
246
- }
247
-
248
- /**
249
- * Gets ellipsoid equatorial size.
250
- * @public
251
- * @returns {number} -
252
- */
253
- getEquatorialSize() {
254
- return this._a;
255
- }
256
-
257
- /**
258
- * Gets ellipsoid polar size.
259
- * @public
260
- * @returns {number} -
261
- */
262
- getPolarSize() {
263
- return this._b;
264
- }
265
-
266
- /**
267
- * Gets cartesian ECEF from Wgs84 geodetic coordiantes.
268
- * @public
269
- * @param {LonLat} lonlat - Degrees geodetic coordiantes.
270
- * @returns {Vec3} -
271
- */
272
- lonLatToCartesian(lonlat) {
273
- var latrad = math.RADIANS * lonlat.lat,
274
- lonrad = math.RADIANS * lonlat.lon;
275
-
276
- var slt = Math.sin(latrad);
277
-
278
- var N = this._a / Math.sqrt(1.0 - this._e2 * slt * slt);
279
- var nc = (N + lonlat.height) * Math.cos(latrad);
280
-
281
- return new Vec3(
282
- nc * Math.sin(lonrad),
283
- (N * (1.0 - this._e2) + lonlat.height) * slt,
284
- nc * Math.cos(lonrad)
285
- );
286
- }
287
-
288
- /**
289
- * Gets cartesian ECEF from Wgs84 geodetic coordiantes.
290
- * @public
291
- * @param {LonLat} lonlat - Degrees geodetic coordiantes.
292
- * @param {Vec3} res - Output result.
293
- * @returns {Vec3} -
294
- */
295
- lonLatToCartesianRes(lonlat, res) {
296
- var latrad = math.RADIANS * lonlat.lat,
297
- lonrad = math.RADIANS * lonlat.lon;
298
-
299
- var slt = Math.sin(latrad);
300
-
301
- var N = this._a / Math.sqrt(1.0 - this._e2 * slt * slt);
302
- var nc = (N + lonlat.height) * Math.cos(latrad);
303
-
304
- res.x = nc * Math.sin(lonrad);
305
- res.y = (N * (1.0 - this._e2) + lonlat.height) * slt;
306
- res.z = nc * Math.cos(lonrad);
307
-
308
- return res;
309
- }
310
-
311
- /**
312
- * Gets cartesian ECEF from Wgs84 geodetic coordiantes.
313
- * @public
314
- * @param {Number} lon - Longitude.
315
- * @param {Number} lat - Latitude.
316
- * @param {Number} height - Height.
317
- * @returns {Vec3} -
318
- */
319
- geodeticToCartesian(lon, lat, height = 0) {
320
- var latrad = math.RADIANS * lat,
321
- lonrad = math.RADIANS * lon;
322
-
323
- var slt = Math.sin(latrad);
324
-
325
- var N = this._a / Math.sqrt(1 - this._e2 * slt * slt);
326
- var nc = (N + height) * Math.cos(latrad);
327
-
328
- return new Vec3(
329
- nc * Math.sin(lonrad),
330
- (N * (1 - this._e2) + height) * slt,
331
- nc * Math.cos(lonrad)
332
- );
333
- }
334
-
335
- /**
336
- * Gets Wgs84 geodetic coordiantes from cartesian ECEF.
337
- * @public
338
- * @param {Vec3} cartesian - Cartesian coordinates.
339
- * @returns {LonLat} -
340
- */
341
- cartesianToLonLat(cartesian) {
342
- var x = cartesian.z,
343
- y = cartesian.x,
344
- z = cartesian.y;
345
- var ecc2 = this._e2;
346
- var ecc22 = this._e22;
347
- var r2 = x * x + y * y;
348
- var r = Math.sqrt(r2);
349
- var z2 = z * z;
350
- var f = 54.0 * this._b2 * z2;
351
- var g = r2 + (1.0 - ecc2) * z2 + ecc2 * (this._a2 - this._b2);
352
- var g2 = g * g;
353
- var c = (ecc22 * f * r2) / (g2 * g);
354
- var s = Math.pow(1.0 + c + Math.sqrt(c * (c + 2.0)), 0.33333333333333333);
355
- var p = f / (3.0 * Math.pow(1.0 + s + 1.0 / s, 2.0) * g2);
356
- var q = Math.sqrt(1.0 + 2.0 * ecc22 * p);
357
- var recc2r0 =
358
- r -
359
- ecc2 *
360
- (-(p * ecc2 * r) / 1 +
361
- q +
362
- Math.sqrt(
363
- 0.5 * this._a2 * (1.0 + 1.0 / q) -
364
- (p * (1.0 - ecc2) * z2) / (q * (1.0 + q)) -
365
- 0.5 * p * r2
366
- ));
367
- var recc2r02 = recc2r0 * recc2r0;
368
- var v = Math.sqrt(recc2r02 + (1.0 - ecc2) * z2);
369
- var z0 = (this._b2 * z) / (this._a * v);
370
- var lat = Math.atan((z + this._k2 * z0) / r) * math.DEGREES;
371
- var lon = Math.atan2(y, x) * math.DEGREES;
372
- return new LonLat(
373
- lon,
374
- lat,
375
- cartesian.length() - this.geodeticToCartesian(lon, lat).length()
376
- );
377
- }
378
-
379
- /**
380
- * Gets ellipsoid surface normal.
381
- * @public
382
- * @param {Vec3} coord - Spatial coordiantes.
383
- * @returns {Vec3} -
384
- */
385
- getSurfaceNormal3v(coord) {
386
- var r2 = this._invRadii2;
387
- var nx = coord.x * r2.x,
388
- ny = coord.y * r2.y,
389
- nz = coord.z * r2.z;
390
- var l = 1.0 / Math.sqrt(nx * nx + ny * ny + nz * nz);
391
- return new Vec3(nx * l, ny * l, nz * l);
392
- }
393
-
394
- getBearingDestination(lonLat1, bearing, distance) {
395
- bearing = bearing * math.RADIANS;
396
- var nlon = ((lonLat1.lon + 540) % 360) - 180;
397
- var f1 = lonLat1.lat * math.RADIANS,
398
- l1 = nlon * math.RADIANS;
399
- var dR = distance / this._a;
400
- var f2 = Math.asin(
401
- Math.sin(f1) * Math.cos(dR) + Math.cos(f1) * Math.sin(dR) * Math.cos(bearing)
402
- );
403
- return new LonLat(
404
- (l1 +
405
- Math.atan2(
406
- Math.sin(bearing) * Math.sin(dR) * Math.cos(f1),
407
- Math.cos(dR) - Math.sin(f1) * Math.sin(f2)
408
- )) *
409
- math.DEGREES,
410
- f2 * math.DEGREES
411
- );
412
- }
413
-
414
- /**
415
- * Returns the distance from one point to another(using haversine formula) on the great circle.
416
- * @param {LonLat} lonLat1 - Longitude/latitude of source point.
417
- * @param {LonLat} lonLat2 - Longitude/latitude of destination point.
418
- * @return {number} Distance between points.
419
- */
420
- getGreatCircleDistance(lonLat1, lonLat2) {
421
- var dLat = (lonLat2.lat - lonLat1.lat) * math.RADIANS;
422
- var dLon = (lonLat2.lon - lonLat1.lon) * math.RADIANS;
423
- var a =
424
- Math.sin(dLat / 2.0) * Math.sin(dLat / 2.0) +
425
- Math.sin(dLon / 2.0) *
426
- Math.sin(dLon / 2) *
427
- Math.cos(lonLat1.lat * math.RADIANS) *
428
- Math.cos(lonLat2.lat * math.RADIANS);
429
- return this._a * 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1.0 - a));
430
- }
431
-
432
- /**
433
- * Calculates the destination point given start point lat / lon, bearing(deg) and distance (m).
434
- *
435
- * Taken from http://movable-type.co.uk/scripts/latlong-vincenty-direct.html and optimized / cleaned up by Mathias Bynens <http://mathiasbynens.be/>
436
- * Based on the Vincenty direct formula by T. Vincenty, “Direct and Inverse Solutions of Geodesics on the Ellipsoid with application of nested equations”, Survey Review, vol XXII no 176, 1975 <http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf>
437
- */
438
- getGreatCircleDestination(lonLat, brng, dist) {
439
- var lon1 = lonLat.lon,
440
- lat1 = lonLat.lat;
441
- var a = this._a,
442
- b = this._b,
443
- f = 1.0 / this._f,
444
- s = dist,
445
- alpha1 = brng * math.RADIANS,
446
- sinAlpha1 = Math.sin(alpha1),
447
- cosAlpha1 = Math.cos(alpha1),
448
- tanU1 = (1 - f) * Math.tan(lat1 * math.RADIANS),
449
- cosU1 = 1 / Math.sqrt(1 + tanU1 * tanU1),
450
- sinU1 = tanU1 * cosU1,
451
- sigma1 = Math.atan2(tanU1, cosAlpha1),
452
- sinAlpha = cosU1 * sinAlpha1,
453
- cosSqAlpha = 1 - sinAlpha * sinAlpha,
454
- uSq = (cosSqAlpha * (a * a - b * b)) / (b * b),
455
- A = 1 + (uSq / 16384) * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq))),
456
- B = (uSq / 1024) * (256 + uSq * (-128 + uSq * (74 - 47 * uSq))),
457
- sigma = s / (b * A),
458
- sigmaP = 2 * Math.PI;
459
- while (Math.abs(sigma - sigmaP) > 1e-12) {
460
- var cos2SigmaM = Math.cos(2 * sigma1 + sigma),
461
- sinSigma = Math.sin(sigma),
462
- cosSigma = Math.cos(sigma),
463
- deltaSigma =
464
- B *
465
- sinSigma *
466
- (cos2SigmaM +
467
- (B / 4) *
468
- (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) -
469
- (B / 6) *
470
- cos2SigmaM *
471
- (-3 + 4 * sinSigma * sinSigma) *
472
- (-3 + 4 * cos2SigmaM * cos2SigmaM)));
473
- sigmaP = sigma;
474
- sigma = s / (b * A) + deltaSigma;
475
- }
476
- var tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1,
477
- lat2 = Math.atan2(
478
- sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
479
- (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp)
480
- ),
481
- lambda = Math.atan2(
482
- sinSigma * sinAlpha1,
483
- cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1
484
- ),
485
- C = (f / 16) * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha)),
486
- L =
487
- lambda -
488
- (1 - C) *
489
- f *
490
- sinAlpha *
491
- (sigma +
492
- C *
493
- sinSigma *
494
- (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))),
495
- revAz = Math.atan2(sinAlpha, -tmp); // final bearing
496
- return new LonLat(lon1 + L * math.DEGREES, lat2 * math.DEGREES);
497
- }
498
-
499
- /**
500
- * Returns ray vector hit ellipsoid coordinates.
501
- * If the ray doesn't hit ellipsoid returns null.
502
- * @public
503
- * @param {Vec3} origin - Ray origin point.
504
- * @param {Vec3} direction - Ray direction.
505
- * @returns {Vec3} -
506
- */
507
- hitRay(origin, direction) {
508
- var q = this._invRadii.mul(origin);
509
- var w = this._invRadii.mul(direction);
510
-
511
- var q2 = q.dot(q);
512
- var qw = q.dot(w);
513
-
514
- var difference, w2, product, discriminant, temp;
515
-
516
- if (q2 > 1.0) {
517
- // Outside ellipsoid.
518
- if (qw >= 0.0) {
519
- // Looking outward or tangent (0 intersections).
520
- return null;
521
- }
522
-
523
- // qw < 0.0.
524
- var qw2 = qw * qw;
525
- difference = q2 - 1.0; // Positively valued.
526
- w2 = w.dot(w);
527
- product = w2 * difference;
528
-
529
- if (qw2 < product) {
530
- // Imaginary roots (0 intersections).
531
- return null;
532
- } else if (qw2 > product) {
533
- // Distinct roots (2 intersections).
534
- discriminant = qw * qw - product;
535
- temp = -qw + Math.sqrt(discriminant); // Avoid cancellation.
536
- var root0 = temp / w2;
537
- var root1 = difference / temp;
538
- if (root0 < root1) {
539
- return origin.add(direction.scaleTo(root0));
540
- }
541
- return origin.add(direction.scaleTo(root1));
542
- } else {
543
- // qw2 == product. Repeated roots (2 intersections).
544
- var root = Math.sqrt(difference / w2);
545
- return origin.add(direction.scaleTo(root));
546
- }
547
- } else if (q2 < 1.0) {
548
- // Inside ellipsoid (2 intersections).
549
- difference = q2 - 1.0; // Negatively valued.
550
- w2 = w.dot(w);
551
- product = w2 * difference; // Negatively valued.
552
- discriminant = qw * qw - product;
553
- temp = -qw + Math.sqrt(discriminant); // Positively valued.
554
- return origin.add(direction.scaleTo(temp / w2));
555
- } else {
556
- // q2 == 1.0. On ellipsoid.
557
- if (qw < 0.0) {
558
- // Looking inward.
559
- w2 = w.dot(w);
560
- return origin.add(direction.scaleTo(-qw / w2));
561
- }
562
- // qw >= 0.0. Looking outward or tangent.
563
- return null;
564
- }
1
+ /**
2
+ * @module og/ellipsoid/Ellipsoid
3
+ */
4
+
5
+ "use strict";
6
+
7
+ import * as math from "../math.js";
8
+ import { Vec3 } from "../math/Vec3.js";
9
+ import { LonLat } from "../LonLat.js";
10
+ import { EPSILON10 } from "../math.js";
11
+
12
+ /**
13
+ * Class represents a plant ellipsoid.
14
+ * @class
15
+ * @param {number} equatorialSize - Equatorial ellipsoid size.
16
+ * @param {number} polarSize - Polar ellipsoid size.
17
+ */
18
+ class Ellipsoid {
19
+ /**
20
+ * @param {number} equatorialSize - Equatorial ellipsoid size.
21
+ * @param {number} polarSize - Polar ellipsoid size.
22
+ */
23
+ constructor(equatorialSize, polarSize) {
24
+ this._a = equatorialSize;
25
+ this._b = polarSize;
26
+ this._flattening = (equatorialSize - polarSize) / equatorialSize;
27
+ this._f = 1 / this._flattening;
28
+
29
+ this._a2 = equatorialSize * equatorialSize;
30
+ this._b2 = polarSize * polarSize;
31
+
32
+ var qa2b2 = Math.sqrt(this._a2 - this._b2);
33
+
34
+ this._e = qa2b2 / equatorialSize;
35
+ this._e2 = this._e * this._e;
36
+ this._e22 = this._e2 * this._e2;
37
+
38
+ this._k = qa2b2 / polarSize;
39
+ this._k2 = this._k * this._k;
40
+
41
+ this._radii = new Vec3(equatorialSize, polarSize, equatorialSize);
42
+ this._radii2 = new Vec3(this._a2, this._b2, this._a2);
43
+ this._invRadii = new Vec3(1.0 / equatorialSize, 1.0 / polarSize, 1.0 / equatorialSize);
44
+ this._invRadii2 = new Vec3(1.0 / this._a2, 1.0 / this._b2, 1.0 / this._a2);
565
45
  }
566
- }
567
-
568
- export { Ellipsoid };
46
+
47
+ static getRelativeBearing(a, b) {
48
+ let a_y = Math.cos(a),
49
+ a_x = Math.sin(a),
50
+ b_y = Math.cos(b),
51
+ b_x = Math.sin(b);
52
+ let c = a_y * b_x - b_y * a_x,
53
+ d = a_x * b_x + a_y * b_y;
54
+ if (c > 0.0) {
55
+ return Math.acos(d);
56
+ }
57
+ return -Math.acos(d);
58
+ }
59
+
60
+ /**
61
+ * Returns the midpoint between two points on the great circle.
62
+ * @param {LonLat} lonLat1 - Longitude/latitude of first point.
63
+ * @param {LonLat} lonLat2 - Longitude/latitude of second point.
64
+ * @return {LonLat} Midpoint between points.
65
+ */
66
+ static getMiddlePointOnGreatCircle(lonLat1, lonLat2) {
67
+ var f1 = lonLat1.lat * math.RADIANS,
68
+ l1 = lonLat1.lon * math.RADIANS;
69
+ var f2 = lonLat2.lat * math.RADIANS;
70
+ var dl = (lonLat2.lon - lonLat1.lon) * math.RADIANS;
71
+
72
+ var Bx = Math.cos(f2) * Math.cos(dl);
73
+ var By = Math.cos(f2) * Math.sin(dl);
74
+
75
+ var x = Math.sqrt((Math.cos(f1) + Bx) * (Math.cos(f1) + Bx) + By * By);
76
+ var y = Math.sin(f1) + Math.sin(f2);
77
+ var f3 = Math.atan2(y, x);
78
+
79
+ var l3 = l1 + Math.atan2(By, Math.cos(f1) + Bx);
80
+
81
+ return new LonLat(((l3 * math.DEGREES + 540) % 360) - 180, f3 * math.DEGREES);
82
+ }
83
+
84
+ /**
85
+ * Returns the point at given fraction between two points on the great circle.
86
+ * @param {LonLat} lonLat1 - Longitude/Latitude of source point.
87
+ * @param {LonLat} lonLat2 - Longitude/Latitude of destination point.
88
+ * @param {number} fraction - Fraction between the two points (0 = source point, 1 = destination point).
89
+ * @returns {LonLat} Intermediate point between points.
90
+ */
91
+ static getIntermediatePointOnGreatCircle(lonLat1, lonLat2, fraction) {
92
+ var f1 = lonLat1.lat * math.RADIANS,
93
+ l1 = lonLat1.lon * math.RADIANS;
94
+ var f2 = lonLat2.lat * math.RADIANS,
95
+ l2 = lonLat2.lon * math.RADIANS;
96
+
97
+ var sinf1 = Math.sin(f1),
98
+ cosf1 = Math.cos(f1),
99
+ sinl1 = Math.sin(l1),
100
+ cosl1 = Math.cos(l1);
101
+ var sinf2 = Math.sin(f2),
102
+ cosf2 = Math.cos(f2),
103
+ sinl2 = Math.sin(l2),
104
+ cosl2 = Math.cos(l2);
105
+
106
+ var df = f2 - f1,
107
+ dl = l2 - l1;
108
+ var a =
109
+ Math.sin(df / 2) * Math.sin(df / 2) +
110
+ Math.cos(f1) * Math.cos(f2) * Math.sin(dl / 2) * Math.sin(dl / 2);
111
+ var d = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
112
+
113
+ var A = Math.sin((1 - fraction) * d) / Math.sin(d);
114
+ var B = Math.sin(fraction * d) / Math.sin(d);
115
+
116
+ var x = A * cosf1 * cosl1 + B * cosf2 * cosl2;
117
+ var y = A * cosf1 * sinl1 + B * cosf2 * sinl2;
118
+ var z = A * sinf1 + B * sinf2;
119
+
120
+ var f3 = Math.atan2(z, Math.sqrt(x * x + y * y));
121
+ var l3 = Math.atan2(y, x);
122
+
123
+ return new LonLat(((l3 * math.DEGREES + 540) % 360) - 180, f3 * math.DEGREES);
124
+ }
125
+
126
+ static getRhumbBearing(lonLat1, lonLat2) {
127
+ var dLon = (lonLat2.lon - lonLat1.lon) * math.RADIANS;
128
+ var dPhi = Math.log(
129
+ Math.tan((lonLat2.lat * math.RADIANS) / 2 + Math.PI / 4) /
130
+ Math.tan((lonLat1.lat * math.RADIANS) / 2 + Math.PI / 4)
131
+ );
132
+ if (Math.abs(dLon) > Math.PI) {
133
+ if (dLon > 0) {
134
+ dLon = (2 * Math.PI - dLon) * -1;
135
+ } else {
136
+ dLon = 2 * Math.PI + dLon;
137
+ }
138
+ }
139
+ return (Math.atan2(dLon, dPhi) * math.DEGREES + 360) % 360;
140
+ }
141
+
142
+ static getBearing(lonLat1, lonLat2) {
143
+ var f1 = lonLat1.lat * math.RADIANS,
144
+ l1 = lonLat1.lon * math.RADIANS;
145
+ var f2 = lonLat2.lat * math.RADIANS,
146
+ l2 = lonLat2.lon * math.RADIANS;
147
+ var y = Math.sin(l2 - l1) * Math.cos(f2);
148
+ var x = Math.cos(f1) * Math.sin(f2) - Math.sin(f1) * Math.cos(f2) * Math.cos(l2 - l1);
149
+ return Math.atan2(y, x) * math.DEGREES;
150
+ }
151
+
152
+ /**
153
+ * Returns the (initial) bearing from source to destination point on the great circle.
154
+ * @param {LonLat} lonLat1 - Longitude/latitude of source point.
155
+ * @param {LonLat} lonLat2 - Longitude/latitude of destination point.
156
+ * @return {number} Initial bearing in degrees from north.
157
+ */
158
+ static getInitialBearing(lonLat1, lonLat2) {
159
+ var f1 = lonLat1.lat * math.RADIANS,
160
+ f2 = lonLat2.lat * math.RADIANS;
161
+ var dl = (lonLat2.lon - lonLat1.lon) * math.RADIANS;
162
+ var y = Math.sin(dl) * Math.cos(f2);
163
+ var x = Math.cos(f1) * Math.sin(f2) - Math.sin(f1) * Math.cos(f2) * Math.cos(dl);
164
+ var D = Math.atan2(y, x);
165
+ return (D * math.DEGREES + 360) % 360;
166
+ }
167
+
168
+ /**
169
+ * Returns the point of intersection of two paths defined by point and bearing.
170
+ * @param {LonLat} p1 - First point.
171
+ * @param {number} brng1 - Initial bearing from first point.
172
+ * @param {LonLat} p2 - Second point.
173
+ * @param {number} brng2 - Initial bearing from second point.
174
+ * @return {LonLat|null} Destination point (null if no unique intersection defined).
175
+ */
176
+ static intersection(p1, brng1, p2, brng2) {
177
+ var f1 = p1.lat * math.RADIANS,
178
+ l1 = p1.lon * math.RADIANS;
179
+ var f2 = p2.lat * math.RADIANS,
180
+ l2 = p2.lon * math.RADIANS;
181
+ var D13 = brng1 * math.RADIANS,
182
+ D23 = brng2 * math.RADIANS;
183
+ var df = f2 - f1,
184
+ dl = l2 - l1;
185
+
186
+ var d12 =
187
+ 2 *
188
+ Math.asin(
189
+ Math.sqrt(
190
+ Math.sin(df / 2) * Math.sin(df / 2) +
191
+ Math.cos(f1) * Math.cos(f2) * Math.sin(dl / 2) * Math.sin(dl / 2)
192
+ )
193
+ );
194
+ if (d12 == 0) return null;
195
+
196
+ // initial/final bearings between points
197
+ var Da = Math.acos(
198
+ (Math.sin(f2) - Math.sin(f1) * Math.cos(d12)) / (Math.sin(d12) * Math.cos(f1))
199
+ );
200
+ if (isNaN(Da)) Da = 0; // protect against rounding
201
+ var Db = Math.acos(
202
+ (Math.sin(f1) - Math.sin(f2) * Math.cos(d12)) / (Math.sin(d12) * Math.cos(f2))
203
+ );
204
+
205
+ var D12 = Math.sin(l2 - l1) > 0 ? Da : 2 * Math.PI - Da;
206
+ var D21 = Math.sin(l2 - l1) > 0 ? 2 * Math.PI - Db : Db;
207
+
208
+ var a1 = ((D13 - D12 + Math.PI) % (2 * Math.PI)) - Math.PI;
209
+ var a2 = ((D21 - D23 + Math.PI) % (2 * Math.PI)) - Math.PI;
210
+
211
+ if (Math.sin(a1) == 0 && Math.sin(a2) == 0) return null; // infinite intersections
212
+ if (Math.sin(a1) * Math.sin(a2) < 0) return null; // ambiguous intersection
213
+
214
+ // a1 = Math.abs(a1);
215
+ // a2 = Math.abs(a2);
216
+ // ... Ed Williams takes abs of a1/a2, but seems to break calculation?
217
+
218
+ var a3 = Math.acos(
219
+ -Math.cos(a1) * Math.cos(a2) + Math.sin(a1) * Math.sin(a2) * Math.cos(d12)
220
+ );
221
+ var d13 = Math.atan2(
222
+ Math.sin(d12) * Math.sin(a1) * Math.sin(a2),
223
+ Math.cos(a2) + Math.cos(a1) * Math.cos(a3)
224
+ );
225
+ var f3 = Math.asin(
226
+ Math.sin(f1) * Math.cos(d13) + Math.cos(f1) * Math.sin(d13) * Math.cos(D13)
227
+ );
228
+ var dl13 = Math.atan2(
229
+ Math.sin(D13) * Math.sin(d13) * Math.cos(f1),
230
+ Math.cos(d13) - Math.sin(f1) * Math.sin(f3)
231
+ );
232
+ var l3 = l1 + dl13;
233
+
234
+ return new LonLat(((l3 * math.DEGREES + 540) % 360) - 180, f3 * math.DEGREES);
235
+ }
236
+
237
+ /**
238
+ * Returns final bearing arriving at destination destination point from lonLat1 point; the final bearing
239
+ * will differ from the initial bearing by varying degrees according to distance and latitude.
240
+ * @param {LonLat} lonLat1 - Longitude/latitude of source point.
241
+ * @param {LonLat} lonLat2 - Longitude/latitude of destination point.
242
+ * @return {number} Final bearing in degrees from north.
243
+ */
244
+ static getFinalBearing(lonLat1, lonLat2) {
245
+ // get initial bearing from destination lonLat2 to lonLat1 & reverse it by adding 180°
246
+ return (Ellipsoid.getInitialBearing(lonLat2, lonLat1) + 180) % 360;
247
+ }
248
+
249
+ /**
250
+ * Gets ellipsoid equatorial size.
251
+ * @public
252
+ * @returns {number} -
253
+ */
254
+ getEquatorialSize() {
255
+ return this._a;
256
+ }
257
+
258
+ /**
259
+ * Gets ellipsoid polar size.
260
+ * @public
261
+ * @returns {number} -
262
+ */
263
+ getPolarSize() {
264
+ return this._b;
265
+ }
266
+
267
+ /**
268
+ * Gets cartesian ECEF from Wgs84 geodetic coordiantes.
269
+ * @public
270
+ * @param {LonLat} lonlat - Degrees geodetic coordiantes.
271
+ * @returns {Vec3} -
272
+ */
273
+ lonLatToCartesian(lonlat) {
274
+ var latrad = math.RADIANS * lonlat.lat,
275
+ lonrad = math.RADIANS * lonlat.lon;
276
+
277
+ var slt = Math.sin(latrad);
278
+
279
+ var N = this._a / Math.sqrt(1.0 - this._e2 * slt * slt);
280
+ var nc = (N + lonlat.height) * Math.cos(latrad);
281
+
282
+ return new Vec3(
283
+ nc * Math.sin(lonrad),
284
+ (N * (1.0 - this._e2) + lonlat.height) * slt,
285
+ nc * Math.cos(lonrad)
286
+ );
287
+ }
288
+
289
+ /**
290
+ * Gets cartesian ECEF from Wgs84 geodetic coordiantes.
291
+ * @public
292
+ * @param {LonLat} lonlat - Degrees geodetic coordiantes.
293
+ * @param {Vec3} res - Output result.
294
+ * @returns {Vec3} -
295
+ */
296
+ lonLatToCartesianRes(lonlat, res) {
297
+ var latrad = math.RADIANS * lonlat.lat,
298
+ lonrad = math.RADIANS * lonlat.lon;
299
+
300
+ var slt = Math.sin(latrad);
301
+
302
+ var N = this._a / Math.sqrt(1.0 - this._e2 * slt * slt);
303
+ var nc = (N + lonlat.height) * Math.cos(latrad);
304
+
305
+ res.x = nc * Math.sin(lonrad);
306
+ res.y = (N * (1.0 - this._e2) + lonlat.height) * slt;
307
+ res.z = nc * Math.cos(lonrad);
308
+
309
+ return res;
310
+ }
311
+
312
+ /**
313
+ * Gets cartesian ECEF from Wgs84 geodetic coordiantes.
314
+ * @public
315
+ * @param {Number} lon - Longitude.
316
+ * @param {Number} lat - Latitude.
317
+ * @param {Number} height - Height.
318
+ * @returns {Vec3} -
319
+ */
320
+ geodeticToCartesian(lon, lat, height = 0) {
321
+ var latrad = math.RADIANS * lat,
322
+ lonrad = math.RADIANS * lon;
323
+
324
+ var slt = Math.sin(latrad);
325
+
326
+ var N = this._a / Math.sqrt(1 - this._e2 * slt * slt);
327
+ var nc = (N + height) * Math.cos(latrad);
328
+
329
+ return new Vec3(
330
+ nc * Math.sin(lonrad),
331
+ (N * (1 - this._e2) + height) * slt,
332
+ nc * Math.cos(lonrad)
333
+ );
334
+ }
335
+
336
+ /**
337
+ * Gets Wgs84 geodetic coordiantes from cartesian ECEF.
338
+ * @public
339
+ * @param {Vec3} cartesian - Cartesian coordinates.
340
+ * @returns {LonLat} -
341
+ */
342
+ cartesianToLonLat(cartesian) {
343
+ var x = cartesian.z,
344
+ y = cartesian.x,
345
+ z = cartesian.y;
346
+ var ecc2 = this._e2;
347
+ var ecc22 = this._e22;
348
+ var r2 = x * x + y * y;
349
+ var r = Math.sqrt(r2);
350
+ var z2 = z * z;
351
+ var f = 54.0 * this._b2 * z2;
352
+ var g = r2 + (1.0 - ecc2) * z2 + ecc2 * (this._a2 - this._b2);
353
+ var g2 = g * g;
354
+ var c = (ecc22 * f * r2) / (g2 * g);
355
+ var s = Math.pow(1.0 + c + Math.sqrt(c * (c + 2.0)), 0.33333333333333333);
356
+ var p = f / (3.0 * Math.pow(1.0 + s + 1.0 / s, 2.0) * g2);
357
+ var q = Math.sqrt(1.0 + 2.0 * ecc22 * p);
358
+ var recc2r0 =
359
+ r -
360
+ ecc2 *
361
+ (-(p * ecc2 * r) / 1 +
362
+ q +
363
+ Math.sqrt(
364
+ 0.5 * this._a2 * (1.0 + 1.0 / q) -
365
+ (p * (1.0 - ecc2) * z2) / (q * (1.0 + q)) -
366
+ 0.5 * p * r2
367
+ ));
368
+ var recc2r02 = recc2r0 * recc2r0;
369
+ var v = Math.sqrt(recc2r02 + (1.0 - ecc2) * z2);
370
+ var z0 = (this._b2 * z) / (this._a * v);
371
+ var lat = Math.atan((z + this._k2 * z0) / r) * math.DEGREES;
372
+ var lon = Math.atan2(y, x) * math.DEGREES;
373
+ return new LonLat(
374
+ lon,
375
+ lat,
376
+ cartesian.length() - this.geodeticToCartesian(lon, lat).length()
377
+ );
378
+ }
379
+
380
+ /**
381
+ * Gets ellipsoid surface normal.
382
+ * @public
383
+ * @param {Vec3} coord - Spatial coordiantes.
384
+ * @return {Vec3} -
385
+ */
386
+ getSurfaceNormal3v(coord) {
387
+ var r2 = this._invRadii2;
388
+ var nx = coord.x * r2.x,
389
+ ny = coord.y * r2.y,
390
+ nz = coord.z * r2.z;
391
+ var l = 1.0 / Math.sqrt(nx * nx + ny * ny + nz * nz);
392
+ return new Vec3(nx * l, ny * l, nz * l);
393
+ }
394
+
395
+ /**
396
+ *
397
+ * @param {LonLat} lonLat1
398
+ * @param {number} [bearing]
399
+ * @param {number} [distance]
400
+ * @return {LonLat} -
401
+ */
402
+ getBearingDestination(lonLat1, bearing = 0.0, distance = 0) {
403
+ bearing = bearing * math.RADIANS;
404
+ var nlon = ((lonLat1.lon + 540) % 360) - 180;
405
+ var f1 = lonLat1.lat * math.RADIANS,
406
+ l1 = nlon * math.RADIANS;
407
+ var dR = distance / this._a;
408
+ var f2 = Math.asin(
409
+ Math.sin(f1) * Math.cos(dR) + Math.cos(f1) * Math.sin(dR) * Math.cos(bearing)
410
+ );
411
+ return new LonLat(
412
+ (l1 +
413
+ Math.atan2(
414
+ Math.sin(bearing) * Math.sin(dR) * Math.cos(f1),
415
+ Math.cos(dR) - Math.sin(f1) * Math.sin(f2)
416
+ )) *
417
+ math.DEGREES,
418
+ f2 * math.DEGREES
419
+ );
420
+ }
421
+
422
+ /**
423
+ * Returns the distance from one point to another(using haversine formula) on the great circle.
424
+ * @param {LonLat} lonLat1 - Longitude/latitude of source point.
425
+ * @param {LonLat} lonLat2 - Longitude/latitude of destination point.
426
+ * @return {number} Distance between points.
427
+ */
428
+ getGreatCircleDistance(lonLat1, lonLat2) {
429
+ var dLat = (lonLat2.lat - lonLat1.lat) * math.RADIANS;
430
+ var dLon = (lonLat2.lon - lonLat1.lon) * math.RADIANS;
431
+ var a =
432
+ Math.sin(dLat / 2.0) * Math.sin(dLat / 2.0) +
433
+ Math.sin(dLon / 2.0) *
434
+ Math.sin(dLon / 2) *
435
+ Math.cos(lonLat1.lat * math.RADIANS) *
436
+ Math.cos(lonLat2.lat * math.RADIANS);
437
+ return this._a * 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1.0 - a));
438
+ }
439
+
440
+ /**
441
+ * Calculates the destination point given start point lat / lon, bearing(deg) and distance (m).
442
+ *
443
+ * Taken from http://movable-type.co.uk/scripts/latlong-vincenty-direct.html and optimized / cleaned up by Mathias Bynens <http://mathiasbynens.be/>
444
+ * Based on the Vincenty direct formula by T. Vincenty, “Direct and Inverse Solutions of Geodesics on the Ellipsoid with application of nested equations”, Survey Review, vol XXII no 176, 1975 <http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf>
445
+ */
446
+ getGreatCircleDestination(lonLat, brng, dist) {
447
+ var lon1 = lonLat.lon,
448
+ lat1 = lonLat.lat;
449
+ var a = this._a,
450
+ b = this._b,
451
+ f = 1.0 / this._f,
452
+ s = dist,
453
+ alpha1 = brng * math.RADIANS,
454
+ sinAlpha1 = Math.sin(alpha1),
455
+ cosAlpha1 = Math.cos(alpha1),
456
+ tanU1 = (1 - f) * Math.tan(lat1 * math.RADIANS),
457
+ cosU1 = 1 / Math.sqrt(1 + tanU1 * tanU1),
458
+ sinU1 = tanU1 * cosU1,
459
+ sigma1 = Math.atan2(tanU1, cosAlpha1),
460
+ sinAlpha = cosU1 * sinAlpha1,
461
+ cosSqAlpha = 1 - sinAlpha * sinAlpha,
462
+ uSq = (cosSqAlpha * (a * a - b * b)) / (b * b),
463
+ A = 1 + (uSq / 16384) * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq))),
464
+ B = (uSq / 1024) * (256 + uSq * (-128 + uSq * (74 - 47 * uSq))),
465
+ sigma = s / (b * A),
466
+ sigmaP = 2 * Math.PI;
467
+ while (Math.abs(sigma - sigmaP) > 1e-12) {
468
+ var cos2SigmaM = Math.cos(2 * sigma1 + sigma),
469
+ sinSigma = Math.sin(sigma),
470
+ cosSigma = Math.cos(sigma),
471
+ deltaSigma =
472
+ B *
473
+ sinSigma *
474
+ (cos2SigmaM +
475
+ (B / 4) *
476
+ (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) -
477
+ (B / 6) *
478
+ cos2SigmaM *
479
+ (-3 + 4 * sinSigma * sinSigma) *
480
+ (-3 + 4 * cos2SigmaM * cos2SigmaM)));
481
+ sigmaP = sigma;
482
+ sigma = s / (b * A) + deltaSigma;
483
+ }
484
+ var tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1,
485
+ lat2 = Math.atan2(
486
+ sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
487
+ (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp)
488
+ ),
489
+ lambda = Math.atan2(
490
+ sinSigma * sinAlpha1,
491
+ cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1
492
+ ),
493
+ C = (f / 16) * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha)),
494
+ L =
495
+ lambda -
496
+ (1 - C) *
497
+ f *
498
+ sinAlpha *
499
+ (sigma +
500
+ C *
501
+ sinSigma *
502
+ (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))),
503
+ revAz = Math.atan2(sinAlpha, -tmp); // final bearing
504
+ return new LonLat(lon1 + L * math.DEGREES, lat2 * math.DEGREES);
505
+ }
506
+
507
+ /**
508
+ * Returns ray vector hit ellipsoid coordinates.
509
+ * If the ray doesn't hit ellipsoid returns null.
510
+ * @public
511
+ * @param {Vec3} origin - Ray origin point.
512
+ * @param {Vec3} direction - Ray direction.
513
+ * @returns {Vec3} -
514
+ */
515
+ hitRay(origin, direction) {
516
+ var q = this._invRadii.mul(origin);
517
+ var w = this._invRadii.mul(direction);
518
+
519
+ var q2 = q.dot(q);
520
+ var qw = q.dot(w);
521
+
522
+ var difference, w2, product, discriminant, temp;
523
+
524
+ if (q2 > 1.0) {
525
+ // Outside ellipsoid.
526
+ if (qw >= 0.0) {
527
+ // Looking outward or tangent (0 intersections).
528
+ return null;
529
+ }
530
+
531
+ // qw < 0.0.
532
+ var qw2 = qw * qw;
533
+ difference = q2 - 1.0; // Positively valued.
534
+ w2 = w.dot(w);
535
+ product = w2 * difference;
536
+
537
+ let eps = Math.abs(qw2 - product);
538
+
539
+ if (eps > math.EPSILON10 && qw2 < product) {
540
+ // Imaginary roots (0 intersections).
541
+ return null;
542
+ } else if (qw2 > product) {
543
+ // Distinct roots (2 intersections).
544
+ discriminant = qw * qw - product;
545
+ temp = -qw + Math.sqrt(discriminant); // Avoid cancellation.
546
+ var root0 = temp / w2;
547
+ var root1 = difference / temp;
548
+ if (root0 < root1) {
549
+ return origin.add(direction.scaleTo(root0));
550
+ }
551
+ return origin.add(direction.scaleTo(root1));
552
+ } else {
553
+ // qw2 == product. Repeated roots (2 intersections).
554
+ var root = Math.sqrt(difference / w2);
555
+ return origin.add(direction.scaleTo(root));
556
+ }
557
+ } else if (q2 < 1.0) {
558
+ // Inside ellipsoid (2 intersections).
559
+ difference = q2 - 1.0; // Negatively valued.
560
+ w2 = w.dot(w);
561
+ product = w2 * difference; // Negatively valued.
562
+ discriminant = qw * qw - product;
563
+ temp = -qw + Math.sqrt(discriminant); // Positively valued.
564
+ return origin.add(direction.scaleTo(temp / w2));
565
+ } else {
566
+ // q2 == 1.0. On ellipsoid.
567
+ if (qw < 0.0) {
568
+ // Looking inward.
569
+ w2 = w.dot(w);
570
+ return origin.add(direction.scaleTo(-qw / w2));
571
+ }
572
+ // qw >= 0.0. Looking outward or tangent.
573
+ return null;
574
+ }
575
+ }
576
+ }
577
+
578
+ export { Ellipsoid };