Comparing version 2.3.0 to 2.4.0
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | ||
/* Vincenty Direct and Inverse Solution of Geodesics on the Ellipsoid (c) Chris Veness 2002-2021 */ | ||
/* Vincenty Direct and Inverse Solution of Geodesics on the Ellipsoid (c) Chris Veness 2002-2022 */ | ||
/* MIT Licence */ | ||
@@ -226,3 +226,3 @@ /* www.movable-type.co.uk/scripts/latlong-vincenty.html */ | ||
σ = s / (b*A) + Δσ; | ||
} while (Math.abs(σ-σʹ) > 1e-12 && ++iterations<100); | ||
} while (Math.abs(σ-σʹ) > 1e-12 && ++iterations<100); // TV: 'iterate until negligible change in λ' (≈0.006mm) | ||
if (iterations >= 100) throw new EvalError('Vincenty formula failed to converge'); // not possible? | ||
@@ -289,3 +289,3 @@ | ||
sinSqσ = (cosU2*sinλ)**2 + (cosU1*sinU2-sinU1*cosU2*cosλ)**2; | ||
if (Math.abs(sinSqσ) < ε) break; // co-incident/antipodal points (falls back on λ/σ = L) | ||
if (Math.abs(sinSqσ) < 1e-24) break; // co-incident/antipodal points (σ < ≈0.006mm) | ||
sinσ = Math.sqrt(sinSqσ); | ||
@@ -302,3 +302,3 @@ cosσ = sinU1*sinU2 + cosU1*cosU2*cosλ; | ||
if (iterationCheck > π) throw new EvalError('λ > π'); | ||
} while (Math.abs(λ-λʹ) > 1e-12 && ++iterations<1000); | ||
} while (Math.abs(λ-λʹ) > 1e-12 && ++iterations<1000); // TV: 'iterate until negligible change in λ' (≈0.006mm) | ||
if (iterations >= 1000) throw new EvalError('Vincenty formula failed to converge'); | ||
@@ -305,0 +305,0 @@ |
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | ||
/* Geodesy tools for an ellipsoidal earth model (c) Chris Veness 2005-2019 */ | ||
/* Geodesy tools for an ellipsoidal earth model (c) Chris Veness 2005-2022 */ | ||
/* MIT Licence */ | ||
/* Core class for latlon-ellipsoidal-datum & latlon-ellipsoidal-referenceframe. */ | ||
/* */ | ||
/* www.movable-type.co.uk/scripts/latlong-convert-coords.html */ | ||
@@ -80,5 +82,5 @@ /* www.movable-type.co.uk/scripts/geodesy-library.html#latlon-ellipsoidal */ | ||
constructor(lat, lon, height=0) { | ||
if (isNaN(lat)) throw new TypeError(`invalid lat ‘${lat}’`); | ||
if (isNaN(lon)) throw new TypeError(`invalid lon ‘${lon}’`); | ||
if (isNaN(height)) throw new TypeError(`invalid height ‘${height}’`); | ||
if (isNaN(lat) || lat == null) throw new TypeError(`invalid lat ‘${lat}’`); | ||
if (isNaN(lon) || lon == null) throw new TypeError(`invalid lon ‘${lon}’`); | ||
if (isNaN(height) || height == null) throw new TypeError(`invalid height ‘${height}’`); | ||
@@ -85,0 +87,0 @@ this._lat = Dms.wrap90(Number(lat)); |
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | ||
/* Vector-based spherical geodetic (latitude/longitude) functions (c) Chris Veness 2011-2021 */ | ||
/* Vector-based spherical geodetic (latitude/longitude) functions (c) Chris Veness 2011-2022 */ | ||
/* MIT Licence */ | ||
@@ -543,3 +543,3 @@ /* www.movable-type.co.uk/scripts/latlong-vectors.html */ | ||
* @param {LatLon} point2 - End point of great circle segment. | ||
* @returns {LatLon} point on segment. | ||
* @returns {LatLon} Closest point on segment. | ||
* | ||
@@ -718,8 +718,4 @@ * @example | ||
// close the polygon so that the last point equals the first point | ||
const closed = polygon[0].equals(polygon[polygon.length-1]); | ||
if (!closed) polygon.push(polygon[0]); | ||
const nVertices = polygon.length; | ||
const nVertices = polygon.length - 1; | ||
const p = this.toNvector(); | ||
@@ -738,4 +734,2 @@ | ||
if (!closed) polygon.pop(); // restore polygon to pristine condition | ||
return Math.abs(Σθ) > π; | ||
@@ -762,17 +756,12 @@ } | ||
// close the polygon so that the last point equals the first point | ||
const closed = polygon[0].equals(polygon[polygon.length-1]); | ||
if (!closed) polygon.push(polygon[0]); | ||
const n = polygon.length - 1; // number of vertices | ||
// get great-circle vector for each segment | ||
// get great-circle vector representing each segment | ||
const c = []; | ||
for (let v=0; v<n; v++) { | ||
for (let v=0; v<polygon.length; v++) { | ||
if (polygon[v].equals(polygon[(v+1) % polygon.length])) continue; // ignore final vertex of closed polygon | ||
const i = polygon[v].toNvector(); | ||
const j = polygon[v+1].toNvector(); | ||
c[v] = i.cross(j); // great circle for segment v..v+1 | ||
const j = polygon[(v+1) % polygon.length].toNvector(); | ||
c.push(i.cross(j)); // great circle for segment v..v+1 | ||
} | ||
c.push(c[0]); | ||
const n = c.length; // number of segments (≡ distinct vertices) | ||
// sum interior angles; depending on whether polygon is cw or ccw, angle between edges is | ||
@@ -784,3 +773,3 @@ // π−α or π+α, where α is angle between great-circle vectors; so sum α, then take n·π − |Σα| | ||
let Σα = 0; | ||
for (let v=0; v<n; v++) Σα += c[v].angleTo(c[v+1], n1); | ||
for (let v=0; v<n; v++) Σα += c[v].angleTo(c[(v+1) % n], n1); | ||
const Σθ = n*π - Math.abs(Σα); | ||
@@ -792,7 +781,5 @@ | ||
const E = (Σθ - (n-2)*π); // spherical excess (in steradians) | ||
const A = E * R*R; // area in units of R² | ||
const E = Σθ - (n-2)*π; // spherical excess (in steradians) | ||
const A = E * R*R; // area (in units of R²) | ||
if (!closed) polygon.pop(); // restore polygon to pristine condition | ||
return A; | ||
@@ -803,2 +790,36 @@ } | ||
/** | ||
* Calculates the centre of a spherical polygon where the sides of the polygon are great circle | ||
* arcs joining the vertices. | ||
* | ||
* Based on a ‘non-obvious application of Stokes’ theorem’ giving C = Σ[a×b / |a×b| ⋅ θab/2] for | ||
* each pair of consecutive vertices a, b; stackoverflow.com/questions/19897187#answer-38201499. | ||
* | ||
* @param {LatLon[]} polygon - Array of points defining vertices of the polygon. | ||
* @returns {LatLon} Centre point of the polygon. | ||
* | ||
* @example | ||
* const polygon = [ new LatLon(0, 0), new LatLon(1, 0), new LatLon(1, 1), new LatLon(0, 1) ]; | ||
* const centre = LatLon.centreOf(polygon); // 0.500°N, 0.500°E | ||
*/ | ||
static centreOf(polygon) { | ||
let centreV = new NvectorSpherical(0, 0, 0); | ||
for (let vertex=0; vertex<polygon.length; vertex++) { | ||
const a = polygon[vertex].toNvector(); // current vertex | ||
const b = polygon[(vertex+1) % polygon.length].toNvector(); // next vertex | ||
const v = a.cross(b).unit().times(a.angleTo(b)/2); // a×b / |a×b| ⋅ θab/2 | ||
centreV = centreV.plus(v); | ||
} | ||
// if centreV is pointing in opposite direction to 1st vertex (depending on cw/ccw), negate it | ||
const θ = centreV.angleTo(polygon[0].toNvector()); | ||
if (θ > π/2) centreV = centreV.negate(); | ||
const centreP = new NvectorSpherical(centreV.x, centreV.y, centreV.z).toLatLon(); | ||
return centreP; | ||
} | ||
static centerOf(polygon) { return LatLonNvectorSpherical.centreOf(polygon); } // for en-us American English | ||
/** | ||
* Returns point representing geographic mean of supplied points. | ||
@@ -805,0 +826,0 @@ * |
@@ -11,3 +11,3 @@ { | ||
"author": "Chris Veness", | ||
"version": "2.3.0", | ||
"version": "2.4.0", | ||
"license": "MIT", | ||
@@ -14,0 +14,0 @@ "type": "module", |
@@ -99,3 +99,3 @@ Geodesy functions | ||
<script type="module"> | ||
import LatLon from 'https://cdn.jsdelivr.net/npm/geodesy@2.3.0/latlon-spherical.min.js'; | ||
import LatLon from 'https://cdn.jsdelivr.net/npm/geodesy@2.4.0/latlon-spherical.min.js'; | ||
@@ -132,11 +132,13 @@ const p1 = new LatLon(50.06632, -5.71475); | ||
import LatLon from 'geodesy/latlon-nvector-ellipsoidal.js'; | ||
import LatLonV from 'geodesy/latlon-ellipsoidal-vincenty.js'; | ||
```javascript | ||
import LatLon from 'geodesy/latlon-nvector-ellipsoidal.js'; | ||
import LatLonV from 'geodesy/latlon-ellipsoidal-vincenty.js'; | ||
for (const method of Object.getOwnPropertyNames(LatLonV.prototype)) { | ||
LatLon.prototype[method] = LatLonV.prototype[method]; | ||
} | ||
for (const method of Object.getOwnPropertyNames(LatLonV.prototype)) { | ||
LatLon.prototype[method] = LatLonV.prototype[method]; | ||
} | ||
const d = new LatLon(51, 0).distanceTo(new LatLon(52, 1)); | ||
const δ = new LatLon(51, 0).deltaTo(new LatLon(52, 1)); | ||
const d = new LatLon(51, 0).distanceTo(new LatLon(52, 1)); // vincenty | ||
const δ = new LatLon(51, 0).deltaTo(new LatLon(52, 1)); // n-vector | ||
``` | ||
@@ -143,0 +145,0 @@ More care is of course required if there are conflicting constructors or method names. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
271025
4779
239