@itowns/geographic 2.45.1-next.0 → 2.45.1-next.1

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.
@@ -0,0 +1,402 @@
1
+ import { Euler, MathUtils, Matrix4, Quaternion, Vector3 } from 'three';
2
+ import proj4 from 'proj4';
3
+ import Coordinates from "./Coordinates.js";
4
+ const DEG2RAD = MathUtils.DEG2RAD;
5
+ const matrix = new Matrix4();
6
+ const north = new Vector3();
7
+ const east = new Vector3();
8
+ const axis = new Vector3().set(0, 0, 1);
9
+ const coord = new Coordinates('EPSG:4326', 0, 0, 0);
10
+ const euler = new Euler();
11
+ const quat = new Quaternion();
12
+ /**
13
+ * The transform from the platform frame to the local East, North, Up (ENU)
14
+ * frame is `RotationZ(heading).RotationX(pitch).RotationY(roll)`.
15
+ *
16
+ * @param roll - angle in degrees. Default is 0.
17
+ * @param pitch - angle in degrees. Default is 0.
18
+ * @param heading - angle in degrees. Default is 0
19
+ * @param target - output Quaternion
20
+ *
21
+ * @returns The target quaternion
22
+ */
23
+ export function quaternionFromRollPitchHeading() {
24
+ let roll = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
25
+ let pitch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
26
+ let heading = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
27
+ let target = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new Quaternion();
28
+ roll *= DEG2RAD;
29
+ pitch *= DEG2RAD;
30
+ heading *= DEG2RAD;
31
+ // return setFromEuler(euler.set(pitch, roll, heading , 'ZXY')).conjugate();
32
+ // Below is optimized version of above line
33
+ return target.setFromEuler(euler.set(-pitch, -roll, -heading, 'YXZ'));
34
+ }
35
+
36
+ /**
37
+ * From
38
+ * [DocMicMac](https://github.com/micmacIGN/Documentation/raw/master/DocMicMac.pdf),
39
+ * the transform from the platform frame to the local East, North, Up (ENU)
40
+ * frame is:
41
+ *
42
+ * ```
43
+ * RotationX(omega).RotationY(phi).RotationZ(kappa).RotationX(PI)
44
+ * Converts between the 2 conventions for the camera local frame:
45
+ * RotationX(PI) <=> Quaternion(1,0,0,0)
46
+ * X right, Y bottom, Z front : convention in photogrammetry and computer vision
47
+ * X right, Y top, Z back : convention in webGL, threejs
48
+ * ```
49
+ *
50
+ * @param omega - angle in degrees. Default is 0.
51
+ * @param phi - angle in degrees. Default is 0.
52
+ * @param kappa - angle in degrees. Default is 0.
53
+ * @param target - output quaternion
54
+ *
55
+ * @returns The target quaternion
56
+ */
57
+ export function quaternionFromOmegaPhiKappa() {
58
+ let omega = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
59
+ let phi = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
60
+ let kappa = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
61
+ let target = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new Quaternion();
62
+ omega *= DEG2RAD;
63
+ phi *= DEG2RAD;
64
+ kappa *= DEG2RAD;
65
+ target.setFromEuler(euler.set(omega, phi, kappa, 'XYZ'));
66
+ target.set(target.w, target.z, -target.y, -target.x);
67
+ // <=> target.multiply(new THREE.Quaternion(1, 0, 0, 0));
68
+ return target;
69
+ }
70
+
71
+ /**
72
+ * Sets the quaternion according to the rotation from the platform frame to the
73
+ * local frame.
74
+ *
75
+ * @param attitude - either euler angles or photogrammetry angles
76
+ * @param target - output Quaternion
77
+ *
78
+ * @returns The target quaternion
79
+ */
80
+ export function quaternionFromAttitude(attitude) {
81
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
82
+ if ('roll' in attitude || 'pitch' in attitude || 'heading' in attitude) {
83
+ return quaternionFromRollPitchHeading(attitude.roll, attitude.pitch, attitude.heading, target);
84
+ }
85
+ if ('omega' in attitude || 'phi' in attitude || 'kappa' in attitude) {
86
+ return quaternionFromOmegaPhiKappa(attitude.omega, attitude.phi, attitude.kappa, target);
87
+ }
88
+ return target.set(0, 0, 0, 1);
89
+ }
90
+ /**
91
+ * Sets the quaternion according to the rotation from the local East North Up
92
+ * (ENU) frame to the geocentric frame. The up direction of the ENU frame is
93
+ * provided by the normalized geodetic normal of the provided coordinates
94
+ * (geodeticNormal property).
95
+ *
96
+ * @param coordinates - origin of the local East North Up (ENU) frame
97
+ * @param target - output Quaternion
98
+ * @returns The target quaternion if coordinates is defined. Otherwise, a
99
+ * function to compute it from coordinates.
100
+ */
101
+ export function quaternionFromEnuToGeocent(coordinates) {
102
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
103
+ if (coordinates) {
104
+ return quaternionFromEnuToGeocent()(coordinates, target);
105
+ }
106
+ return function (coordinates) {
107
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
108
+ const up = coordinates.geodesicNormal;
109
+ if (up.x == 0 && up.y == 0) {
110
+ return target.set(0, 0, 0, 1);
111
+ }
112
+ // this is an optimized version of
113
+ // matrix.lookAt(up, new THREE.Vector3(), new THREE.Vector3(0, 0, 1));
114
+ east.set(-up.y, up.x, 0).normalize();
115
+ north.crossVectors(up, east);
116
+ matrix.makeBasis(east, north, up);
117
+ return target.setFromRotationMatrix(matrix);
118
+ };
119
+ }
120
+ /**
121
+ * Sets the quaternion according to the rotation from a geocentric frame
122
+ * to the local East North Up (ENU) frame. The up direction of the ENU frame is
123
+ * provided by the normalized geodetic normal of the provided coordinates
124
+ * (geodeticNormal property).
125
+ *
126
+ * @param coordinates - origin of the local East North Up (ENU) frame
127
+ * @param target - output Quaternion
128
+ * @returns The target quaternion if coordinates is defined. Otherwise, a
129
+ * function to compute it from coordinates.
130
+ */
131
+ export function quaternionFromGeocentToEnu(coordinates) {
132
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
133
+ if (coordinates) {
134
+ return quaternionFromGeocentToEnu()(coordinates, target);
135
+ }
136
+ const toGeocent = quaternionFromEnuToGeocent();
137
+ return function (coordinates) {
138
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
139
+ return toGeocent(coordinates, target).conjugate();
140
+ };
141
+ }
142
+ /**
143
+ * Computes the rotation from a Lambert Conformal Conic (LCC) frame to the local
144
+ * East North Up (ENU) frame.
145
+ * The quaternion accounts for the
146
+ * <a href="https://geodesie.ign.fr/contenu/fichiers/documentation/algorithmes/alg0060.pdf">meridian convergence</a>
147
+ * between the ENU and LCC frames.
148
+ * This is a generally small rotation around Z.
149
+ *
150
+ * @param proj - the lcc projection (may be parsed using proj4)
151
+ * @param coordinates - origin of the local East North Up (ENU) frame
152
+ * @param target - output Quaternion
153
+ * @returns The target quaternion if coordinates is defined. Otherwise, a
154
+ * function to compute it from coordinates.
155
+ */
156
+ export function quaternionFromLCCToEnu(proj, coordinates) {
157
+ let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
158
+ if (coordinates) {
159
+ return quaternionFromLCCToEnu(proj)(coordinates, target);
160
+ }
161
+ const sinlat0 = Math.sin(proj.lat0);
162
+ return function (coordinates) {
163
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
164
+ const long = coordinates.as(coord.crs, coord).longitude * DEG2RAD;
165
+ return target.setFromAxisAngle(axis, sinlat0 * (proj.long0 - long));
166
+ };
167
+ }
168
+ /**
169
+ * Computes the rotation from the local East North Up (ENU) frame to a Lambert
170
+ * Conformal Conic (LCC) frame. The quaternion accounts for the
171
+ * <a href="https://geodesie.ign.fr/contenu/fichiers/documentation/algorithmes/alg0060.pdf">meridian convergence</a>
172
+ * between the ENU and LCC frames.
173
+ * This is a generally small rotation around Z.
174
+ *
175
+ * @param proj - the lcc projection (may be parsed using proj4)
176
+ * @param coordinates - origin of the local East North Up (ENU) frame
177
+ * @param target - output Quaternion
178
+ * @returns The target quaternion if coordinates is defined. Otherwise, a
179
+ * function to compute it from coordinates.
180
+ */
181
+ export function quaternionFromEnuToLCC(proj, coordinates) {
182
+ let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
183
+ if (coordinates) {
184
+ return quaternionFromEnuToLCC(proj)(coordinates, target);
185
+ }
186
+ const fromLCC = quaternionFromLCCToEnu(proj);
187
+ return function (coordinates) {
188
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
189
+ return fromLCC(coordinates, target).conjugate();
190
+ };
191
+ }
192
+ /**
193
+ * Computes the rotation from a Transverse Mercator frame (TMerc) to the
194
+ * local East North Up (ENU) frame. The quaternion accounts for the
195
+ * <a href="https://geodesie.ign.fr/contenu/fichiers/documentation/algorithmes/alg0061.pdf">meridian convergence</a>
196
+ * between the ENU and TMerc frames.
197
+ * This is a generally small rotation around Z.
198
+ *
199
+ * @param proj - the tmerc projection (may be parsed using proj4)
200
+ * @param coordinates - origin of the local East North Up (ENU) frame
201
+ * @param target - output Quaternion
202
+ * @returns The target quaternion if coordinates is defined. Otherwise, a
203
+ * function to compute it from coordinates.
204
+ */
205
+ export function quaternionFromTMercToEnu(proj, coordinates) {
206
+ let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
207
+ if (coordinates) {
208
+ return quaternionFromTMercToEnu(proj)(coordinates, target);
209
+ }
210
+ let eta0;
211
+ if (!proj.e) {
212
+ const a2 = proj.a * proj.a;
213
+ const b2 = proj.b * proj.b;
214
+ eta0 = a2 / b2 - 1;
215
+ } else {
216
+ const e2 = proj.e * proj.e;
217
+ eta0 = e2 / (1 - e2);
218
+ }
219
+ return function (coordinates) {
220
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
221
+ coordinates.as(coord.crs, coord);
222
+ const long = coord.longitude * DEG2RAD;
223
+ const lat = coord.latitude * DEG2RAD;
224
+ const dlong = proj.long0 - long;
225
+ const coslat = Math.cos(lat);
226
+ const sinlat = Math.sin(lat);
227
+ const tanlat = sinlat / coslat;
228
+ const coslat2 = coslat * coslat;
229
+ const dl2 = dlong * dlong * coslat2;
230
+ const eta2 = eta0 * coslat2;
231
+ const gamma = dlong * sinlat * (1 + dl2 / 3 * (1 + 3 * eta2 + 2 * eta2 * eta2) + dl2 * dl2 * (2 - tanlat) / 15);
232
+ return target.setFromAxisAngle(axis, gamma);
233
+ };
234
+ }
235
+ /**
236
+ * Computes the rotation from the local East North Up (ENU) to a Transverse
237
+ * Mercator frame. The quaternion accounts for the
238
+ * <a href="https://geodesie.ign.fr/contenu/fichiers/documentation/algorithmes/alg0061.pdf">meridian convergence</a>
239
+ * between the ENU and TMerc frames.
240
+ * This is a generally small rotation around Z.
241
+ *
242
+ * @param proj - the tmerc projection (may be parsed using proj4)
243
+ * @param coordinates - origin of the local East North Up (ENU) frame
244
+ * @param target - output Quaternion
245
+ * @returns The target quaternion if coordinates is defined. Otherwise, a
246
+ * function to compute it from coordinates.
247
+ */
248
+ export function quaternionFromEnuToTMerc(proj, coordinates) {
249
+ let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
250
+ if (coordinates) {
251
+ return quaternionFromEnuToTMerc(proj)(coordinates, target);
252
+ }
253
+ const fromTMerc = quaternionFromTMercToEnu(proj);
254
+ return function (coordinates) {
255
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
256
+ return fromTMerc(coordinates, target).conjugate();
257
+ };
258
+ }
259
+ /**
260
+ * Computes the rotation from a LongLat frame to the local East North Up
261
+ * (ENU) frame. The identity quaternion (0,0,0,1) is returned, as longlat
262
+ * and ENU frame are assumed to be aligned.
263
+ *
264
+ * @param coordinates - coordinates the origin of the local East North Up
265
+ * (ENU) frame
266
+ * @param target - output Quaternion
267
+ * @returns The target quaternion if coordinates is defined, otherwise, a
268
+ * function to compute it from coordinates.
269
+ */
270
+ export function quaternionFromLongLatToEnu(coordinates) {
271
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
272
+ return coordinates ? target.set(0, 0, 0, 1) : function (coordinates) {
273
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
274
+ return quaternionFromLongLatToEnu(coordinates, target);
275
+ };
276
+ }
277
+ /**
278
+ * Computes the rotation from the local East North Up (ENU) frame to a
279
+ * LongLat frame. The identity quaternion (0,0,0,1) is returned, as longlat
280
+ * and ENU frame are assumed to be aligned.
281
+ *
282
+ * @param coordinates - the origin of the local East North Up (ENU) frame
283
+ * @param target - output Quaternion
284
+ * @returns The target quaternion if coordinates is defined, otherwise, a
285
+ * function to compute it from coordinates.
286
+ */
287
+ export function quaternionFromEnuToLongLat(coordinates) {
288
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
289
+ return coordinates ? target.set(0, 0, 0, 1) : function (coordinates) {
290
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
291
+ return quaternionFromEnuToLongLat(coordinates, target);
292
+ };
293
+ }
294
+ /**
295
+ * Warns for an unimplemented projection, sets the quaternion to the
296
+ * identity (0,0,0,1).
297
+ *
298
+ * @param proj - the unimplemented projection (may be parsed using proj4)
299
+ * @param coordinates - the origin of the local East North Up (ENU) frame
300
+ * @param target - output Quaternion
301
+ * @returns The target quaternion if coordinates is defined, otherwise, a
302
+ * function to compute it from coordinates.
303
+ */
304
+ export function quaternionUnimplemented(proj, coordinates) {
305
+ let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
306
+ console.warn('This quaternion function is not implemented for projections of type', proj.projName);
307
+ return coordinates ? target.set(0, 0, 0, 1) : function (coordinates) {
308
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
309
+ return quaternionUnimplemented(proj, coordinates, target);
310
+ };
311
+ }
312
+ /**
313
+ * Compute the quaternion that models the rotation from the local East North
314
+ * Up (ENU) frame to the frame of the given crs.
315
+ *
316
+ * @param crsOrProj - the CRS of the target frame or its proj4-compatible
317
+ * object.
318
+ * @param coordinates - the origin of the local East North Up (ENU) frame
319
+ * @param target - output Quaternion
320
+ * @returns The target quaternion if coordinates is defined, otherwise, a
321
+ * function to compute it from coordinates.
322
+ */
323
+ export function quaternionFromEnuToCRS(crsOrProj, coordinates) {
324
+ let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
325
+ if (coordinates) {
326
+ return quaternionFromEnuToCRS(crsOrProj)(coordinates, target);
327
+ }
328
+ const proj = typeof crsOrProj === 'string' ? proj4.defs(crsOrProj) : crsOrProj;
329
+ switch (proj.projName) {
330
+ case 'geocent':
331
+ return quaternionFromEnuToGeocent();
332
+ case 'lcc':
333
+ return quaternionFromEnuToLCC(proj);
334
+ case 'tmerc':
335
+ return quaternionFromEnuToTMerc(proj);
336
+ case 'longlat':
337
+ return quaternionFromEnuToLongLat();
338
+ default:
339
+ return quaternionUnimplemented(proj);
340
+ }
341
+ }
342
+ /**
343
+ * Compute the quaternion that models the rotation from the frame of the
344
+ * given crs to the local East North Up (ENU) frame.
345
+ *
346
+ * @param crsOrProj - the CRS of the target frame or its proj4-compatible
347
+ * object.
348
+ * @param coordinates - the origin of the local East North Up (ENU) frame
349
+ * @param target - output Quaternion
350
+ * @returns The target quaternion if coordinates is defined, otherwise, a
351
+ * function to compute it from coordinates.
352
+ */
353
+ export function quaternionFromCRSToEnu(crsOrProj, coordinates) {
354
+ let target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Quaternion();
355
+ if (coordinates) {
356
+ return quaternionFromCRSToEnu(crsOrProj)(coordinates, target);
357
+ }
358
+ const proj = typeof crsOrProj === 'string' ? proj4.defs(crsOrProj) : crsOrProj;
359
+ switch (proj.projName) {
360
+ case 'geocent':
361
+ return quaternionFromGeocentToEnu();
362
+ case 'lcc':
363
+ return quaternionFromLCCToEnu(proj);
364
+ case 'tmerc':
365
+ return quaternionFromTMercToEnu(proj);
366
+ case 'longlat':
367
+ return quaternionFromLongLatToEnu();
368
+ default:
369
+ return quaternionUnimplemented(proj);
370
+ }
371
+ }
372
+ /**
373
+ * Return the function that computes the quaternion that represents a
374
+ * rotation of coordinates between two CRS frames.
375
+ *
376
+ * @param crsIn - the CRS of the input frame.
377
+ * @param crsOut - the CRS of the output frame.
378
+ * @param coordinates - the origin of the local East North Up (ENU) frame
379
+ * @param target - output Quaternion
380
+ * @returns The target quaternion if coordinates is defined, otherwise, a
381
+ * function to compute it from coordinates.
382
+ */
383
+ export function quaternionFromCRSToCRS(crsIn, crsOut, coordinates) {
384
+ let target = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new Quaternion();
385
+ if (coordinates) {
386
+ return quaternionFromCRSToCRS(crsIn, crsOut)(coordinates, target);
387
+ }
388
+ if (crsIn == crsOut) {
389
+ return function (origin) {
390
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
391
+ return target.set(0, 0, 0, 1);
392
+ };
393
+ }
394
+
395
+ // get rotations from the local East/North/Up (ENU) frame to both CRS.
396
+ const fromCrs = quaternionFromCRSToEnu(crsIn);
397
+ const toCrs = quaternionFromEnuToCRS(crsOut);
398
+ return function (origin) {
399
+ let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Quaternion();
400
+ return toCrs(origin, target).multiply(fromCrs(origin, quat));
401
+ };
402
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itowns/geographic",
3
- "version": "2.45.1-next.0",
3
+ "version": "2.45.1-next.1",
4
4
  "description": "Geodesy",
5
5
  "type": "module",
6
6
  "main": "lib/Main.js",