tsgeo
Version:
TsGeo provides abstractions to geographical coordinates (including support for different ellipsoids) and allows you to calculate geographical distances between coordinates with high precision.
187 lines (186 loc) • 8.79 kB
JavaScript
"use strict";
/**
* Calculation of bearing between two points using a
* simple spherical model of the earth.
*
* @author clemdesign <contact@clemdesign.fr>
* @license https://opensource.org/licenses/MIT
* @link
*/
Object.defineProperty(exports, "__esModule", { value: true });
const Coordinate_1 = require("../Coordinate");
const MathMore_1 = require("../Functions/MathMore");
const DirectVincentyBearing_1 = require("./DirectVincentyBearing");
const InverseVincentyBearing_1 = require("./InverseVincentyBearing");
class BearingSpherical {
/**
* This method calculates the initial bearing between the
* two points.
*
* @param {Coordinate} point1
* @param {Coordinate} point2
* @returns {number} Bearing Angle
*/
calculateBearing(point1, point2) {
let inverseVincenty = this.inverseVincenty(point1, point2);
if (inverseVincenty === null)
return -999;
return inverseVincenty.getBearingInitial();
}
/**
* Calculates the final bearing between the two points.
*
* @param {Coordinate} point1
* @param {Coordinate} point2
* @returns {number}
*/
calculateFinalBearing(point1, point2) {
let inverseVincenty = this.inverseVincenty(point1, point2);
if (inverseVincenty === null)
return -999;
return inverseVincenty.getBearingFinal();
}
/**
* Calculates a destination point for the given point, bearing angle,
* and distance.
*
* @param {Coordinate} point
* @param {number} bearing the bearing angle between 0 and 360 degrees
* @param {number} distance the distance to the destination point in meters
* @returns {Coordinate}
*/
calculateDestination(point, bearing, distance) {
return this.directVincenty(point, bearing, distance).getDestination();
}
/**
* Calculates the final bearing angle for a destination point.
* The method expects a starting point point, the bearing angle,
* and the distance to destination.
*
* @param {Coordinate} point
* @param {number} bearing the bearing angle between 0 and 360 degrees
* @param {number} distance the distance to the destination point in meters
* @returns {number}
*/
calculateDestinationFinalBearing(point, bearing, distance) {
return this.directVincenty(point, bearing, distance).getBearingFinal();
}
directVincenty(point, bearing, distance) {
let phi1 = MathMore_1.MathMore.deg2rad(point.getLat());
let lambda1 = MathMore_1.MathMore.deg2rad(point.getLng());
let alpha1 = MathMore_1.MathMore.deg2rad(bearing);
let a = point.getEllipsoid().getA();
let b = point.getEllipsoid().getB();
let f = 1 / point.getEllipsoid().getF();
let sin_alpha1 = Math.sin(alpha1);
let cos_alpha1 = Math.cos(alpha1);
let tanU1 = (1 - f) * Math.tan(phi1);
let cosU1 = 1 / Math.sqrt(1 + tanU1 * tanU1);
let sinU1 = tanU1 * cosU1;
let omega1 = Math.atan2(tanU1, cos_alpha1);
let sin_alpha = cosU1 * sin_alpha1;
let cosSq_alpha = 1 - sin_alpha * sin_alpha;
let uSq = cosSq_alpha * (a * a - b * b) / (b * b);
let A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
let B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
let omega = distance / (b * A);
let iterations = 0;
let omega_s;
let cos2omega;
let sin_omega;
let cos_omega;
let teta_omega;
do {
cos2omega = Math.cos(2 * omega1 + omega);
sin_omega = Math.sin(omega);
cos_omega = Math.cos(omega);
teta_omega = B * sin_omega * (cos2omega + B / 4 * (cos_omega * (-1 + 2 * cos2omega * cos2omega) - B / 6 * cos2omega * (-3 + 4 * sin_omega * sin_omega) * (-3 + 4 * cos2omega * cos2omega)));
omega_s = omega;
omega = distance / (b * A) + teta_omega;
} while (Math.abs(omega - omega_s) > 1e-12 && ++iterations < 200);
if (iterations >= 200) {
throw null;
}
let tmp = sinU1 * sin_omega - cosU1 * cos_omega * cos_alpha1;
let phi2 = Math.atan2(sinU1 * cos_omega + cosU1 * sin_omega * cos_alpha1, (1 - f) * Math.sqrt(sin_alpha * sin_alpha + tmp * tmp));
let lambda = Math.atan2(sin_omega * sin_alpha1, cosU1 * cos_omega - sinU1 * sin_omega * cos_alpha1);
let C = f / 16 * cosSq_alpha * (4 + f * (4 - 3 * cosSq_alpha));
let L = lambda - (1 - C) * f * sin_alpha * (omega + C * sin_omega * (cos2omega + C * cos_omega * (-1 + 2 * cos2omega * cos2omega)));
let lambda2 = MathMore_1.MathMore.fmod(lambda1 + L + 3 * Math.PI, 2 * Math.PI) - Math.PI;
let alpha2 = Math.atan2(sin_alpha, -tmp);
alpha2 = MathMore_1.MathMore.fmod(alpha2 + 2 * Math.PI, 2 * Math.PI);
return new DirectVincentyBearing_1.DirectVincentyBearing(new Coordinate_1.Coordinate(MathMore_1.MathMore.rad2deg(phi2), MathMore_1.MathMore.rad2deg(lambda2), point.getEllipsoid()), MathMore_1.MathMore.rad2deg(alpha2));
}
inverseVincenty(point1, point2) {
if ((point1 === null) || (point2 === null)) {
return null;
}
let phi1 = MathMore_1.MathMore.deg2rad(point1.getLat());
let phi2 = MathMore_1.MathMore.deg2rad(point2.getLat());
let lambda1 = MathMore_1.MathMore.deg2rad(point1.getLng());
let lambda2 = MathMore_1.MathMore.deg2rad(point2.getLng());
let a = point1.getEllipsoid().getA();
let b = point1.getEllipsoid().getB();
let f = 1 / point1.getEllipsoid().getF();
let L = lambda2 - lambda1;
let tanU1 = (1 - f) * Math.tan(phi1);
let cosU1 = 1 / Math.sqrt(1 + tanU1 * tanU1);
let sinU1 = tanU1 * cosU1;
let tanU2 = (1 - f) * Math.tan(phi2);
let cosU2 = 1 / Math.sqrt(1 + tanU2 * tanU2);
let sinU2 = tanU2 * cosU2;
let lambda = L;
let iterations = 0;
let sin_lambda;
let cos_lambda;
let sinSq_omega;
let sin_omega;
let cos_omega;
let omega;
let sin_alpha;
let cosSq_alpha;
let cos2omega;
let C;
let lambda_p;
do {
sin_lambda = Math.sin(lambda);
cos_lambda = Math.cos(lambda);
sinSq_omega = (cosU2 * sin_lambda) * (cosU2 * sin_lambda)
+ (cosU1 * sinU2 - sinU1 * cosU2 * cos_lambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cos_lambda);
sin_omega = Math.sqrt(sinSq_omega);
if (sin_omega == 0) {
return null;
}
cos_omega = sinU1 * sinU2 + cosU1 * cosU2 * cos_lambda;
omega = Math.atan2(sin_omega, cos_omega);
sin_alpha = cosU1 * cosU2 * sin_lambda / sin_omega;
cosSq_alpha = 1 - sin_alpha * sin_alpha;
cos2omega = 0;
if (cosSq_alpha !== 0.0) {
cos2omega = cos_omega - 2 * sinU1 * sinU2 / cosSq_alpha;
}
C = f / 16 * cosSq_alpha * (4 + f * (4 - 3 * cosSq_alpha));
lambda_p = lambda;
lambda = L + (1 - C) * f * sin_alpha * (omega + C * sin_omega * (cos2omega + C * cos_omega * (-1 + 2 * cos2omega * cos2omega)));
} while (Math.abs(lambda - lambda_p) > 1e-12 && ++iterations < 200);
if (iterations >= 200) {
return null;
}
let uSq = cosSq_alpha * (a * a - b * b) / (b * b);
let A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
let B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
let teta_omega = B * sin_omega * (cos2omega + B / 4 * (cos_omega * (-1 + 2 * cos2omega * cos2omega) - B / 6 * cos2omega * (-3 + 4 * sin_omega * sin_omega) * (-3 + 4 * cos2omega * cos2omega)));
let s = b * A * (omega - teta_omega);
let alpha1 = Math.atan2(cosU2 * sin_lambda, cosU1 * sinU2 - sinU1 * cosU2 * cos_lambda);
let alpha2 = Math.atan2(cosU1 * sin_lambda, -sinU1 * cosU2 + cosU1 * sinU2 * cos_lambda);
alpha1 = MathMore_1.MathMore.fmod(alpha1 + 2 * Math.PI, 2 * Math.PI);
alpha2 = MathMore_1.MathMore.fmod(alpha2 + 2 * Math.PI, 2 * Math.PI);
s = MathMore_1.MathMore.round10(s, -3);
return new InverseVincentyBearing_1.InverseVincentyBearing(s, MathMore_1.MathMore.rad2deg(alpha1), MathMore_1.MathMore.rad2deg(alpha2));
}
}
/**
* Earth radius in meters.
*/
BearingSpherical.EARTH_RADIUS = 6371009.0;
exports.BearingSpherical = BearingSpherical;