/*! Leaflet.Geodesic 2.5.2 - (c) Henry Thasler - https://github.com/henrythasler/Leaflet.Geodesic */
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('leaflet')) :
    typeof define === 'function' && define.amd ? define(['exports', 'leaflet'], factory) :
    (global = global || self, factory((global.L = global.L || {}, global.L.geodesic = {}), global.L));
}(this, (function (exports, L) { 'use strict';

    L = L && L.hasOwnProperty('default') ? L['default'] : L;

    /*! *****************************************************************************
    Copyright (c) Microsoft Corporation. All rights reserved.
    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
    this file except in compliance with the License. You may obtain a copy of the
    License at http://www.apache.org/licenses/LICENSE-2.0

    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
    MERCHANTABLITY OR NON-INFRINGEMENT.

    See the Apache Version 2.0 License for specific language governing permissions
    and limitations under the License.
    ***************************************************************************** */
    /* global Reflect, Promise */

    var extendStatics = function(d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };

    function __extends(d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    }

    var __assign = function() {
        __assign = Object.assign || function __assign(t) {
            for (var s, i = 1, n = arguments.length; i < n; i++) {
                s = arguments[i];
                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
            }
            return t;
        };
        return __assign.apply(this, arguments);
    };

    function __spreadArrays() {
        for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
        for (var r = Array(s), k = 0, i = 0; i < il; i++)
            for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
                r[k] = a[j];
        return r;
    }

    var GeodesicCore = /** @class */ (function () {
        function GeodesicCore(options) {
            this.options = { wrap: true, steps: 3 };
            this.ellipsoid = {
                a: 6378137,
                b: 6356752.3142,
                f: 1 / 298.257223563
            }; // WGS-84
            this.options = __assign(__assign({}, this.options), options);
        }
        GeodesicCore.prototype.toRadians = function (degree) {
            return degree * Math.PI / 180;
        };
        GeodesicCore.prototype.toDegrees = function (radians) {
            return radians * 180 / Math.PI;
        };
        /**
         * implements scientific modulus
         * source: http://www.codeavenger.com/2017/05/19/JavaScript-Modulo-operation-and-the-Caesar-Cipher.html
         * @param n
         * @param p
         * @return
         */
        GeodesicCore.prototype.mod = function (n, p) {
            var r = n % p;
            return r < 0 ? r + p : r;
        };
        /**
         * source: https://github.com/chrisveness/geodesy/blob/master/dms.js
         * @param degrees arbitrary value
         * @return degrees between 0..360
         */
        GeodesicCore.prototype.wrap360 = function (degrees) {
            if (0 <= degrees && degrees < 360) {
                return degrees; // avoid rounding due to arithmetic ops if within range
            }
            else {
                return this.mod(degrees, 360);
            }
        };
        /**
         * general wrap function with arbitrary max value
         * @param degrees arbitrary value
         * @param max
         * @return degrees between `-max`..`+max`
         */
        GeodesicCore.prototype.wrap = function (degrees, max) {
            if (max === void 0) { max = 360; }
            if (-max <= degrees && degrees <= max) {
                return degrees;
            }
            else {
                return this.mod((degrees + max), 2 * max) - max;
            }
        };
        /**
         * Vincenty direct calculation.
         * based on the work of Chris Veness (https://github.com/chrisveness/geodesy)
         * source: https://github.com/chrisveness/geodesy/blob/master/latlon-ellipsoidal-vincenty.js
         *
         * @param start starting point
         * @param bearing initial bearing (in degrees)
         * @param distance distance from starting point to calculate along given bearing in meters.
         * @param maxInterations How many iterations can be made to reach the allowed deviation (`ε`), before an error will be thrown.
         * @return Final point (destination point) and bearing (in degrees)
         */
        GeodesicCore.prototype.direct = function (start, bearing, distance, maxInterations) {
            if (maxInterations === void 0) { maxInterations = 100; }
            var φ1 = this.toRadians(start.lat);
            var λ1 = this.toRadians(start.lng);
            var α1 = this.toRadians(bearing);
            var s = distance;
            var ε = Number.EPSILON * 1000;
            var _a = this.ellipsoid, a = _a.a, b = _a.b, f = _a.f;
            var sinα1 = Math.sin(α1);
            var cosα1 = Math.cos(α1);
            var tanU1 = (1 - f) * Math.tan(φ1), cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
            var σ1 = Math.atan2(tanU1, cosα1); // σ1 = angular distance on the sphere from the equator to P1
            var sinα = cosU1 * sinα1; // α = azimuth of the geodesic at the equator
            var cosSqα = 1 - sinα * sinα;
            var uSq = cosSqα * (a * a - b * b) / (b * b);
            var A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
            var B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
            var σ = s / (b * A), sinσ = null, cosσ = null, Δσ = null; // σ = angular distance P₁ P₂ on the sphere
            var cos2σₘ = null; // σₘ = angular distance on the sphere from the equator to the midpoint of the line
            var σʹ = null, iterations = 0;
            do {
                cos2σₘ = Math.cos(2 * σ1 + σ);
                sinσ = Math.sin(σ);
                cosσ = Math.cos(σ);
                Δσ = B * sinσ * (cos2σₘ + B / 4 * (cosσ * (-1 + 2 * cos2σₘ * cos2σₘ) -
                    B / 6 * cos2σₘ * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σₘ * cos2σₘ)));
                σʹ = σ;
                σ = s / (b * A) + Δσ;
            } while (Math.abs(σ - σʹ) > ε && ++iterations < maxInterations);
            if (iterations >= maxInterations) {
                throw new EvalError("Direct vincenty formula failed to converge after " + maxInterations + " iterations \n                (start=" + start.lat + "/" + start.lng + "; bearing=" + bearing + "; distance=" + distance + ")"); // not possible?
            }
            var x = sinU1 * sinσ - cosU1 * cosσ * cosα1;
            var φ2 = Math.atan2(sinU1 * cosσ + cosU1 * sinσ * cosα1, (1 - f) * Math.sqrt(sinα * sinα + x * x));
            var λ = Math.atan2(sinσ * sinα1, cosU1 * cosσ - sinU1 * sinσ * cosα1);
            var C = f / 16 * cosSqα * (4 + f * (4 - 3 * cosSqα));
            var dL = λ - (1 - C) * f * sinα * (σ + C * sinσ * (cos2σₘ + C * cosσ * (-1 + 2 * cos2σₘ * cos2σₘ)));
            var λ2 = λ1 + dL;
            var α2 = Math.atan2(sinα, -x);
            return {
                lat: this.toDegrees(φ2),
                lng: this.toDegrees(λ2),
                bearing: this.wrap360(this.toDegrees(α2))
            };
        };
        /**
         * Vincenty inverse calculation.
         * based on the work of Chris Veness (https://github.com/chrisveness/geodesy)
         * source: https://github.com/chrisveness/geodesy/blob/master/latlon-ellipsoidal-vincenty.js
         *
         * @param start Latitude/longitude of starting point.
         * @param dest Latitude/longitude of destination point.
         * @return Object including distance, initialBearing, finalBearing.
         */
        GeodesicCore.prototype.inverse = function (start, dest, maxInterations, mitigateConvergenceError) {
            if (maxInterations === void 0) { maxInterations = 100; }
            if (mitigateConvergenceError === void 0) { mitigateConvergenceError = true; }
            var p1 = start, p2 = dest;
            var φ1 = this.toRadians(p1.lat), λ1 = this.toRadians(p1.lng);
            var φ2 = this.toRadians(p2.lat), λ2 = this.toRadians(p2.lng);
            var π = Math.PI;
            var ε = Number.EPSILON;
            // allow alternative ellipsoid to be specified
            var _a = this.ellipsoid, a = _a.a, b = _a.b, f = _a.f;
            var dL = λ2 - λ1; // L = difference in longitude, U = reduced latitude, defined by tan U = (1-f)·tanφ.
            var tanU1 = (1 - f) * Math.tan(φ1), cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
            var tanU2 = (1 - f) * Math.tan(φ2), cosU2 = 1 / Math.sqrt((1 + tanU2 * tanU2)), sinU2 = tanU2 * cosU2;
            var antipodal = Math.abs(dL) > π / 2 || Math.abs(φ2 - φ1) > π / 2;
            var λ = dL, sinλ = null, cosλ = null; // λ = difference in longitude on an auxiliary sphere
            var σ = antipodal ? π : 0, sinσ = 0, cosσ = antipodal ? -1 : 1, sinSqσ = null; // σ = angular distance P₁ P₂ on the sphere
            var cos2σₘ = 1; // σₘ = angular distance on the sphere from the equator to the midpoint of the line
            var sinα = null, cosSqα = 1; // α = azimuth of the geodesic at the equator
            var C = null;
            var λʹ = null, iterations = 0;
            do {
                sinλ = Math.sin(λ);
                cosλ = Math.cos(λ);
                sinSqσ = (cosU2 * sinλ) * (cosU2 * sinλ) + (cosU1 * sinU2 - sinU1 * cosU2 * cosλ) * (cosU1 * sinU2 - sinU1 * cosU2 * cosλ);
                if (Math.abs(sinSqσ) < ε) {
                    break; // co-incident/antipodal points (falls back on λ/σ = L)
                }
                sinσ = Math.sqrt(sinSqσ);
                cosσ = sinU1 * sinU2 + cosU1 * cosU2 * cosλ;
                σ = Math.atan2(sinσ, cosσ);
                sinα = cosU1 * cosU2 * sinλ / sinσ;
                cosSqα = 1 - sinα * sinα;
                cos2σₘ = (cosSqα !== 0) ? (cosσ - 2 * sinU1 * sinU2 / cosSqα) : 0; // on equatorial line cos²α = 0 (§6)
                C = f / 16 * cosSqα * (4 + f * (4 - 3 * cosSqα));
                λʹ = λ;
                λ = dL + (1 - C) * f * sinα * (σ + C * sinσ * (cos2σₘ + C * cosσ * (-1 + 2 * cos2σₘ * cos2σₘ)));
                var iterationCheck = antipodal ? Math.abs(λ) - π : Math.abs(λ);
                if (iterationCheck > π) {
                    throw new EvalError('λ > π');
                }
            } while (Math.abs(λ - λʹ) > 1e-12 && ++iterations < maxInterations);
            if (iterations >= maxInterations) {
                if (mitigateConvergenceError) {
                    return this.inverse(start, new L.LatLng(dest.lat, dest.lng - 0.01), maxInterations, mitigateConvergenceError);
                }
                else {
                    throw new EvalError("Inverse vincenty formula failed to converge after " + maxInterations + " iterations \n                    (start=" + start.lat + "/" + start.lng + "; dest=" + dest.lat + "/" + dest.lng + ")");
                }
            }
            var uSq = cosSqα * (a * a - b * b) / (b * b);
            var A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
            var B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
            var Δσ = B * sinσ * (cos2σₘ + B / 4 * (cosσ * (-1 + 2 * cos2σₘ * cos2σₘ) -
                B / 6 * cos2σₘ * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σₘ * cos2σₘ)));
            var s = b * A * (σ - Δσ); // s = length of the geodesic
            // note special handling of exactly antipodal points where sin²σ = 0 (due to discontinuity
            // atan2(0, 0) = 0 but atan2(this.ε, 0) = π/2 / 90°) - in which case bearing is always meridional,
            // due north (or due south!)
            // α = azimuths of the geodesic; α2 the direction P₁ P₂ produced
            var α1 = Math.abs(sinSqσ) < ε ? 0 : Math.atan2(cosU2 * sinλ, cosU1 * sinU2 - sinU1 * cosU2 * cosλ);
            var α2 = Math.abs(sinSqσ) < ε ? π : Math.atan2(cosU1 * sinλ, -sinU1 * cosU2 + cosU1 * sinU2 * cosλ);
            return {
                distance: s,
                initialBearing: Math.abs(s) < ε ? NaN : this.wrap360(this.toDegrees(α1)),
                finalBearing: Math.abs(s) < ε ? NaN : this.wrap360(this.toDegrees(α2))
            };
        };
        /**
         * Returns the point of intersection of two paths defined by position and bearing.
         * This calculation uses a spherical model of the earth. This will lead to small errors compared to an ellipsiod model.
         * based on the work of Chris Veness (https://github.com/chrisveness/geodesy)
         * source: https://github.com/chrisveness/geodesy/blob/master/latlon-spherical.js
         *
         * @param firstPos 1st path: position and bearing
         * @param firstBearing
         * @param secondPos 2nd path: position and bearing
         * @param secondBearing
         */
        GeodesicCore.prototype.intersection = function (firstPos, firstBearing, secondPos, secondBearing) {
            var φ1 = this.toRadians(firstPos.lat);
            var λ1 = this.toRadians(firstPos.lng);
            var φ2 = this.toRadians(secondPos.lat);
            var λ2 = this.toRadians(secondPos.lng);
            var θ13 = this.toRadians(firstBearing);
            var θ23 = this.toRadians(secondBearing);
            var Δφ = φ2 - φ1, Δλ = λ2 - λ1;
            var π = Math.PI;
            var ε = Number.EPSILON;
            // angular distance p1-p2
            var δ12 = 2 * Math.asin(Math.sqrt(Math.sin(Δφ / 2) * Math.sin(Δφ / 2)
                + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2)));
            if (Math.abs(δ12) < ε) {
                return firstPos; // coincident points
            }
            // initial/final bearings between points
            var cosθa = (Math.sin(φ2) - Math.sin(φ1) * Math.cos(δ12)) / (Math.sin(δ12) * Math.cos(φ1));
            var cosθb = (Math.sin(φ1) - Math.sin(φ2) * Math.cos(δ12)) / (Math.sin(δ12) * Math.cos(φ2));
            var θa = Math.acos(Math.min(Math.max(cosθa, -1), 1)); // protect against rounding errors
            var θb = Math.acos(Math.min(Math.max(cosθb, -1), 1)); // protect against rounding errors
            var θ12 = Math.sin(λ2 - λ1) > 0 ? θa : 2 * π - θa;
            var θ21 = Math.sin(λ2 - λ1) > 0 ? 2 * π - θb : θb;
            var α1 = θ13 - θ12; // angle 2-1-3
            var α2 = θ21 - θ23; // angle 1-2-3
            if (Math.sin(α1) === 0 && Math.sin(α2) === 0) {
                return null; // infinite intersections
            }
            if (Math.sin(α1) * Math.sin(α2) < 0) {
                return null; // ambiguous intersection (antipodal?)
            }
            var cosα3 = -Math.cos(α1) * Math.cos(α2) + Math.sin(α1) * Math.sin(α2) * Math.cos(δ12);
            var δ13 = Math.atan2(Math.sin(δ12) * Math.sin(α1) * Math.sin(α2), Math.cos(α2) + Math.cos(α1) * cosα3);
            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));
            var Δλ13 = Math.atan2(Math.sin(θ13) * Math.sin(δ13) * Math.cos(φ1), Math.cos(δ13) - Math.sin(φ1) * Math.sin(φ3));
            var λ3 = λ1 + Δλ13;
            return new L.LatLng(this.toDegrees(φ3), this.toDegrees(λ3));
        };
        GeodesicCore.prototype.midpoint = function (start, dest) {
            // φm = atan2( sinφ1 + sinφ2, √( (cosφ1 + cosφ2⋅cosΔλ)² + cos²φ2⋅sin²Δλ ) )
            // λm = λ1 + atan2(cosφ2⋅sinΔλ, cosφ1 + cosφ2⋅cosΔλ)
            // midpoint is sum of vectors to two points: mathforum.org/library/drmath/view/51822.html
            var φ1 = this.toRadians(start.lat);
            var λ1 = this.toRadians(start.lng);
            var φ2 = this.toRadians(dest.lat);
            var Δλ = this.toRadians(dest.lng - start.lng);
            // get cartesian coordinates for the two points
            var A = { x: Math.cos(φ1), y: 0, z: Math.sin(φ1) }; // place point A on prime meridian y=0
            var B = { x: Math.cos(φ2) * Math.cos(Δλ), y: Math.cos(φ2) * Math.sin(Δλ), z: Math.sin(φ2) };
            // vector to midpoint is sum of vectors to two points (no need to normalise)
            var C = { x: A.x + B.x, y: A.y + B.y, z: A.z + B.z };
            var φm = Math.atan2(C.z, Math.sqrt(C.x * C.x + C.y * C.y));
            var λm = λ1 + Math.atan2(C.y, C.x);
            return new L.LatLng(this.toDegrees(φm), this.toDegrees(λm));
        };
        return GeodesicCore;
    }());

    var GeodesicGeometry = /** @class */ (function () {
        function GeodesicGeometry(options) {
            this.geodesic = new GeodesicCore();
            this.steps = (options && options.steps !== undefined) ? options.steps : 3;
        }
        /**
         * A geodesic line between `start` and `dest` is created with this recursive function.
         * It calculates the geodesic midpoint between `start` and `dest` and uses this midpoint to call itself again (twice!).
         * The results are then merged into one continuous linestring.
         *
         * The number of resulting vertices (incl. `start` and `dest`) depends on the initial value for `iterations`
         * and can be calculated with: vertices == 1 + 2 ** (initialIterations + 1)
         *
         * As this is an exponential function, be extra careful to limit the initial value for `iterations` (8 results in 513 vertices).
         *
         * @param start start position
         * @param dest destination
         * @param iterations
         * @return resulting linestring
         */
        GeodesicGeometry.prototype.recursiveMidpoint = function (start, dest, iterations) {
            var geom = [start, dest];
            var midpoint = this.geodesic.midpoint(start, dest);
            if (iterations > 0) {
                geom.splice.apply(geom, __spreadArrays([0, 1], this.recursiveMidpoint(start, midpoint, iterations - 1)));
                geom.splice.apply(geom, __spreadArrays([geom.length - 2, 2], this.recursiveMidpoint(midpoint, dest, iterations - 1)));
            }
            else {
                geom.splice(1, 0, midpoint);
            }
            return geom;
        };
        /**
         * This is the wrapper-function to generate a geodesic line. It's just for future backwards-compatibility
         * if there is another algorithm used to create the actual line.
         *
         * The `steps`-property is used to define the number of resulting vertices of the linestring: vertices == 1 + 2 ** (steps + 1)
         * The value for `steps` is currently limited to 8 (513 vertices) for performance reasons until another algorithm is found.
         *
         * @param start start position
         * @param dest destination
         * @return resulting linestring
         */
        GeodesicGeometry.prototype.line = function (start, dest) {
            return this.recursiveMidpoint(start, dest, Math.min(8, this.steps));
        };
        GeodesicGeometry.prototype.multiLineString = function (latlngs) {
            var _this = this;
            var multiLineString = [];
            latlngs.forEach(function (linestring) {
                var segment = [];
                for (var j = 1; j < linestring.length; j++) {
                    segment.splice.apply(segment, __spreadArrays([segment.length - 1, 1], _this.line(linestring[j - 1], linestring[j])));
                }
                multiLineString.push(segment);
            });
            return multiLineString;
        };
        GeodesicGeometry.prototype.lineString = function (latlngs) {
            return this.multiLineString([latlngs])[0];
        };
        /**
         *
         * Is much (10x) faster than the previous implementation:
         *
         * ```
         * Benchmark (no split):  splitLine x 459,044 ops/sec ±0.53% (95 runs sampled)
         * Benchmark (split):     splitLine x 42,999 ops/sec ±0.51% (97 runs sampled)
         * ```
         *
         * @param startPosition
         * @param destPosition
         */
        GeodesicGeometry.prototype.splitLine = function (startPosition, destPosition) {
            var antimeridianWest = {
                point: new L.LatLng(89.9, -180.0000001),
                bearing: 180
            };
            var antimeridianEast = {
                point: new L.LatLng(89.9, 180.0000001),
                bearing: 180
            };
            // make a copy to work with
            var start = new L.LatLng(startPosition.lat, startPosition.lng);
            var dest = new L.LatLng(destPosition.lat, destPosition.lng);
            start.lng = this.geodesic.wrap(start.lng, 360);
            dest.lng = this.geodesic.wrap(dest.lng, 360);
            if ((dest.lng - start.lng) > 180) {
                dest.lng = dest.lng - 360;
            }
            else if ((dest.lng - start.lng) < -180) {
                dest.lng = dest.lng + 360;
            }
            var result = [[new L.LatLng(start.lat, this.geodesic.wrap(start.lng, 180)), new L.LatLng(dest.lat, this.geodesic.wrap(dest.lng, 180))]];
            // crossing antimeridian from "this" side?
            if ((start.lng >= -180) && (start.lng <= 180)) {
                // crossing the "western" antimeridian
                if (dest.lng < -180) {
                    var bearing = this.geodesic.inverse(start, dest).initialBearing;
                    var intersection = this.geodesic.intersection(start, bearing, antimeridianWest.point, antimeridianWest.bearing);
                    if (intersection) {
                        result = [[start, intersection], [new L.LatLng(intersection.lat, intersection.lng + 360), new L.LatLng(dest.lat, dest.lng + 360)]];
                    }
                }
                // crossing the "eastern" antimeridian
                else if (dest.lng > 180) {
                    var bearing = this.geodesic.inverse(start, dest).initialBearing;
                    var intersection = this.geodesic.intersection(start, bearing, antimeridianEast.point, antimeridianEast.bearing);
                    if (intersection) {
                        result = [[start, intersection], [new L.LatLng(intersection.lat, intersection.lng - 360), new L.LatLng(dest.lat, dest.lng - 360)]];
                    }
                }
            }
            // coming back over the antimeridian from the "other" side?
            else if ((dest.lng >= -180) && (dest.lng <= 180)) {
                // crossing the "western" antimeridian
                if (start.lng < -180) {
                    var bearing = this.geodesic.inverse(start, dest).initialBearing;
                    var intersection = this.geodesic.intersection(start, bearing, antimeridianWest.point, antimeridianWest.bearing);
                    if (intersection) {
                        result = [[new L.LatLng(start.lat, start.lng + 360), new L.LatLng(intersection.lat, intersection.lng + 360)], [intersection, dest]];
                    }
                }
                // crossing the "eastern" antimeridian
                else if (start.lng > 180) {
                    var bearing = this.geodesic.inverse(start, dest).initialBearing;
                    var intersection = this.geodesic.intersection(start, bearing, antimeridianWest.point, antimeridianWest.bearing);
                    if (intersection) {
                        result = [[new L.LatLng(start.lat, start.lng - 360), new L.LatLng(intersection.lat, intersection.lng - 360)], [intersection, dest]];
                    }
                }
            }
            return result;
        };
        /**
         * Linestrings of a given multilinestring that cross the antimeridian will be split in two separate linestrings.
         * This function is used to wrap lines around when they cross the antimeridian
         * It iterates over all linestrings and reconstructs the step-by-step if no split is needed.
         * In case the line was split, the linestring ends at the antimeridian and a new linestring is created for the
         * remaining points of the original linestring.
         *
         * @param multilinestring
         * @return another multilinestring where segments crossing the antimeridian are split
         */
        GeodesicGeometry.prototype.splitMultiLineString = function (multilinestring) {
            var _this = this;
            var result = [];
            multilinestring.forEach(function (linestring) {
                if (linestring.length === 1) {
                    result.push(linestring); // just a single point in linestring, no need to split
                }
                else {
                    var segment = [];
                    for (var j = 1; j < linestring.length; j++) {
                        var split = _this.splitLine(linestring[j - 1], linestring[j]);
                        segment.pop();
                        segment = segment.concat(split[0]);
                        if (split.length > 1) {
                            result.push(segment); // the line was split, so we end the linestring right here
                            segment = split[1]; // begin the new linestring with the second part of the split line
                        }
                    }
                    result.push(segment);
                }
            });
            return result;
        };
        /**
         * Creates a circular (constant radius), closed (1st pos == last pos) geodesic linestring.
         * The number of vertices is calculated with: `vertices == steps + 1` (where 1st == last)
         *
         * @param center
         * @param radius
         * @return resulting linestring
         */
        GeodesicGeometry.prototype.circle = function (center, radius) {
            var vertices = [];
            for (var i = 0; i < this.steps; i++) {
                var point = this.geodesic.direct(center, 360 / this.steps * i, radius);
                vertices.push(new L.LatLng(point.lat, point.lng));
            }
            // append first vertice to the end to close the linestring
            vertices.push(new L.LatLng(vertices[0].lat, vertices[0].lng));
            return vertices;
        };
        /**
         * Handles splitting of circles at the antimeridian.
         * @param linestring a linestring that resembles the geodesic circle
         * @return a multilinestring that consist of one or two linestrings
         */
        GeodesicGeometry.prototype.splitCircle = function (linestring) {
            var result = [];
            result = this.splitMultiLineString([linestring]);
            // If the circle was split, it results in exactly three linestrings where first and last  
            // must be re-assembled because they belong to the same "side" of the split circle.
            if (result.length === 3) {
                result[2] = __spreadArrays(result[2], result[0]);
                result.shift();
            }
            return result;
        };
        /**
         * Calculates the distance between two positions on the earths surface
         * @param start 1st position
         * @param dest 2nd position
         * @return the distance in **meters**
         */
        GeodesicGeometry.prototype.distance = function (start, dest) {
            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;
        };
        GeodesicGeometry.prototype.multilineDistance = function (multilinestring) {
            var _this = this;
            var dist = [];
            multilinestring.forEach(function (linestring) {
                var segmentDistance = 0;
                for (var j = 1; j < linestring.length; j++) {
                    segmentDistance += _this.distance(linestring[j - 1], linestring[j]);
                }
                dist.push(segmentDistance);
            });
            return dist;
        };
        GeodesicGeometry.prototype.updateStatistics = function (points, vertices) {
            var stats = {};
            stats.distanceArray = this.multilineDistance(points);
            stats.totalDistance = stats.distanceArray.reduce(function (x, y) { return x + y; }, 0);
            stats.points = 0;
            points.forEach(function (item) {
                stats.points += item.reduce(function (x) { return x + 1; }, 0);
            });
            stats.vertices = 0;
            vertices.forEach(function (item) {
                stats.vertices += item.reduce(function (x) { return x + 1; }, 0);
            });
            return stats;
        };
        return GeodesicGeometry;
    }());

    function instanceOfLatLngLiteral(object) {
        return ((typeof object === "object")
            && (object !== null)
            && ('lat' in object)
            && ('lng' in object)
            && (typeof object.lat === "number")
            && (typeof object.lng === "number"));
    }
    function instanceOfLatLngTuple(object) {
        return ((object instanceof Array)
            && (typeof object[0] === "number")
            && (typeof object[1] === "number"));
    }
    function instanceOfLatLngExpression(object) {
        if (object instanceof L.LatLng) {
            return true;
        }
        else if (instanceOfLatLngTuple(object)) {
            return true;
        }
        else if (instanceOfLatLngLiteral(object)) {
            return true;
        }
        else {
            return false;
        }
    }
    function latlngExpressiontoLatLng(input) {
        if (input instanceof L.LatLng) {
            return input;
        }
        else if (instanceOfLatLngTuple(input)) {
            return new L.LatLng(input[0], input[1]);
        }
        else if (instanceOfLatLngLiteral(input)) {
            return new L.LatLng(input.lat, input.lng);
        }
        else {
            throw new Error("L.LatLngExpression expected. Unknown object found.");
        }
    }
    function latlngExpressionArraytoLatLngArray(input) {
        var latlng = [];
        var _loop_1 = function (group) {
            // it's a 1D-Array L.LatLngExpression[]
            if (instanceOfLatLngExpression(group)) {
                var sub_1 = [];
                input.forEach(function (point) {
                    sub_1.push(latlngExpressiontoLatLng(point));
                });
                latlng.push(sub_1);
                return "break";
            }
            // it's a 2D-Array L.LatLngExpression[][]
            else if (group instanceof Array) {
                if (instanceOfLatLngExpression(group[0])) {
                    var sub_2 = [];
                    group.forEach(function (point) {
                        sub_2.push(latlngExpressiontoLatLng(point));
                    });
                    latlng.push(sub_2);
                }
                else {
                    throw new Error("L.LatLngExpression[] | L.LatLngExpression[][] expected. Unknown object found.");
                }
            }
            else {
                throw new Error("L.LatLngExpression[] | L.LatLngExpression[][] expected. Unknown object found.");
            }
        };
        for (var _i = 0, input_1 = input; _i < input_1.length; _i++) {
            var group = input_1[_i];
            var state_1 = _loop_1(group);
            if (state_1 === "break")
                break;
        }
        return latlng;
    }

    /**
     * Draw geodesic lines based on L.Polyline
     */
    var GeodesicLine = /** @class */ (function (_super) {
        __extends(GeodesicLine, _super);
        function GeodesicLine(latlngs, options) {
            var _this = _super.call(this, [], options) || this;
            /** these should be good for most use-cases */
            _this.defaultOptions = { wrap: true, steps: 3 };
            /** use this if you need some detailled info about the current geometry */
            _this.statistics = {};
            /** stores all positions that are used to create the geodesic line */
            _this.points = [];
            L.Util.setOptions(_this, __assign(__assign({}, _this.defaultOptions), options));
            _this.geom = new GeodesicGeometry(_this.options);
            if (latlngs !== undefined) {
                _this.setLatLngs(latlngs);
            }
            return _this;
        }
        /** calculates the geodesics and update the polyline-object accordingly */
        GeodesicLine.prototype.updateGeometry = function () {
            var geodesic = [];
            geodesic = this.geom.multiLineString(this.points);
            this.statistics = this.geom.updateStatistics(this.points, geodesic);
            if (this.options.wrap) {
                var split = this.geom.splitMultiLineString(geodesic);
                _super.prototype.setLatLngs.call(this, split);
            }
            else {
                _super.prototype.setLatLngs.call(this, geodesic);
            }
        };
        /**
         * overwrites the original function with additional functionality to create a geodesic line
         * @param latlngs an array (or 2d-array) of positions
         */
        GeodesicLine.prototype.setLatLngs = function (latlngs) {
            this.points = latlngExpressionArraytoLatLngArray(latlngs);
            this.updateGeometry();
            return this;
        };
        /**
         * add a given point to the geodesic line object
         * @param latlng point to add. The point will always be added to the last linestring of a multiline
         * @param latlngs define a linestring to add the new point to. Read from points-property before (e.g. `line.addLatLng(Beijing, line.points[0]);`)
         */
        GeodesicLine.prototype.addLatLng = function (latlng, latlngs) {
            var point = latlngExpressiontoLatLng(latlng);
            if (this.points.length === 0) {
                this.points.push([point]);
            }
            else {
                if (latlngs === undefined) {
                    this.points[this.points.length - 1].push(point);
                }
                else {
                    latlngs.push(point);
                }
            }
            this.updateGeometry();
            return this;
        };
        /**
         * Creates geodesic lines from a given GeoJSON-Object.
         * @param input GeoJSON-Object
         */
        GeodesicLine.prototype.fromGeoJson = function (input) {
            var latlngs = [];
            var features = [];
            if (input.type === "FeatureCollection") {
                features = input.features;
            }
            else if (input.type === "Feature") {
                features = [input];
            }
            else if (["MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon"].includes(input.type)) {
                features = [{
                        type: "Feature",
                        geometry: input,
                        properties: {}
                    }];
            }
            else {
                console.log("[Leaflet.Geodesic] fromGeoJson() - Type \"" + input.type + "\" not supported.");
            }
            features.forEach(function (feature) {
                switch (feature.geometry.type) {
                    case "MultiPoint":
                    case "LineString":
                        latlngs = __spreadArrays(latlngs, [L.GeoJSON.coordsToLatLngs(feature.geometry.coordinates, 0)]);
                        break;
                    case "MultiLineString":
                    case "Polygon":
                        latlngs = __spreadArrays(latlngs, L.GeoJSON.coordsToLatLngs(feature.geometry.coordinates, 1));
                        break;
                    case "MultiPolygon":
                        feature.geometry.coordinates.forEach(function (item) {
                            latlngs = __spreadArrays(latlngs, L.GeoJSON.coordsToLatLngs(item, 1));
                        });
                        break;
                    default:
                        console.log("[Leaflet.Geodesic] fromGeoJson() - Type \"" + feature.geometry.type + "\" not supported.");
                }
            });
            if (latlngs.length) {
                this.setLatLngs(latlngs);
            }
            return this;
        };
        /**
         * Calculates the distance between two geo-positions
         * @param start 1st position
         * @param dest 2nd position
         * @return the distance in meters
         */
        GeodesicLine.prototype.distance = function (start, dest) {
            return this.geom.distance(latlngExpressiontoLatLng(start), latlngExpressiontoLatLng(dest));
        };
        return GeodesicLine;
    }(L.Polyline));

    /**
     * Can be used to create a geodesic circle based on L.Polyline
     */
    var GeodesicCircleClass = /** @class */ (function (_super) {
        __extends(GeodesicCircleClass, _super);
        function GeodesicCircleClass(center, options) {
            var _this = _super.call(this, [], options) || this;
            _this.defaultOptions = { wrap: true, steps: 24, fill: true, noClip: true };
            _this.statistics = {};
            L.Util.setOptions(_this, __assign(__assign({}, _this.defaultOptions), options));
            // merge/set options
            var extendedOptions = _this.options;
            _this.radius = (extendedOptions.radius === undefined) ? 1000 * 1000 : extendedOptions.radius;
            _this.center = (center === undefined) ? new L.LatLng(0, 0) : latlngExpressiontoLatLng(center);
            _this.geom = new GeodesicGeometry(_this.options);
            // update the geometry
            _this.update();
            return _this;
        }
        /**
         * Updates the geometry and re-calculates some statistics
         */
        GeodesicCircleClass.prototype.update = function () {
            var circle = this.geom.circle(this.center, this.radius);
            this.statistics = this.geom.updateStatistics([[this.center]], [circle]);
            // circumfence must be re-calculated from geodesic 
            this.statistics.totalDistance = this.geom.multilineDistance([circle]).reduce(function (x, y) { return x + y; }, 0);
            if (this.options.wrap) {
                var split = this.geom.splitCircle(circle);
                _super.prototype.setLatLngs.call(this, split);
            }
            else {
                _super.prototype.setLatLngs.call(this, circle);
            }
        };
        /**
         * Calculate the distance between the current center and an arbitrary position.
         * @param latlng geo-position to calculate distance to
         * @return distance in meters
         */
        GeodesicCircleClass.prototype.distanceTo = function (latlng) {
            var dest = latlngExpressiontoLatLng(latlng);
            return this.geom.distance(this.center, dest);
        };
        /**
         * Set a new center for the geodesic circle and update the geometry. Radius may also be set.
         * @param center the new center
         * @param radius the new radius
         */
        GeodesicCircleClass.prototype.setLatLng = function (center, radius) {
            this.center = latlngExpressiontoLatLng(center);
            this.radius = radius ? radius : this.radius;
            this.update();
        };
        /**
         * Set a new radius for the geodesic circle and update the geometry. Center may also be set.
         * @param radius the new radius
         * @param center the new center
         */
        GeodesicCircleClass.prototype.setRadius = function (radius, center) {
            this.radius = radius;
            this.center = center ? latlngExpressiontoLatLng(center) : this.center;
            this.update();
        };
        return GeodesicCircleClass;
    }(L.Polyline));

    L.Geodesic = GeodesicLine;
    L.geodesic = function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
        return new (GeodesicLine.bind.apply(GeodesicLine, __spreadArrays([void 0], args)))();
    };
    L.GeodesicCircle = GeodesicCircleClass;
    L.geodesiccircle = function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
        return new (GeodesicCircleClass.bind.apply(GeodesicCircleClass, __spreadArrays([void 0], args)))();
    };

    exports.GeodesicCircleClass = GeodesicCircleClass;
    exports.GeodesicLine = GeodesicLine;

    Object.defineProperty(exports, '__esModule', { value: true });

})));
