@openglobus/og 0.16.3 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/README.md +1 -1
  2. package/css/og.css +24 -2
  3. package/dist/@openglobus/og.css +1 -1
  4. package/dist/@openglobus/og.esm.js +2 -2
  5. package/dist/@openglobus/og.esm.js.map +1 -1
  6. package/dist/@openglobus/og.umd.js +2 -2
  7. package/dist/@openglobus/og.umd.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/og/Clock.js +1 -5
  10. package/src/og/Events.js +2 -6
  11. package/src/og/Extent.js +5 -9
  12. package/src/og/Globe.js +93 -56
  13. package/src/og/ImageCanvas.js +13 -6
  14. package/src/og/Lock.js +44 -48
  15. package/src/og/LonLat.js +1 -5
  16. package/src/og/Object3d.js +2 -2
  17. package/src/og/QueueArray.js +1 -1
  18. package/src/og/Stack.js +49 -53
  19. package/src/og/astro/earth.js +112 -116
  20. package/src/og/astro/jd.js +514 -518
  21. package/src/og/astro/orbit.js +119 -123
  22. package/src/og/astro/rotation.js +36 -40
  23. package/src/og/bv/Box.js +77 -81
  24. package/src/og/bv/Sphere.js +57 -61
  25. package/src/og/camera/Camera.js +20 -7
  26. package/src/og/camera/Frustum.js +4 -4
  27. package/src/og/camera/PlanetCamera.js +15 -22
  28. package/src/og/cons.js +1 -1
  29. package/src/og/control/Atmosphere.js +0 -4
  30. package/src/og/control/Control.js +9 -8
  31. package/src/og/control/DebugInfo.js +3 -7
  32. package/src/og/control/DrawingSwitcher.js +117 -0
  33. package/src/og/control/EarthCoordinates.js +214 -214
  34. package/src/og/control/EarthNavigation.js +6 -13
  35. package/src/og/control/GeoImageDragControl.js +0 -4
  36. package/src/og/control/KeyboardNavigation.js +0 -4
  37. package/src/og/control/LayerAnimation.js +0 -4
  38. package/src/og/control/LayerSwitcher.js +2 -4
  39. package/src/og/control/Lighting.js +12 -16
  40. package/src/og/control/MouseNavigation.js +1 -5
  41. package/src/og/control/MouseWheelZoomControl.js +0 -4
  42. package/src/og/control/RulerSwitcher.js +0 -4
  43. package/src/og/control/ScaleControl.js +2 -6
  44. package/src/og/control/ShowFps.js +40 -44
  45. package/src/og/control/SimpleNavigation.js +10 -10
  46. package/src/og/control/SimpleSkyBackground.js +0 -4
  47. package/src/og/control/Sun.js +0 -4
  48. package/src/og/control/ToggleWireframe.js +35 -39
  49. package/src/og/control/ZoomControl.js +0 -4
  50. package/src/og/control/drawing/DrawingControl.js +51 -0
  51. package/src/og/control/drawing/LineStringDrawingScene.js +203 -0
  52. package/src/og/control/drawing/PolygonDrawingScene.js +785 -0
  53. package/src/og/control/elevationProfile/ElevationProfile.js +275 -0
  54. package/src/og/control/index.js +5 -1
  55. package/src/og/control/ruler/Ruler.js +0 -4
  56. package/src/og/control/ruler/RulerScene.js +3 -2
  57. package/src/og/control/selection/Selection.js +0 -4
  58. package/src/og/control/selection/SelectionScene.js +2 -4
  59. package/src/og/control/timeline/TimelineControl.js +4 -4
  60. package/src/og/control/timeline/TimelineModel.js +2 -2
  61. package/src/og/control/timeline/TimelineView.js +1 -0
  62. package/src/og/control/visibleExtent/Segment.js +7 -7
  63. package/src/og/control/visibleExtent/drawnode.js +0 -4
  64. package/src/og/ellipsoid/Ellipsoid.js +236 -325
  65. package/src/og/ellipsoid/wgs84.js +0 -4
  66. package/src/og/entity/BaseBillboard.js +375 -379
  67. package/src/og/entity/Billboard.js +0 -4
  68. package/src/og/entity/BillboardHandler.js +3 -7
  69. package/src/og/entity/Entity.js +22 -22
  70. package/src/og/entity/EntityCollection.js +14 -18
  71. package/src/og/entity/GeoObject.js +6 -8
  72. package/src/og/entity/GeoObjectHandler.js +30 -24
  73. package/src/og/entity/Geometry.js +5 -4
  74. package/src/og/entity/GeometryHandler.js +18 -19
  75. package/src/og/entity/Label.js +0 -4
  76. package/src/og/entity/LabelHandler.js +10 -14
  77. package/src/og/entity/LabelWorker.js +1 -1
  78. package/src/og/entity/PointCloud.js +2 -6
  79. package/src/og/entity/PointCloudHandler.js +122 -126
  80. package/src/og/entity/Polyline.js +15 -17
  81. package/src/og/entity/PolylineHandler.js +104 -108
  82. package/src/og/entity/Ray.js +29 -29
  83. package/src/og/entity/RayHandler.js +2 -2
  84. package/src/og/entity/Strip.js +1 -1
  85. package/src/og/entity/StripHandler.js +2 -2
  86. package/src/og/index.js +3 -1
  87. package/src/og/input/KeyboardHandler.js +128 -132
  88. package/src/og/input/MouseHandler.js +94 -98
  89. package/src/og/input/TouchHandler.js +59 -63
  90. package/src/og/input/input.js +44 -48
  91. package/src/og/layer/BaseGeoImage.js +6 -9
  92. package/src/og/layer/CanvasTiles.js +1 -5
  93. package/src/og/layer/GeoImage.js +0 -4
  94. package/src/og/layer/GeoTexture2d.js +0 -4
  95. package/src/og/layer/GeoVideo.js +0 -4
  96. package/src/og/layer/KML.js +1 -0
  97. package/src/og/layer/Layer.js +22 -26
  98. package/src/og/layer/Material.js +0 -4
  99. package/src/og/layer/Vector.js +25 -22
  100. package/src/og/layer/WMS.js +182 -186
  101. package/src/og/layer/XYZ.js +0 -4
  102. package/src/og/light/LightSource.js +0 -4
  103. package/src/og/math/Line2.js +28 -32
  104. package/src/og/math/Mat3.js +152 -156
  105. package/src/og/math/Ray.js +0 -4
  106. package/src/og/math/Vec2.js +0 -4
  107. package/src/og/math/Vec3.js +5 -4
  108. package/src/og/math/Vec4.js +0 -4
  109. package/src/og/math/coder.js +104 -104
  110. package/src/og/math.js +42 -10
  111. package/src/og/mercator.js +1 -1
  112. package/src/og/proj/EPSG3857.js +13 -13
  113. package/src/og/proj/Proj.js +59 -63
  114. package/src/og/quadTree/EPSG4326QuadTreeStrategy.js +1 -0
  115. package/src/og/quadTree/EarthQuadTreeStrategy.js +1 -0
  116. package/src/og/quadTree/EntityCollectionNode.js +12 -16
  117. package/src/og/quadTree/MarsQuadTreeStrategy.js +1 -0
  118. package/src/og/quadTree/Node.js +15 -14
  119. package/src/og/quadTree/QuadTreeStrategy.js +1 -0
  120. package/src/og/quadTree/Wgs84QuadTreeStrategy.js +1 -0
  121. package/src/og/renderer/Renderer.js +114 -27
  122. package/src/og/renderer/RendererEvents.js +0 -4
  123. package/src/og/scene/Axes.js +74 -78
  124. package/src/og/scene/BaseNode.js +1 -5
  125. package/src/og/scene/Planet.js +127 -130
  126. package/src/og/scene/RenderNode.js +20 -12
  127. package/src/og/scene/SkyBox.js +104 -108
  128. package/src/og/segment/Segment.js +10 -6
  129. package/src/og/segment/SegmentLonLat.js +3 -7
  130. package/src/og/segment/segmentHelper.js +229 -225
  131. package/src/og/shaders/atmos.js +1 -1
  132. package/src/og/shaders/billboard.js +0 -4
  133. package/src/og/shaders/drawnode.js +5 -9
  134. package/src/og/shaders/geoObject.js +0 -4
  135. package/src/og/shaders/label.js +0 -4
  136. package/src/og/shaders/pickingMask.js +0 -4
  137. package/src/og/shaders/pointCloud.js +0 -4
  138. package/src/og/shaders/polyline.js +0 -4
  139. package/src/og/shaders/ray.js +0 -4
  140. package/src/og/shaders/skybox.js +0 -4
  141. package/src/og/terrain/EmptyTerrain.js +0 -4
  142. package/src/og/utils/FontAtlas.js +0 -4
  143. package/src/og/utils/GeoImageCreator.js +2 -3
  144. package/src/og/utils/NormalMapCreator.js +11 -12
  145. package/src/og/utils/PlainSegmentWorker.js +9 -9
  146. package/src/og/utils/TerrainWorker.js +2 -2
  147. package/src/og/utils/TextureAtlas.js +1 -1
  148. package/src/og/utils/VectorTileCreator.js +3 -9
  149. package/src/og/utils/earcut.js +8 -8
  150. package/src/og/utils/shared.js +30 -11
  151. package/src/og/webgl/Framebuffer.js +2 -6
  152. package/src/og/webgl/Handler.js +46 -20
  153. package/src/og/webgl/Multisample.js +2 -6
  154. package/src/og/webgl/Program.js +4 -8
  155. package/src/og/webgl/ProgramController.js +2 -2
  156. package/types/Clock.d.ts +1 -1
  157. package/types/Extent.d.ts +5 -5
  158. package/types/Globe.d.ts +15 -1
  159. package/types/ImageCanvas.d.ts +5 -0
  160. package/types/bv/Box.d.ts +1 -1
  161. package/types/bv/Sphere.d.ts +3 -3
  162. package/types/camera/Camera.d.ts +3 -2
  163. package/types/camera/PlanetCamera.d.ts +4 -4
  164. package/types/control/DrawingSwitcher.d.ts +13 -0
  165. package/types/control/EarthCoordinates.d.ts +1 -1
  166. package/types/control/Lighting.d.ts +8 -8
  167. package/types/control/drawing/DrawingControl.d.ts +14 -0
  168. package/types/control/drawing/LineStringDrawingScene.d.ts +5 -0
  169. package/types/control/drawing/PolygonDrawingScene.d.ts +99 -0
  170. package/types/control/index.d.ts +3 -1
  171. package/types/control/ruler/RulerScene.d.ts +0 -1
  172. package/types/control/selection/SelectionScene.d.ts +0 -1
  173. package/types/ellipsoid/Ellipsoid.d.ts +53 -64
  174. package/types/entity/Entity.d.ts +3 -2
  175. package/types/entity/GeoObject.d.ts +1 -2
  176. package/types/entity/Geometry.d.ts +5 -0
  177. package/types/entity/Polyline.d.ts +1 -1
  178. package/types/entity/Ray.d.ts +1 -0
  179. package/types/index.d.ts +1 -0
  180. package/types/layer/BaseGeoImage.d.ts +1 -1
  181. package/types/layer/CanvasTiles.d.ts +1 -1
  182. package/types/layer/Layer.d.ts +1 -1
  183. package/types/layer/Vector.d.ts +1 -0
  184. package/types/math/Vec3.d.ts +2 -0
  185. package/types/math.d.ts +7 -0
  186. package/types/renderer/Renderer.d.ts +7 -1
  187. package/types/scene/Axes.d.ts +0 -1
  188. package/types/scene/Planet.d.ts +27 -26
  189. package/types/scene/RenderNode.d.ts +2 -0
  190. package/types/scene/SkyBox.d.ts +0 -1
  191. package/types/segment/Segment.d.ts +3 -2
  192. package/types/segment/SegmentLonLat.d.ts +0 -1
  193. package/types/utils/GeoImageCreator.d.ts +1 -1
  194. package/types/utils/NormalMapCreator.d.ts +1 -1
  195. package/types/utils/VectorTileCreator.d.ts +2 -2
  196. package/types/utils/shared.d.ts +2 -0
  197. package/types/webgl/Handler.d.ts +6 -4
@@ -1,12 +1,9 @@
1
- /**
2
- * @module og/ellipsoid/Ellipsoid
3
- */
4
-
5
1
  "use strict";
6
2
 
7
3
  import { LonLat } from "../LonLat.js";
8
4
  import { DEGREES, EPS1, EPS12, EPS15, RADIANS, zeroTwoPI } from "../math.js";
9
5
  import { Vec3 } from "../math/Vec3.js";
6
+ import { Quat } from "../math/Quat.js";
10
7
 
11
8
  /**
12
9
  * Class represents a plant ellipsoid.
@@ -37,40 +34,14 @@ class Ellipsoid {
37
34
  this._k = qa2b2 / polarSize;
38
35
  this._k2 = this._k * this._k;
39
36
 
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
- /**
47
- * Returns angle between to bearin angles
48
- * @param {number} a - First bearing angle
49
- * @param {number} b - Second bearing angle
50
- * @returns {number}
51
- */
52
- static getAngleBetweenTwoBearings(a, b) {
53
- a = zeroTwoPI(a);
54
- b = zeroTwoPI(b);
55
- return ((((a - b) % 360) + 360 + 180) % 360) - 180;
56
- }
57
-
58
- static getRelativeBearing(a, b) {
59
- let a_y = Math.cos(a),
60
- a_x = Math.sin(a),
61
- b_y = Math.cos(b),
62
- b_x = Math.sin(b);
63
- let c = a_y * b_x - b_y * a_x,
64
- d = a_x * b_x + a_y * b_y;
65
- if (c > 0.0) {
66
- return Math.acos(d);
67
- }
68
- return -Math.acos(d);
37
+ this._radii = new Vec3(equatorialSize, equatorialSize, polarSize);
38
+ this._radii2 = new Vec3(this._a2, this._a2, this._b2);
39
+ this._invRadii = new Vec3(1.0 / equatorialSize, 1.0 / equatorialSize, 1.0 / polarSize);
40
+ this._invRadii2 = new Vec3(1.0 / this._a2, 1.0 / this._a2, 1.0 / this._b2);
69
41
  }
70
42
 
71
43
  /**
72
44
  * Returns the distance travelling from ‘this’ point to destination point along a rhumb line.
73
- *
74
45
  * @param {LonLat} start coordinates.
75
46
  * @param {LonLat} end coordinates
76
47
  * @returns {number} Distance in m between this point and destination point (same units as radius).
@@ -87,30 +58,6 @@ class Ellipsoid {
87
58
  return t * this._a;
88
59
  }
89
60
 
90
- /**
91
- * Returns the midpoint between two points on the great circle.
92
- * @param {LonLat} lonLat1 - Longitude/latitude of first point.
93
- * @param {LonLat} lonLat2 - Longitude/latitude of second point.
94
- * @return {LonLat} Midpoint between points.
95
- */
96
- static getMiddlePointOnGreatCircle(lonLat1, lonLat2) {
97
- var f1 = lonLat1.lat * RADIANS,
98
- l1 = lonLat1.lon * RADIANS;
99
- var f2 = lonLat2.lat * RADIANS;
100
- var dl = (lonLat2.lon - lonLat1.lon) * RADIANS;
101
-
102
- var Bx = Math.cos(f2) * Math.cos(dl);
103
- var By = Math.cos(f2) * Math.sin(dl);
104
-
105
- var x = Math.sqrt((Math.cos(f1) + Bx) * (Math.cos(f1) + Bx) + By * By);
106
- var y = Math.sin(f1) + Math.sin(f2);
107
- var f3 = Math.atan2(y, x);
108
-
109
- var l3 = l1 + Math.atan2(By, Math.cos(f1) + Bx);
110
-
111
- return new LonLat(((l3 * DEGREES + 540) % 360) - 180, f3 * DEGREES);
112
- }
113
-
114
61
  /**
115
62
  * Returns the point at given fraction between two points on the great circle.
116
63
  * @param {LonLat} lonLat1 - Longitude/Latitude of source point.
@@ -118,57 +65,23 @@ class Ellipsoid {
118
65
  * @param {number} fraction - Fraction between the two points (0 = source point, 1 = destination point).
119
66
  * @returns {LonLat} Intermediate point between points.
120
67
  */
121
- static getIntermediatePointOnGreatCircle(lonLat1, lonLat2, fraction) {
122
- var f1 = lonLat1.lat * RADIANS,
123
- l1 = lonLat1.lon * RADIANS;
124
- var f2 = lonLat2.lat * RADIANS,
125
- l2 = lonLat2.lon * RADIANS;
126
-
127
- var sinf1 = Math.sin(f1),
128
- cosf1 = Math.cos(f1),
129
- sinl1 = Math.sin(l1),
130
- cosl1 = Math.cos(l1);
131
- var sinf2 = Math.sin(f2),
132
- cosf2 = Math.cos(f2),
133
- sinl2 = Math.sin(l2),
134
- cosl2 = Math.cos(l2);
135
-
136
- var df = f2 - f1,
137
- dl = l2 - l1;
138
- var a =
139
- Math.sin(df / 2) * Math.sin(df / 2) +
140
- Math.cos(f1) * Math.cos(f2) * Math.sin(dl / 2) * Math.sin(dl / 2);
141
- var d = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
142
-
143
- var A = Math.sin((1 - fraction) * d) / Math.sin(d);
144
- var B = Math.sin(fraction * d) / Math.sin(d);
145
-
146
- var x = A * cosf1 * cosl1 + B * cosf2 * cosl2;
147
- var y = A * cosf1 * sinl1 + B * cosf2 * sinl2;
148
- var z = A * sinf1 + B * sinf2;
68
+ getIntermediatePointOnGreatCircle(lonLat1, lonLat2, fraction) {
149
69
 
150
- var f3 = Math.atan2(z, Math.sqrt(x * x + y * y));
151
- var l3 = Math.atan2(y, x);
70
+ if (fraction == 0) return lonLat1.clone();
71
+ if (fraction == 1) return lonLat2.clone();
152
72
 
153
- return new LonLat(((l3 * DEGREES + 540) % 360) - 180, f3 * DEGREES);
154
- }
155
-
156
- static getRhumbBearing(lonLat1, lonLat2) {
157
- var dLon = (lonLat2.lon - lonLat1.lon) * RADIANS;
158
- var dPhi = Math.log(
159
- Math.tan((lonLat2.lat * RADIANS) / 2 + Math.PI / 4) /
160
- Math.tan((lonLat1.lat * RADIANS) / 2 + Math.PI / 4)
161
- );
162
- if (Math.abs(dLon) > Math.PI) {
163
- if (dLon > 0) {
164
- dLon = (2 * Math.PI - dLon) * -1;
165
- } else {
166
- dLon = 2 * Math.PI + dLon;
167
- }
168
- }
169
- return (Math.atan2(dLon, dPhi) * DEGREES + 360) % 360;
73
+ const inverse = this.inverse(lonLat1, lonLat2);
74
+ const dist = inverse.distance;
75
+ const azimuth = inverse.initialAzimuth;
76
+ return isNaN(azimuth) ? lonLat1 : this.getGreatCircleDestination(lonLat1, azimuth, dist * fraction);
170
77
  }
171
78
 
79
+ /**
80
+ * REMOVE ASAP after
81
+ * @param lonLat1
82
+ * @param lonLat2
83
+ * @returns {number}
84
+ */
172
85
  static getBearing(lonLat1, lonLat2) {
173
86
  var f1 = lonLat1.lat * RADIANS,
174
87
  l1 = lonLat1.lon * RADIANS;
@@ -179,101 +92,8 @@ class Ellipsoid {
179
92
  return Math.atan2(y, x) * DEGREES;
180
93
  }
181
94
 
182
- /**
183
- * Returns the (initial) bearing from source to destination point on the great circle.
184
- * @param {LonLat} lonLat1 - Longitude/latitude of source point.
185
- * @param {LonLat} lonLat2 - Longitude/latitude of destination point.
186
- * @return {number} Initial bearing in degrees from north.
187
- */
188
- static getInitialBearing(lonLat1, lonLat2) {
189
- var f1 = lonLat1.lat * RADIANS,
190
- f2 = lonLat2.lat * RADIANS;
191
- var dl = (lonLat2.lon - lonLat1.lon) * RADIANS;
192
- var y = Math.sin(dl) * Math.cos(f2);
193
- var x = Math.cos(f1) * Math.sin(f2) - Math.sin(f1) * Math.cos(f2) * Math.cos(dl);
194
- var D = Math.atan2(y, x);
195
- return (D * DEGREES + 360) % 360;
196
- }
197
-
198
- /**
199
- * Returns the point of intersection of two paths defined by point and bearing.
200
- * @param {LonLat} p1 - First point.
201
- * @param {number} brng1 - Initial bearing from first point.
202
- * @param {LonLat} p2 - Second point.
203
- * @param {number} brng2 - Initial bearing from second point.
204
- * @return {LonLat|null} Destination point (null if no unique intersection defined).
205
- */
206
- static intersection(p1, brng1, p2, brng2) {
207
- var f1 = p1.lat * RADIANS,
208
- l1 = p1.lon * RADIANS;
209
- var f2 = p2.lat * RADIANS,
210
- l2 = p2.lon * RADIANS;
211
- var D13 = brng1 * RADIANS,
212
- D23 = brng2 * RADIANS;
213
- var df = f2 - f1,
214
- dl = l2 - l1;
215
-
216
- var d12 =
217
- 2 *
218
- Math.asin(
219
- Math.sqrt(
220
- Math.sin(df / 2) * Math.sin(df / 2) +
221
- Math.cos(f1) * Math.cos(f2) * Math.sin(dl / 2) * Math.sin(dl / 2)
222
- )
223
- );
224
- if (d12 == 0) return null;
225
-
226
- // initial/final bearings between points
227
- var Da = Math.acos(
228
- (Math.sin(f2) - Math.sin(f1) * Math.cos(d12)) / (Math.sin(d12) * Math.cos(f1))
229
- );
230
- if (isNaN(Da)) Da = 0; // protect against rounding
231
- var Db = Math.acos(
232
- (Math.sin(f1) - Math.sin(f2) * Math.cos(d12)) / (Math.sin(d12) * Math.cos(f2))
233
- );
234
-
235
- var D12 = Math.sin(l2 - l1) > 0 ? Da : 2 * Math.PI - Da;
236
- var D21 = Math.sin(l2 - l1) > 0 ? 2 * Math.PI - Db : Db;
237
-
238
- var a1 = ((D13 - D12 + Math.PI) % (2 * Math.PI)) - Math.PI;
239
- var a2 = ((D21 - D23 + Math.PI) % (2 * Math.PI)) - Math.PI;
240
-
241
- if (Math.sin(a1) == 0 && Math.sin(a2) == 0) return null; // infinite intersections
242
- if (Math.sin(a1) * Math.sin(a2) < 0) return null; // ambiguous intersection
243
-
244
- // a1 = Math.abs(a1);
245
- // a2 = Math.abs(a2);
246
- // ... Ed Williams takes abs of a1/a2, but seems to break calculation?
247
-
248
- var a3 = Math.acos(
249
- -Math.cos(a1) * Math.cos(a2) + Math.sin(a1) * Math.sin(a2) * Math.cos(d12)
250
- );
251
- var d13 = Math.atan2(
252
- Math.sin(d12) * Math.sin(a1) * Math.sin(a2),
253
- Math.cos(a2) + Math.cos(a1) * Math.cos(a3)
254
- );
255
- var f3 = Math.asin(
256
- Math.sin(f1) * Math.cos(d13) + Math.cos(f1) * Math.sin(d13) * Math.cos(D13)
257
- );
258
- var dl13 = Math.atan2(
259
- Math.sin(D13) * Math.sin(d13) * Math.cos(f1),
260
- Math.cos(d13) - Math.sin(f1) * Math.sin(f3)
261
- );
262
- var l3 = l1 + dl13;
263
-
264
- return new LonLat(((l3 * DEGREES + 540) % 360) - 180, f3 * DEGREES);
265
- }
266
-
267
- /**
268
- * Returns final bearing arriving at destination destination point from lonLat1 point; the final bearing
269
- * will differ from the initial bearing by varying degrees according to distance and latitude.
270
- * @param {LonLat} lonLat1 - Longitude/latitude of source point.
271
- * @param {LonLat} lonLat2 - Longitude/latitude of destination point.
272
- * @return {number} Final bearing in degrees from north.
273
- */
274
- static getFinalBearing(lonLat1, lonLat2) {
275
- // get initial bearing from destination lonLat2 to lonLat1 & reverse it by adding 180°
276
- return (Ellipsoid.getInitialBearing(lonLat2, lonLat1) + 180) % 360;
95
+ getFlattening() {
96
+ return this._flattening;
277
97
  }
278
98
 
279
99
  /**
@@ -295,48 +115,24 @@ class Ellipsoid {
295
115
  }
296
116
 
297
117
  /**
298
- * Gets cartesian ECEF from Wgs84 geodetic coordinates.
118
+ * Calculate cartesian coordinates by its ECEF geodetic coordinates.
299
119
  * @public
300
- * @param {LonLat} lonlat - Degrees geodetic coordinates.
120
+ * @param {LonLat} lonlat - Geodetic coordinates.
301
121
  * @returns {Vec3} -
302
122
  */
303
123
  lonLatToCartesian(lonlat) {
304
- var latrad = RADIANS * lonlat.lat,
305
- lonrad = RADIANS * lonlat.lon;
306
-
307
- var slt = Math.sin(latrad);
308
-
309
- var N = this._a / Math.sqrt(1.0 - this._e2 * slt * slt);
310
- var nc = (N + lonlat.height) * Math.cos(latrad);
311
-
312
- return new Vec3(
313
- nc * Math.sin(lonrad),
314
- (N * (1.0 - this._e2) + lonlat.height) * slt,
315
- nc * Math.cos(lonrad)
316
- );
124
+ return this.geodeticToCartesian(lonlat.lon, lonlat.lat, lonlat.height);
317
125
  }
318
126
 
319
127
  /**
320
- * Gets cartesian ECEF from Wgs84 geodetic coordinates.
128
+ * Calculate cartesian coordinates by its ECEF geodetic coordinates.
321
129
  * @public
322
- * @param {LonLat} lonlat - Degrees geodetic coordinates.
323
- * @param {Vec3} res - Output result.
130
+ * @param {LonLat} lonlat - Geodetic coordinates.
131
+ * @param {Vec3} res - Output variable reference.
324
132
  * @returns {Vec3} -
325
133
  */
326
134
  lonLatToCartesianRes(lonlat, res) {
327
- var latrad = RADIANS * lonlat.lat,
328
- lonrad = RADIANS * lonlat.lon;
329
-
330
- var slt = Math.sin(latrad);
331
-
332
- var N = this._a / Math.sqrt(1.0 - this._e2 * slt * slt);
333
- var nc = (N + lonlat.height) * Math.cos(latrad);
334
-
335
- res.x = nc * Math.sin(lonrad);
336
- res.y = (N * (1.0 - this._e2) + lonlat.height) * slt;
337
- res.z = nc * Math.cos(lonrad);
338
-
339
- return res;
135
+ return this.geodeticToCartesian(lonlat.lon, lonlat.lat, lonlat.height, res);
340
136
  }
341
137
 
342
138
  /**
@@ -347,20 +143,22 @@ class Ellipsoid {
347
143
  * @param {Number} height - Height.
348
144
  * @returns {Vec3} -
349
145
  */
350
- geodeticToCartesian(lon, lat, height = 0) {
351
- var latrad = RADIANS * lat,
146
+ geodeticToCartesian(lon, lat, height = 0, res) {
147
+ res = res || new Vec3();
148
+
149
+ let latrad = RADIANS * lat,
352
150
  lonrad = RADIANS * lon;
353
151
 
354
- var slt = Math.sin(latrad);
152
+ let slt = Math.sin(latrad);
355
153
 
356
- var N = this._a / Math.sqrt(1 - this._e2 * slt * slt);
357
- var nc = (N + height) * Math.cos(latrad);
154
+ let N = this._a / Math.sqrt(1 - this._e2 * slt * slt);
155
+ let nc = (N + height) * Math.cos(latrad);
358
156
 
359
- return new Vec3(
360
- nc * Math.sin(lonrad),
361
- (N * (1 - this._e2) + height) * slt,
362
- nc * Math.cos(lonrad)
363
- );
157
+ res.x = nc * Math.cos(lonrad);
158
+ res.y = nc * Math.sin(lonrad);
159
+ res.z = (N * (1 - this._e2) + height) * slt;
160
+
161
+ return res;
364
162
  }
365
163
 
366
164
  /**
@@ -375,6 +173,8 @@ class Ellipsoid {
375
173
  pY = p.y || 0.0,
376
174
  pZ = p.z || 0.0;
377
175
 
176
+ let length = Math.sqrt(pX * pX + pY * pY + pZ * pZ);
177
+
378
178
  let invRadii2X = this._invRadii2.x,
379
179
  invRadii2Y = this._invRadii2.y,
380
180
  invRadii2Z = this._invRadii2.z;
@@ -391,9 +191,9 @@ class Ellipsoid {
391
191
  return !Number.isFinite(ratio) ? new Vec3() : first
392
192
  }
393
193
 
394
- let lambda = ((1.0 - ratio) * p.length()) / first.mulA(this._invRadii2).length();
194
+ let lambda = ((1.0 - ratio) * length) / first.mulA(this._invRadii2).length();
395
195
 
396
- let m_X, m_Y, m_Z;
196
+ let m_X = 0.0, m_Y = 0.0, m_Z = 0.0;
397
197
 
398
198
  do {
399
199
  m_X = 1.0 / (1.0 + lambda * invRadii2X);
@@ -427,14 +227,7 @@ class Ellipsoid {
427
227
  * @returns {LonLat} - Geodetic coordinates
428
228
  */
429
229
  cartesianToLonLat(cart) {
430
- let p = this.projToSurface(cart);
431
- let n = this.getSurfaceNormal3v(p),
432
- h = cart.sub(p);
433
- return new LonLat(
434
- Math.atan2(n.x, n.z) * DEGREES,
435
- Math.asin(n.y) * DEGREES,
436
- Math.sign(h.dot(cart)) * h.length()
437
- );
230
+ return this.cartesianToLonLatRes(cart);
438
231
  }
439
232
 
440
233
  /**
@@ -444,12 +237,15 @@ class Ellipsoid {
444
237
  * @returns {LonLat} - Geodetic coordinates
445
238
  */
446
239
  cartesianToLonLatRes(cart, res) {
240
+ res = res || new LonLat();
447
241
  let p = this.projToSurface(cart);
448
242
  let n = this.getSurfaceNormal3v(p),
449
243
  h = cart.sub(p);
450
- res.lon = Math.atan2(n.x, n.z) * DEGREES;
451
- res.lat = Math.asin(n.y) * DEGREES;
244
+
245
+ res.lon = Math.atan2(n.y, n.x) * DEGREES;
246
+ res.lat = Math.asin(n.z) * DEGREES;
452
247
  res.height = Math.sign(h.dot(cart)) * h.length();
248
+
453
249
  return res;
454
250
  }
455
251
 
@@ -468,65 +264,96 @@ class Ellipsoid {
468
264
  return new Vec3(nx * l, ny * l, nz * l);
469
265
  }
470
266
 
471
- /**
472
- *
473
- * @param {LonLat} lonLat1
474
- * @param {number} [bearing]
475
- * @param {number} [distance]
476
- * @return {LonLat} -
477
- */
478
- getBearingDestination(lonLat1, bearing = 0.0, distance = 0) {
479
- bearing = bearing * RADIANS;
480
- var nlon = ((lonLat1.lon + 540) % 360) - 180;
481
- var f1 = lonLat1.lat * RADIANS,
482
- l1 = nlon * RADIANS;
483
- var dR = distance / this._a;
484
- var f2 = Math.asin(
485
- Math.sin(f1) * Math.cos(dR) + Math.cos(f1) * Math.sin(dR) * Math.cos(bearing)
486
- );
487
- return new LonLat(
488
- (l1 +
489
- Math.atan2(
490
- Math.sin(bearing) * Math.sin(dR) * Math.cos(f1),
491
- Math.cos(dR) - Math.sin(f1) * Math.sin(f2)
492
- )) *
493
- DEGREES,
494
- f2 * DEGREES
495
- );
267
+ getGreatCircleDistance(lonLat1, lonLat2) {
268
+ return this.inverse(lonLat1, lonLat2).distance;
496
269
  }
497
270
 
498
271
  /**
499
- * Returns the distance from one point to another(using haversine formula) on the great circle.
500
- * @param {LonLat} lonLat1 - Longitude/latitude of source point.
501
- * @param {LonLat} lonLat2 - Longitude/latitude of destination point.
502
- * @return {number} Distance between points.
272
+ * Calculates the destination point given start point lat / lon, azimuth(deg) and distance (m).
273
+ * Source: http://movable-type.co.uk/scripts/latlong-vincenty-direct.html and optimized / cleaned up by Mathias Bynens <http://mathiasbynens.be/>
274
+ * 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>
275
+ * @param {LonLat} lonLat - Origin coordinates
276
+ * @param {number} azimuth - View azimuth in degrees
277
+ * @param {number} dist - Distance to the destination point coordinates in meters
278
+ * @returns {LonLat} - Destination point coordinates
503
279
  */
504
- getGreatCircleDistance(lonLat1, lonLat2) {
505
- var dLat = (lonLat2.lat - lonLat1.lat) * RADIANS;
506
- var dLon = (lonLat2.lon - lonLat1.lon) * RADIANS;
507
- var a =
508
- Math.sin(dLat / 2.0) * Math.sin(dLat / 2.0) +
509
- Math.sin(dLon / 2.0) *
510
- Math.sin(dLon / 2) *
511
- Math.cos(lonLat1.lat * RADIANS) *
512
- Math.cos(lonLat2.lat * RADIANS);
513
- return this._a * 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1.0 - a));
280
+ getGreatCircleDestination(lonLat, azimuth, dist) {
281
+ return this.direct(lonLat, azimuth, dist).destination;
282
+ }
283
+
284
+ inverse(lonLat1, lonLat2) {
285
+
286
+ let a = this._a, b = this._b, f = this._flattening;
287
+
288
+ const fi1 = lonLat1.lat * RADIANS, lambda1 = lonLat1.lon * RADIANS;
289
+ const fi2 = lonLat2.lat * RADIANS, lambda2 = lonLat2.lon * RADIANS;
290
+
291
+ const L = lambda2 - lambda1; // L = difference in longitude, U = reduced latitude, defined by tan U = (1-f)·tanφ.
292
+ const tanU1 = (1 - f) * Math.tan(fi1), cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
293
+ const tanU2 = (1 - f) * Math.tan(fi2), cosU2 = 1 / Math.sqrt((1 + tanU2 * tanU2)), sinU2 = tanU2 * cosU2;
294
+
295
+ const antipodal = Math.abs(L) > Math.PI / 2 || Math.abs(fi2 - fi1) > Math.PI / 2;
296
+
297
+ let lmb = L, sinLmb = null, cosLmb = null; // lmb - difference in longitude on an auxiliary sphere
298
+ let s = antipodal ? Math.PI : 0, sin_s = 0, cos_s = antipodal ? -1 : 1, sinSqs = null; // s - angular distance lonLat1 lonLat2 on the sphere
299
+ let cos2sm = 1; // sm - angular distance on the sphere from the equator to the midpoint of the line
300
+ let cosSqa = 1; // a - azimuth of the geodesic at the equator
301
+
302
+ let lmb_ = null, iterations = 0;
303
+ do {
304
+ sinLmb = Math.sin(lmb);
305
+ cosLmb = Math.cos(lmb);
306
+ sinSqs = (cosU2 * sinLmb) ** 2 + (cosU1 * sinU2 - sinU1 * cosU2 * cosLmb) ** 2;
307
+ if (Math.abs(sinSqs) < 1e-24) break; // co-incident/antipodal points (σ < ≈0.006mm)
308
+ sin_s = Math.sqrt(sinSqs);
309
+ cos_s = sinU1 * sinU2 + cosU1 * cosU2 * cosLmb;
310
+ s = Math.atan2(sin_s, cos_s);
311
+ const sin_a = cosU1 * cosU2 * sinLmb / sin_s;
312
+ cosSqa = 1 - sin_a * sin_a;
313
+ cos2sm = (cosSqa != 0) ? (cos_s - 2 * sinU1 * sinU2 / cosSqa) : 0; // on equatorial line cos²α = 0 (§6)
314
+ const C = f / 16 * cosSqa * (4 + f * (4 - 3 * cosSqa));
315
+ lmb_ = lmb;
316
+ lmb = L + (1 - C) * f * sin_a * (s + C * sin_s * (cos2sm + C * cos_s * (-1 + 2 * cos2sm * cos2sm)));
317
+ } while (Math.abs(lmb - lmb_) > EPS12 && ++iterations < 1000);
318
+
319
+ const uSq = cosSqa * (a * a - b * b) / (b * b);
320
+ const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
321
+ const B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
322
+ const ds = B * sin_s * (cos2sm + B / 4 * (cos_s * (-1 + 2 * cos2sm * cos2sm) - B / 6 * cos2sm * (-3 + 4 * sin_s * sin_s) * (-3 + 4 * cos2sm * cos2sm)));
323
+
324
+ const dist = b * A * (s - ds); // s = length of the geodesic
325
+
326
+ // note special handling of exactly antipodal points where sin²σ = 0 (due to discontinuity
327
+ // atan2(0, 0) = 0 but atan2(ε, 0) = π/2 / 90°) - in which case bearing is always meridional,
328
+ // due north (or due south!)
329
+ // α = azimuths of the geodesic; α2 the direction P₁ P₂ produced
330
+ const a1 = Math.abs(sinSqs) < Number.EPSILON ? 0 : Math.atan2(cosU2 * sinLmb, cosU1 * sinU2 - sinU1 * cosU2 * cosLmb);
331
+ const a2 = Math.abs(sinSqs) < Number.EPSILON ? Math.PI : Math.atan2(cosU1 * sinLmb, -sinU1 * cosU2 + cosU1 * sinU2 * cosLmb);
332
+
333
+ return {
334
+ distance: dist,
335
+ initialAzimuth: Math.abs(dist) < Number.EPSILON ? NaN : zeroTwoPI(a1) * DEGREES,
336
+ finalAzimuth: Math.abs(dist) < Number.EPSILON ? NaN : zeroTwoPI(a2) * DEGREES
337
+ };
514
338
  }
515
339
 
516
340
  /**
517
- * Calculates the destination point given start point lat / lon, bearing(deg) and distance (m).
518
- *
519
- * Taken from http://movable-type.co.uk/scripts/latlong-vincenty-direct.html and optimized / cleaned up by Mathias Bynens <http://mathiasbynens.be/>
341
+ * Calculates the destination point given start point lat / lon, azimuth(deg) and distance (m).
342
+ * Source: http://movable-type.co.uk/scripts/latlong-vincenty-direct.html and optimized / cleaned up by Mathias Bynens <http://mathiasbynens.be/>
520
343
  * 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>
344
+ * @param {LonLat} lonLat - Origin coordinates
345
+ * @param {number} azimuth - View azimuth in degrees
346
+ * @param {number} dist - Distance to the destination point coordinates in meters
347
+ * @returns {LonLat} - Destination point coordinates
521
348
  */
522
- getGreatCircleDestination(lonLat, brng, dist) {
523
- var lon1 = lonLat.lon,
349
+ direct(lonLat, azimuth, dist) {
350
+ let lon1 = lonLat.lon,
524
351
  lat1 = lonLat.lat;
525
- var a = this._a,
352
+ let a = this._a,
526
353
  b = this._b,
527
- f = 1.0 / this._f,
354
+ f = this._flattening,
528
355
  s = dist,
529
- alpha1 = brng * RADIANS,
356
+ alpha1 = azimuth * RADIANS,
530
357
  sinAlpha1 = Math.sin(alpha1),
531
358
  cosAlpha1 = Math.cos(alpha1),
532
359
  tanU1 = (1 - f) * Math.tan(lat1 * RADIANS),
@@ -544,20 +371,16 @@ class Ellipsoid {
544
371
  var cos2SigmaM = Math.cos(2 * sigma1 + sigma),
545
372
  sinSigma = Math.sin(sigma),
546
373
  cosSigma = Math.cos(sigma),
547
- deltaSigma =
548
- B *
549
- sinSigma *
550
- (cos2SigmaM +
551
- (B / 4) *
552
- (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) -
553
- (B / 6) *
554
- cos2SigmaM *
555
- (-3 + 4 * sinSigma * sinSigma) *
556
- (-3 + 4 * cos2SigmaM * cos2SigmaM)));
374
+ deltaSigma = B * sinSigma *
375
+ (cos2SigmaM + (B / 4) *
376
+ (
377
+ cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) -
378
+ (B / 6) * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)
379
+ ));
557
380
  sigmaP = sigma;
558
381
  sigma = s / (b * A) + deltaSigma;
559
382
  }
560
- var tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1,
383
+ let tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1,
561
384
  lat2 = Math.atan2(
562
385
  sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
563
386
  (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp)
@@ -566,22 +389,19 @@ class Ellipsoid {
566
389
  sinSigma * sinAlpha1,
567
390
  cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1
568
391
  ),
569
- C = (f / 16) * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha)),
570
- L =
571
- lambda -
572
- (1 - C) *
573
- f *
574
- sinAlpha *
575
- (sigma +
576
- C *
577
- sinSigma *
578
- (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))),
579
- revAz = Math.atan2(sinAlpha, -tmp); // final bearing
580
- return new LonLat(lon1 + L * DEGREES, lat2 * DEGREES);
392
+ C = (f / 16.0) * cosSqAlpha * (4.0 + f * (4.0 - 3.0 * cosSqAlpha)),
393
+ L = lambda - (1.0 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1.0 + 2.0 * cos2SigmaM * cos2SigmaM))),
394
+ revAz = Math.atan2(sinAlpha, -tmp);
395
+
396
+ return {
397
+ destination: new LonLat(lon1 + L * DEGREES, lat2 * DEGREES),
398
+ finalAzimuth: revAz * DEGREES
399
+ };
581
400
  }
582
401
 
402
+
583
403
  /**
584
- * Returns ray vector hit ellipsoid coordinates.
404
+ * Returns cartesian coordinates of the intersection of a ray and an ellipsoid.
585
405
  * If the ray doesn't hit ellipsoid returns null.
586
406
  * @public
587
407
  * @param {Vec3} origin - Ray origin point.
@@ -646,9 +466,100 @@ class Ellipsoid {
646
466
  return origin.add(direction.scaleTo(-qw / w2));
647
467
  }
648
468
  // qw >= 0.0. Looking outward or tangent.
649
- return null;
469
+ // return undefined
650
470
  }
651
471
  }
472
+
473
+ getNorthFrameRotation(cartesian) {
474
+ let n = this.getSurfaceNormal3v(cartesian);
475
+ let t = Vec3.proj_b_to_plane(Vec3.NORTH, n);
476
+ return Quat.getLookRotation(t, n);
477
+ }
478
+
479
+ /**
480
+ * @todo this is not precise function, needs to be replaced or removed
481
+ * @param lonLat1
482
+ * @param bearing
483
+ * @param distance
484
+ * @returns {LonLat}
485
+ */
486
+ getBearingDestination(lonLat1, bearing = 0.0, distance = 0) {
487
+ bearing = bearing * RADIANS;
488
+ var nlon = ((lonLat1.lon + 540) % 360) - 180;
489
+ var f1 = lonLat1.lat * RADIANS,
490
+ l1 = nlon * RADIANS;
491
+ var dR = distance / this._a;
492
+ var f2 = Math.asin(
493
+ Math.sin(f1) * Math.cos(dR) + Math.cos(f1) * Math.sin(dR) * Math.cos(bearing)
494
+ );
495
+ return new LonLat(
496
+ (l1 +
497
+ Math.atan2(
498
+ Math.sin(bearing) * Math.sin(dR) * Math.cos(f1),
499
+ Math.cos(dR) - Math.sin(f1) * Math.sin(f2)
500
+ )) *
501
+ DEGREES,
502
+ f2 * DEGREES
503
+ );
504
+ }
505
+
506
+ /**
507
+ * Returns the point at given fraction between two points on the great circle.
508
+ * @param {LonLat} lonLat1 - Longitude/Latitude of source point.
509
+ * @param {LonLat} lonLat2 - Longitude/Latitude of destination point.
510
+ * @param {number} fraction - Fraction between the two points (0 = source point, 1 = destination point).
511
+ * @returns {LonLat} Intermediate point between points.
512
+ */
513
+ static getIntermediatePointOnGreatCircle(lonLat1, lonLat2, fraction) {
514
+ var f1 = lonLat1.lat * RADIANS,
515
+ l1 = lonLat1.lon * RADIANS;
516
+ var f2 = lonLat2.lat * RADIANS,
517
+ l2 = lonLat2.lon * RADIANS;
518
+
519
+ var sinf1 = Math.sin(f1),
520
+ cosf1 = Math.cos(f1),
521
+ sinl1 = Math.sin(l1),
522
+ cosl1 = Math.cos(l1);
523
+ var sinf2 = Math.sin(f2),
524
+ cosf2 = Math.cos(f2),
525
+ sinl2 = Math.sin(l2),
526
+ cosl2 = Math.cos(l2);
527
+
528
+ var df = f2 - f1,
529
+ dl = l2 - l1;
530
+ var a =
531
+ Math.sin(df / 2) * Math.sin(df / 2) +
532
+ Math.cos(f1) * Math.cos(f2) * Math.sin(dl / 2) * Math.sin(dl / 2);
533
+ var d = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
534
+
535
+ var A = Math.sin((1 - fraction) * d) / Math.sin(d);
536
+ var B = Math.sin(fraction * d) / Math.sin(d);
537
+
538
+ var x = A * cosf1 * cosl1 + B * cosf2 * cosl2;
539
+ var y = A * cosf1 * sinl1 + B * cosf2 * sinl2;
540
+ var z = A * sinf1 + B * sinf2;
541
+
542
+ var f3 = Math.atan2(z, Math.sqrt(x * x + y * y));
543
+ var l3 = Math.atan2(y, x);
544
+
545
+ return new LonLat(((l3 * DEGREES + 540) % 360) - 180, f3 * DEGREES);
546
+ }
547
+
548
+ static getRhumbBearing(lonLat1, lonLat2) {
549
+ var dLon = (lonLat2.lon - lonLat1.lon) * RADIANS;
550
+ var dPhi = Math.log(
551
+ Math.tan((lonLat2.lat * RADIANS) / 2 + Math.PI / 4) /
552
+ Math.tan((lonLat1.lat * RADIANS) / 2 + Math.PI / 4)
553
+ );
554
+ if (Math.abs(dLon) > Math.PI) {
555
+ if (dLon > 0) {
556
+ dLon = (2 * Math.PI - dLon) * -1;
557
+ } else {
558
+ dLon = 2 * Math.PI + dLon;
559
+ }
560
+ }
561
+ return (Math.atan2(dLon, dPhi) * DEGREES + 360) % 360;
562
+ }
652
563
  }
653
564
 
654
565
  export { Ellipsoid };