gnss-js 2.6.0 → 2.7.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/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 +89 -24
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -2
- package/dist/positioning.js +1 -1
- 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,
|
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 };
|
package/dist/coordinates.d.ts
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 };
|
package/dist/coordinates.js
CHANGED
|
@@ -7,10 +7,12 @@ import {
|
|
|
7
7
|
geodeticToUtm,
|
|
8
8
|
greatCircleMidpoint,
|
|
9
9
|
horizonDistance,
|
|
10
|
+
meridianArc,
|
|
10
11
|
rad2deg,
|
|
11
12
|
rhumbLine,
|
|
12
|
-
vincenty
|
|
13
|
-
|
|
13
|
+
vincenty,
|
|
14
|
+
vincentyDirect
|
|
15
|
+
} from "./chunk-CVCTRIWU.js";
|
|
14
16
|
import {
|
|
15
17
|
clampUnit,
|
|
16
18
|
ecefToGeodetic,
|
|
@@ -33,7 +35,9 @@ export {
|
|
|
33
35
|
getEnuDifference,
|
|
34
36
|
greatCircleMidpoint,
|
|
35
37
|
horizonDistance,
|
|
38
|
+
meridianArc,
|
|
36
39
|
rad2deg,
|
|
37
40
|
rhumbLine,
|
|
38
|
-
vincenty
|
|
41
|
+
vincenty,
|
|
42
|
+
vincentyDirect
|
|
39
43
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -261,6 +261,7 @@ __export(src_exports, {
|
|
|
261
261
|
lambdaSearch: () => lambdaSearch,
|
|
262
262
|
looksLikeRtcm2: () => looksLikeRtcm2,
|
|
263
263
|
maskRadForAzimuth: () => maskRadForAzimuth,
|
|
264
|
+
meridianArc: () => meridianArc,
|
|
264
265
|
moonEcef: () => moonEcef,
|
|
265
266
|
navTimesFromEph: () => navTimesFromEph,
|
|
266
267
|
niellMapping: () => niellMapping,
|
|
@@ -355,6 +356,7 @@ __export(src_exports, {
|
|
|
355
356
|
updateStreamStats: () => updateStreamStats,
|
|
356
357
|
verifyChecksum: () => verifyChecksum,
|
|
357
358
|
vincenty: () => vincenty,
|
|
359
|
+
vincentyDirect: () => vincentyDirect,
|
|
358
360
|
wlWavelength: () => wlWavelength,
|
|
359
361
|
writeCrx: () => writeCrx,
|
|
360
362
|
writeModernObsBlob: () => writeModernObsBlob,
|
|
@@ -1125,7 +1127,12 @@ function vincenty(lat1, lon1, lat2, lon2) {
|
|
|
1125
1127
|
(cosU2 * sinLambda2) ** 2 + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda2) ** 2
|
|
1126
1128
|
);
|
|
1127
1129
|
if (sinSigma === 0) {
|
|
1128
|
-
return {
|
|
1130
|
+
return {
|
|
1131
|
+
distance: 0,
|
|
1132
|
+
initialBearing: 0,
|
|
1133
|
+
finalBearing: 0,
|
|
1134
|
+
converged: true
|
|
1135
|
+
};
|
|
1129
1136
|
}
|
|
1130
1137
|
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda2;
|
|
1131
1138
|
sigma = Math.atan2(sinSigma, cosSigma);
|
|
@@ -1155,7 +1162,7 @@ function vincenty(lat1, lon1, lat2, lon2) {
|
|
|
1155
1162
|
cosLat1 * Math.sin(-dLon),
|
|
1156
1163
|
-sinLat1 * cosLat2 + cosLat1 * sinLat2 * Math.cos(-dLon)
|
|
1157
1164
|
);
|
|
1158
|
-
return { distance: distance2, initialBearing: ib, finalBearing: fb };
|
|
1165
|
+
return { distance: distance2, initialBearing: ib, finalBearing: fb, converged: false };
|
|
1159
1166
|
}
|
|
1160
1167
|
const uSq = cos2Alpha * (WGS84_SEMI_MAJOR_AXIS ** 2 - WGS84_SEMI_MINOR_AXIS ** 2) / WGS84_SEMI_MINOR_AXIS ** 2;
|
|
1161
1168
|
const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
|
|
@@ -1172,10 +1179,53 @@ function vincenty(lat1, lon1, lat2, lon2) {
|
|
|
1172
1179
|
cosU1 * sinLambda,
|
|
1173
1180
|
-sinU1 * cosU2 + cosU1 * sinU2 * cosLambda
|
|
1174
1181
|
);
|
|
1175
|
-
return { distance, initialBearing, finalBearing };
|
|
1182
|
+
return { distance, initialBearing, finalBearing, converged: true };
|
|
1183
|
+
}
|
|
1184
|
+
function vincentyDirect(lat1, lon1, bearing, distance) {
|
|
1185
|
+
const a = WGS84_SEMI_MAJOR_AXIS, b = WGS84_SEMI_MINOR_AXIS, f = WGS84_FLATTENING;
|
|
1186
|
+
const sinA1 = Math.sin(bearing), cosA1 = Math.cos(bearing);
|
|
1187
|
+
const tanU1 = (1 - f) * Math.tan(lat1);
|
|
1188
|
+
const cosU1 = 1 / Math.sqrt(1 + tanU1 ** 2), sinU1 = tanU1 * cosU1;
|
|
1189
|
+
const sigma1 = Math.atan2(tanU1, cosA1);
|
|
1190
|
+
const sinAlpha = cosU1 * sinA1;
|
|
1191
|
+
const cos2Alpha = 1 - sinAlpha ** 2;
|
|
1192
|
+
const uSq = cos2Alpha * (a ** 2 - b ** 2) / b ** 2;
|
|
1193
|
+
const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
|
|
1194
|
+
const B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
|
|
1195
|
+
let sigma = distance / (b * A);
|
|
1196
|
+
let sigmaPrev;
|
|
1197
|
+
let cos2SigmaM, sinSigma, cosSigma;
|
|
1198
|
+
let iter = 0;
|
|
1199
|
+
do {
|
|
1200
|
+
cos2SigmaM = Math.cos(2 * sigma1 + sigma);
|
|
1201
|
+
sinSigma = Math.sin(sigma);
|
|
1202
|
+
cosSigma = Math.cos(sigma);
|
|
1203
|
+
const deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM ** 2) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma ** 2) * (-3 + 4 * cos2SigmaM ** 2)));
|
|
1204
|
+
sigmaPrev = sigma;
|
|
1205
|
+
sigma = distance / (b * A) + deltaSigma;
|
|
1206
|
+
} while (Math.abs(sigma - sigmaPrev) > 1e-12 && ++iter < 200);
|
|
1207
|
+
const tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosA1;
|
|
1208
|
+
const lat2 = Math.atan2(
|
|
1209
|
+
sinU1 * cosSigma + cosU1 * sinSigma * cosA1,
|
|
1210
|
+
(1 - f) * Math.sqrt(sinAlpha ** 2 + tmp ** 2)
|
|
1211
|
+
);
|
|
1212
|
+
const lambda = Math.atan2(
|
|
1213
|
+
sinSigma * sinA1,
|
|
1214
|
+
cosU1 * cosSigma - sinU1 * sinSigma * cosA1
|
|
1215
|
+
);
|
|
1216
|
+
const C2 = f / 16 * cos2Alpha * (4 + f * (4 - 3 * cos2Alpha));
|
|
1217
|
+
const L = lambda - (1 - C2) * f * sinAlpha * (sigma + C2 * sinSigma * (cos2SigmaM + C2 * cosSigma * (-1 + 2 * cos2SigmaM ** 2)));
|
|
1218
|
+
let lon2 = lon1 + L;
|
|
1219
|
+
lon2 = Math.atan2(Math.sin(lon2), Math.cos(lon2));
|
|
1220
|
+
return [lat2, lon2];
|
|
1221
|
+
}
|
|
1222
|
+
function meridianArc(lat) {
|
|
1223
|
+
const n = WGS84_FLATTENING / (2 - WGS84_FLATTENING);
|
|
1224
|
+
const n2 = n * n, n3 = n2 * n, n4 = n3 * n;
|
|
1225
|
+
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);
|
|
1226
|
+
return WGS84_SEMI_MAJOR_AXIS / (1 + n) * (1 + n2 / 4 + n4 / 64) * mu;
|
|
1176
1227
|
}
|
|
1177
1228
|
function rhumbLine(lat1, lon1, lat2, lon2) {
|
|
1178
|
-
const dLat = lat2 - lat1;
|
|
1179
1229
|
let dLon = lon2 - lon1;
|
|
1180
1230
|
const MAX_LAT = Math.PI / 2 - 1e-10;
|
|
1181
1231
|
const isometricLat = (lat) => {
|
|
@@ -1189,33 +1239,28 @@ function rhumbLine(lat1, lon1, lat2, lon2) {
|
|
|
1189
1239
|
const psi1 = isometricLat(lat1);
|
|
1190
1240
|
const psi2 = isometricLat(lat2);
|
|
1191
1241
|
const dPsi = psi2 - psi1;
|
|
1192
|
-
const q = Math.abs(dPsi) > 1e-12 ? dLat / dPsi : Math.cos(lat1);
|
|
1193
1242
|
if (Math.abs(dLon) > Math.PI) {
|
|
1194
1243
|
dLon = dLon > 0 ? -(2 * Math.PI - dLon) : 2 * Math.PI + dLon;
|
|
1195
1244
|
}
|
|
1196
1245
|
const bearing = Math.atan2(dLon, dPsi);
|
|
1197
|
-
const
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1246
|
+
const dM = meridianArc(lat2) - meridianArc(lat1);
|
|
1247
|
+
let q;
|
|
1248
|
+
if (Math.abs(dPsi) > 1e-6) {
|
|
1249
|
+
q = dM / dPsi;
|
|
1250
|
+
} else {
|
|
1251
|
+
const latM = (lat1 + lat2) / 2;
|
|
1252
|
+
q = WGS84_SEMI_MAJOR_AXIS * Math.cos(latM) / Math.sqrt(1 - WGS84_ECCENTRICITY_SQUARED * Math.sin(latM) ** 2);
|
|
1253
|
+
}
|
|
1254
|
+
const distance = Math.sqrt(dM ** 2 + (q * dLon) ** 2);
|
|
1202
1255
|
return { distance, bearing };
|
|
1203
1256
|
}
|
|
1204
1257
|
function euclidean3D(x12, y1, z1, x22, y2, z2) {
|
|
1205
1258
|
return Math.sqrt((x22 - x12) ** 2 + (y2 - y1) ** 2 + (z2 - z1) ** 2);
|
|
1206
1259
|
}
|
|
1207
1260
|
function greatCircleMidpoint(lat1, lon1, lat2, lon2) {
|
|
1208
|
-
const
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
const sumX = Math.cos(lat1) + Bx;
|
|
1212
|
-
const denom = Math.sqrt(sumX ** 2 + By ** 2);
|
|
1213
|
-
if (denom < 1e-15) {
|
|
1214
|
-
return [(lat1 + lat2) / 2, lon1];
|
|
1215
|
-
}
|
|
1216
|
-
const latMid = Math.atan2(Math.sin(lat1) + Math.sin(lat2), denom);
|
|
1217
|
-
const lonMid = lon1 + Math.atan2(By, sumX);
|
|
1218
|
-
return [latMid, lonMid];
|
|
1261
|
+
const { distance, initialBearing } = vincenty(lat1, lon1, lat2, lon2);
|
|
1262
|
+
if (distance === 0) return [lat1, lon1];
|
|
1263
|
+
return vincentyDirect(lat1, lon1, initialBearing, distance / 2);
|
|
1219
1264
|
}
|
|
1220
1265
|
function horizonDistance(heightMeters) {
|
|
1221
1266
|
if (heightMeters <= 0) return 0;
|
|
@@ -1228,18 +1273,36 @@ function horizonDistance(heightMeters) {
|
|
|
1228
1273
|
// src/coordinates/projections.ts
|
|
1229
1274
|
var e2 = WGS84_ECCENTRICITY_SQUARED;
|
|
1230
1275
|
var k0 = 0.9996;
|
|
1276
|
+
var UTM_BANDS = "CDEFGHJKLMNPQRSTUVWX";
|
|
1277
|
+
function utmZone(latDeg, lonDeg) {
|
|
1278
|
+
let zone = Math.floor((lonDeg + 180) / 6) + 1;
|
|
1279
|
+
if (zone > 60) zone = 1;
|
|
1280
|
+
if (latDeg >= 56 && latDeg < 64 && lonDeg >= 3 && lonDeg < 12) return 32;
|
|
1281
|
+
if (latDeg >= 72 && latDeg <= 84) {
|
|
1282
|
+
if (lonDeg >= 0 && lonDeg < 9) return 31;
|
|
1283
|
+
if (lonDeg >= 9 && lonDeg < 21) return 33;
|
|
1284
|
+
if (lonDeg >= 21 && lonDeg < 33) return 35;
|
|
1285
|
+
if (lonDeg >= 33 && lonDeg < 42) return 37;
|
|
1286
|
+
}
|
|
1287
|
+
return zone;
|
|
1288
|
+
}
|
|
1231
1289
|
function geodeticToUtm(lat, lon) {
|
|
1290
|
+
const rawLatDeg = lat * 180 / Math.PI;
|
|
1291
|
+
const inRange = rawLatDeg >= -80 && rawLatDeg <= 84;
|
|
1232
1292
|
const MAX_UTM_LAT = 84 * (Math.PI / 180);
|
|
1233
1293
|
lat = Math.max(-MAX_UTM_LAT, Math.min(MAX_UTM_LAT, lat));
|
|
1234
1294
|
const latDeg = lat * 180 / Math.PI;
|
|
1295
|
+
lon = Math.atan2(Math.sin(lon), Math.cos(lon));
|
|
1235
1296
|
const lonDeg = lon * 180 / Math.PI;
|
|
1236
|
-
const zone =
|
|
1297
|
+
const zone = utmZone(latDeg, lonDeg);
|
|
1298
|
+
const band = UTM_BANDS[Math.max(0, Math.min(19, Math.floor((latDeg + 80) / 8)))];
|
|
1237
1299
|
const lon0 = ((zone - 1) * 6 - 180 + 3) * (Math.PI / 180);
|
|
1238
1300
|
const ep2 = e2 / (1 - e2);
|
|
1239
1301
|
const N = WGS84_SEMI_MAJOR_AXIS / Math.sqrt(1 - e2 * Math.sin(lat) ** 2);
|
|
1240
1302
|
const T = Math.tan(lat) ** 2;
|
|
1241
1303
|
const C2 = ep2 * Math.cos(lat) ** 2;
|
|
1242
|
-
const
|
|
1304
|
+
const dLon0 = Math.atan2(Math.sin(lon - lon0), Math.cos(lon - lon0));
|
|
1305
|
+
const A = Math.cos(lat) * dLon0;
|
|
1243
1306
|
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));
|
|
1244
1307
|
const easting = k0 * N * (A + (1 - T + C2) * A ** 3 / 6 + (5 - 18 * T + T ** 2 + 72 * C2 - 58 * ep2) * A ** 5 / 120) + 5e5;
|
|
1245
1308
|
let northing = k0 * (M + N * Math.tan(lat) * (A ** 2 / 2 + (5 - T + 9 * C2 + 4 * C2 ** 2) * A ** 4 / 24 + (61 - 58 * T + T ** 2 + 600 * C2 - 330 * ep2) * A ** 6 / 720));
|
|
@@ -1247,7 +1310,7 @@ function geodeticToUtm(lat, lon) {
|
|
|
1247
1310
|
if (hemisphere === "S") {
|
|
1248
1311
|
northing += 1e7;
|
|
1249
1312
|
}
|
|
1250
|
-
return { easting, northing, zone, hemisphere };
|
|
1313
|
+
return { easting, northing, zone, hemisphere, band, inRange };
|
|
1251
1314
|
}
|
|
1252
1315
|
function geodeticToMaidenhead(lat, lon) {
|
|
1253
1316
|
const latDeg = Math.max(0, Math.min(179.999999, lat * 180 / Math.PI + 90));
|
|
@@ -19692,6 +19755,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
19692
19755
|
lambdaSearch,
|
|
19693
19756
|
looksLikeRtcm2,
|
|
19694
19757
|
maskRadForAzimuth,
|
|
19758
|
+
meridianArc,
|
|
19695
19759
|
moonEcef,
|
|
19696
19760
|
navTimesFromEph,
|
|
19697
19761
|
niellMapping,
|
|
@@ -19786,6 +19850,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
19786
19850
|
updateStreamStats,
|
|
19787
19851
|
verifyChecksum,
|
|
19788
19852
|
vincenty,
|
|
19853
|
+
vincentyDirect,
|
|
19789
19854
|
wlWavelength,
|
|
19790
19855
|
writeCrx,
|
|
19791
19856
|
writeModernObsBlob,
|
package/dist/index.d.cts
CHANGED
|
@@ -3,7 +3,7 @@ export { D as DAYS_MJD2000_MJD, a as DAYS_MJD_JULIAN, H as HourCode, M as MILLIS
|
|
|
3
3
|
export { WGS84_ECCENTRICITY_SQUARED, WGS84_FLATTENING, WGS84_SEMI_MAJOR_AXIS, WGS84_SEMI_MINOR_AXIS } from './constants.cjs';
|
|
4
4
|
export { A as ARC_GAP_FACTOR, B as BAND_LABELS, a as BDS_SATELLITES, b as BdsSatellite, C as C_LIGHT, D as DEFAULT_ELEV_MASK_DEG, c as DUAL_FREQ_PAIRS, F as FREQ, G as GLO_CHANNEL_FALLBACK, d as GLO_F1_BASE, e as GLO_F1_STEP, f as GLO_F2_BASE, g as GLO_F2_STEP, h as GLO_F3, O as OMEGA_E, i as OnSlipDetected, S as SYSTEM_NAMES, j as SYS_SHORT, k as buildGloChannelMap, l as buildObsIndices, m as formatUTCTime, n as getFreq, o as gloFreq } from './gnss-BtXrG3zH.cjs';
|
|
5
5
|
export { Scale, getBdsTime, getDateFromBdsTime, getDateFromDayOfWeek, getDateFromDayOfYear, getDateFromGalTime, getDateFromGloN, getDateFromGpsData, getDateFromGpsTime, getDateFromHourCode, getDateFromJulianDate, getDateFromMJD, getDateFromMJD2000, getDateFromRINEX, getDateFromTai, getDateFromTimeOfDay, getDateFromTt, getDateFromUnixTime, getDateFromUtc, getDayOfWeek, getDayOfYear, getGalTime, getGloN4, getGloNA, getGpsLeap, getGpsTime, getHourCode, getHoursFromTimeDifference, getJulianDate, getLeap, getMJD, getMJD2000, getMinutesFromTimeDifference, getNtpTime, getRINEX, getSecondsFromTimeDifference, getTaiDate, getTimeDifference, getTimeDifferenceFromDays, getTimeDifferenceFromHours, getTimeDifferenceFromMinutes, getTimeDifferenceFromObject, getTimeDifferenceFromSeconds, getTimeOfDay, getTimeOfWeek, getTotalDaysFromTimeDifference, getTtDate, getUnixTime, getUtcDate, getWeekNumber, getWeekOfYear } from './time.cjs';
|
|
6
|
-
export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, rad2deg, rhumbLine, vincenty } from './coordinates.cjs';
|
|
6
|
+
export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, meridianArc, rad2deg, rhumbLine, vincenty, vincentyDirect } from './coordinates.cjs';
|
|
7
7
|
export { c as clampUnit, e as ecefToGeodetic, g as geodeticToEcef, a as getAer, b as getEnuDifference } from './ecef-CF0uAysr.cjs';
|
|
8
8
|
export { BiasRecord, BiasSinex, CompactEpoch, CrxField, DiffState, EMPTY_WARNINGS, Nav4Input, RinexWarning, RinexWarnings, WarningAccumulator, WarningSeverity, WriteCrxOptions, biasMetres, createGzipLineSink, crxDecompress, crxRepair, findSatBias, fmtD, fmtF, hdrLine, padL, padR, parseBiasSinex, parseCrxDataLine, stationHeaderLines, writeCrx, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexNav4, writeRinexObsBlob } from './rinex.cjs';
|
|
9
9
|
export { E as EpochSummary, P as ParseOptions, R as RinexHeader, a as RinexResult, b as RinexStats, S as SYSTEM_ORDER, c as SatObsCallback, p as parseRinexStream, s as systemCmp, d as systemName } from './parser-JPjjFgeP.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { D as DAYS_MJD2000_MJD, a as DAYS_MJD_JULIAN, H as HourCode, M as MILLIS
|
|
|
3
3
|
export { WGS84_ECCENTRICITY_SQUARED, WGS84_FLATTENING, WGS84_SEMI_MAJOR_AXIS, WGS84_SEMI_MINOR_AXIS } from './constants.js';
|
|
4
4
|
export { A as ARC_GAP_FACTOR, B as BAND_LABELS, a as BDS_SATELLITES, b as BdsSatellite, C as C_LIGHT, D as DEFAULT_ELEV_MASK_DEG, c as DUAL_FREQ_PAIRS, F as FREQ, G as GLO_CHANNEL_FALLBACK, d as GLO_F1_BASE, e as GLO_F1_STEP, f as GLO_F2_BASE, g as GLO_F2_STEP, h as GLO_F3, O as OMEGA_E, i as OnSlipDetected, S as SYSTEM_NAMES, j as SYS_SHORT, k as buildGloChannelMap, l as buildObsIndices, m as formatUTCTime, n as getFreq, o as gloFreq } from './gnss-DhnEr0Dy.js';
|
|
5
5
|
export { Scale, getBdsTime, getDateFromBdsTime, getDateFromDayOfWeek, getDateFromDayOfYear, getDateFromGalTime, getDateFromGloN, getDateFromGpsData, getDateFromGpsTime, getDateFromHourCode, getDateFromJulianDate, getDateFromMJD, getDateFromMJD2000, getDateFromRINEX, getDateFromTai, getDateFromTimeOfDay, getDateFromTt, getDateFromUnixTime, getDateFromUtc, getDayOfWeek, getDayOfYear, getGalTime, getGloN4, getGloNA, getGpsLeap, getGpsTime, getHourCode, getHoursFromTimeDifference, getJulianDate, getLeap, getMJD, getMJD2000, getMinutesFromTimeDifference, getNtpTime, getRINEX, getSecondsFromTimeDifference, getTaiDate, getTimeDifference, getTimeDifferenceFromDays, getTimeDifferenceFromHours, getTimeDifferenceFromMinutes, getTimeDifferenceFromObject, getTimeDifferenceFromSeconds, getTimeOfDay, getTimeOfWeek, getTotalDaysFromTimeDifference, getTtDate, getUnixTime, getUtcDate, getWeekNumber, getWeekOfYear } from './time.js';
|
|
6
|
-
export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, rad2deg, rhumbLine, vincenty } from './coordinates.js';
|
|
6
|
+
export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, meridianArc, rad2deg, rhumbLine, vincenty, vincentyDirect } from './coordinates.js';
|
|
7
7
|
export { c as clampUnit, e as ecefToGeodetic, g as geodeticToEcef, a as getAer, b as getEnuDifference } from './ecef-CF0uAysr.js';
|
|
8
8
|
export { BiasRecord, BiasSinex, CompactEpoch, CrxField, DiffState, EMPTY_WARNINGS, Nav4Input, RinexWarning, RinexWarnings, WarningAccumulator, WarningSeverity, WriteCrxOptions, biasMetres, createGzipLineSink, crxDecompress, crxRepair, findSatBias, fmtD, fmtF, hdrLine, padL, padR, parseBiasSinex, parseCrxDataLine, stationHeaderLines, writeCrx, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexNav4, writeRinexObsBlob } from './rinex.js';
|
|
9
9
|
export { E as EpochSummary, P as ParseOptions, R as RinexHeader, a as RinexResult, b as RinexStats, S as SYSTEM_ORDER, c as SatObsCallback, p as parseRinexStream, s as systemCmp, d as systemName } from './parser-JPjjFgeP.js';
|
package/dist/index.js
CHANGED
|
@@ -280,10 +280,12 @@ import {
|
|
|
280
280
|
geodeticToUtm,
|
|
281
281
|
greatCircleMidpoint,
|
|
282
282
|
horizonDistance,
|
|
283
|
+
meridianArc,
|
|
283
284
|
rad2deg,
|
|
284
285
|
rhumbLine,
|
|
285
|
-
vincenty
|
|
286
|
-
|
|
286
|
+
vincenty,
|
|
287
|
+
vincentyDirect
|
|
288
|
+
} from "./chunk-CVCTRIWU.js";
|
|
287
289
|
import {
|
|
288
290
|
clampUnit,
|
|
289
291
|
ecefToGeodetic,
|
|
@@ -638,6 +640,7 @@ export {
|
|
|
638
640
|
lambdaSearch,
|
|
639
641
|
looksLikeRtcm2,
|
|
640
642
|
maskRadForAzimuth,
|
|
643
|
+
meridianArc,
|
|
641
644
|
moonEcef,
|
|
642
645
|
navTimesFromEph,
|
|
643
646
|
niellMapping,
|
|
@@ -732,6 +735,7 @@ export {
|
|
|
732
735
|
updateStreamStats,
|
|
733
736
|
verifyChecksum,
|
|
734
737
|
vincenty,
|
|
738
|
+
vincentyDirect,
|
|
735
739
|
wlWavelength,
|
|
736
740
|
writeCrx,
|
|
737
741
|
writeModernObsBlob,
|
package/dist/positioning.js
CHANGED
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
import "./chunk-WQX354KD.js";
|
|
45
45
|
import "./chunk-JYWYQ7DW.js";
|
|
46
46
|
import "./chunk-HVXYFUCB.js";
|
|
47
|
-
import "./chunk-
|
|
47
|
+
import "./chunk-CVCTRIWU.js";
|
|
48
48
|
import "./chunk-37QNKGTC.js";
|
|
49
49
|
import "./chunk-LEEU5OIO.js";
|
|
50
50
|
import "./chunk-6FAL6P4G.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gnss-js",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Comprehensive GNSS library for JavaScript — time scales, coordinates, RINEX parsing, RTCM3 decoding, orbit computation, and signal analysis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|