@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.
- package/README.md +0 -2
- package/dist/@openglobus/og.esm.js +2 -2
- package/dist/@openglobus/og.esm.js.map +1 -1
- package/dist/@openglobus/og.umd.js +2 -2
- package/dist/@openglobus/og.umd.js.map +1 -1
- package/package.json +7 -7
- package/src/og/Extent.js +3 -3
- package/src/og/Globe.js +277 -275
- package/src/og/ImageCanvas.js +3 -3
- package/src/og/LonLat.js +9 -9
- package/src/og/QueueArray.js +2 -2
- package/src/og/Rectangle.js +5 -5
- package/src/og/bv/Box.js +1 -1
- package/src/og/bv/Sphere.js +2 -2
- package/src/og/camera/Camera.js +1 -1
- package/src/og/camera/PlanetCamera.js +649 -646
- package/src/og/control/LayerSwitcher.js +1 -1
- package/src/og/control/MouseNavigation.js +22 -13
- package/src/og/control/MouseWheelZoomControl.js +2 -2
- package/src/og/control/ScaleControl.js +1 -1
- package/src/og/control/TouchNavigation.js +319 -319
- package/src/og/control/ZoomControl.js +132 -132
- package/src/og/control/visibleExtent/Segment.js +12 -12
- package/src/og/ellipsoid/Ellipsoid.js +577 -567
- package/src/og/entity/BillboardHandler.js +878 -1051
- package/src/og/entity/Entity.js +1 -1
- package/src/og/entity/EntityCollection.js +847 -857
- package/src/og/entity/GeoObjectHandler.js +2 -2
- package/src/og/entity/LabelHandler.js +888 -1040
- package/src/og/entity/LabelWorker.js +0 -6
- package/src/og/entity/PointCloud.js +495 -495
- package/src/og/entity/Polyline.js +2218 -2222
- package/src/og/entity/RayHandler.js +1 -1
- package/src/og/layer/CanvasTiles.js +6 -3
- package/src/og/layer/GeoImage.js +1 -1
- package/src/og/layer/KML.js +5 -5
- package/src/og/layer/Layer.js +800 -752
- package/src/og/layer/Vector.js +997 -1010
- package/src/og/layer/XYZ.js +359 -357
- package/src/og/math/Line2.js +4 -4
- package/src/og/math/Quat.js +2 -2
- package/src/og/math/Ray.js +2 -2
- package/src/og/math/Vec2.js +524 -524
- package/src/og/math/Vec3.js +900 -900
- package/src/og/math/Vec4.js +261 -261
- package/src/og/math.js +397 -398
- package/src/og/quadTree/EntityCollectionNode.js +389 -387
- package/src/og/renderer/Renderer.js +14 -20
- package/src/og/renderer/RendererEvents.js +1034 -1045
- package/src/og/scene/Planet.js +12 -9
- package/src/og/scene/RenderNode.js +347 -354
- package/src/og/segment/Segment.js +3 -1
- package/src/og/segment/SegmentLonLat.js +1 -4
- package/src/og/shaders/billboard.js +220 -204
- package/src/og/shaders/drawnode.js +603 -603
- package/src/og/shaders/label.js +596 -456
- package/src/og/shaders/polyline.js +365 -361
- package/src/og/terrain/MapboxTerrain.js +1 -1
- package/src/og/utils/FontAtlas.js +209 -200
- package/src/og/utils/GeoImageCreator.js +4 -5
- package/src/og/utils/TextureAtlas.js +4 -4
- package/src/og/utils/VectorTileCreator.js +414 -414
- package/src/og/utils/shared.js +1 -1
- package/src/og/webgl/Handler.js +28 -60
- package/src/og/webgl/Multisample.js +260 -260
- package/src/og/webgl/Program.js +411 -396
- package/types/bv/Box.d.ts +1 -1
- package/types/bv/Sphere.d.ts +1 -1
- package/types/camera/PlanetCamera.d.ts +3 -3
- package/types/control/LayerSwitcher.d.ts +1 -1
- package/types/ellipsoid/Ellipsoid.d.ts +9 -2
- package/types/entity/BillboardHandler.d.ts +0 -4
- package/types/entity/EntityCollection.d.ts +0 -8
- package/types/entity/LabelHandler.d.ts +0 -2
- package/types/entity/Polyline.d.ts +1 -1
- package/types/layer/KML.d.ts +11 -11
- package/types/layer/Layer.d.ts +4 -0
- package/types/layer/Vector.d.ts +1 -8
- package/types/math/Line2.d.ts +4 -4
- package/types/math/Ray.d.ts +2 -2
- package/types/math/Vec2.d.ts +1 -1
- package/types/math/Vec3.d.ts +1 -1
- package/types/math/Vec4.d.ts +2 -2
- package/types/math.d.ts +1 -1
- package/types/renderer/Renderer.d.ts +1 -1
- package/types/renderer/RendererEvents.d.ts +1 -1
- package/types/shaders/label.d.ts +1 -1
- package/types/utils/FontAtlas.d.ts +3 -2
- package/types/utils/TextureAtlas.d.ts +3 -3
- package/types/utils/VectorTileCreator.d.ts +4 -4
- package/types/utils/shared.d.ts +1 -1
- package/types/webgl/Handler.d.ts +6 -31
- 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
|
-
|
|
13
|
-
*
|
|
14
|
-
* @
|
|
15
|
-
* @param {number}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* @param {number}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
this.
|
|
35
|
-
this.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
this.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
this.
|
|
42
|
-
this.
|
|
43
|
-
this.
|
|
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
|
-
|
|
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 };
|