@itowns/geographic 2.46.1-next.6 → 2.46.1-next.60
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/lib/CoordStars.d.ts +13 -0
- package/lib/Coordinates.d.ts +208 -0
- package/lib/Coordinates.js +21 -34
- package/lib/Crs.d.ts +111 -0
- package/lib/Crs.js +67 -11
- package/lib/Ellipsoid.d.ts +77 -0
- package/lib/Ellipsoid.js +9 -11
- package/lib/Extent.d.ts +287 -0
- package/lib/Extent.js +68 -46
- package/lib/OrientationUtils.d.ts +105 -0
- package/lib/OrientationUtils.js +42 -74
- package/lib/index.d.ts +7 -0
- package/package.json +7 -8
- package/src/Coordinates.ts +19 -24
- package/src/Crs.ts +82 -12
- package/src/Ellipsoid.ts +6 -3
- package/src/Extent.ts +62 -21
- package/src/OrientationUtils.ts +29 -10
package/lib/OrientationUtils.js
CHANGED
|
@@ -8,6 +8,9 @@ const axis = /* @__PURE__ */new Vector3().set(0, 0, 1);
|
|
|
8
8
|
const coord = /* @__PURE__ */new Coordinates('EPSG:4326', 0, 0, 0);
|
|
9
9
|
const euler = /* @__PURE__ */new Euler();
|
|
10
10
|
const quat = /* @__PURE__ */new Quaternion();
|
|
11
|
+
|
|
12
|
+
// To unify with Crs.js ProjectionLike type ?
|
|
13
|
+
|
|
11
14
|
/**
|
|
12
15
|
* The transform from the platform frame to the local East, North, Up (ENU)
|
|
13
16
|
* frame is `RotationZ(heading).RotationX(pitch).RotationY(roll)`.
|
|
@@ -19,11 +22,7 @@ const quat = /* @__PURE__ */new Quaternion();
|
|
|
19
22
|
*
|
|
20
23
|
* @returns The target quaternion
|
|
21
24
|
*/
|
|
22
|
-
export function quaternionFromRollPitchHeading() {
|
|
23
|
-
let roll = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
|
24
|
-
let pitch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
25
|
-
let heading = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
|
26
|
-
let target = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new Quaternion();
|
|
25
|
+
export function quaternionFromRollPitchHeading(roll = 0, pitch = 0, heading = 0, target = new Quaternion()) {
|
|
27
26
|
roll *= MathUtils.DEG2RAD;
|
|
28
27
|
pitch *= MathUtils.DEG2RAD;
|
|
29
28
|
heading *= MathUtils.DEG2RAD;
|
|
@@ -53,11 +52,7 @@ export function quaternionFromRollPitchHeading() {
|
|
|
53
52
|
*
|
|
54
53
|
* @returns The target quaternion
|
|
55
54
|
*/
|
|
56
|
-
export function quaternionFromOmegaPhiKappa() {
|
|
57
|
-
let omega = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
|
58
|
-
let phi = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
59
|
-
let kappa = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
|
60
|
-
let target = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new Quaternion();
|
|
55
|
+
export function quaternionFromOmegaPhiKappa(omega = 0, phi = 0, kappa = 0, target = new Quaternion()) {
|
|
61
56
|
omega *= MathUtils.DEG2RAD;
|
|
62
57
|
phi *= MathUtils.DEG2RAD;
|
|
63
58
|
kappa *= MathUtils.DEG2RAD;
|
|
@@ -76,8 +71,7 @@ export function quaternionFromOmegaPhiKappa() {
|
|
|
76
71
|
*
|
|
77
72
|
* @returns The target quaternion
|
|
78
73
|
*/
|
|
79
|
-
export function quaternionFromAttitude(attitude) {
|
|
80
|
-
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
|
|
74
|
+
export function quaternionFromAttitude(attitude, target = new Quaternion()) {
|
|
81
75
|
if ('roll' in attitude || 'pitch' in attitude || 'heading' in attitude) {
|
|
82
76
|
return quaternionFromRollPitchHeading(attitude.roll, attitude.pitch, attitude.heading, target);
|
|
83
77
|
}
|
|
@@ -97,13 +91,11 @@ export function quaternionFromAttitude(attitude) {
|
|
|
97
91
|
* @returns The target quaternion if coordinates is defined. Otherwise, a
|
|
98
92
|
* function to compute it from coordinates.
|
|
99
93
|
*/
|
|
100
|
-
export function quaternionFromEnuToGeocent(coordinates) {
|
|
101
|
-
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
|
|
94
|
+
export function quaternionFromEnuToGeocent(coordinates, target = new Quaternion()) {
|
|
102
95
|
if (coordinates) {
|
|
103
96
|
return quaternionFromEnuToGeocent()(coordinates, target);
|
|
104
97
|
}
|
|
105
|
-
return
|
|
106
|
-
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
|
|
98
|
+
return (coordinates, target = new Quaternion()) => {
|
|
107
99
|
const up = coordinates.geodesicNormal;
|
|
108
100
|
if (up.x == 0 && up.y == 0) {
|
|
109
101
|
return target.set(0, 0, 0, 1);
|
|
@@ -127,16 +119,12 @@ export function quaternionFromEnuToGeocent(coordinates) {
|
|
|
127
119
|
* @returns The target quaternion if coordinates is defined. Otherwise, a
|
|
128
120
|
* function to compute it from coordinates.
|
|
129
121
|
*/
|
|
130
|
-
export function quaternionFromGeocentToEnu(coordinates) {
|
|
131
|
-
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
|
|
122
|
+
export function quaternionFromGeocentToEnu(coordinates, target = new Quaternion()) {
|
|
132
123
|
if (coordinates) {
|
|
133
124
|
return quaternionFromGeocentToEnu()(coordinates, target);
|
|
134
125
|
}
|
|
135
126
|
const toGeocent = quaternionFromEnuToGeocent();
|
|
136
|
-
return
|
|
137
|
-
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
|
|
138
|
-
return toGeocent(coordinates, target).conjugate();
|
|
139
|
-
};
|
|
127
|
+
return (coordinates, target = new Quaternion()) => toGeocent(coordinates, target).conjugate();
|
|
140
128
|
}
|
|
141
129
|
/**
|
|
142
130
|
* Computes the rotation from a Lambert Conformal Conic (LCC) frame to the local
|
|
@@ -152,14 +140,12 @@ export function quaternionFromGeocentToEnu(coordinates) {
|
|
|
152
140
|
* @returns The target quaternion if coordinates is defined. Otherwise, a
|
|
153
141
|
* function to compute it from coordinates.
|
|
154
142
|
*/
|
|
155
|
-
export function quaternionFromLCCToEnu(proj, coordinates) {
|
|
156
|
-
let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
|
|
143
|
+
export function quaternionFromLCCToEnu(proj, coordinates, target = new Quaternion()) {
|
|
157
144
|
if (coordinates) {
|
|
158
145
|
return quaternionFromLCCToEnu(proj)(coordinates, target);
|
|
159
146
|
}
|
|
160
147
|
const sinlat0 = Math.sin(proj.lat0);
|
|
161
|
-
return
|
|
162
|
-
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
|
|
148
|
+
return (coordinates, target = new Quaternion()) => {
|
|
163
149
|
const long = coordinates.as(coord.crs, coord).longitude * MathUtils.DEG2RAD;
|
|
164
150
|
return target.setFromAxisAngle(axis, sinlat0 * (proj.long0 - long));
|
|
165
151
|
};
|
|
@@ -177,16 +163,12 @@ export function quaternionFromLCCToEnu(proj, coordinates) {
|
|
|
177
163
|
* @returns The target quaternion if coordinates is defined. Otherwise, a
|
|
178
164
|
* function to compute it from coordinates.
|
|
179
165
|
*/
|
|
180
|
-
export function quaternionFromEnuToLCC(proj, coordinates) {
|
|
181
|
-
let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
|
|
166
|
+
export function quaternionFromEnuToLCC(proj, coordinates, target = new Quaternion()) {
|
|
182
167
|
if (coordinates) {
|
|
183
168
|
return quaternionFromEnuToLCC(proj)(coordinates, target);
|
|
184
169
|
}
|
|
185
170
|
const fromLCC = quaternionFromLCCToEnu(proj);
|
|
186
|
-
return
|
|
187
|
-
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
|
|
188
|
-
return fromLCC(coordinates, target).conjugate();
|
|
189
|
-
};
|
|
171
|
+
return (coordinates, target = new Quaternion()) => fromLCC(coordinates, target).conjugate();
|
|
190
172
|
}
|
|
191
173
|
/**
|
|
192
174
|
* Computes the rotation from a Transverse Mercator frame (TMerc) to the
|
|
@@ -201,8 +183,7 @@ export function quaternionFromEnuToLCC(proj, coordinates) {
|
|
|
201
183
|
* @returns The target quaternion if coordinates is defined. Otherwise, a
|
|
202
184
|
* function to compute it from coordinates.
|
|
203
185
|
*/
|
|
204
|
-
export function quaternionFromTMercToEnu(proj, coordinates) {
|
|
205
|
-
let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
|
|
186
|
+
export function quaternionFromTMercToEnu(proj, coordinates, target = new Quaternion()) {
|
|
206
187
|
if (coordinates) {
|
|
207
188
|
return quaternionFromTMercToEnu(proj)(coordinates, target);
|
|
208
189
|
}
|
|
@@ -215,8 +196,7 @@ export function quaternionFromTMercToEnu(proj, coordinates) {
|
|
|
215
196
|
const e2 = proj.e * proj.e;
|
|
216
197
|
eta0 = e2 / (1 - e2);
|
|
217
198
|
}
|
|
218
|
-
return
|
|
219
|
-
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
|
|
199
|
+
return (coordinates, target = new Quaternion()) => {
|
|
220
200
|
coordinates.as(coord.crs, coord);
|
|
221
201
|
const long = coord.longitude * MathUtils.DEG2RAD;
|
|
222
202
|
const lat = coord.latitude * MathUtils.DEG2RAD;
|
|
@@ -244,16 +224,12 @@ export function quaternionFromTMercToEnu(proj, coordinates) {
|
|
|
244
224
|
* @returns The target quaternion if coordinates is defined. Otherwise, a
|
|
245
225
|
* function to compute it from coordinates.
|
|
246
226
|
*/
|
|
247
|
-
export function quaternionFromEnuToTMerc(proj, coordinates) {
|
|
248
|
-
let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
|
|
227
|
+
export function quaternionFromEnuToTMerc(proj, coordinates, target = new Quaternion()) {
|
|
249
228
|
if (coordinates) {
|
|
250
229
|
return quaternionFromEnuToTMerc(proj)(coordinates, target);
|
|
251
230
|
}
|
|
252
231
|
const fromTMerc = quaternionFromTMercToEnu(proj);
|
|
253
|
-
return
|
|
254
|
-
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
|
|
255
|
-
return fromTMerc(coordinates, target).conjugate();
|
|
256
|
-
};
|
|
232
|
+
return (coordinates, target = new Quaternion()) => fromTMerc(coordinates, target).conjugate();
|
|
257
233
|
}
|
|
258
234
|
/**
|
|
259
235
|
* Computes the rotation from a LongLat frame to the local East North Up
|
|
@@ -266,12 +242,8 @@ export function quaternionFromEnuToTMerc(proj, coordinates) {
|
|
|
266
242
|
* @returns The target quaternion if coordinates is defined, otherwise, a
|
|
267
243
|
* function to compute it from coordinates.
|
|
268
244
|
*/
|
|
269
|
-
export function quaternionFromLongLatToEnu(coordinates) {
|
|
270
|
-
|
|
271
|
-
return coordinates ? target.set(0, 0, 0, 1) : function (coordinates) {
|
|
272
|
-
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
|
|
273
|
-
return quaternionFromLongLatToEnu(coordinates, target);
|
|
274
|
-
};
|
|
245
|
+
export function quaternionFromLongLatToEnu(coordinates, target = new Quaternion()) {
|
|
246
|
+
return coordinates ? target.set(0, 0, 0, 1) : (coordinates, target = new Quaternion()) => quaternionFromLongLatToEnu(coordinates, target);
|
|
275
247
|
}
|
|
276
248
|
/**
|
|
277
249
|
* Computes the rotation from the local East North Up (ENU) frame to a
|
|
@@ -283,30 +255,23 @@ export function quaternionFromLongLatToEnu(coordinates) {
|
|
|
283
255
|
* @returns The target quaternion if coordinates is defined, otherwise, a
|
|
284
256
|
* function to compute it from coordinates.
|
|
285
257
|
*/
|
|
286
|
-
export function quaternionFromEnuToLongLat(coordinates) {
|
|
287
|
-
|
|
288
|
-
return coordinates ? target.set(0, 0, 0, 1) : function (coordinates) {
|
|
289
|
-
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
|
|
290
|
-
return quaternionFromEnuToLongLat(coordinates, target);
|
|
291
|
-
};
|
|
258
|
+
export function quaternionFromEnuToLongLat(coordinates, target = new Quaternion()) {
|
|
259
|
+
return coordinates ? target.set(0, 0, 0, 1) : (coordinates, target = new Quaternion()) => quaternionFromEnuToLongLat(coordinates, target);
|
|
292
260
|
}
|
|
293
261
|
/**
|
|
294
262
|
* Warns for an unimplemented projection, sets the quaternion to the
|
|
295
263
|
* identity (0,0,0,1).
|
|
296
264
|
*
|
|
297
265
|
* @param proj - the unimplemented projection (may be parsed using proj4)
|
|
266
|
+
* @param proj.projName - the name of the projection
|
|
298
267
|
* @param coordinates - the origin of the local East North Up (ENU) frame
|
|
299
268
|
* @param target - output Quaternion
|
|
300
269
|
* @returns The target quaternion if coordinates is defined, otherwise, a
|
|
301
270
|
* function to compute it from coordinates.
|
|
302
271
|
*/
|
|
303
|
-
export function quaternionUnimplemented(proj, coordinates) {
|
|
304
|
-
let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
|
|
272
|
+
export function quaternionUnimplemented(proj, coordinates, target = new Quaternion()) {
|
|
305
273
|
console.warn('This quaternion function is not implemented for projections of type', proj.projName);
|
|
306
|
-
return coordinates ? target.set(0, 0, 0, 1) :
|
|
307
|
-
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
|
|
308
|
-
return quaternionUnimplemented(proj, coordinates, target);
|
|
309
|
-
};
|
|
274
|
+
return coordinates ? target.set(0, 0, 0, 1) : (coordinates, target = new Quaternion()) => quaternionUnimplemented(proj, coordinates, target);
|
|
310
275
|
}
|
|
311
276
|
/**
|
|
312
277
|
* Compute the quaternion that models the rotation from the local East North
|
|
@@ -319,8 +284,7 @@ export function quaternionUnimplemented(proj, coordinates) {
|
|
|
319
284
|
* @returns The target quaternion if coordinates is defined, otherwise, a
|
|
320
285
|
* function to compute it from coordinates.
|
|
321
286
|
*/
|
|
322
|
-
export function quaternionFromEnuToCRS(crsOrProj, coordinates) {
|
|
323
|
-
let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
|
|
287
|
+
export function quaternionFromEnuToCRS(crsOrProj, coordinates, target = new Quaternion()) {
|
|
324
288
|
if (coordinates) {
|
|
325
289
|
return quaternionFromEnuToCRS(crsOrProj)(coordinates, target);
|
|
326
290
|
}
|
|
@@ -331,6 +295,7 @@ export function quaternionFromEnuToCRS(crsOrProj, coordinates) {
|
|
|
331
295
|
return quaternionFromEnuToGeocent();
|
|
332
296
|
case 'Lambert Tangential Conformal Conic Projection':
|
|
333
297
|
return quaternionFromEnuToLCC(proj);
|
|
298
|
+
case 'Universal Transverse Mercator System':
|
|
334
299
|
case 'Fast_Transverse_Mercator':
|
|
335
300
|
return quaternionFromEnuToTMerc(proj);
|
|
336
301
|
case 'longlat':
|
|
@@ -350,8 +315,7 @@ export function quaternionFromEnuToCRS(crsOrProj, coordinates) {
|
|
|
350
315
|
* @returns The target quaternion if coordinates is defined, otherwise, a
|
|
351
316
|
* function to compute it from coordinates.
|
|
352
317
|
*/
|
|
353
|
-
export function quaternionFromCRSToEnu(crsOrProj, coordinates) {
|
|
354
|
-
let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
|
|
318
|
+
export function quaternionFromCRSToEnu(crsOrProj, coordinates, target = new Quaternion()) {
|
|
355
319
|
if (coordinates) {
|
|
356
320
|
return quaternionFromCRSToEnu(crsOrProj)(coordinates, target);
|
|
357
321
|
}
|
|
@@ -362,6 +326,7 @@ export function quaternionFromCRSToEnu(crsOrProj, coordinates) {
|
|
|
362
326
|
return quaternionFromGeocentToEnu();
|
|
363
327
|
case 'Lambert Tangential Conformal Conic Projection':
|
|
364
328
|
return quaternionFromLCCToEnu(proj);
|
|
329
|
+
case 'Universal Transverse Mercator System':
|
|
365
330
|
case 'Fast_Transverse_Mercator':
|
|
366
331
|
return quaternionFromTMercToEnu(proj);
|
|
367
332
|
case 'longlat':
|
|
@@ -370,6 +335,7 @@ export function quaternionFromCRSToEnu(crsOrProj, coordinates) {
|
|
|
370
335
|
return quaternionUnimplemented(proj);
|
|
371
336
|
}
|
|
372
337
|
}
|
|
338
|
+
const quaternionCrs2CrsCache = {};
|
|
373
339
|
/**
|
|
374
340
|
* Return the function that computes the quaternion that represents a
|
|
375
341
|
* rotation of coordinates between two CRS frames.
|
|
@@ -381,23 +347,25 @@ export function quaternionFromCRSToEnu(crsOrProj, coordinates) {
|
|
|
381
347
|
* @returns The target quaternion if coordinates is defined, otherwise, a
|
|
382
348
|
* function to compute it from coordinates.
|
|
383
349
|
*/
|
|
384
|
-
export function quaternionFromCRSToCRS(crsIn, crsOut, coordinates) {
|
|
385
|
-
let target = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new Quaternion();
|
|
350
|
+
export function quaternionFromCRSToCRS(crsIn, crsOut, coordinates, target = new Quaternion()) {
|
|
386
351
|
if (coordinates) {
|
|
387
352
|
return quaternionFromCRSToCRS(crsIn, crsOut)(coordinates, target);
|
|
388
353
|
}
|
|
389
354
|
if (crsIn == crsOut) {
|
|
390
|
-
return
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
355
|
+
return (origin, target = new Quaternion()) => target.set(0, 0, 0, 1);
|
|
356
|
+
}
|
|
357
|
+
if (quaternionCrs2CrsCache[crsIn] && quaternionCrs2CrsCache[crsIn][crsOut]) {
|
|
358
|
+
return quaternionCrs2CrsCache[crsIn][crsOut];
|
|
394
359
|
}
|
|
395
360
|
|
|
396
361
|
// get rotations from the local East/North/Up (ENU) frame to both CRS.
|
|
397
362
|
const fromCrs = quaternionFromCRSToEnu(crsIn);
|
|
398
363
|
const toCrs = quaternionFromEnuToCRS(crsOut);
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
364
|
+
if (!quaternionCrs2CrsCache[crsIn]) {
|
|
365
|
+
quaternionCrs2CrsCache[crsIn] = {};
|
|
366
|
+
}
|
|
367
|
+
if (!quaternionCrs2CrsCache[crsIn][crsOut]) {
|
|
368
|
+
quaternionCrs2CrsCache[crsIn][crsOut] = (origin, target = new Quaternion()) => toCrs(origin, target).multiply(fromCrs(origin, quat));
|
|
369
|
+
}
|
|
370
|
+
return quaternionCrs2CrsCache[crsIn][crsOut];
|
|
403
371
|
}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as Extent, type ExtentLike } from './Extent';
|
|
2
|
+
export { default as Coordinates, type CoordinatesLike } from './Coordinates';
|
|
3
|
+
export * as CRS from './Crs';
|
|
4
|
+
export { type ProjectionLike } from './Crs';
|
|
5
|
+
export { default as CoordStars } from './CoordStars';
|
|
6
|
+
export * as OrientationUtils from './OrientationUtils';
|
|
7
|
+
export { default as Ellipsoid, ellipsoidSizes } from './Ellipsoid';
|
package/package.json
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itowns/geographic",
|
|
3
|
-
"version": "2.46.1-next.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.46.1-next.60",
|
|
4
|
+
"description": "Proj4-based three.js geodesy toolkit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|
|
8
|
-
"types": "./
|
|
8
|
+
"types": "./lib/index.d.ts",
|
|
9
9
|
"default": "./lib/index.js"
|
|
10
10
|
},
|
|
11
|
-
"types": "./
|
|
11
|
+
"types": "./lib/index.d.ts",
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build": "",
|
|
14
|
-
"lint": "eslint \"src/**/*.{js,ts,tsx}\" \"test/**/*.js\"",
|
|
15
14
|
"transpile": "tsc && cross-env BABEL_DISABLE_CACHE=1 babel src --out-dir lib --extensions .js,.ts",
|
|
16
15
|
"test-unit": "npm run base-test-unit test/unit",
|
|
17
|
-
"base-test-unit": "cross-env BABEL_DISABLE_CACHE=1 mocha --import=../../config/babel-register/register.mjs",
|
|
16
|
+
"base-test-unit": "cross-env BABEL_DISABLE_CACHE=1 NODE_OPTIONS=--no-experimental-strip-types mocha --import=../../config/babel-register/register.mjs",
|
|
18
17
|
"test-with-coverage": "c8 -n src -r html cross-env npm run test-unit",
|
|
19
18
|
"test-with-coverage_lcov": "c8 -n src --reporter=lcov cross-env npm run test-unit",
|
|
20
19
|
"watch": "npm run transpile -- --watch",
|
|
@@ -35,8 +34,8 @@
|
|
|
35
34
|
"url": "https://github.com/itowns/itowns/issues"
|
|
36
35
|
},
|
|
37
36
|
"peerDependencies": {
|
|
38
|
-
"proj4": "
|
|
39
|
-
"three": "
|
|
37
|
+
"proj4": ">=2.20.0",
|
|
38
|
+
"three": ">=0.182.0"
|
|
40
39
|
},
|
|
41
40
|
"homepage": "https://itowns.github.io/"
|
|
42
41
|
}
|
package/src/Coordinates.ts
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import { Vector3, type Vector3Like, type Matrix4, MathUtils } from 'three';
|
|
2
|
-
import proj4, { type Converter } from 'proj4';
|
|
3
2
|
import Ellipsoid from './Ellipsoid';
|
|
4
3
|
import * as CRS from './Crs';
|
|
5
4
|
|
|
6
5
|
import type { ProjectionLike } from './Crs';
|
|
7
6
|
|
|
8
7
|
const ellipsoid = /* @__PURE__ */ new Ellipsoid();
|
|
9
|
-
const projectionCache: Record<string, Record<string, Converter>> = {};
|
|
10
8
|
|
|
11
9
|
const v0 = /* @__PURE__ */ new Vector3();
|
|
12
10
|
const v1 = /* @__PURE__ */ new Vector3();
|
|
13
11
|
|
|
14
|
-
let coord0: Coordinates;
|
|
15
|
-
let coord1: Coordinates;
|
|
16
|
-
|
|
17
12
|
export interface CoordinatesLike {
|
|
18
13
|
readonly crs: string;
|
|
19
14
|
readonly x: number;
|
|
@@ -21,18 +16,6 @@ export interface CoordinatesLike {
|
|
|
21
16
|
readonly z: number;
|
|
22
17
|
}
|
|
23
18
|
|
|
24
|
-
function proj4cache(crsIn: ProjectionLike, crsOut: ProjectionLike): Converter {
|
|
25
|
-
if (!projectionCache[crsIn]) {
|
|
26
|
-
projectionCache[crsIn] = {};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (!projectionCache[crsIn][crsOut]) {
|
|
30
|
-
projectionCache[crsIn][crsOut] = proj4(crsIn, crsOut);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return projectionCache[crsIn][crsOut];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
19
|
/**
|
|
37
20
|
* A class representing a geographic or geocentric coordinate.
|
|
38
21
|
*
|
|
@@ -91,7 +74,7 @@ class Coordinates {
|
|
|
91
74
|
* @param y - y or latitude value.
|
|
92
75
|
* @param z - z or altitude value.
|
|
93
76
|
*/
|
|
94
|
-
constructor(crs: ProjectionLike, x
|
|
77
|
+
constructor(crs: ProjectionLike, x = 0, y = 0, z = 0) {
|
|
95
78
|
this.isCoordinates = true;
|
|
96
79
|
|
|
97
80
|
CRS.isValid(crs);
|
|
@@ -132,6 +115,7 @@ class Coordinates {
|
|
|
132
115
|
/**
|
|
133
116
|
* Sets the Coordinate Reference System.
|
|
134
117
|
* @param crs - Coordinate Reference System (e.g. 'EPSG:4978')
|
|
118
|
+
* @returns
|
|
135
119
|
*/
|
|
136
120
|
setCrs(crs: ProjectionLike): this {
|
|
137
121
|
CRS.isValid(crs);
|
|
@@ -145,8 +129,9 @@ class Coordinates {
|
|
|
145
129
|
* @param x - x or longitude value.
|
|
146
130
|
* @param y - y or latitude value.
|
|
147
131
|
* @param z - z or altitude value.
|
|
132
|
+
* @returns
|
|
148
133
|
*/
|
|
149
|
-
setFromValues(x
|
|
134
|
+
setFromValues(x = 0, y = 0, z = 0): this {
|
|
150
135
|
this.x = x;
|
|
151
136
|
this.y = y;
|
|
152
137
|
this.z = z;
|
|
@@ -163,8 +148,9 @@ class Coordinates {
|
|
|
163
148
|
*
|
|
164
149
|
* @param array - The source array.
|
|
165
150
|
* @param offset - Optional offset into the array. Default is 0.
|
|
151
|
+
* @returns
|
|
166
152
|
*/
|
|
167
|
-
setFromArray(array: number[], offset
|
|
153
|
+
setFromArray(array: number[], offset = 0): this {
|
|
168
154
|
return this.setFromValues(
|
|
169
155
|
array[offset],
|
|
170
156
|
array[offset + 1],
|
|
@@ -178,6 +164,7 @@ class Coordinates {
|
|
|
178
164
|
* properties.
|
|
179
165
|
*
|
|
180
166
|
* @param v - The source object.
|
|
167
|
+
* @returns
|
|
181
168
|
*/
|
|
182
169
|
setFromVector3(v: Vector3Like): this {
|
|
183
170
|
return this.setFromValues(v.x, v.y, v.z);
|
|
@@ -186,6 +173,7 @@ class Coordinates {
|
|
|
186
173
|
/**
|
|
187
174
|
* Returns a new coordinate with the same `(x, y, z)` vector and crs as this
|
|
188
175
|
* one.
|
|
176
|
+
* @returns
|
|
189
177
|
*/
|
|
190
178
|
clone(): Coordinates {
|
|
191
179
|
return new Coordinates(this.crs, this.x, this.y, this.z);
|
|
@@ -196,6 +184,7 @@ class Coordinates {
|
|
|
196
184
|
* to this coordinate.
|
|
197
185
|
*
|
|
198
186
|
* @param src - The source coordinate to copy from.
|
|
187
|
+
* @returns
|
|
199
188
|
*/
|
|
200
189
|
copy(src: CoordinatesLike): this {
|
|
201
190
|
this.crs = src.crs;
|
|
@@ -220,6 +209,7 @@ class Coordinates {
|
|
|
220
209
|
|
|
221
210
|
/**
|
|
222
211
|
* The geodesic normal of the coordinate.
|
|
212
|
+
* @returns
|
|
223
213
|
*/
|
|
224
214
|
get geodesicNormal() {
|
|
225
215
|
if (this._normalNeedsUpdate) {
|
|
@@ -260,7 +250,7 @@ class Coordinates {
|
|
|
260
250
|
* @returns An array [x, y, z], or copies x, y and z into the provided
|
|
261
251
|
* array.
|
|
262
252
|
*/
|
|
263
|
-
toArray(array: number[] = [], offset
|
|
253
|
+
toArray(array: number[] = [], offset = 0): ArrayLike<number> {
|
|
264
254
|
return Vector3.prototype.toArray.call(this, array, offset);
|
|
265
255
|
}
|
|
266
256
|
|
|
@@ -268,6 +258,8 @@ class Coordinates {
|
|
|
268
258
|
* Computes the planar distance from this coordinates to `coord`.
|
|
269
259
|
* **Planar distance** is the straight-line euclidean distance calculated in
|
|
270
260
|
* a 2D cartesian coordinate system.
|
|
261
|
+
* @param coord
|
|
262
|
+
* @returns
|
|
271
263
|
*/
|
|
272
264
|
planarDistanceTo(coord: Coordinates): number {
|
|
273
265
|
this.toVector3(v0).setZ(0);
|
|
@@ -279,6 +271,8 @@ class Coordinates {
|
|
|
279
271
|
* Computes the geodetic distance from this coordinates to `coord`.
|
|
280
272
|
* **Geodetic distance** is calculated in an ellipsoid space as the shortest
|
|
281
273
|
* distance across the curved surface of the ellipsoid.
|
|
274
|
+
* @param coord
|
|
275
|
+
* @returns
|
|
282
276
|
*/
|
|
283
277
|
geodeticDistanceTo(coord: Coordinates): number {
|
|
284
278
|
this.as('EPSG:4326', coord0);
|
|
@@ -304,6 +298,7 @@ class Coordinates {
|
|
|
304
298
|
* by `mat`, and divides by perspective.
|
|
305
299
|
*
|
|
306
300
|
* @param mat - The matrix.
|
|
301
|
+
* @returns
|
|
307
302
|
*/
|
|
308
303
|
applyMatrix4(mat: Matrix4): this {
|
|
309
304
|
Vector3.prototype.applyMatrix4.call(this, mat);
|
|
@@ -348,7 +343,7 @@ class Coordinates {
|
|
|
348
343
|
this.y = MathUtils.clamp(this.y, -89.999999, 89.999999);
|
|
349
344
|
}
|
|
350
345
|
|
|
351
|
-
target.setFromArray(
|
|
346
|
+
target.setFromArray(CRS.transform(this.crs, crs)
|
|
352
347
|
.forward([this.x, this.y, this.z]));
|
|
353
348
|
}
|
|
354
349
|
|
|
@@ -358,7 +353,7 @@ class Coordinates {
|
|
|
358
353
|
}
|
|
359
354
|
}
|
|
360
355
|
|
|
361
|
-
coord0 = /* @__PURE__ */ new Coordinates('EPSG:4326', 0, 0, 0);
|
|
362
|
-
coord1 = /* @__PURE__ */ new Coordinates('EPSG:4326', 0, 0, 0);
|
|
356
|
+
const coord0 = /* @__PURE__ */ new Coordinates('EPSG:4326', 0, 0, 0);
|
|
357
|
+
const coord1 = /* @__PURE__ */ new Coordinates('EPSG:4326', 0, 0, 0);
|
|
363
358
|
|
|
364
359
|
export default Coordinates;
|
package/src/Crs.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import proj4 from 'proj4';
|
|
2
|
-
|
|
2
|
+
import type { Converter } from 'proj4';
|
|
3
3
|
import type { ProjectionDefinition } from 'proj4/dist/lib/defs';
|
|
4
4
|
|
|
5
|
+
interface proj4Def {
|
|
6
|
+
type: string;
|
|
7
|
+
PROJCS: proj4Def;
|
|
8
|
+
unknown?: string;
|
|
9
|
+
(alias: string): proj4Def & { name: string };
|
|
10
|
+
title: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
proj4.defs('EPSG:4978', '+proj=geocent +datum=WGS84 +units=m +no_defs');
|
|
6
14
|
|
|
7
15
|
// Redefining proj4 global projections to match epsg.org database axis order.
|
|
@@ -15,8 +23,22 @@ proj4.defs('WGS84').axis = 'neu';
|
|
|
15
23
|
* projection definition previously defined with
|
|
16
24
|
* [`proj4.defs`](https://github.com/proj4js/proj4js#named-projections).
|
|
17
25
|
*/
|
|
26
|
+
// TODO Unify with OrientationUtils.js
|
|
18
27
|
export type ProjectionLike = string;
|
|
19
28
|
|
|
29
|
+
const proj4Cache: Record<string, Record<string, Converter>> = {};
|
|
30
|
+
export function transform(crsIn: ProjectionLike, crsOut: ProjectionLike): Converter {
|
|
31
|
+
if (!proj4Cache[crsIn]) {
|
|
32
|
+
proj4Cache[crsIn] = {};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!proj4Cache[crsIn][crsOut]) {
|
|
36
|
+
proj4Cache[crsIn][crsOut] = proj4(crsIn, crsOut);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return proj4Cache[crsIn][crsOut];
|
|
40
|
+
}
|
|
41
|
+
|
|
20
42
|
function isString(s: unknown): s is string {
|
|
21
43
|
return typeof s === 'string' || s instanceof String;
|
|
22
44
|
}
|
|
@@ -52,6 +74,7 @@ export const UNIT = {
|
|
|
52
74
|
* @internal
|
|
53
75
|
*
|
|
54
76
|
* @param crs - The CRS to test.
|
|
77
|
+
* @returns
|
|
55
78
|
*/
|
|
56
79
|
export function is4326(crs: ProjectionLike) {
|
|
57
80
|
return crs === 'EPSG:4326';
|
|
@@ -62,7 +85,7 @@ function unitFromProj4Unit(proj: ProjectionDefinition) {
|
|
|
62
85
|
return UNIT.DEGREE;
|
|
63
86
|
} else if (proj.units === 'm' || proj.units === 'meter') {
|
|
64
87
|
return UNIT.METER;
|
|
65
|
-
} else if (proj.units === 'foot') {
|
|
88
|
+
} else if (proj.units === 'foot' || proj.units === 'ft') {
|
|
66
89
|
return UNIT.FOOT;
|
|
67
90
|
} else if (proj.units === undefined && proj.to_meter === undefined) {
|
|
68
91
|
// See https://proj.org/en/9.4/usage/projections.html [17/10/2024]
|
|
@@ -92,7 +115,8 @@ export function getUnit(crs: ProjectionLike) {
|
|
|
92
115
|
* Asserts that the CRS is using metric units.
|
|
93
116
|
*
|
|
94
117
|
* @param crs - The CRS to check.
|
|
95
|
-
* @
|
|
118
|
+
* @returns
|
|
119
|
+
* @throws {Error} if the CRS is not valid.
|
|
96
120
|
*/
|
|
97
121
|
export function isMetricUnit(crs: ProjectionLike) {
|
|
98
122
|
return getUnit(crs) === UNIT.METER;
|
|
@@ -102,7 +126,8 @@ export function isMetricUnit(crs: ProjectionLike) {
|
|
|
102
126
|
* Asserts that the CRS is geographic.
|
|
103
127
|
*
|
|
104
128
|
* @param crs - The CRS to check.
|
|
105
|
-
* @
|
|
129
|
+
* @returns
|
|
130
|
+
* @throws {Error} if the CRS is not valid.
|
|
106
131
|
*/
|
|
107
132
|
export function isGeographic(crs: ProjectionLike) {
|
|
108
133
|
return getUnit(crs) === UNIT.DEGREE;
|
|
@@ -125,12 +150,12 @@ export function isGeocentric(crs: ProjectionLike) {
|
|
|
125
150
|
* includes an unit.
|
|
126
151
|
*
|
|
127
152
|
* @param crs - The CRS to test.
|
|
128
|
-
* @throws {
|
|
153
|
+
* @throws {Error} if the crs is not valid.
|
|
129
154
|
*/
|
|
130
155
|
export function isValid(crs: ProjectionLike) {
|
|
131
156
|
const proj = proj4.defs(crs);
|
|
132
157
|
if (!proj) {
|
|
133
|
-
throw new Error(`Undefined crs '${crs}'. Add it with
|
|
158
|
+
throw new Error(`Undefined crs '${crs}'. Add it with itowns.CRS.defs('${crs}', wktString)`);
|
|
134
159
|
}
|
|
135
160
|
if (!unitFromProj4Unit(proj)) {
|
|
136
161
|
throw new Error(`No valid unit found for crs '${crs}', found ${proj.units}`);
|
|
@@ -166,11 +191,56 @@ export function axisOrder(crs: ProjectionLike) {
|
|
|
166
191
|
|
|
167
192
|
/**
|
|
168
193
|
* Defines a proj4 projection as a named alias.
|
|
169
|
-
* This function is
|
|
170
|
-
* [`proj4.defs`](https://github.com/proj4js/proj4js#named-projections)
|
|
171
|
-
|
|
194
|
+
* This function is an alias for the
|
|
195
|
+
* [`proj4.defs`](https://github.com/proj4js/proj4js#named-projections) function.
|
|
196
|
+
*/
|
|
197
|
+
export const defs = proj4.defs;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Fetches a CRS definition from epsg.io and registers it with proj4.
|
|
201
|
+
* If the CRS is already defined, returns the existing definition.
|
|
172
202
|
*
|
|
173
|
-
* @param
|
|
174
|
-
* @
|
|
203
|
+
* @param crs - The EPSG code string (e.g. "EPSG:2154").
|
|
204
|
+
* @returns The proj4 projection definition.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* // Register EPSG:2154 (RGF93 / Lambert-93)
|
|
208
|
+
* await CRS.fromEPSG('EPSG:2154');
|
|
209
|
+
*
|
|
210
|
+
* // Register EPSG:4269 (NAD83)
|
|
211
|
+
* await CRS.fromEPSG('EPSG:4269');
|
|
175
212
|
*/
|
|
176
|
-
export
|
|
213
|
+
export async function fromEPSG(crs: string): Promise<ProjectionDefinition> {
|
|
214
|
+
const def = proj4.defs(crs);
|
|
215
|
+
if (def) {
|
|
216
|
+
return def;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const code = crs.replace(/^EPSG:/i, '');
|
|
220
|
+
const response = await fetch(`https://epsg.io/${code}.proj4`);
|
|
221
|
+
if (!response.ok) {
|
|
222
|
+
throw new Error(`Failed to fetch EPSG:${code} from epsg.io: ${response.status}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const proj4def = await response.text();
|
|
226
|
+
proj4.defs(crs, proj4def);
|
|
227
|
+
return proj4.defs(crs);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function defsFromWkt(wkt: string): string {
|
|
231
|
+
proj4.defs('unknown', wkt);
|
|
232
|
+
const proj4Defs = proj4.defs as unknown as proj4Def;
|
|
233
|
+
let projCS;
|
|
234
|
+
if (proj4Defs('unknown').type === 'COMPD_CS') {
|
|
235
|
+
console.warn('Compound coordinate system is not yet supported.');
|
|
236
|
+
projCS = proj4Defs('unknown').PROJCS;
|
|
237
|
+
} else {
|
|
238
|
+
projCS = proj4Defs('unknown');
|
|
239
|
+
}
|
|
240
|
+
const crsAlias = (projCS.title || projCS.name || 'EPSG:XXXX');
|
|
241
|
+
if (!(crsAlias in proj4.defs)) {
|
|
242
|
+
proj4.defs(crsAlias, projCS);
|
|
243
|
+
}
|
|
244
|
+
delete proj4Defs.unknown;
|
|
245
|
+
return crsAlias;
|
|
246
|
+
}
|