gnss-js 2.6.0 → 2.8.0
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.
- package/dist/{chunk-MIIM4LDY.js → chunk-CVCTRIWU.js} +87 -24
- package/dist/{chunk-JYWYQ7DW.js → chunk-GLMQRZDL.js} +51 -46
- package/dist/{chunk-BWLLHLLS.js → chunk-PHS7OFHN.js} +3 -24
- package/dist/coordinates.cjs +91 -26
- package/dist/coordinates.d.cts +26 -3
- package/dist/coordinates.d.ts +26 -3
- package/dist/coordinates.js +7 -3
- package/dist/index.cjs +141 -94
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -4
- package/dist/orbit.cjs +50 -46
- package/dist/orbit.d.cts +23 -6
- package/dist/orbit.d.ts +23 -6
- package/dist/orbit.js +1 -1
- package/dist/positioning.cjs +48 -69
- package/dist/positioning.js +3 -3
- package/package.json +1 -1
|
@@ -47,7 +47,12 @@ function vincenty(lat1, lon1, lat2, lon2) {
|
|
|
47
47
|
(cosU2 * sinLambda2) ** 2 + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda2) ** 2
|
|
48
48
|
);
|
|
49
49
|
if (sinSigma === 0) {
|
|
50
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
distance: 0,
|
|
52
|
+
initialBearing: 0,
|
|
53
|
+
finalBearing: 0,
|
|
54
|
+
converged: true
|
|
55
|
+
};
|
|
51
56
|
}
|
|
52
57
|
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda2;
|
|
53
58
|
sigma = Math.atan2(sinSigma, cosSigma);
|
|
@@ -77,7 +82,7 @@ function vincenty(lat1, lon1, lat2, lon2) {
|
|
|
77
82
|
cosLat1 * Math.sin(-dLon),
|
|
78
83
|
-sinLat1 * cosLat2 + cosLat1 * sinLat2 * Math.cos(-dLon)
|
|
79
84
|
);
|
|
80
|
-
return { distance: distance2, initialBearing: ib, finalBearing: fb };
|
|
85
|
+
return { distance: distance2, initialBearing: ib, finalBearing: fb, converged: false };
|
|
81
86
|
}
|
|
82
87
|
const uSq = cos2Alpha * (WGS84_SEMI_MAJOR_AXIS ** 2 - WGS84_SEMI_MINOR_AXIS ** 2) / WGS84_SEMI_MINOR_AXIS ** 2;
|
|
83
88
|
const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
|
|
@@ -94,10 +99,53 @@ function vincenty(lat1, lon1, lat2, lon2) {
|
|
|
94
99
|
cosU1 * sinLambda,
|
|
95
100
|
-sinU1 * cosU2 + cosU1 * sinU2 * cosLambda
|
|
96
101
|
);
|
|
97
|
-
return { distance, initialBearing, finalBearing };
|
|
102
|
+
return { distance, initialBearing, finalBearing, converged: true };
|
|
103
|
+
}
|
|
104
|
+
function vincentyDirect(lat1, lon1, bearing, distance) {
|
|
105
|
+
const a = WGS84_SEMI_MAJOR_AXIS, b = WGS84_SEMI_MINOR_AXIS, f = WGS84_FLATTENING;
|
|
106
|
+
const sinA1 = Math.sin(bearing), cosA1 = Math.cos(bearing);
|
|
107
|
+
const tanU1 = (1 - f) * Math.tan(lat1);
|
|
108
|
+
const cosU1 = 1 / Math.sqrt(1 + tanU1 ** 2), sinU1 = tanU1 * cosU1;
|
|
109
|
+
const sigma1 = Math.atan2(tanU1, cosA1);
|
|
110
|
+
const sinAlpha = cosU1 * sinA1;
|
|
111
|
+
const cos2Alpha = 1 - sinAlpha ** 2;
|
|
112
|
+
const uSq = cos2Alpha * (a ** 2 - b ** 2) / b ** 2;
|
|
113
|
+
const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
|
|
114
|
+
const B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
|
|
115
|
+
let sigma = distance / (b * A);
|
|
116
|
+
let sigmaPrev;
|
|
117
|
+
let cos2SigmaM, sinSigma, cosSigma;
|
|
118
|
+
let iter = 0;
|
|
119
|
+
do {
|
|
120
|
+
cos2SigmaM = Math.cos(2 * sigma1 + sigma);
|
|
121
|
+
sinSigma = Math.sin(sigma);
|
|
122
|
+
cosSigma = Math.cos(sigma);
|
|
123
|
+
const deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM ** 2) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma ** 2) * (-3 + 4 * cos2SigmaM ** 2)));
|
|
124
|
+
sigmaPrev = sigma;
|
|
125
|
+
sigma = distance / (b * A) + deltaSigma;
|
|
126
|
+
} while (Math.abs(sigma - sigmaPrev) > 1e-12 && ++iter < 200);
|
|
127
|
+
const tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosA1;
|
|
128
|
+
const lat2 = Math.atan2(
|
|
129
|
+
sinU1 * cosSigma + cosU1 * sinSigma * cosA1,
|
|
130
|
+
(1 - f) * Math.sqrt(sinAlpha ** 2 + tmp ** 2)
|
|
131
|
+
);
|
|
132
|
+
const lambda = Math.atan2(
|
|
133
|
+
sinSigma * sinA1,
|
|
134
|
+
cosU1 * cosSigma - sinU1 * sinSigma * cosA1
|
|
135
|
+
);
|
|
136
|
+
const C = f / 16 * cos2Alpha * (4 + f * (4 - 3 * cos2Alpha));
|
|
137
|
+
const L = lambda - (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM ** 2)));
|
|
138
|
+
let lon2 = lon1 + L;
|
|
139
|
+
lon2 = Math.atan2(Math.sin(lon2), Math.cos(lon2));
|
|
140
|
+
return [lat2, lon2];
|
|
141
|
+
}
|
|
142
|
+
function meridianArc(lat) {
|
|
143
|
+
const n = WGS84_FLATTENING / (2 - WGS84_FLATTENING);
|
|
144
|
+
const n2 = n * n, n3 = n2 * n, n4 = n3 * n;
|
|
145
|
+
const mu = lat + (-3 * n / 2 + 9 * n3 / 16) * Math.sin(2 * lat) + (15 * n2 / 16 - 15 * n4 / 32) * Math.sin(4 * lat) + -35 * n3 / 48 * Math.sin(6 * lat) + 315 * n4 / 512 * Math.sin(8 * lat);
|
|
146
|
+
return WGS84_SEMI_MAJOR_AXIS / (1 + n) * (1 + n2 / 4 + n4 / 64) * mu;
|
|
98
147
|
}
|
|
99
148
|
function rhumbLine(lat1, lon1, lat2, lon2) {
|
|
100
|
-
const dLat = lat2 - lat1;
|
|
101
149
|
let dLon = lon2 - lon1;
|
|
102
150
|
const MAX_LAT = Math.PI / 2 - 1e-10;
|
|
103
151
|
const isometricLat = (lat) => {
|
|
@@ -111,33 +159,28 @@ function rhumbLine(lat1, lon1, lat2, lon2) {
|
|
|
111
159
|
const psi1 = isometricLat(lat1);
|
|
112
160
|
const psi2 = isometricLat(lat2);
|
|
113
161
|
const dPsi = psi2 - psi1;
|
|
114
|
-
const q = Math.abs(dPsi) > 1e-12 ? dLat / dPsi : Math.cos(lat1);
|
|
115
162
|
if (Math.abs(dLon) > Math.PI) {
|
|
116
163
|
dLon = dLon > 0 ? -(2 * Math.PI - dLon) : 2 * Math.PI + dLon;
|
|
117
164
|
}
|
|
118
165
|
const bearing = Math.atan2(dLon, dPsi);
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
166
|
+
const dM = meridianArc(lat2) - meridianArc(lat1);
|
|
167
|
+
let q;
|
|
168
|
+
if (Math.abs(dPsi) > 1e-6) {
|
|
169
|
+
q = dM / dPsi;
|
|
170
|
+
} else {
|
|
171
|
+
const latM = (lat1 + lat2) / 2;
|
|
172
|
+
q = WGS84_SEMI_MAJOR_AXIS * Math.cos(latM) / Math.sqrt(1 - WGS84_ECCENTRICITY_SQUARED * Math.sin(latM) ** 2);
|
|
173
|
+
}
|
|
174
|
+
const distance = Math.sqrt(dM ** 2 + (q * dLon) ** 2);
|
|
124
175
|
return { distance, bearing };
|
|
125
176
|
}
|
|
126
177
|
function euclidean3D(x1, y1, z1, x2, y2, z2) {
|
|
127
178
|
return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2 + (z2 - z1) ** 2);
|
|
128
179
|
}
|
|
129
180
|
function greatCircleMidpoint(lat1, lon1, lat2, lon2) {
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const sumX = Math.cos(lat1) + Bx;
|
|
134
|
-
const denom = Math.sqrt(sumX ** 2 + By ** 2);
|
|
135
|
-
if (denom < 1e-15) {
|
|
136
|
-
return [(lat1 + lat2) / 2, lon1];
|
|
137
|
-
}
|
|
138
|
-
const latMid = Math.atan2(Math.sin(lat1) + Math.sin(lat2), denom);
|
|
139
|
-
const lonMid = lon1 + Math.atan2(By, sumX);
|
|
140
|
-
return [latMid, lonMid];
|
|
181
|
+
const { distance, initialBearing } = vincenty(lat1, lon1, lat2, lon2);
|
|
182
|
+
if (distance === 0) return [lat1, lon1];
|
|
183
|
+
return vincentyDirect(lat1, lon1, initialBearing, distance / 2);
|
|
141
184
|
}
|
|
142
185
|
function horizonDistance(heightMeters) {
|
|
143
186
|
if (heightMeters <= 0) return 0;
|
|
@@ -150,18 +193,36 @@ function horizonDistance(heightMeters) {
|
|
|
150
193
|
// src/coordinates/projections.ts
|
|
151
194
|
var e2 = WGS84_ECCENTRICITY_SQUARED;
|
|
152
195
|
var k0 = 0.9996;
|
|
196
|
+
var UTM_BANDS = "CDEFGHJKLMNPQRSTUVWX";
|
|
197
|
+
function utmZone(latDeg, lonDeg) {
|
|
198
|
+
let zone = Math.floor((lonDeg + 180) / 6) + 1;
|
|
199
|
+
if (zone > 60) zone = 1;
|
|
200
|
+
if (latDeg >= 56 && latDeg < 64 && lonDeg >= 3 && lonDeg < 12) return 32;
|
|
201
|
+
if (latDeg >= 72 && latDeg <= 84) {
|
|
202
|
+
if (lonDeg >= 0 && lonDeg < 9) return 31;
|
|
203
|
+
if (lonDeg >= 9 && lonDeg < 21) return 33;
|
|
204
|
+
if (lonDeg >= 21 && lonDeg < 33) return 35;
|
|
205
|
+
if (lonDeg >= 33 && lonDeg < 42) return 37;
|
|
206
|
+
}
|
|
207
|
+
return zone;
|
|
208
|
+
}
|
|
153
209
|
function geodeticToUtm(lat, lon) {
|
|
210
|
+
const rawLatDeg = lat * 180 / Math.PI;
|
|
211
|
+
const inRange = rawLatDeg >= -80 && rawLatDeg <= 84;
|
|
154
212
|
const MAX_UTM_LAT = 84 * (Math.PI / 180);
|
|
155
213
|
lat = Math.max(-MAX_UTM_LAT, Math.min(MAX_UTM_LAT, lat));
|
|
156
214
|
const latDeg = lat * 180 / Math.PI;
|
|
215
|
+
lon = Math.atan2(Math.sin(lon), Math.cos(lon));
|
|
157
216
|
const lonDeg = lon * 180 / Math.PI;
|
|
158
|
-
const zone =
|
|
217
|
+
const zone = utmZone(latDeg, lonDeg);
|
|
218
|
+
const band = UTM_BANDS[Math.max(0, Math.min(19, Math.floor((latDeg + 80) / 8)))];
|
|
159
219
|
const lon0 = ((zone - 1) * 6 - 180 + 3) * (Math.PI / 180);
|
|
160
220
|
const ep2 = e2 / (1 - e2);
|
|
161
221
|
const N = WGS84_SEMI_MAJOR_AXIS / Math.sqrt(1 - e2 * Math.sin(lat) ** 2);
|
|
162
222
|
const T = Math.tan(lat) ** 2;
|
|
163
223
|
const C = ep2 * Math.cos(lat) ** 2;
|
|
164
|
-
const
|
|
224
|
+
const dLon0 = Math.atan2(Math.sin(lon - lon0), Math.cos(lon - lon0));
|
|
225
|
+
const A = Math.cos(lat) * dLon0;
|
|
165
226
|
const M = WGS84_SEMI_MAJOR_AXIS * ((1 - e2 / 4 - 3 * e2 ** 2 / 64 - 5 * e2 ** 3 / 256) * lat - (3 * e2 / 8 + 3 * e2 ** 2 / 32 + 45 * e2 ** 3 / 1024) * Math.sin(2 * lat) + (15 * e2 ** 2 / 256 + 45 * e2 ** 3 / 1024) * Math.sin(4 * lat) - 35 * e2 ** 3 / 3072 * Math.sin(6 * lat));
|
|
166
227
|
const easting = k0 * N * (A + (1 - T + C) * A ** 3 / 6 + (5 - 18 * T + T ** 2 + 72 * C - 58 * ep2) * A ** 5 / 120) + 5e5;
|
|
167
228
|
let northing = k0 * (M + N * Math.tan(lat) * (A ** 2 / 2 + (5 - T + 9 * C + 4 * C ** 2) * A ** 4 / 24 + (61 - 58 * T + T ** 2 + 600 * C - 330 * ep2) * A ** 6 / 720));
|
|
@@ -169,7 +230,7 @@ function geodeticToUtm(lat, lon) {
|
|
|
169
230
|
if (hemisphere === "S") {
|
|
170
231
|
northing += 1e7;
|
|
171
232
|
}
|
|
172
|
-
return { easting, northing, zone, hemisphere };
|
|
233
|
+
return { easting, northing, zone, hemisphere, band, inRange };
|
|
173
234
|
}
|
|
174
235
|
function geodeticToMaidenhead(lat, lon) {
|
|
175
236
|
const latDeg = Math.max(0, Math.min(179.999999, lat * 180 / Math.PI + 90));
|
|
@@ -229,6 +290,8 @@ export {
|
|
|
229
290
|
deg2rad,
|
|
230
291
|
deg2dms,
|
|
231
292
|
vincenty,
|
|
293
|
+
vincentyDirect,
|
|
294
|
+
meridianArc,
|
|
232
295
|
rhumbLine,
|
|
233
296
|
euclidean3D,
|
|
234
297
|
greatCircleMidpoint,
|
|
@@ -13,6 +13,30 @@ import {
|
|
|
13
13
|
OMEGA_E
|
|
14
14
|
} from "./chunk-FIEWO4J4.js";
|
|
15
15
|
|
|
16
|
+
// src/orbit/matrix.ts
|
|
17
|
+
function invertMatrix(A) {
|
|
18
|
+
const n = A.length;
|
|
19
|
+
const M = A.map((row, i) => [
|
|
20
|
+
...row,
|
|
21
|
+
...Array.from({ length: n }, (_, j) => i === j ? 1 : 0)
|
|
22
|
+
]);
|
|
23
|
+
for (let col = 0; col < n; col++) {
|
|
24
|
+
let piv = col;
|
|
25
|
+
for (let r = col + 1; r < n; r++)
|
|
26
|
+
if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
|
|
27
|
+
if (Math.abs(M[piv][col]) < 1e-12) return null;
|
|
28
|
+
[M[col], M[piv]] = [M[piv], M[col]];
|
|
29
|
+
const d = M[col][col];
|
|
30
|
+
for (let j = 0; j < 2 * n; j++) M[col][j] /= d;
|
|
31
|
+
for (let r = 0; r < n; r++) {
|
|
32
|
+
if (r === col) continue;
|
|
33
|
+
const f = M[r][col];
|
|
34
|
+
for (let j = 0; j < 2 * n; j++) M[r][j] -= f * M[col][j];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return M.map((row) => row.slice(n));
|
|
38
|
+
}
|
|
39
|
+
|
|
16
40
|
// src/orbit/almanac.ts
|
|
17
41
|
var GM_GPS = 3986005e8;
|
|
18
42
|
var GM_GAL = 3986004418e5;
|
|
@@ -382,15 +406,20 @@ function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
|
|
|
382
406
|
}
|
|
383
407
|
function computeDop(satAzEls) {
|
|
384
408
|
const n = satAzEls.length;
|
|
385
|
-
|
|
409
|
+
const clocks = [...new Set(satAzEls.map((s) => s.prn?.[0] ?? ""))];
|
|
410
|
+
const m = 3 + clocks.length;
|
|
411
|
+
if (n < Math.max(4, m)) return null;
|
|
386
412
|
const H = [];
|
|
387
|
-
for (const { az, el } of satAzEls) {
|
|
413
|
+
for (const { az, el, prn } of satAzEls) {
|
|
388
414
|
const cosEl = Math.cos(el);
|
|
389
|
-
|
|
415
|
+
const row = [cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el)];
|
|
416
|
+
const sys = prn?.[0] ?? "";
|
|
417
|
+
for (const c of clocks) row.push(c === sys ? 1 : 0);
|
|
418
|
+
H.push(row);
|
|
390
419
|
}
|
|
391
|
-
const HtH = Array.from({ length:
|
|
392
|
-
for (let i = 0; i <
|
|
393
|
-
for (let j = 0; j <
|
|
420
|
+
const HtH = Array.from({ length: m }, () => new Array(m).fill(0));
|
|
421
|
+
for (let i = 0; i < m; i++) {
|
|
422
|
+
for (let j = 0; j < m; j++) {
|
|
394
423
|
let sum = 0;
|
|
395
424
|
for (let k = 0; k < n; k++) {
|
|
396
425
|
sum += H[k][i] * H[k][j];
|
|
@@ -398,46 +427,18 @@ function computeDop(satAzEls) {
|
|
|
398
427
|
HtH[i][j] = sum;
|
|
399
428
|
}
|
|
400
429
|
}
|
|
401
|
-
const Q =
|
|
430
|
+
const Q = invertMatrix(HtH);
|
|
402
431
|
if (!Q) return null;
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
inv[4] = -a[4] * a[10] * a[15] + a[4] * a[11] * a[14] + a[8] * a[6] * a[15] - a[8] * a[7] * a[14] - a[12] * a[6] * a[11] + a[12] * a[7] * a[10];
|
|
414
|
-
inv[8] = a[4] * a[9] * a[15] - a[4] * a[11] * a[13] - a[8] * a[5] * a[15] + a[8] * a[7] * a[13] + a[12] * a[5] * a[11] - a[12] * a[7] * a[9];
|
|
415
|
-
inv[12] = -a[4] * a[9] * a[14] + a[4] * a[10] * a[13] + a[8] * a[5] * a[14] - a[8] * a[6] * a[13] - a[12] * a[5] * a[10] + a[12] * a[6] * a[9];
|
|
416
|
-
inv[1] = -a[1] * a[10] * a[15] + a[1] * a[11] * a[14] + a[9] * a[2] * a[15] - a[9] * a[3] * a[14] - a[13] * a[2] * a[11] + a[13] * a[3] * a[10];
|
|
417
|
-
inv[5] = a[0] * a[10] * a[15] - a[0] * a[11] * a[14] - a[8] * a[2] * a[15] + a[8] * a[3] * a[14] + a[12] * a[2] * a[11] - a[12] * a[3] * a[10];
|
|
418
|
-
inv[9] = -a[0] * a[9] * a[15] + a[0] * a[11] * a[13] + a[8] * a[1] * a[15] - a[8] * a[3] * a[13] - a[12] * a[1] * a[11] + a[12] * a[3] * a[9];
|
|
419
|
-
inv[13] = a[0] * a[9] * a[14] - a[0] * a[10] * a[13] - a[8] * a[1] * a[14] + a[8] * a[2] * a[13] + a[12] * a[1] * a[10] - a[12] * a[2] * a[9];
|
|
420
|
-
inv[2] = a[1] * a[6] * a[15] - a[1] * a[7] * a[14] - a[5] * a[2] * a[15] + a[5] * a[3] * a[14] + a[13] * a[2] * a[7] - a[13] * a[3] * a[6];
|
|
421
|
-
inv[6] = -a[0] * a[6] * a[15] + a[0] * a[7] * a[14] + a[4] * a[2] * a[15] - a[4] * a[3] * a[14] - a[12] * a[2] * a[7] + a[12] * a[3] * a[6];
|
|
422
|
-
inv[10] = a[0] * a[5] * a[15] - a[0] * a[7] * a[13] - a[4] * a[1] * a[15] + a[4] * a[3] * a[13] + a[12] * a[1] * a[7] - a[12] * a[3] * a[5];
|
|
423
|
-
inv[14] = -a[0] * a[5] * a[14] + a[0] * a[6] * a[13] + a[4] * a[1] * a[14] - a[4] * a[2] * a[13] - a[12] * a[1] * a[6] + a[12] * a[2] * a[5];
|
|
424
|
-
inv[3] = -a[1] * a[6] * a[11] + a[1] * a[7] * a[10] + a[5] * a[2] * a[11] - a[5] * a[3] * a[10] - a[9] * a[2] * a[7] + a[9] * a[3] * a[6];
|
|
425
|
-
inv[7] = a[0] * a[6] * a[11] - a[0] * a[7] * a[10] - a[4] * a[2] * a[11] + a[4] * a[3] * a[10] + a[8] * a[2] * a[7] - a[8] * a[3] * a[6];
|
|
426
|
-
inv[11] = -a[0] * a[5] * a[11] + a[0] * a[7] * a[9] + a[4] * a[1] * a[11] - a[4] * a[3] * a[9] - a[8] * a[1] * a[7] + a[8] * a[3] * a[5];
|
|
427
|
-
inv[15] = a[0] * a[5] * a[10] - a[0] * a[6] * a[9] - a[4] * a[1] * a[10] + a[4] * a[2] * a[9] + a[8] * a[1] * a[6] - a[8] * a[2] * a[5];
|
|
428
|
-
let det = a[0] * inv[0] + a[1] * inv[4] + a[2] * inv[8] + a[3] * inv[12];
|
|
429
|
-
if (Math.abs(det) < 1e-20) return null;
|
|
430
|
-
det = 1 / det;
|
|
431
|
-
const result = Array.from(
|
|
432
|
-
{ length: 4 },
|
|
433
|
-
() => new Array(4).fill(0)
|
|
434
|
-
);
|
|
435
|
-
for (let i = 0; i < 4; i++) {
|
|
436
|
-
for (let j = 0; j < 4; j++) {
|
|
437
|
-
result[i][j] = inv[i * 4 + j] * det;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
return result;
|
|
432
|
+
let clockVar = 0;
|
|
433
|
+
for (let i = 3; i < m; i++) clockVar += Q[i][i];
|
|
434
|
+
const posVar = Q[0][0] + Q[1][1] + Q[2][2];
|
|
435
|
+
return {
|
|
436
|
+
gdop: Math.sqrt(posVar + clockVar),
|
|
437
|
+
pdop: Math.sqrt(posVar),
|
|
438
|
+
hdop: Math.sqrt(Q[0][0] + Q[1][1]),
|
|
439
|
+
vdop: Math.sqrt(Q[2][2]),
|
|
440
|
+
tdop: Math.sqrt(clockVar)
|
|
441
|
+
};
|
|
441
442
|
}
|
|
442
443
|
function selectEphemeris(ephemerides, prn, timeMs) {
|
|
443
444
|
return selectBest(
|
|
@@ -697,6 +698,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
|
|
|
697
698
|
const gdop = new Array(times.length).fill(null);
|
|
698
699
|
const hdop = new Array(times.length).fill(null);
|
|
699
700
|
const vdop = new Array(times.length).fill(null);
|
|
701
|
+
const tdop = new Array(times.length).fill(null);
|
|
700
702
|
const visiblePerEpoch = times.map(() => []);
|
|
701
703
|
for (const prn of all.prns) {
|
|
702
704
|
const series = all.positions[prn];
|
|
@@ -710,7 +712,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
|
|
|
710
712
|
visibleCount[i]++;
|
|
711
713
|
const sys = prn[0];
|
|
712
714
|
(visibleBySystem[sys] ??= new Array(times.length).fill(0))[i]++;
|
|
713
|
-
visiblePerEpoch[i].push({ az: p.az, el: p.el });
|
|
715
|
+
visiblePerEpoch[i].push({ az: p.az, el: p.el, prn });
|
|
714
716
|
}
|
|
715
717
|
}
|
|
716
718
|
}
|
|
@@ -720,6 +722,7 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
|
|
|
720
722
|
gdop[i] = dop ? dop.gdop : null;
|
|
721
723
|
hdop[i] = dop ? dop.hdop : null;
|
|
722
724
|
vdop[i] = dop ? dop.vdop : null;
|
|
725
|
+
tdop[i] = dop ? dop.tdop : null;
|
|
723
726
|
}
|
|
724
727
|
const passes = [];
|
|
725
728
|
for (const prn of all.prns) {
|
|
@@ -766,11 +769,13 @@ function computeVisibilityFromPositions(all, elevationMaskDeg = 10) {
|
|
|
766
769
|
gdop,
|
|
767
770
|
hdop,
|
|
768
771
|
vdop,
|
|
772
|
+
tdop,
|
|
769
773
|
passes
|
|
770
774
|
};
|
|
771
775
|
}
|
|
772
776
|
|
|
773
777
|
export {
|
|
778
|
+
invertMatrix,
|
|
774
779
|
almanacEpochMs,
|
|
775
780
|
almanacSatPosition,
|
|
776
781
|
glonassAlmanacEpochMs,
|
|
@@ -5,8 +5,9 @@ import {
|
|
|
5
5
|
computeDop,
|
|
6
6
|
computeSatPosition,
|
|
7
7
|
ecefToAzEl,
|
|
8
|
+
invertMatrix,
|
|
8
9
|
selectEphemeris
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-GLMQRZDL.js";
|
|
10
11
|
import {
|
|
11
12
|
ecefToGeodetic,
|
|
12
13
|
getAer,
|
|
@@ -3730,28 +3731,6 @@ function sbasAirTropoVar(elRad) {
|
|
|
3730
3731
|
const sigTropo = 0.12 * m;
|
|
3731
3732
|
return sigMp * sigMp + NOISE_DIVG_AADA * NOISE_DIVG_AADA + sigTropo * sigTropo;
|
|
3732
3733
|
}
|
|
3733
|
-
function invertMatrix(A) {
|
|
3734
|
-
const n = A.length;
|
|
3735
|
-
const M = A.map((row, i) => [
|
|
3736
|
-
...row,
|
|
3737
|
-
...Array.from({ length: n }, (_, j) => i === j ? 1 : 0)
|
|
3738
|
-
]);
|
|
3739
|
-
for (let col = 0; col < n; col++) {
|
|
3740
|
-
let piv = col;
|
|
3741
|
-
for (let r = col + 1; r < n; r++)
|
|
3742
|
-
if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
|
|
3743
|
-
if (Math.abs(M[piv][col]) < 1e-12) return null;
|
|
3744
|
-
[M[col], M[piv]] = [M[piv], M[col]];
|
|
3745
|
-
const d = M[col][col];
|
|
3746
|
-
for (let j = 0; j < 2 * n; j++) M[col][j] /= d;
|
|
3747
|
-
for (let r = 0; r < n; r++) {
|
|
3748
|
-
if (r === col) continue;
|
|
3749
|
-
const f = M[r][col];
|
|
3750
|
-
for (let j = 0; j < 2 * n; j++) M[r][j] -= f * M[col][j];
|
|
3751
|
-
}
|
|
3752
|
-
}
|
|
3753
|
-
return M.map((row) => row.slice(n));
|
|
3754
|
-
}
|
|
3755
3734
|
function sbasProtectionLevels(sats, opts = {}) {
|
|
3756
3735
|
const kH = opts.kH ?? 6;
|
|
3757
3736
|
const kV = opts.kV ?? 5.33;
|
|
@@ -3978,7 +3957,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
3978
3957
|
const azel = ecefToAzEl(x, y, z, sat.x, sat.y, sat.z);
|
|
3979
3958
|
if (azel.el >= elevationMaskDeg * Math.PI / 180) {
|
|
3980
3959
|
used.push(prn);
|
|
3981
|
-
azels.push(azel);
|
|
3960
|
+
azels.push({ ...azel, prn });
|
|
3982
3961
|
if (sbas) {
|
|
3983
3962
|
const dx = sat.x - x, dy = sat.y - y, dz = sat.z - z;
|
|
3984
3963
|
const dr = Math.hypot(dx, dy, dz) || 1;
|
package/dist/coordinates.cjs
CHANGED
|
@@ -33,9 +33,11 @@ __export(coordinates_exports, {
|
|
|
33
33
|
getEnuDifference: () => getEnuDifference,
|
|
34
34
|
greatCircleMidpoint: () => greatCircleMidpoint,
|
|
35
35
|
horizonDistance: () => horizonDistance,
|
|
36
|
+
meridianArc: () => meridianArc,
|
|
36
37
|
rad2deg: () => rad2deg,
|
|
37
38
|
rhumbLine: () => rhumbLine,
|
|
38
|
-
vincenty: () => vincenty
|
|
39
|
+
vincenty: () => vincenty,
|
|
40
|
+
vincentyDirect: () => vincentyDirect
|
|
39
41
|
});
|
|
40
42
|
module.exports = __toCommonJS(coordinates_exports);
|
|
41
43
|
|
|
@@ -140,7 +142,12 @@ function vincenty(lat1, lon1, lat2, lon2) {
|
|
|
140
142
|
(cosU2 * sinLambda2) ** 2 + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda2) ** 2
|
|
141
143
|
);
|
|
142
144
|
if (sinSigma === 0) {
|
|
143
|
-
return {
|
|
145
|
+
return {
|
|
146
|
+
distance: 0,
|
|
147
|
+
initialBearing: 0,
|
|
148
|
+
finalBearing: 0,
|
|
149
|
+
converged: true
|
|
150
|
+
};
|
|
144
151
|
}
|
|
145
152
|
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda2;
|
|
146
153
|
sigma = Math.atan2(sinSigma, cosSigma);
|
|
@@ -170,7 +177,7 @@ function vincenty(lat1, lon1, lat2, lon2) {
|
|
|
170
177
|
cosLat1 * Math.sin(-dLon),
|
|
171
178
|
-sinLat1 * cosLat2 + cosLat1 * sinLat2 * Math.cos(-dLon)
|
|
172
179
|
);
|
|
173
|
-
return { distance: distance2, initialBearing: ib, finalBearing: fb };
|
|
180
|
+
return { distance: distance2, initialBearing: ib, finalBearing: fb, converged: false };
|
|
174
181
|
}
|
|
175
182
|
const uSq = cos2Alpha * (WGS84_SEMI_MAJOR_AXIS ** 2 - WGS84_SEMI_MINOR_AXIS ** 2) / WGS84_SEMI_MINOR_AXIS ** 2;
|
|
176
183
|
const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
|
|
@@ -187,10 +194,53 @@ function vincenty(lat1, lon1, lat2, lon2) {
|
|
|
187
194
|
cosU1 * sinLambda,
|
|
188
195
|
-sinU1 * cosU2 + cosU1 * sinU2 * cosLambda
|
|
189
196
|
);
|
|
190
|
-
return { distance, initialBearing, finalBearing };
|
|
197
|
+
return { distance, initialBearing, finalBearing, converged: true };
|
|
198
|
+
}
|
|
199
|
+
function vincentyDirect(lat1, lon1, bearing, distance) {
|
|
200
|
+
const a = WGS84_SEMI_MAJOR_AXIS, b = WGS84_SEMI_MINOR_AXIS, f = WGS84_FLATTENING;
|
|
201
|
+
const sinA1 = Math.sin(bearing), cosA1 = Math.cos(bearing);
|
|
202
|
+
const tanU1 = (1 - f) * Math.tan(lat1);
|
|
203
|
+
const cosU1 = 1 / Math.sqrt(1 + tanU1 ** 2), sinU1 = tanU1 * cosU1;
|
|
204
|
+
const sigma1 = Math.atan2(tanU1, cosA1);
|
|
205
|
+
const sinAlpha = cosU1 * sinA1;
|
|
206
|
+
const cos2Alpha = 1 - sinAlpha ** 2;
|
|
207
|
+
const uSq = cos2Alpha * (a ** 2 - b ** 2) / b ** 2;
|
|
208
|
+
const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
|
|
209
|
+
const B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
|
|
210
|
+
let sigma = distance / (b * A);
|
|
211
|
+
let sigmaPrev;
|
|
212
|
+
let cos2SigmaM, sinSigma, cosSigma;
|
|
213
|
+
let iter = 0;
|
|
214
|
+
do {
|
|
215
|
+
cos2SigmaM = Math.cos(2 * sigma1 + sigma);
|
|
216
|
+
sinSigma = Math.sin(sigma);
|
|
217
|
+
cosSigma = Math.cos(sigma);
|
|
218
|
+
const deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM ** 2) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma ** 2) * (-3 + 4 * cos2SigmaM ** 2)));
|
|
219
|
+
sigmaPrev = sigma;
|
|
220
|
+
sigma = distance / (b * A) + deltaSigma;
|
|
221
|
+
} while (Math.abs(sigma - sigmaPrev) > 1e-12 && ++iter < 200);
|
|
222
|
+
const tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosA1;
|
|
223
|
+
const lat2 = Math.atan2(
|
|
224
|
+
sinU1 * cosSigma + cosU1 * sinSigma * cosA1,
|
|
225
|
+
(1 - f) * Math.sqrt(sinAlpha ** 2 + tmp ** 2)
|
|
226
|
+
);
|
|
227
|
+
const lambda = Math.atan2(
|
|
228
|
+
sinSigma * sinA1,
|
|
229
|
+
cosU1 * cosSigma - sinU1 * sinSigma * cosA1
|
|
230
|
+
);
|
|
231
|
+
const C = f / 16 * cos2Alpha * (4 + f * (4 - 3 * cos2Alpha));
|
|
232
|
+
const L = lambda - (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM ** 2)));
|
|
233
|
+
let lon2 = lon1 + L;
|
|
234
|
+
lon2 = Math.atan2(Math.sin(lon2), Math.cos(lon2));
|
|
235
|
+
return [lat2, lon2];
|
|
236
|
+
}
|
|
237
|
+
function meridianArc(lat) {
|
|
238
|
+
const n = WGS84_FLATTENING / (2 - WGS84_FLATTENING);
|
|
239
|
+
const n2 = n * n, n3 = n2 * n, n4 = n3 * n;
|
|
240
|
+
const mu = lat + (-3 * n / 2 + 9 * n3 / 16) * Math.sin(2 * lat) + (15 * n2 / 16 - 15 * n4 / 32) * Math.sin(4 * lat) + -35 * n3 / 48 * Math.sin(6 * lat) + 315 * n4 / 512 * Math.sin(8 * lat);
|
|
241
|
+
return WGS84_SEMI_MAJOR_AXIS / (1 + n) * (1 + n2 / 4 + n4 / 64) * mu;
|
|
191
242
|
}
|
|
192
243
|
function rhumbLine(lat1, lon1, lat2, lon2) {
|
|
193
|
-
const dLat = lat2 - lat1;
|
|
194
244
|
let dLon = lon2 - lon1;
|
|
195
245
|
const MAX_LAT = Math.PI / 2 - 1e-10;
|
|
196
246
|
const isometricLat = (lat) => {
|
|
@@ -204,33 +254,28 @@ function rhumbLine(lat1, lon1, lat2, lon2) {
|
|
|
204
254
|
const psi1 = isometricLat(lat1);
|
|
205
255
|
const psi2 = isometricLat(lat2);
|
|
206
256
|
const dPsi = psi2 - psi1;
|
|
207
|
-
const q = Math.abs(dPsi) > 1e-12 ? dLat / dPsi : Math.cos(lat1);
|
|
208
257
|
if (Math.abs(dLon) > Math.PI) {
|
|
209
258
|
dLon = dLon > 0 ? -(2 * Math.PI - dLon) : 2 * Math.PI + dLon;
|
|
210
259
|
}
|
|
211
260
|
const bearing = Math.atan2(dLon, dPsi);
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
261
|
+
const dM = meridianArc(lat2) - meridianArc(lat1);
|
|
262
|
+
let q;
|
|
263
|
+
if (Math.abs(dPsi) > 1e-6) {
|
|
264
|
+
q = dM / dPsi;
|
|
265
|
+
} else {
|
|
266
|
+
const latM = (lat1 + lat2) / 2;
|
|
267
|
+
q = WGS84_SEMI_MAJOR_AXIS * Math.cos(latM) / Math.sqrt(1 - WGS84_ECCENTRICITY_SQUARED * Math.sin(latM) ** 2);
|
|
268
|
+
}
|
|
269
|
+
const distance = Math.sqrt(dM ** 2 + (q * dLon) ** 2);
|
|
217
270
|
return { distance, bearing };
|
|
218
271
|
}
|
|
219
272
|
function euclidean3D(x1, y1, z1, x2, y2, z2) {
|
|
220
273
|
return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2 + (z2 - z1) ** 2);
|
|
221
274
|
}
|
|
222
275
|
function greatCircleMidpoint(lat1, lon1, lat2, lon2) {
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const sumX = Math.cos(lat1) + Bx;
|
|
227
|
-
const denom = Math.sqrt(sumX ** 2 + By ** 2);
|
|
228
|
-
if (denom < 1e-15) {
|
|
229
|
-
return [(lat1 + lat2) / 2, lon1];
|
|
230
|
-
}
|
|
231
|
-
const latMid = Math.atan2(Math.sin(lat1) + Math.sin(lat2), denom);
|
|
232
|
-
const lonMid = lon1 + Math.atan2(By, sumX);
|
|
233
|
-
return [latMid, lonMid];
|
|
276
|
+
const { distance, initialBearing } = vincenty(lat1, lon1, lat2, lon2);
|
|
277
|
+
if (distance === 0) return [lat1, lon1];
|
|
278
|
+
return vincentyDirect(lat1, lon1, initialBearing, distance / 2);
|
|
234
279
|
}
|
|
235
280
|
function horizonDistance(heightMeters) {
|
|
236
281
|
if (heightMeters <= 0) return 0;
|
|
@@ -243,18 +288,36 @@ function horizonDistance(heightMeters) {
|
|
|
243
288
|
// src/coordinates/projections.ts
|
|
244
289
|
var e2 = WGS84_ECCENTRICITY_SQUARED;
|
|
245
290
|
var k0 = 0.9996;
|
|
291
|
+
var UTM_BANDS = "CDEFGHJKLMNPQRSTUVWX";
|
|
292
|
+
function utmZone(latDeg, lonDeg) {
|
|
293
|
+
let zone = Math.floor((lonDeg + 180) / 6) + 1;
|
|
294
|
+
if (zone > 60) zone = 1;
|
|
295
|
+
if (latDeg >= 56 && latDeg < 64 && lonDeg >= 3 && lonDeg < 12) return 32;
|
|
296
|
+
if (latDeg >= 72 && latDeg <= 84) {
|
|
297
|
+
if (lonDeg >= 0 && lonDeg < 9) return 31;
|
|
298
|
+
if (lonDeg >= 9 && lonDeg < 21) return 33;
|
|
299
|
+
if (lonDeg >= 21 && lonDeg < 33) return 35;
|
|
300
|
+
if (lonDeg >= 33 && lonDeg < 42) return 37;
|
|
301
|
+
}
|
|
302
|
+
return zone;
|
|
303
|
+
}
|
|
246
304
|
function geodeticToUtm(lat, lon) {
|
|
305
|
+
const rawLatDeg = lat * 180 / Math.PI;
|
|
306
|
+
const inRange = rawLatDeg >= -80 && rawLatDeg <= 84;
|
|
247
307
|
const MAX_UTM_LAT = 84 * (Math.PI / 180);
|
|
248
308
|
lat = Math.max(-MAX_UTM_LAT, Math.min(MAX_UTM_LAT, lat));
|
|
249
309
|
const latDeg = lat * 180 / Math.PI;
|
|
310
|
+
lon = Math.atan2(Math.sin(lon), Math.cos(lon));
|
|
250
311
|
const lonDeg = lon * 180 / Math.PI;
|
|
251
|
-
const zone =
|
|
312
|
+
const zone = utmZone(latDeg, lonDeg);
|
|
313
|
+
const band = UTM_BANDS[Math.max(0, Math.min(19, Math.floor((latDeg + 80) / 8)))];
|
|
252
314
|
const lon0 = ((zone - 1) * 6 - 180 + 3) * (Math.PI / 180);
|
|
253
315
|
const ep2 = e2 / (1 - e2);
|
|
254
316
|
const N = WGS84_SEMI_MAJOR_AXIS / Math.sqrt(1 - e2 * Math.sin(lat) ** 2);
|
|
255
317
|
const T = Math.tan(lat) ** 2;
|
|
256
318
|
const C = ep2 * Math.cos(lat) ** 2;
|
|
257
|
-
const
|
|
319
|
+
const dLon0 = Math.atan2(Math.sin(lon - lon0), Math.cos(lon - lon0));
|
|
320
|
+
const A = Math.cos(lat) * dLon0;
|
|
258
321
|
const M = WGS84_SEMI_MAJOR_AXIS * ((1 - e2 / 4 - 3 * e2 ** 2 / 64 - 5 * e2 ** 3 / 256) * lat - (3 * e2 / 8 + 3 * e2 ** 2 / 32 + 45 * e2 ** 3 / 1024) * Math.sin(2 * lat) + (15 * e2 ** 2 / 256 + 45 * e2 ** 3 / 1024) * Math.sin(4 * lat) - 35 * e2 ** 3 / 3072 * Math.sin(6 * lat));
|
|
259
322
|
const easting = k0 * N * (A + (1 - T + C) * A ** 3 / 6 + (5 - 18 * T + T ** 2 + 72 * C - 58 * ep2) * A ** 5 / 120) + 5e5;
|
|
260
323
|
let northing = k0 * (M + N * Math.tan(lat) * (A ** 2 / 2 + (5 - T + 9 * C + 4 * C ** 2) * A ** 4 / 24 + (61 - 58 * T + T ** 2 + 600 * C - 330 * ep2) * A ** 6 / 720));
|
|
@@ -262,7 +325,7 @@ function geodeticToUtm(lat, lon) {
|
|
|
262
325
|
if (hemisphere === "S") {
|
|
263
326
|
northing += 1e7;
|
|
264
327
|
}
|
|
265
|
-
return { easting, northing, zone, hemisphere };
|
|
328
|
+
return { easting, northing, zone, hemisphere, band, inRange };
|
|
266
329
|
}
|
|
267
330
|
function geodeticToMaidenhead(lat, lon) {
|
|
268
331
|
const latDeg = Math.max(0, Math.min(179.999999, lat * 180 / Math.PI + 90));
|
|
@@ -331,7 +394,9 @@ function geodeticToGeohash(lat, lon, precision = 8) {
|
|
|
331
394
|
getEnuDifference,
|
|
332
395
|
greatCircleMidpoint,
|
|
333
396
|
horizonDistance,
|
|
397
|
+
meridianArc,
|
|
334
398
|
rad2deg,
|
|
335
399
|
rhumbLine,
|
|
336
|
-
vincenty
|
|
400
|
+
vincenty,
|
|
401
|
+
vincentyDirect
|
|
337
402
|
});
|
package/dist/coordinates.d.cts
CHANGED
|
@@ -14,13 +14,29 @@ declare function deg2dms(deg: number): [number, number, number];
|
|
|
14
14
|
* Vincenty's inverse formula — orthodromic (great-circle) distance,
|
|
15
15
|
* initial bearing, and final bearing on the WGS84 ellipsoid.
|
|
16
16
|
* All angles in radians.
|
|
17
|
-
* @returns { distance (m), initialBearing (rad), finalBearing (rad)
|
|
17
|
+
* @returns { distance (m), initialBearing (rad), finalBearing (rad),
|
|
18
|
+
* converged } — `converged` is false for near-antipodal pairs, where the
|
|
19
|
+
* iteration fails and a spherical approximation is returned instead
|
|
20
|
+
* (distance good to ~0.5 %, bearings unreliable).
|
|
18
21
|
*/
|
|
19
22
|
declare function vincenty(lat1: number, lon1: number, lat2: number, lon2: number): {
|
|
20
23
|
distance: number;
|
|
21
24
|
initialBearing: number;
|
|
22
25
|
finalBearing: number;
|
|
26
|
+
converged: boolean;
|
|
23
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* Vincenty's direct formula — the point reached from (lat1, lon1) after
|
|
30
|
+
* `distance` metres along the geodesic leaving at `bearing` (radians,
|
|
31
|
+
* clockwise from north) on the WGS84 ellipsoid.
|
|
32
|
+
* @returns [lat, lon] in radians, lon wrapped to (-π, π]
|
|
33
|
+
*/
|
|
34
|
+
declare function vincentyDirect(lat1: number, lon1: number, bearing: number, distance: number): [number, number];
|
|
35
|
+
/**
|
|
36
|
+
* Meridian arc length from the equator to geodetic latitude `lat` (radians)
|
|
37
|
+
* on WGS84, via the rectifying latitude (Helmert series in n, sub-mm).
|
|
38
|
+
*/
|
|
39
|
+
declare function meridianArc(lat: number): number;
|
|
24
40
|
/**
|
|
25
41
|
* Loxodromic (rhumb line) distance and constant bearing on the WGS84 ellipsoid.
|
|
26
42
|
* Uses the ellipsoidal rhumb-line formula with isometric latitudes.
|
|
@@ -41,7 +57,8 @@ declare function rhumbLine(lat1: number, lon1: number, lat2: number, lon2: numbe
|
|
|
41
57
|
*/
|
|
42
58
|
declare function euclidean3D(x1: number, y1: number, z1: number, x2: number, y2: number, z2: number): number;
|
|
43
59
|
/**
|
|
44
|
-
*
|
|
60
|
+
* Midpoint of the geodesic (shortest path on the WGS84 ellipsoid) between
|
|
61
|
+
* two points: half the Vincenty distance along the initial bearing.
|
|
45
62
|
* @returns [lat, lon] in radians
|
|
46
63
|
*/
|
|
47
64
|
declare function greatCircleMidpoint(lat1: number, lon1: number, lat2: number, lon2: number): [number, number];
|
|
@@ -57,12 +74,18 @@ declare function horizonDistance(heightMeters: number): number;
|
|
|
57
74
|
* Convert geodetic coordinates to UTM.
|
|
58
75
|
* @param lat Latitude in radians
|
|
59
76
|
* @param lon Longitude in radians
|
|
77
|
+
* @returns easting/northing (m), zone (1–60, with the Norway/Svalbard
|
|
78
|
+
* exceptions), hemisphere, MGRS latitude `band` letter (C–X), and
|
|
79
|
+
* `inRange` — false outside UTM's 80°S–84°N, where the values are
|
|
80
|
+
* computed at the clamped latitude and are not a valid UTM position.
|
|
60
81
|
*/
|
|
61
82
|
declare function geodeticToUtm(lat: number, lon: number): {
|
|
62
83
|
easting: number;
|
|
63
84
|
northing: number;
|
|
64
85
|
zone: number;
|
|
65
86
|
hemisphere: 'N' | 'S';
|
|
87
|
+
band: string;
|
|
88
|
+
inRange: boolean;
|
|
66
89
|
};
|
|
67
90
|
/**
|
|
68
91
|
* Maidenhead grid locator (6 characters) from geodetic coordinates.
|
|
@@ -78,4 +101,4 @@ declare function geodeticToMaidenhead(lat: number, lon: number): string;
|
|
|
78
101
|
*/
|
|
79
102
|
declare function geodeticToGeohash(lat: number, lon: number, precision?: number): string;
|
|
80
103
|
|
|
81
|
-
export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, rad2deg, rhumbLine, vincenty };
|
|
104
|
+
export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, meridianArc, rad2deg, rhumbLine, vincenty, vincentyDirect };
|