UNPKG

36.2 kBJavaScriptView Raw
1/*! Leaflet.Geodesic 2.5.1 - (c) Henry Thasler - https://github.com/henrythasler/Leaflet.Geodesic */
2'use strict';
3
4Object.defineProperty(exports, '__esModule', { value: true });
5
6function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
7
8var L = _interopDefault(require('leaflet'));
9
10/*! *****************************************************************************
11Copyright (c) Microsoft Corporation. All rights reserved.
12Licensed under the Apache License, Version 2.0 (the "License"); you may not use
13this file except in compliance with the License. You may obtain a copy of the
14License at http://www.apache.org/licenses/LICENSE-2.0
15
16THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
18WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
19MERCHANTABLITY OR NON-INFRINGEMENT.
20
21See the Apache Version 2.0 License for specific language governing permissions
22and limitations under the License.
23***************************************************************************** */
24/* global Reflect, Promise */
25
26var extendStatics = function(d, b) {
27 extendStatics = Object.setPrototypeOf ||
28 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
29 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
30 return extendStatics(d, b);
31};
32
33function __extends(d, b) {
34 extendStatics(d, b);
35 function __() { this.constructor = d; }
36 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
37}
38
39var __assign = function() {
40 __assign = Object.assign || function __assign(t) {
41 for (var s, i = 1, n = arguments.length; i < n; i++) {
42 s = arguments[i];
43 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
44 }
45 return t;
46 };
47 return __assign.apply(this, arguments);
48};
49
50function __spreadArrays() {
51 for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
52 for (var r = Array(s), k = 0, i = 0; i < il; i++)
53 for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
54 r[k] = a[j];
55 return r;
56}
57
58var GeodesicCore = /** @class */ (function () {
59 function GeodesicCore(options) {
60 this.options = { wrap: true, steps: 3 };
61 this.ellipsoid = {
62 a: 6378137,
63 b: 6356752.3142,
64 f: 1 / 298.257223563
65 }; // WGS-84
66 this.options = __assign(__assign({}, this.options), options);
67 }
68 GeodesicCore.prototype.toRadians = function (degree) {
69 return degree * Math.PI / 180;
70 };
71 GeodesicCore.prototype.toDegrees = function (radians) {
72 return radians * 180 / Math.PI;
73 };
74 /**
75 * implements scientific modulus
76 * source: http://www.codeavenger.com/2017/05/19/JavaScript-Modulo-operation-and-the-Caesar-Cipher.html
77 * @param n
78 * @param p
79 * @return
80 */
81 GeodesicCore.prototype.mod = function (n, p) {
82 var r = n % p;
83 return r < 0 ? r + p : r;
84 };
85 /**
86 * source: https://github.com/chrisveness/geodesy/blob/master/dms.js
87 * @param degrees arbitrary value
88 * @return degrees between 0..360
89 */
90 GeodesicCore.prototype.wrap360 = function (degrees) {
91 if (0 <= degrees && degrees < 360) {
92 return degrees; // avoid rounding due to arithmetic ops if within range
93 }
94 else {
95 return this.mod(degrees, 360);
96 }
97 };
98 /**
99 * general wrap function with arbitrary max value
100 * @param degrees arbitrary value
101 * @param max
102 * @return degrees between `-max`..`+max`
103 */
104 GeodesicCore.prototype.wrap = function (degrees, max) {
105 if (max === void 0) { max = 360; }
106 if (-max <= degrees && degrees <= max) {
107 return degrees;
108 }
109 else {
110 return this.mod((degrees + max), 2 * max) - max;
111 }
112 };
113 /**
114 * Vincenty direct calculation.
115 * based on the work of Chris Veness (https://github.com/chrisveness/geodesy)
116 * source: https://github.com/chrisveness/geodesy/blob/master/latlon-ellipsoidal-vincenty.js
117 *
118 * @param start starting point
119 * @param bearing initial bearing (in degrees)
120 * @param distance distance from starting point to calculate along given bearing in meters.
121 * @param maxInterations How many iterations can be made to reach the allowed deviation (`ε`), before an error will be thrown.
122 * @return Final point (destination point) and bearing (in degrees)
123 */
124 GeodesicCore.prototype.direct = function (start, bearing, distance, maxInterations) {
125 if (maxInterations === void 0) { maxInterations = 100; }
126 var φ1 = this.toRadians(start.lat);
127 var λ1 = this.toRadians(start.lng);
128 var α1 = this.toRadians(bearing);
129 var s = distance;
130 var ε = Number.EPSILON * 1000;
131 var _a = this.ellipsoid, a = _a.a, b = _a.b, f = _a.f;
132 var sinα1 = Math.sin(α1);
133 var cosα1 = Math.cos(α1);
134 var tanU1 = (1 - f) * Math.tan(φ1), cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
135 var σ1 = Math.atan2(tanU1, cosα1); // σ1 = angular distance on the sphere from the equator to P1
136 var sinα = cosU1 * sinα1; // α = azimuth of the geodesic at the equator
137 var cosSqα = 1 - sinα * sinα;
138 var uSq = cosSqα * (a * a - b * b) / (b * b);
139 var A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
140 var B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
141 var σ = s / (b * A), sinσ = null, cosσ = null, Δσ = null; // σ = angular distance P₁ P₂ on the sphere
142 var cos2σₘ = null; // σₘ = angular distance on the sphere from the equator to the midpoint of the line
143 var σʹ = null, iterations = 0;
144 do {
145 cos2σₘ = Math.cos(2 * σ1 + σ);
146 sinσ = Math.sin(σ);
147 cosσ = Math.cos(σ);
148 Δσ = B * sinσ * (cos2σₘ + B / 4 * (cosσ * (-1 + 2 * cos2σₘ * cos2σₘ) -
149 B / 6 * cos2σₘ * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σₘ * cos2σₘ)));
150 σʹ = σ;
151 σ = s / (b * A) + Δσ;
152 } while (Math.abs(σ - σʹ) > ε && ++iterations < maxInterations);
153 if (iterations >= maxInterations) {
154 throw new EvalError("Direct vincenty formula failed to converge after " + maxInterations + " iterations (start=" + start.lat + "/" + start.lng + "; bearing=" + bearing + "; distance=" + distance + ")"); // not possible?
155 }
156 var x = sinU1 * sinσ - cosU1 * cosσ * cosα1;
157 var φ2 = Math.atan2(sinU1 * cosσ + cosU1 * sinσ * cosα1, (1 - f) * Math.sqrt(sinα * sinα + x * x));
158 var λ = Math.atan2(sinσ * sinα1, cosU1 * cosσ - sinU1 * sinσ * cosα1);
159 var C = f / 16 * cosSqα * (4 + f * (4 - 3 * cosSqα));
160 var dL = λ - (1 - C) * f * sinα * (σ + C * sinσ * (cos2σₘ + C * cosσ * (-1 + 2 * cos2σₘ * cos2σₘ)));
161 var λ2 = λ1 + dL;
162 var α2 = Math.atan2(sinα, -x);
163 return {
164 lat: this.toDegrees(φ2),
165 lng: this.toDegrees(λ2),
166 bearing: this.wrap360(this.toDegrees(α2))
167 };
168 };
169 /**
170 * Vincenty inverse calculation.
171 * based on the work of Chris Veness (https://github.com/chrisveness/geodesy)
172 * source: https://github.com/chrisveness/geodesy/blob/master/latlon-ellipsoidal-vincenty.js
173 *
174 * @param start Latitude/longitude of starting point.
175 * @param dest Latitude/longitude of destination point.
176 * @return Object including distance, initialBearing, finalBearing.
177 */
178 GeodesicCore.prototype.inverse = function (start, dest, maxInterations, mitigateConvergenceError) {
179 if (maxInterations === void 0) { maxInterations = 100; }
180 if (mitigateConvergenceError === void 0) { mitigateConvergenceError = true; }
181 var p1 = start, p2 = dest;
182 var φ1 = this.toRadians(p1.lat), λ1 = this.toRadians(p1.lng);
183 var φ2 = this.toRadians(p2.lat), λ2 = this.toRadians(p2.lng);
184 var π = Math.PI;
185 var ε = Number.EPSILON;
186 // allow alternative ellipsoid to be specified
187 var _a = this.ellipsoid, a = _a.a, b = _a.b, f = _a.f;
188 var dL = λ2 - λ1; // L = difference in longitude, U = reduced latitude, defined by tan U = (1-f)·tanφ.
189 var tanU1 = (1 - f) * Math.tan(φ1), cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
190 var tanU2 = (1 - f) * Math.tan(φ2), cosU2 = 1 / Math.sqrt((1 + tanU2 * tanU2)), sinU2 = tanU2 * cosU2;
191 var antipodal = Math.abs(dL) > π / 2 || Math.abs(φ2 - φ1) > π / 2;
192 var λ = dL, sinλ = null, cosλ = null; // λ = difference in longitude on an auxiliary sphere
193 var σ = antipodal ? π : 0, sinσ = 0, cosσ = antipodal ? -1 : 1, sinSqσ = null; // σ = angular distance P₁ P₂ on the sphere
194 var cos2σₘ = 1; // σₘ = angular distance on the sphere from the equator to the midpoint of the line
195 var sinα = null, cosSqα = 1; // α = azimuth of the geodesic at the equator
196 var C = null;
197 var λʹ = null, iterations = 0;
198 do {
199 sinλ = Math.sin(λ);
200 cosλ = Math.cos(λ);
201 sinSqσ = (cosU2 * sinλ) * (cosU2 * sinλ) + (cosU1 * sinU2 - sinU1 * cosU2 * cosλ) * (cosU1 * sinU2 - sinU1 * cosU2 * cosλ);
202 if (Math.abs(sinSqσ) < ε) {
203 break; // co-incident/antipodal points (falls back on λ/σ = L)
204 }
205 sinσ = Math.sqrt(sinSqσ);
206 cosσ = sinU1 * sinU2 + cosU1 * cosU2 * cosλ;
207 σ = Math.atan2(sinσ, cosσ);
208 sinα = cosU1 * cosU2 * sinλ / sinσ;
209 cosSqα = 1 - sinα * sinα;
210 cos2σₘ = (cosSqα !== 0) ? (cosσ - 2 * sinU1 * sinU2 / cosSqα) : 0; // on equatorial line cos²α = 0 (§6)
211 C = f / 16 * cosSqα * (4 + f * (4 - 3 * cosSqα));
212 λʹ = λ;
213 λ = dL + (1 - C) * f * sinα * (σ + C * sinσ * (cos2σₘ + C * cosσ * (-1 + 2 * cos2σₘ * cos2σₘ)));
214 var iterationCheck = antipodal ? Math.abs(λ) - π : Math.abs(λ);
215 if (iterationCheck > π) {
216 throw new EvalError('λ > π');
217 }
218 } while (Math.abs(λ - λʹ) > 1e-12 && ++iterations < maxInterations);
219 if (iterations >= maxInterations) {
220 if (mitigateConvergenceError) {
221 return this.inverse(start, new L.LatLng(dest.lat, dest.lng - 0.01), maxInterations, mitigateConvergenceError);
222 }
223 else {
224 throw new EvalError("Inverse vincenty formula failed to converge after " + maxInterations + " iterations (start=" + start.lat + "/" + start.lng + "; dest=" + dest.lat + "/" + dest.lng + ")");
225 }
226 }
227 var uSq = cosSqα * (a * a - b * b) / (b * b);
228 var A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
229 var B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
230 var Δσ = B * sinσ * (cos2σₘ + B / 4 * (cosσ * (-1 + 2 * cos2σₘ * cos2σₘ) -
231 B / 6 * cos2σₘ * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σₘ * cos2σₘ)));
232 var s = b * A * (σ - Δσ); // s = length of the geodesic
233 // note special handling of exactly antipodal points where sin²σ = 0 (due to discontinuity
234 // atan2(0, 0) = 0 but atan2(this.ε, 0) = π/2 / 90°) - in which case bearing is always meridional,
235 // due north (or due south!)
236 // α = azimuths of the geodesic; α2 the direction P₁ P₂ produced
237 var α1 = Math.abs(sinSqσ) < ε ? 0 : Math.atan2(cosU2 * sinλ, cosU1 * sinU2 - sinU1 * cosU2 * cosλ);
238 var α2 = Math.abs(sinSqσ) < ε ? π : Math.atan2(cosU1 * sinλ, -sinU1 * cosU2 + cosU1 * sinU2 * cosλ);
239 return {
240 distance: s,
241 initialBearing: Math.abs(s) < ε ? NaN : this.wrap360(this.toDegrees(α1)),
242 finalBearing: Math.abs(s) < ε ? NaN : this.wrap360(this.toDegrees(α2))
243 };
244 };
245 /**
246 * Returns the point of intersection of two paths defined by position and bearing.
247 * This calculation uses a spherical model of the earth. This will lead to small errors compared to an ellipsiod model.
248 * based on the work of Chris Veness (https://github.com/chrisveness/geodesy)
249 * source: https://github.com/chrisveness/geodesy/blob/master/latlon-spherical.js
250 *
251 * @param firstPos 1st path: position and bearing
252 * @param firstBearing
253 * @param secondPos 2nd path: position and bearing
254 * @param secondBearing
255 */
256 GeodesicCore.prototype.intersection = function (firstPos, firstBearing, secondPos, secondBearing) {
257 var φ1 = this.toRadians(firstPos.lat);
258 var λ1 = this.toRadians(firstPos.lng);
259 var φ2 = this.toRadians(secondPos.lat);
260 var λ2 = this.toRadians(secondPos.lng);
261 var θ13 = this.toRadians(firstBearing);
262 var θ23 = this.toRadians(secondBearing);
263 var Δφ = φ2 - φ1, Δλ = λ2 - λ1;
264 var π = Math.PI;
265 var ε = Number.EPSILON;
266 // angular distance p1-p2
267 var δ12 = 2 * Math.asin(Math.sqrt(Math.sin(Δφ / 2) * Math.sin(Δφ / 2)
268 + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2)));
269 if (Math.abs(δ12) < ε) {
270 return firstPos; // coincident points
271 }
272 // initial/final bearings between points
273 var cosθa = (Math.sin(φ2) - Math.sin(φ1) * Math.cos(δ12)) / (Math.sin(δ12) * Math.cos(φ1));
274 var cosθb = (Math.sin(φ1) - Math.sin(φ2) * Math.cos(δ12)) / (Math.sin(δ12) * Math.cos(φ2));
275 var θa = Math.acos(Math.min(Math.max(cosθa, -1), 1)); // protect against rounding errors
276 var θb = Math.acos(Math.min(Math.max(cosθb, -1), 1)); // protect against rounding errors
277 var θ12 = Math.sin(λ2 - λ1) > 0 ? θa : 2 * π - θa;
278 var θ21 = Math.sin(λ2 - λ1) > 0 ? 2 * π - θb : θb;
279 var α1 = θ13 - θ12; // angle 2-1-3
280 var α2 = θ21 - θ23; // angle 1-2-3
281 if (Math.sin(α1) === 0 && Math.sin(α2) === 0) {
282 return null; // infinite intersections
283 }
284 if (Math.sin(α1) * Math.sin(α2) < 0) {
285 return null; // ambiguous intersection (antipodal?)
286 }
287 var cosα3 = -Math.cos(α1) * Math.cos(α2) + Math.sin(α1) * Math.sin(α2) * Math.cos(δ12);
288 var δ13 = Math.atan2(Math.sin(δ12) * Math.sin(α1) * Math.sin(α2), Math.cos(α2) + Math.cos(α1) * cosα3);
289 var φ3 = Math.asin(Math.min(Math.max(Math.sin(φ1) * Math.cos(δ13) + Math.cos(φ1) * Math.sin(δ13) * Math.cos(θ13), -1), 1));
290 var Δλ13 = Math.atan2(Math.sin(θ13) * Math.sin(δ13) * Math.cos(φ1), Math.cos(δ13) - Math.sin(φ1) * Math.sin(φ3));
291 var λ3 = λ1 + Δλ13;
292 return new L.LatLng(this.toDegrees(φ3), this.toDegrees(λ3));
293 };
294 GeodesicCore.prototype.midpoint = function (start, dest) {
295 // φm = atan2( sinφ1 + sinφ2, √( (cosφ1 + cosφ2⋅cosΔλ)² + cos²φ2⋅sin²Δλ ) )
296 // λm = λ1 + atan2(cosφ2⋅sinΔλ, cosφ1 + cosφ2⋅cosΔλ)
297 // midpoint is sum of vectors to two points: mathforum.org/library/drmath/view/51822.html
298 var φ1 = this.toRadians(start.lat);
299 var λ1 = this.toRadians(start.lng);
300 var φ2 = this.toRadians(dest.lat);
301 var Δλ = this.toRadians(dest.lng - start.lng);
302 // get cartesian coordinates for the two points
303 var A = { x: Math.cos(φ1), y: 0, z: Math.sin(φ1) }; // place point A on prime meridian y=0
304 var B = { x: Math.cos(φ2) * Math.cos(Δλ), y: Math.cos(φ2) * Math.sin(Δλ), z: Math.sin(φ2) };
305 // vector to midpoint is sum of vectors to two points (no need to normalise)
306 var C = { x: A.x + B.x, y: A.y + B.y, z: A.z + B.z };
307 var φm = Math.atan2(C.z, Math.sqrt(C.x * C.x + C.y * C.y));
308 var λm = λ1 + Math.atan2(C.y, C.x);
309 return new L.LatLng(this.toDegrees(φm), this.toDegrees(λm));
310 };
311 return GeodesicCore;
312}());
313
314var GeodesicGeometry = /** @class */ (function () {
315 function GeodesicGeometry(options) {
316 this.geodesic = new GeodesicCore();
317 this.options = { wrap: true, steps: 3 };
318 this.options = __assign(__assign({}, this.options), options);
319 this.steps = (this.options.steps === undefined) ? 3 : this.options.steps;
320 }
321 GeodesicGeometry.prototype.recursiveMidpoint = function (start, dest, iterations) {
322 var geom = [start, dest];
323 var midpoint = this.geodesic.midpoint(start, dest);
324 if (this.options.wrap) {
325 midpoint.lng = this.geodesic.wrap(midpoint.lng, 180);
326 }
327 if (iterations > 0) {
328 geom.splice.apply(geom, __spreadArrays([0, 1], this.recursiveMidpoint(start, midpoint, iterations - 1)));
329 geom.splice.apply(geom, __spreadArrays([geom.length - 2, 2], this.recursiveMidpoint(midpoint, dest, iterations - 1)));
330 }
331 else {
332 geom.splice(1, 0, midpoint);
333 }
334 return geom;
335 };
336 GeodesicGeometry.prototype.line = function (start, dest) {
337 return this.recursiveMidpoint(start, dest, Math.min(8, this.steps));
338 };
339 GeodesicGeometry.prototype.circle = function (center, radius) {
340 var points = [];
341 for (var i = 0; i < this.steps + 1; i++) {
342 var point = this.geodesic.direct(center, 360 / this.steps * i, radius);
343 points.push(new L.LatLng(point.lat, point.lng));
344 }
345 return points;
346 };
347 GeodesicGeometry.prototype.multiLineString = function (latlngs) {
348 var _this = this;
349 var multiLineString = [];
350 latlngs.forEach(function (linestring) {
351 var segment = [];
352 for (var j = 1; j < linestring.length; j++) {
353 segment.splice.apply(segment, __spreadArrays([segment.length - 1, 1], _this.line(linestring[j - 1], linestring[j])));
354 }
355 multiLineString.push(segment);
356 });
357 return multiLineString;
358 };
359 GeodesicGeometry.prototype.lineString = function (latlngs) {
360 return this.multiLineString([latlngs])[0];
361 };
362 /**
363 *
364 * Is much (10x) faster than the previous implementation:
365 *
366 * ```
367 * Benchmark (no split): splitLine x 459,044 ops/sec ±0.53% (95 runs sampled)
368 * Benchmark (split): splitLine x 42,999 ops/sec ±0.51% (97 runs sampled)
369 * ```
370 *
371 * @param startPosition
372 * @param destPosition
373 */
374 GeodesicGeometry.prototype.splitLine = function (startPosition, destPosition) {
375 var antimeridianWest = {
376 point: new L.LatLng(89.9, -180),
377 bearing: 180
378 };
379 var antimeridianEast = {
380 point: new L.LatLng(89.9, 180),
381 bearing: 180
382 };
383 // make a copy to work with
384 var start = new L.LatLng(startPosition.lat, startPosition.lng);
385 var dest = new L.LatLng(destPosition.lat, destPosition.lng);
386 start.lng = this.geodesic.wrap(start.lng, 360);
387 dest.lng = this.geodesic.wrap(dest.lng, 360);
388 if ((dest.lng - start.lng) > 180) {
389 dest.lng = dest.lng - 360;
390 }
391 else if ((dest.lng - start.lng) < -180) {
392 dest.lng = dest.lng + 360;
393 }
394 var result = [[new L.LatLng(start.lat, this.geodesic.wrap(start.lng, 180)), new L.LatLng(dest.lat, this.geodesic.wrap(dest.lng, 180))]];
395 // crossing antimeridian from "this" side?
396 if ((start.lng >= -180) && (start.lng <= 180)) {
397 // crossing the "western" antimeridian
398 if (dest.lng < -180) {
399 var bearing = this.geodesic.inverse(start, dest).initialBearing;
400 var intersection = this.geodesic.intersection(start, bearing, antimeridianWest.point, antimeridianWest.bearing);
401 if (intersection) {
402 result = [[start, intersection], [new L.LatLng(intersection.lat, intersection.lng + 360), new L.LatLng(dest.lat, dest.lng + 360)]];
403 }
404 }
405 // crossing the "eastern" antimeridian
406 else if (dest.lng > 180) {
407 var bearing = this.geodesic.inverse(start, dest).initialBearing;
408 var intersection = this.geodesic.intersection(start, bearing, antimeridianEast.point, antimeridianEast.bearing);
409 if (intersection) {
410 result = [[start, intersection], [new L.LatLng(intersection.lat, intersection.lng - 360), new L.LatLng(dest.lat, dest.lng - 360)]];
411 }
412 }
413 }
414 // coming back over the antimeridian from the "other" side?
415 else if ((dest.lng >= -180) && (dest.lng <= 180)) {
416 // crossing the "western" antimeridian
417 if (start.lng < -180) {
418 var bearing = this.geodesic.inverse(start, dest).initialBearing;
419 var intersection = this.geodesic.intersection(start, bearing, antimeridianWest.point, antimeridianWest.bearing);
420 if (intersection) {
421 result = [[new L.LatLng(start.lat, start.lng + 360), new L.LatLng(intersection.lat, intersection.lng + 360)], [intersection, dest]];
422 }
423 }
424 // crossing the "eastern" antimeridian
425 else if (start.lng > 180) {
426 var bearing = this.geodesic.inverse(start, dest).initialBearing;
427 var intersection = this.geodesic.intersection(start, bearing, antimeridianWest.point, antimeridianWest.bearing);
428 if (intersection) {
429 result = [[new L.LatLng(start.lat, start.lng - 360), new L.LatLng(intersection.lat, intersection.lng - 360)], [intersection, dest]];
430 }
431 }
432 }
433 return result;
434 };
435 /**
436 * Linestrings of a given multilinestring that cross the antimeridian will be split in two separate linestrings.
437 * This function is used to wrap lines around when they cross the antimeridian
438 * It iterates over all linestrings and reconstructs the step-by-step if no split is needed.
439 * In case the line was split, the linestring ends at the antimeridian and a new linestring is created for the
440 * remaining points of the original linestring.
441 *
442 * @param multilinestring
443 * @return another multilinestring where segments crossing the antimeridian are split
444 */
445 GeodesicGeometry.prototype.splitMultiLineString = function (multilinestring) {
446 var _this = this;
447 var result = [];
448 multilinestring.forEach(function (linestring) {
449 if (linestring.length === 1) {
450 result.push(linestring); // just a single point in linestring, no need to split
451 }
452 else {
453 var segment = [];
454 for (var j = 1; j < linestring.length; j++) {
455 var split = _this.splitLine(linestring[j - 1], linestring[j]);
456 segment.pop();
457 segment = segment.concat(split[0]);
458 if (split.length > 1) {
459 result.push(segment); // the line was split, so we end the linestring right here
460 segment = split[1]; // begin the new linestring with the second part of the split line
461 }
462 }
463 result.push(segment);
464 }
465 });
466 return result;
467 };
468 GeodesicGeometry.prototype.distance = function (start, dest) {
469 return this.geodesic.inverse(new L.LatLng(start.lat, this.geodesic.wrap(start.lng, 180)), new L.LatLng(dest.lat, this.geodesic.wrap(dest.lng, 180))).distance;
470 };
471 GeodesicGeometry.prototype.multilineDistance = function (multilinestring) {
472 var _this = this;
473 var dist = [];
474 multilinestring.forEach(function (linestring) {
475 var segmentDistance = 0;
476 for (var j = 1; j < linestring.length; j++) {
477 segmentDistance += _this.distance(linestring[j - 1], linestring[j]);
478 }
479 dist.push(segmentDistance);
480 });
481 return dist;
482 };
483 GeodesicGeometry.prototype.updateStatistics = function (points, vertices) {
484 var stats = {};
485 stats.distanceArray = this.multilineDistance(points);
486 stats.totalDistance = stats.distanceArray.reduce(function (x, y) { return x + y; }, 0);
487 stats.points = 0;
488 points.forEach(function (item) {
489 stats.points += item.reduce(function (x) { return x + 1; }, 0);
490 });
491 stats.vertices = 0;
492 vertices.forEach(function (item) {
493 stats.vertices += item.reduce(function (x) { return x + 1; }, 0);
494 });
495 return stats;
496 };
497 return GeodesicGeometry;
498}());
499
500function instanceOfLatLngLiteral(object) {
501 return ((typeof object === "object")
502 && (object !== null)
503 && ('lat' in object)
504 && ('lng' in object)
505 && (typeof object.lat === "number")
506 && (typeof object.lng === "number"));
507}
508function instanceOfLatLngTuple(object) {
509 return ((object instanceof Array)
510 && (typeof object[0] === "number")
511 && (typeof object[1] === "number"));
512}
513function instanceOfLatLngExpression(object) {
514 if (object instanceof L.LatLng) {
515 return true;
516 }
517 else if (instanceOfLatLngTuple(object)) {
518 return true;
519 }
520 else if (instanceOfLatLngLiteral(object)) {
521 return true;
522 }
523 else {
524 return false;
525 }
526}
527function latlngExpressiontoLatLng(input) {
528 if (input instanceof L.LatLng) {
529 return input;
530 }
531 else if (instanceOfLatLngTuple(input)) {
532 return new L.LatLng(input[0], input[1]);
533 }
534 else if (instanceOfLatLngLiteral(input)) {
535 return new L.LatLng(input.lat, input.lng);
536 }
537 else {
538 throw new Error("L.LatLngExpression expected. Unknown object found.");
539 }
540}
541function latlngExpressionArraytoLatLngArray(input) {
542 var latlng = [];
543 var _loop_1 = function (group) {
544 // it's a 1D-Array L.LatLngExpression[]
545 if (instanceOfLatLngExpression(group)) {
546 var sub_1 = [];
547 input.forEach(function (point) {
548 sub_1.push(latlngExpressiontoLatLng(point));
549 });
550 latlng.push(sub_1);
551 return "break";
552 }
553 // it's a 2D-Array L.LatLngExpression[][]
554 else if (group instanceof Array) {
555 if (instanceOfLatLngExpression(group[0])) {
556 var sub_2 = [];
557 group.forEach(function (point) {
558 sub_2.push(latlngExpressiontoLatLng(point));
559 });
560 latlng.push(sub_2);
561 }
562 else {
563 throw new Error("L.LatLngExpression[] | L.LatLngExpression[][] expected. Unknown object found.");
564 }
565 }
566 else {
567 throw new Error("L.LatLngExpression[] | L.LatLngExpression[][] expected. Unknown object found.");
568 }
569 };
570 for (var _i = 0, input_1 = input; _i < input_1.length; _i++) {
571 var group = input_1[_i];
572 var state_1 = _loop_1(group);
573 if (state_1 === "break")
574 break;
575 }
576 return latlng;
577}
578
579/**
580 * Draw geodesic lines based on L.Polyline
581 */
582var GeodesicLine = /** @class */ (function (_super) {
583 __extends(GeodesicLine, _super);
584 function GeodesicLine(latlngs, options) {
585 var _this = _super.call(this, [], options) || this;
586 /** these should be good for most use-cases */
587 _this.defaultOptions = { wrap: true, steps: 3 };
588 /** use this if you need some detailled info about the current geometry */
589 _this.statistics = {};
590 /** stores all positions that are used to create the geodesic line */
591 _this.points = [];
592 L.Util.setOptions(_this, __assign(__assign({}, _this.defaultOptions), options));
593 _this.geom = new GeodesicGeometry(_this.options);
594 if (latlngs !== undefined) {
595 _this.setLatLngs(latlngs);
596 }
597 return _this;
598 }
599 /** calculates the geodesics and update the polyline-object accordingly */
600 GeodesicLine.prototype.updateGeometry = function () {
601 var geodesic = [];
602 geodesic = this.geom.multiLineString(this.points);
603 if (this.options.wrap) {
604 var split = this.geom.splitMultiLineString(geodesic);
605 _super.prototype.setLatLngs.call(this, split);
606 }
607 else {
608 _super.prototype.setLatLngs.call(this, geodesic);
609 }
610 this.statistics = this.geom.updateStatistics(this.points, geodesic);
611 };
612 /**
613 * overwrites the original function with additional functionality to create a geodesic line
614 * @param latlngs an array (or 2d-array) of positions
615 */
616 GeodesicLine.prototype.setLatLngs = function (latlngs) {
617 this.points = latlngExpressionArraytoLatLngArray(latlngs);
618 this.updateGeometry();
619 return this;
620 };
621 /**
622 * add a given point to the geodesic line object
623 * @param latlng point to add. The point will always be added to the last linestring of a multiline
624 * @param latlngs define a linestring to add the new point to. Read from points-property before (e.g. `line.addLatLng(Beijing, line.points[0]);`)
625 */
626 GeodesicLine.prototype.addLatLng = function (latlng, latlngs) {
627 var point = latlngExpressiontoLatLng(latlng);
628 if (this.points.length === 0) {
629 this.points.push([point]);
630 }
631 else {
632 if (latlngs === undefined) {
633 this.points[this.points.length - 1].push(point);
634 }
635 else {
636 latlngs.push(point);
637 }
638 }
639 this.updateGeometry();
640 return this;
641 };
642 /**
643 * Creates geodesic lines from a given GeoJSON-Object.
644 * @param input GeoJSON-Object
645 */
646 GeodesicLine.prototype.fromGeoJson = function (input) {
647 var latlngs = [];
648 var features = [];
649 if (input.type === "FeatureCollection") {
650 features = input.features;
651 }
652 else if (input.type === "Feature") {
653 features = [input];
654 }
655 else if (["MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon"].includes(input.type)) {
656 features = [{
657 type: "Feature",
658 geometry: input,
659 properties: {}
660 }];
661 }
662 else {
663 console.log("[Leaflet.Geodesic] fromGeoJson() - Type \"" + input.type + "\" not supported.");
664 }
665 features.forEach(function (feature) {
666 switch (feature.geometry.type) {
667 case "MultiPoint":
668 case "LineString":
669 latlngs = __spreadArrays(latlngs, [L.GeoJSON.coordsToLatLngs(feature.geometry.coordinates, 0)]);
670 break;
671 case "MultiLineString":
672 case "Polygon":
673 latlngs = __spreadArrays(latlngs, L.GeoJSON.coordsToLatLngs(feature.geometry.coordinates, 1));
674 break;
675 case "MultiPolygon":
676 feature.geometry.coordinates.forEach(function (item) {
677 latlngs = __spreadArrays(latlngs, L.GeoJSON.coordsToLatLngs(item, 1));
678 });
679 break;
680 default:
681 console.log("[Leaflet.Geodesic] fromGeoJson() - Type \"" + feature.geometry.type + "\" not supported.");
682 }
683 });
684 if (latlngs.length) {
685 this.setLatLngs(latlngs);
686 }
687 return this;
688 };
689 /**
690 * Calculates the distance between two geo-positions
691 * @param start 1st position
692 * @param dest 2nd position
693 * @return the distance in meters
694 */
695 GeodesicLine.prototype.distance = function (start, dest) {
696 return this.geom.distance(latlngExpressiontoLatLng(start), latlngExpressiontoLatLng(dest));
697 };
698 return GeodesicLine;
699}(L.Polyline));
700
701/**
702 * Can be used to create a geodesic circle based on L.Polyline
703 */
704var GeodesicCircleClass = /** @class */ (function (_super) {
705 __extends(GeodesicCircleClass, _super);
706 function GeodesicCircleClass(center, options) {
707 var _this = _super.call(this, [], options) || this;
708 _this.defaultOptions = { wrap: true, steps: 24, fill: true, noClip: true };
709 _this.statistics = {};
710 L.Util.setOptions(_this, __assign(__assign({}, _this.defaultOptions), options));
711 // merge/set options
712 var extendedOptions = _this.options;
713 _this.radius = (extendedOptions.radius === undefined) ? 1000 * 1000 : extendedOptions.radius;
714 _this.center = (center === undefined) ? new L.LatLng(0, 0) : latlngExpressiontoLatLng(center);
715 _this.geom = new GeodesicGeometry(_this.options);
716 // update the geometry
717 _this.update();
718 return _this;
719 }
720 /**
721 * Updates the geometry and re-calculates some statistics
722 */
723 GeodesicCircleClass.prototype.update = function () {
724 var latlngs = this.geom.circle(this.center, this.radius);
725 this.statistics = this.geom.updateStatistics([[this.center]], [latlngs]);
726 // circumfence must be re-calculated from geodesic
727 this.statistics.totalDistance = this.geom.multilineDistance([latlngs]).reduce(function (x, y) { return x + y; }, 0);
728 this.setLatLngs(latlngs);
729 };
730 /**
731 * Calculate the distance between the current center and an arbitrary position.
732 * @param latlng geo-position to calculate distance to
733 * @return distance in meters
734 */
735 GeodesicCircleClass.prototype.distanceTo = function (latlng) {
736 var dest = latlngExpressiontoLatLng(latlng);
737 return this.geom.distance(this.center, dest);
738 };
739 /**
740 * Set a new center for the geodesic circle and update the geometry.
741 * @param latlng new geo-position for the center
742 */
743 GeodesicCircleClass.prototype.setLatLng = function (latlng) {
744 this.center = latlngExpressiontoLatLng(latlng);
745 this.update();
746 };
747 /**
748 * set a new radius for the geodesic circle and update the geometry
749 * @param radius new radius in meters
750 */
751 GeodesicCircleClass.prototype.setRadius = function (radius) {
752 this.radius = radius;
753 this.update();
754 };
755 return GeodesicCircleClass;
756}(L.Polyline));
757
758L.Geodesic = GeodesicLine;
759L.geodesic = function () {
760 var args = [];
761 for (var _i = 0; _i < arguments.length; _i++) {
762 args[_i] = arguments[_i];
763 }
764 return new (GeodesicLine.bind.apply(GeodesicLine, __spreadArrays([void 0], args)))();
765};
766L.GeodesicCircle = GeodesicCircleClass;
767L.geodesiccircle = function () {
768 var args = [];
769 for (var _i = 0; _i < arguments.length; _i++) {
770 args[_i] = arguments[_i];
771 }
772 return new (GeodesicCircleClass.bind.apply(GeodesicCircleClass, __spreadArrays([void 0], args)))();
773};
774
775exports.GeodesicCircleClass = GeodesicCircleClass;
776exports.GeodesicLine = GeodesicLine;