@itowns/geographic 2.44.2
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/README.md +67 -0
- package/lib/CoordStars.js +84 -0
- package/lib/Coordinates.js +331 -0
- package/lib/Crs.js +170 -0
- package/lib/Ellipsoid.js +186 -0
- package/lib/Extent.js +531 -0
- package/lib/Main.js +7 -0
- package/lib/OrientationUtils.js +459 -0
- package/package.json +41 -0
- package/src/CoordStars.js +104 -0
- package/src/Coordinates.ts +364 -0
- package/src/Crs.ts +176 -0
- package/src/Ellipsoid.ts +250 -0
- package/src/Extent.ts +602 -0
- package/src/Main.js +8 -0
- package/src/OrientationUtils.js +410 -0
package/src/Ellipsoid.ts
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import proj4 from 'proj4';
|
|
3
|
+
import Coordinates from 'Coordinates';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Length of the semi-axes of the WGS84 ellipsoid.
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export const ellipsoidSizes = new THREE.Vector3(
|
|
10
|
+
proj4.WGS84.a,
|
|
11
|
+
proj4.WGS84.a,
|
|
12
|
+
proj4.WGS84.b);
|
|
13
|
+
|
|
14
|
+
const normal = new THREE.Vector3();
|
|
15
|
+
|
|
16
|
+
class Ellipsoid {
|
|
17
|
+
/**
|
|
18
|
+
* Length of the semi-axes of the ellipsoid.
|
|
19
|
+
*/
|
|
20
|
+
size: THREE.Vector3;
|
|
21
|
+
/**
|
|
22
|
+
* Eccentricity of the ellipsoid.
|
|
23
|
+
*/
|
|
24
|
+
eccentricity: number;
|
|
25
|
+
|
|
26
|
+
private _radiiSquared: THREE.Vector3;
|
|
27
|
+
private _invRadiiSquared: THREE.Vector3;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param size - Length of the semi-axes of the ellipsoid. Defaults to those
|
|
31
|
+
* defined by the WGS84 ellipsoid.
|
|
32
|
+
*/
|
|
33
|
+
constructor(size: THREE.Vector3 = ellipsoidSizes) {
|
|
34
|
+
this.size = new THREE.Vector3();
|
|
35
|
+
this._radiiSquared = new THREE.Vector3();
|
|
36
|
+
this._invRadiiSquared = new THREE.Vector3();
|
|
37
|
+
this.eccentricity = 0;
|
|
38
|
+
|
|
39
|
+
this.setSize(size);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Computes the normal vector to an ellipsoid at the given cartesian
|
|
44
|
+
* coordinate `(x, y, z)`.
|
|
45
|
+
*
|
|
46
|
+
* @param cartesian - The given cartesian coordinate.
|
|
47
|
+
* @param target - An object to store this vector to. If this is not
|
|
48
|
+
* specified, a new vector will be created.
|
|
49
|
+
*/
|
|
50
|
+
geodeticSurfaceNormal(
|
|
51
|
+
cartesian: Coordinates,
|
|
52
|
+
target = new THREE.Vector3(),
|
|
53
|
+
): THREE.Vector3 {
|
|
54
|
+
return cartesian.toVector3(target).multiply(this._invRadiiSquared).normalize();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Computes the normal vector to an ellipsoid at the given geographic
|
|
59
|
+
* coordinate `(longitude, latitude, altitude)`.
|
|
60
|
+
*
|
|
61
|
+
* @param coordCarto - The given geographic coordinate.
|
|
62
|
+
* @param target - An object to store this vector to. If this is not
|
|
63
|
+
* specified, a new vector will be created.
|
|
64
|
+
*/
|
|
65
|
+
geodeticSurfaceNormalCartographic(
|
|
66
|
+
coordCarto: Coordinates,
|
|
67
|
+
target = new THREE.Vector3(),
|
|
68
|
+
): THREE.Vector3 {
|
|
69
|
+
const longitude = THREE.MathUtils.degToRad(coordCarto.longitude);
|
|
70
|
+
const latitude = THREE.MathUtils.degToRad(coordCarto.latitude);
|
|
71
|
+
const cosLatitude = Math.cos(latitude);
|
|
72
|
+
|
|
73
|
+
return target.set(cosLatitude * Math.cos(longitude),
|
|
74
|
+
cosLatitude * Math.sin(longitude),
|
|
75
|
+
Math.sin(latitude));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Sets the length of the semi-axes of this ellipsoid from a 3-dimensional
|
|
80
|
+
* vector-like object. The object shall have both `x`, `y` and `z`
|
|
81
|
+
* properties.
|
|
82
|
+
*
|
|
83
|
+
* @param size - The source vector.
|
|
84
|
+
*/
|
|
85
|
+
setSize(size: THREE.Vector3Like): this {
|
|
86
|
+
this.size.set(size.x, size.y, size.z);
|
|
87
|
+
|
|
88
|
+
this._radiiSquared.multiplyVectors(size, size);
|
|
89
|
+
|
|
90
|
+
this._invRadiiSquared.x = (size.x === 0) ? 0 : 1 / this._radiiSquared.x;
|
|
91
|
+
this._invRadiiSquared.y = (size.y === 0) ? 0 : 1 / this._radiiSquared.y;
|
|
92
|
+
this._invRadiiSquared.z = (size.z === 0) ? 0 : 1 / this._radiiSquared.z;
|
|
93
|
+
|
|
94
|
+
this.eccentricity = Math.sqrt(this._radiiSquared.x - this._radiiSquared.z) / this.size.x;
|
|
95
|
+
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
cartographicToCartesian(
|
|
100
|
+
coordCarto: Coordinates,
|
|
101
|
+
target = new THREE.Vector3(),
|
|
102
|
+
): THREE.Vector3 {
|
|
103
|
+
normal.copy(coordCarto.geodesicNormal);
|
|
104
|
+
|
|
105
|
+
target.multiplyVectors(this._radiiSquared, normal);
|
|
106
|
+
|
|
107
|
+
const gamma = Math.sqrt(normal.dot(target));
|
|
108
|
+
|
|
109
|
+
target.divideScalar(gamma);
|
|
110
|
+
|
|
111
|
+
normal.multiplyScalar(coordCarto.altitude);
|
|
112
|
+
|
|
113
|
+
return target.add(normal);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Convert cartesian coordinates to geographic according to the current
|
|
118
|
+
* ellipsoid of revolution.
|
|
119
|
+
* @param position - The coordinate to convert
|
|
120
|
+
* @param target - coordinate to copy result
|
|
121
|
+
* @returns an object describing the coordinates on the reference ellipsoid,
|
|
122
|
+
* angles are in degree
|
|
123
|
+
*/
|
|
124
|
+
cartesianToCartographic(
|
|
125
|
+
position: THREE.Vector3Like,
|
|
126
|
+
target = new Coordinates('EPSG:4326', 0, 0, 0),
|
|
127
|
+
): Coordinates {
|
|
128
|
+
// for details, see for example http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/geodetic-datum-conversions/equations-used-datum
|
|
129
|
+
// TODO the following is only valable for oblate ellipsoid of
|
|
130
|
+
// revolution. do we want to support triaxial ellipsoid?
|
|
131
|
+
const R = Math.sqrt(position.x * position.x +
|
|
132
|
+
position.y * position.y +
|
|
133
|
+
position.z * position.z);
|
|
134
|
+
const a = this.size.x; // x
|
|
135
|
+
const b = this.size.z; // z
|
|
136
|
+
const e = Math.abs((a * a - b * b) / (a * a));
|
|
137
|
+
const f = 1 - Math.sqrt(1 - e);
|
|
138
|
+
const rsqXY = Math.sqrt(position.x * position.x + position.y * position.y);
|
|
139
|
+
|
|
140
|
+
const theta = Math.atan2(position.y, position.x);
|
|
141
|
+
const nu = Math.atan(position.z / rsqXY * ((1 - f) + e * a / R));
|
|
142
|
+
|
|
143
|
+
const sinu = Math.sin(nu);
|
|
144
|
+
const cosu = Math.cos(nu);
|
|
145
|
+
|
|
146
|
+
const phi = Math.atan(
|
|
147
|
+
(position.z * (1 - f) + e * a * sinu * sinu * sinu) /
|
|
148
|
+
((1 - f) * (rsqXY - e * a * cosu * cosu * cosu)));
|
|
149
|
+
|
|
150
|
+
const h = (rsqXY * Math.cos(phi)) +
|
|
151
|
+
position.z * Math.sin(phi) -
|
|
152
|
+
a * Math.sqrt(1 - e * Math.sin(phi) * Math.sin(phi));
|
|
153
|
+
|
|
154
|
+
return target.setFromValues(
|
|
155
|
+
THREE.MathUtils.radToDeg(theta),
|
|
156
|
+
THREE.MathUtils.radToDeg(phi),
|
|
157
|
+
h,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
cartographicToCartesianArray(coordCartoArray: Coordinates[]): THREE.Vector3[] {
|
|
162
|
+
const cartesianArray = [];
|
|
163
|
+
for (let i = 0; i < coordCartoArray.length; i++) {
|
|
164
|
+
cartesianArray.push(this.cartographicToCartesian(coordCartoArray[i]));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return cartesianArray;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
intersection(ray: THREE.Ray): THREE.Vector3 | false {
|
|
171
|
+
const EPSILON = 0.0001;
|
|
172
|
+
const O_C = ray.origin;
|
|
173
|
+
const dir = ray.direction;
|
|
174
|
+
// normalizeVector( dir );
|
|
175
|
+
|
|
176
|
+
const a =
|
|
177
|
+
((dir.x * dir.x) * this._invRadiiSquared.x) +
|
|
178
|
+
((dir.y * dir.y) * this._invRadiiSquared.y) +
|
|
179
|
+
((dir.z * dir.z) * this._invRadiiSquared.z);
|
|
180
|
+
|
|
181
|
+
const b =
|
|
182
|
+
((2 * O_C.x * dir.x) * this._invRadiiSquared.x) +
|
|
183
|
+
((2 * O_C.y * dir.y) * this._invRadiiSquared.y) +
|
|
184
|
+
((2 * O_C.z * dir.z) * this._invRadiiSquared.z);
|
|
185
|
+
|
|
186
|
+
const c =
|
|
187
|
+
((O_C.x * O_C.x) * this._invRadiiSquared.x) +
|
|
188
|
+
((O_C.y * O_C.y) * this._invRadiiSquared.y) +
|
|
189
|
+
((O_C.z * O_C.z) * this._invRadiiSquared.z) - 1;
|
|
190
|
+
|
|
191
|
+
let d = ((b * b) - (4 * a * c));
|
|
192
|
+
if (d < 0 || a === 0 || b === 0 || c === 0) { return false; }
|
|
193
|
+
|
|
194
|
+
d = Math.sqrt(d);
|
|
195
|
+
|
|
196
|
+
const t1 = (-b + d) / (2 * a);
|
|
197
|
+
const t2 = (-b - d) / (2 * a);
|
|
198
|
+
|
|
199
|
+
if (t1 <= EPSILON && t2 <= EPSILON) {
|
|
200
|
+
// both intersections are behind the ray origin
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let t = 0;
|
|
205
|
+
if (t1 <= EPSILON) { t = t2; } else
|
|
206
|
+
if (t2 <= EPSILON) { t = t1; } else { t = (t1 < t2) ? t1 : t2; }
|
|
207
|
+
|
|
208
|
+
if (t < EPSILON) { return false; } // Too close to intersection
|
|
209
|
+
|
|
210
|
+
const inter = new THREE.Vector3();
|
|
211
|
+
|
|
212
|
+
inter.addVectors(ray.origin, dir.clone().setLength(t));
|
|
213
|
+
|
|
214
|
+
return inter;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Calculate the geodesic distance, between coordCarto1 and coordCarto2.
|
|
219
|
+
* It's most short distance on ellipsoid surface between coordCarto1 and
|
|
220
|
+
* coordCarto2.
|
|
221
|
+
* It's called orthodromy.
|
|
222
|
+
*
|
|
223
|
+
* @param coordCarto1 - The coordinate carto 1
|
|
224
|
+
* @param coordCarto2 - The coordinate carto 2
|
|
225
|
+
* @returns The orthodromic distance between the two given coordinates.
|
|
226
|
+
*/
|
|
227
|
+
geodesicDistance(coordCarto1: Coordinates, coordCarto2: Coordinates): number {
|
|
228
|
+
// The formula uses the distance on approximated sphere,
|
|
229
|
+
// with the nearest local radius of curvature of the ellipsoid
|
|
230
|
+
// https://geodesie.ign.fr/contenu/fichiers/Distance_longitude_latitude.pdf
|
|
231
|
+
const longitude1 = THREE.MathUtils.degToRad(coordCarto1.longitude);
|
|
232
|
+
const latitude1 = THREE.MathUtils.degToRad(coordCarto1.latitude);
|
|
233
|
+
const longitude2 = THREE.MathUtils.degToRad(coordCarto2.longitude);
|
|
234
|
+
const latitude2 = THREE.MathUtils.degToRad(coordCarto2.latitude);
|
|
235
|
+
|
|
236
|
+
const distRad = Math.acos(
|
|
237
|
+
Math.sin(latitude1) * Math.sin(latitude2) +
|
|
238
|
+
Math.cos(latitude1) * Math.cos(latitude2) * Math.cos(longitude2 - longitude1));
|
|
239
|
+
|
|
240
|
+
const e = this.eccentricity;
|
|
241
|
+
const latMoy = (latitude1 + latitude2) * 0.5;
|
|
242
|
+
const es = (e * Math.sin(latMoy)) ** 2;
|
|
243
|
+
const rho = this.size.x * (1 - e ** 2) / ((1 - es) ** (3 / 2));
|
|
244
|
+
const N = this.size.x / Math.sqrt(1 - es);
|
|
245
|
+
|
|
246
|
+
return distRad * Math.sqrt(rho * N);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export default Ellipsoid;
|