@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.
@@ -0,0 +1,13 @@
1
+ declare const CoordStars: {
2
+ getSunPosition(): (date: Date | number, lat: number, lon: number) => {
3
+ EclipticLongitude: number;
4
+ declinaison: number;
5
+ ascension: number;
6
+ H: number;
7
+ SiderealTime: number;
8
+ altitude: number;
9
+ azimuth: number;
10
+ };
11
+ getSunPositionInScene(date: Date | number, lat: number, lon: number): import("three").Vector3;
12
+ };
13
+ export default CoordStars;
@@ -0,0 +1,208 @@
1
+ import { Vector3, type Vector3Like, type Matrix4 } from 'three';
2
+ import type { ProjectionLike } from './Crs';
3
+ export interface CoordinatesLike {
4
+ readonly crs: string;
5
+ readonly x: number;
6
+ readonly y: number;
7
+ readonly z: number;
8
+ }
9
+ /**
10
+ * A class representing a geographic or geocentric coordinate.
11
+ *
12
+ * A coordinate is defined by a [CRS](http://inspire.ec.europa.eu/theme/rs)
13
+ * (Coordinate Reference System) and a 3-dimensional vector `(x, y, z)`.
14
+ * For geocentric projections, it is recommended to use the `latitude`,
15
+ * `longitude` and `altitude` aliases to refer to vector components.
16
+ *
17
+ * To change a value, prefer the use of the `set*` methods.
18
+ *
19
+ * By default, the `EPSG:4978` and `EPSG:4326` projections are supported. To use
20
+ * a different projection, it must have been declared previously with `proj4`.
21
+ * A comprehensive list of projections and their corresponding proj4 string can
22
+ * be found at [epsg.io](https://epsg.io/).
23
+ *
24
+ * @example Geocentric coordinates
25
+ * ```js
26
+ * new Coordinates('EPSG:4978', 20885167, 849862, 23385912);
27
+ * ```
28
+ *
29
+ * @example Geographic coordinates
30
+ * ```js
31
+ * new Coordinates('EPSG:4326', 2.33, 48.24, 24999549);
32
+ * ```
33
+ *
34
+ * @example Defining the EPSG:2154 projection with proj4
35
+ * ```js
36
+ * proj4.defs('EPSG:2154', `+proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44
37
+ * +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m
38
+ * +no_defs +type=crs`);
39
+ * ```
40
+ */
41
+ declare class Coordinates {
42
+ /**
43
+ * Read-only flag to check if a given object is of type `Coordinates`.
44
+ */
45
+ readonly isCoordinates: boolean;
46
+ /**
47
+ * A default or user-defined CRS (see {@link ProjectionLike}).
48
+ */
49
+ crs: ProjectionLike;
50
+ /** The x value (or longitude) of this coordinate. */
51
+ x: number;
52
+ /** The y value (or latitude) of this coordinate. */
53
+ y: number;
54
+ /** The z value (or altitude) of this coordinate. */
55
+ z: number;
56
+ private _normal;
57
+ private _normalNeedsUpdate;
58
+ /**
59
+ * @param crs - A default or user-defined CRS (see {@link ProjectionLike}).
60
+ * @param x - x or longitude value.
61
+ * @param y - y or latitude value.
62
+ * @param z - z or altitude value.
63
+ */
64
+ constructor(crs: ProjectionLike, x?: number, y?: number, z?: number);
65
+ /**
66
+ * Sets the Coordinate Reference System.
67
+ * @param crs - Coordinate Reference System (e.g. 'EPSG:4978')
68
+ * @returns
69
+ */
70
+ setCrs(crs: ProjectionLike): this;
71
+ /**
72
+ * Sets the x, y and z components of this coordinate.
73
+ *
74
+ * @param x - x or longitude value.
75
+ * @param y - y or latitude value.
76
+ * @param z - z or altitude value.
77
+ * @returns
78
+ */
79
+ setFromValues(x?: number, y?: number, z?: number): this;
80
+ /**
81
+ * Sets the coordinates's {@link Coordinates#x | x} component to
82
+ * `array[offset + 0]`, {@link Coordinates#y | y} component to
83
+ * `array[offset + 1]` and {@link Coordinates#z | z} component to
84
+ * `array[offset + 2]`.
85
+ *
86
+ * @param array - The source array.
87
+ * @param offset - Optional offset into the array. Default is 0.
88
+ * @returns
89
+ */
90
+ setFromArray(array: number[], offset?: number): this;
91
+ /**
92
+ * Sets the `(x, y, z)` vector of this coordinate from a 3-dimensional
93
+ * vector-like object. This object shall have both `x`, `y` and `z`
94
+ * properties.
95
+ *
96
+ * @param v - The source object.
97
+ * @returns
98
+ */
99
+ setFromVector3(v: Vector3Like): this;
100
+ /**
101
+ * Returns a new coordinate with the same `(x, y, z)` vector and crs as this
102
+ * one.
103
+ * @returns
104
+ */
105
+ clone(): Coordinates;
106
+ /**
107
+ * Copies the `(x, y, z)` vector components and crs of the passed coordinate
108
+ * to this coordinate.
109
+ *
110
+ * @param src - The source coordinate to copy from.
111
+ * @returns
112
+ */
113
+ copy(src: CoordinatesLike): this;
114
+ get longitude(): number;
115
+ get latitude(): number;
116
+ get altitude(): number;
117
+ set altitude(value: number);
118
+ /**
119
+ * The geodesic normal of the coordinate.
120
+ * @returns
121
+ */
122
+ get geodesicNormal(): Vector3;
123
+ /**
124
+ * Copies the `x`, `y` and `z` components into the provided `THREE.Vector3`.
125
+ *
126
+ * @param target - An object to store this vector to. If this is not
127
+ * specified, a new vector will be created.
128
+ *
129
+ * @returns A vector `(x, y, z)`, or copies x, y and z into the provided
130
+ * vector.
131
+ */
132
+ toVector3(target?: Vector3): Vector3;
133
+ /**
134
+ * Copies the `x`, `y` and `z` components into the provided array.
135
+ *
136
+ * @param array - An array to store this vector to. If this is not
137
+ * provided a new array will be created.
138
+ * @param offset - An optional offset into the array.
139
+ *
140
+ * @returns An array [x, y, z], or copies x, y and z into the provided
141
+ * array.
142
+ */
143
+ toArray(array?: number[], offset?: number): ArrayLike<number>;
144
+ /**
145
+ * Computes the planar distance from this coordinates to `coord`.
146
+ * **Planar distance** is the straight-line euclidean distance calculated in
147
+ * a 2D cartesian coordinate system.
148
+ * @param coord
149
+ * @returns
150
+ */
151
+ planarDistanceTo(coord: Coordinates): number;
152
+ /**
153
+ * Computes the geodetic distance from this coordinates to `coord`.
154
+ * **Geodetic distance** is calculated in an ellipsoid space as the shortest
155
+ * distance across the curved surface of the ellipsoid.
156
+ * @param coord
157
+ * @returns
158
+ */
159
+ geodeticDistanceTo(coord: Coordinates): number;
160
+ /**
161
+ * Computes the euclidean distance from this coordinates to `coord` in a
162
+ * WGS84 projection.
163
+ *
164
+ * @param coord - The coordinate
165
+ * @returns earth euclidean distance
166
+ */
167
+ spatialEuclideanDistanceTo(coord: Coordinates): number;
168
+ /**
169
+ * Multiplies this coordinate (with an implicit 1 in the 4th dimension)
170
+ * by `mat`, and divides by perspective.
171
+ *
172
+ * @param mat - The matrix.
173
+ * @returns
174
+ */
175
+ applyMatrix4(mat: Matrix4): this;
176
+ /**
177
+ * Projects this coordinate to the specified
178
+ * [CRS](http://inspire.ec.europa.eu/theme/rs).
179
+ *
180
+ * @param crs - The target CRS to which the coordinate will be converted.
181
+ * @param target - The target to store the projected coordinate. If this not
182
+ * provided a new coordinate will be created.
183
+ *
184
+ * @returns The coordinate projected into the specified CRS.
185
+ *
186
+ * @example Conversion from a geographic to a geocentric reference system
187
+ * ```js
188
+ * const geographicCoords = new Coordinates('EPSG:4326',
189
+ * 2.33, // longitude
190
+ * 48.24, // latitude
191
+ * 24999549, // altitude
192
+ * );
193
+ * const geocentricCoords = geographicCoords.as('EPSG:4978');
194
+ * ```
195
+ *
196
+ * @example Conversion from a geocentric to a geographic reference system
197
+ * ```js
198
+ * const geocentricCoords = new Coordinates('EPSG:4978',
199
+ * 20885167, // x
200
+ * 849862, // y
201
+ * 23385912, // z
202
+ * );
203
+ * const geographicCoords = geocentricCoords.as('EPSG:4326');
204
+ * ```
205
+ */
206
+ as(crs: ProjectionLike, target?: Coordinates): Coordinates;
207
+ }
208
+ export default Coordinates;
@@ -1,23 +1,9 @@
1
1
  import { Vector3, MathUtils } from 'three';
2
- import proj4 from 'proj4';
3
2
  import Ellipsoid from "./Ellipsoid.js";
4
3
  import * as CRS from "./Crs.js";
5
4
  const ellipsoid = /* @__PURE__ */new Ellipsoid();
6
- const projectionCache = {};
7
5
  const v0 = /* @__PURE__ */new Vector3();
8
6
  const v1 = /* @__PURE__ */new Vector3();
9
- let coord0;
10
- let coord1;
11
- function proj4cache(crsIn, crsOut) {
12
- if (!projectionCache[crsIn]) {
13
- projectionCache[crsIn] = {};
14
- }
15
- if (!projectionCache[crsIn][crsOut]) {
16
- projectionCache[crsIn][crsOut] = proj4(crsIn, crsOut);
17
- }
18
- return projectionCache[crsIn][crsOut];
19
- }
20
-
21
7
  /**
22
8
  * A class representing a geographic or geocentric coordinate.
23
9
  *
@@ -71,10 +57,7 @@ class Coordinates {
71
57
  * @param y - y or latitude value.
72
58
  * @param z - z or altitude value.
73
59
  */
74
- constructor(crs) {
75
- let x = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
76
- let y = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
77
- let z = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
60
+ constructor(crs, x = 0, y = 0, z = 0) {
78
61
  this.isCoordinates = true;
79
62
  CRS.isValid(crs);
80
63
  this.crs = crs;
@@ -108,6 +91,7 @@ class Coordinates {
108
91
  /**
109
92
  * Sets the Coordinate Reference System.
110
93
  * @param crs - Coordinate Reference System (e.g. 'EPSG:4978')
94
+ * @returns
111
95
  */
112
96
  setCrs(crs) {
113
97
  CRS.isValid(crs);
@@ -121,11 +105,9 @@ class Coordinates {
121
105
  * @param x - x or longitude value.
122
106
  * @param y - y or latitude value.
123
107
  * @param z - z or altitude value.
108
+ * @returns
124
109
  */
125
- setFromValues() {
126
- let x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
127
- let y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
128
- let z = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
110
+ setFromValues(x = 0, y = 0, z = 0) {
129
111
  this.x = x;
130
112
  this.y = y;
131
113
  this.z = z;
@@ -141,9 +123,9 @@ class Coordinates {
141
123
  *
142
124
  * @param array - The source array.
143
125
  * @param offset - Optional offset into the array. Default is 0.
126
+ * @returns
144
127
  */
145
- setFromArray(array) {
146
- let offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
128
+ setFromArray(array, offset = 0) {
147
129
  return this.setFromValues(array[offset], array[offset + 1], array[offset + 2]);
148
130
  }
149
131
 
@@ -153,6 +135,7 @@ class Coordinates {
153
135
  * properties.
154
136
  *
155
137
  * @param v - The source object.
138
+ * @returns
156
139
  */
157
140
  setFromVector3(v) {
158
141
  return this.setFromValues(v.x, v.y, v.z);
@@ -161,6 +144,7 @@ class Coordinates {
161
144
  /**
162
145
  * Returns a new coordinate with the same `(x, y, z)` vector and crs as this
163
146
  * one.
147
+ * @returns
164
148
  */
165
149
  clone() {
166
150
  return new Coordinates(this.crs, this.x, this.y, this.z);
@@ -171,6 +155,7 @@ class Coordinates {
171
155
  * to this coordinate.
172
156
  *
173
157
  * @param src - The source coordinate to copy from.
158
+ * @returns
174
159
  */
175
160
  copy(src) {
176
161
  this.crs = src.crs;
@@ -191,6 +176,7 @@ class Coordinates {
191
176
 
192
177
  /**
193
178
  * The geodesic normal of the coordinate.
179
+ * @returns
194
180
  */
195
181
  get geodesicNormal() {
196
182
  if (this._normalNeedsUpdate) {
@@ -215,8 +201,7 @@ class Coordinates {
215
201
  * @returns A vector `(x, y, z)`, or copies x, y and z into the provided
216
202
  * vector.
217
203
  */
218
- toVector3() {
219
- let target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Vector3();
204
+ toVector3(target = new Vector3()) {
220
205
  return target.copy(this);
221
206
  }
222
207
 
@@ -230,9 +215,7 @@ class Coordinates {
230
215
  * @returns An array [x, y, z], or copies x, y and z into the provided
231
216
  * array.
232
217
  */
233
- toArray() {
234
- let array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
235
- let offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
218
+ toArray(array = [], offset = 0) {
236
219
  return Vector3.prototype.toArray.call(this, array, offset);
237
220
  }
238
221
 
@@ -240,6 +223,8 @@ class Coordinates {
240
223
  * Computes the planar distance from this coordinates to `coord`.
241
224
  * **Planar distance** is the straight-line euclidean distance calculated in
242
225
  * a 2D cartesian coordinate system.
226
+ * @param coord
227
+ * @returns
243
228
  */
244
229
  planarDistanceTo(coord) {
245
230
  this.toVector3(v0).setZ(0);
@@ -251,6 +236,8 @@ class Coordinates {
251
236
  * Computes the geodetic distance from this coordinates to `coord`.
252
237
  * **Geodetic distance** is calculated in an ellipsoid space as the shortest
253
238
  * distance across the curved surface of the ellipsoid.
239
+ * @param coord
240
+ * @returns
254
241
  */
255
242
  geodeticDistanceTo(coord) {
256
243
  this.as('EPSG:4326', coord0);
@@ -276,6 +263,7 @@ class Coordinates {
276
263
  * by `mat`, and divides by perspective.
277
264
  *
278
265
  * @param mat - The matrix.
266
+ * @returns
279
267
  */
280
268
  applyMatrix4(mat) {
281
269
  Vector3.prototype.applyMatrix4.call(this, mat);
@@ -312,20 +300,19 @@ class Coordinates {
312
300
  * const geographicCoords = geocentricCoords.as('EPSG:4326');
313
301
  * ```
314
302
  */
315
- as(crs) {
316
- let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Coordinates(crs);
303
+ as(crs, target = new Coordinates(crs)) {
317
304
  if (this.crs == crs) {
318
305
  target.copy(this);
319
306
  } else {
320
307
  if (CRS.is4326(this.crs) && crs == 'EPSG:3857') {
321
308
  this.y = MathUtils.clamp(this.y, -89.999999, 89.999999);
322
309
  }
323
- target.setFromArray(proj4cache(this.crs, crs).forward([this.x, this.y, this.z]));
310
+ target.setFromArray(CRS.transform(this.crs, crs).forward([this.x, this.y, this.z]));
324
311
  }
325
312
  target.crs = crs;
326
313
  return target;
327
314
  }
328
315
  }
329
- coord0 = /* @__PURE__ */new Coordinates('EPSG:4326', 0, 0, 0);
330
- coord1 = /* @__PURE__ */new Coordinates('EPSG:4326', 0, 0, 0);
316
+ const coord0 = /* @__PURE__ */new Coordinates('EPSG:4326', 0, 0, 0);
317
+ const coord1 = /* @__PURE__ */new Coordinates('EPSG:4326', 0, 0, 0);
331
318
  export default Coordinates;
package/lib/Crs.d.ts ADDED
@@ -0,0 +1,111 @@
1
+ import type { Converter } from 'proj4';
2
+ import type { ProjectionDefinition } from 'proj4/dist/lib/defs';
3
+ /**
4
+ * A projection as a CRS identifier string. This identifier references a
5
+ * projection definition previously defined with
6
+ * [`proj4.defs`](https://github.com/proj4js/proj4js#named-projections).
7
+ */
8
+ export type ProjectionLike = string;
9
+ export declare function transform(crsIn: ProjectionLike, crsOut: ProjectionLike): Converter;
10
+ /**
11
+ * System units supported for a coordinate system. See
12
+ * [proj](https://proj4.org/en/9.5/operations/conversions/unitconvert.html#angular-units).
13
+ * Note that only degree and meters units are supported for now.
14
+ */
15
+ export declare const UNIT: {
16
+ /**
17
+ * Angular unit in degree.
18
+ */
19
+ readonly DEGREE: 1;
20
+ /**
21
+ * Distance unit in meter.
22
+ */
23
+ readonly METER: 2;
24
+ /**
25
+ * Distance unit in foot.
26
+ */
27
+ readonly FOOT: 3;
28
+ };
29
+ /**
30
+ * Checks that the CRS is EPSG:4326.
31
+ * @internal
32
+ *
33
+ * @param crs - The CRS to test.
34
+ * @returns
35
+ */
36
+ export declare function is4326(crs: ProjectionLike): crs is "EPSG:4326";
37
+ /**
38
+ * Returns the horizontal coordinates system units associated with this CRS.
39
+ *
40
+ * @param crs - The CRS to extract the unit from.
41
+ * @returns Either `UNIT.METER`, `UNIT.DEGREE`, `UNIT.FOOT` or `undefined`.
42
+ */
43
+ export declare function getUnit(crs: ProjectionLike): 1 | 2 | 3 | undefined;
44
+ /**
45
+ * Asserts that the CRS is using metric units.
46
+ *
47
+ * @param crs - The CRS to check.
48
+ * @returns
49
+ * @throws {Error} if the CRS is not valid.
50
+ */
51
+ export declare function isMetricUnit(crs: ProjectionLike): boolean;
52
+ /**
53
+ * Asserts that the CRS is geographic.
54
+ *
55
+ * @param crs - The CRS to check.
56
+ * @returns
57
+ * @throws {Error} if the CRS is not valid.
58
+ */
59
+ export declare function isGeographic(crs: ProjectionLike): boolean;
60
+ /**
61
+ * Asserts that the CRS is geocentric.
62
+ *
63
+ * @param crs - The CRS to test.
64
+ * @returns false if the crs isn't defined.
65
+ */
66
+ export declare function isGeocentric(crs: ProjectionLike): boolean;
67
+ /**
68
+ * Asserts that the CRS is valid, meaning it has been previously defined and
69
+ * includes an unit.
70
+ *
71
+ * @param crs - The CRS to test.
72
+ * @throws {Error} if the crs is not valid.
73
+ */
74
+ export declare function isValid(crs: ProjectionLike): void;
75
+ /**
76
+ * Gives a reasonable epsilon for this CRS.
77
+ *
78
+ * @param crs - The CRS to use.
79
+ * @returns 0.01 if the CRS is EPSG:4326, 0.001 otherwise.
80
+ */
81
+ export declare function reasonableEpsilon(crs: ProjectionLike): 0.01 | 0.001;
82
+ /**
83
+ * Returns the axis parameter defined in proj4 for the provided crs.
84
+ * Might be undefined depending on crs definition.
85
+ *
86
+ * @param crs - The CRS to get axis from.
87
+ * @returns the matching proj4 axis string, 'enu' for instance (east, north, up)
88
+ */
89
+ export declare function axisOrder(crs: ProjectionLike): string | undefined;
90
+ /**
91
+ * Defines a proj4 projection as a named alias.
92
+ * This function is an alias for the
93
+ * [`proj4.defs`](https://github.com/proj4js/proj4js#named-projections) function.
94
+ */
95
+ export declare const defs: typeof import("proj4/dist/lib/defs").default;
96
+ /**
97
+ * Fetches a CRS definition from epsg.io and registers it with proj4.
98
+ * If the CRS is already defined, returns the existing definition.
99
+ *
100
+ * @param crs - The EPSG code string (e.g. "EPSG:2154").
101
+ * @returns The proj4 projection definition.
102
+ *
103
+ * @example
104
+ * // Register EPSG:2154 (RGF93 / Lambert-93)
105
+ * await CRS.fromEPSG('EPSG:2154');
106
+ *
107
+ * // Register EPSG:4269 (NAD83)
108
+ * await CRS.fromEPSG('EPSG:4269');
109
+ */
110
+ export declare function fromEPSG(crs: string): Promise<ProjectionDefinition>;
111
+ export declare function defsFromWkt(wkt: string): string;
package/lib/Crs.js CHANGED
@@ -12,7 +12,18 @@ proj4.defs('WGS84').axis = 'neu';
12
12
  * projection definition previously defined with
13
13
  * [`proj4.defs`](https://github.com/proj4js/proj4js#named-projections).
14
14
  */
15
+ // TODO Unify with OrientationUtils.js
15
16
 
17
+ const proj4Cache = {};
18
+ export function transform(crsIn, crsOut) {
19
+ if (!proj4Cache[crsIn]) {
20
+ proj4Cache[crsIn] = {};
21
+ }
22
+ if (!proj4Cache[crsIn][crsOut]) {
23
+ proj4Cache[crsIn][crsOut] = proj4(crsIn, crsOut);
24
+ }
25
+ return proj4Cache[crsIn][crsOut];
26
+ }
16
27
  function isString(s) {
17
28
  return typeof s === 'string' || s instanceof String;
18
29
  }
@@ -47,6 +58,7 @@ export const UNIT = {
47
58
  * @internal
48
59
  *
49
60
  * @param crs - The CRS to test.
61
+ * @returns
50
62
  */
51
63
  export function is4326(crs) {
52
64
  return crs === 'EPSG:4326';
@@ -56,7 +68,7 @@ function unitFromProj4Unit(proj) {
56
68
  return UNIT.DEGREE;
57
69
  } else if (proj.units === 'm' || proj.units === 'meter') {
58
70
  return UNIT.METER;
59
- } else if (proj.units === 'foot') {
71
+ } else if (proj.units === 'foot' || proj.units === 'ft') {
60
72
  return UNIT.FOOT;
61
73
  } else if (proj.units === undefined && proj.to_meter === undefined) {
62
74
  // See https://proj.org/en/9.4/usage/projections.html [17/10/2024]
@@ -86,7 +98,8 @@ export function getUnit(crs) {
86
98
  * Asserts that the CRS is using metric units.
87
99
  *
88
100
  * @param crs - The CRS to check.
89
- * @throws {@link Error} if the CRS is not valid.
101
+ * @returns
102
+ * @throws {Error} if the CRS is not valid.
90
103
  */
91
104
  export function isMetricUnit(crs) {
92
105
  return getUnit(crs) === UNIT.METER;
@@ -96,7 +109,8 @@ export function isMetricUnit(crs) {
96
109
  * Asserts that the CRS is geographic.
97
110
  *
98
111
  * @param crs - The CRS to check.
99
- * @throws {@link Error} if the CRS is not valid.
112
+ * @returns
113
+ * @throws {Error} if the CRS is not valid.
100
114
  */
101
115
  export function isGeographic(crs) {
102
116
  return getUnit(crs) === UNIT.DEGREE;
@@ -119,12 +133,12 @@ export function isGeocentric(crs) {
119
133
  * includes an unit.
120
134
  *
121
135
  * @param crs - The CRS to test.
122
- * @throws {@link Error} if the crs is not valid.
136
+ * @throws {Error} if the crs is not valid.
123
137
  */
124
138
  export function isValid(crs) {
125
139
  const proj = proj4.defs(crs);
126
140
  if (!proj) {
127
- throw new Error(`Undefined crs '${crs}'. Add it with proj4.defs('${crs}', string)`);
141
+ throw new Error(`Undefined crs '${crs}'. Add it with itowns.CRS.defs('${crs}', wktString)`);
128
142
  }
129
143
  if (!unitFromProj4Unit(proj)) {
130
144
  throw new Error(`No valid unit found for crs '${crs}', found ${proj.units}`);
@@ -160,11 +174,53 @@ export function axisOrder(crs) {
160
174
 
161
175
  /**
162
176
  * Defines a proj4 projection as a named alias.
163
- * This function is a specialized wrapper over the
164
- * [`proj4.defs`](https://github.com/proj4js/proj4js#named-projections)
165
- * function.
177
+ * This function is an alias for the
178
+ * [`proj4.defs`](https://github.com/proj4js/proj4js#named-projections) function.
179
+ */
180
+ export const defs = proj4.defs;
181
+
182
+ /**
183
+ * Fetches a CRS definition from epsg.io and registers it with proj4.
184
+ * If the CRS is already defined, returns the existing definition.
185
+ *
186
+ * @param crs - The EPSG code string (e.g. "EPSG:2154").
187
+ * @returns The proj4 projection definition.
166
188
  *
167
- * @param code - Named alias of the currently defined projection.
168
- * @param proj4def - Proj4 or WKT string of the defined projection.
189
+ * @example
190
+ * // Register EPSG:2154 (RGF93 / Lambert-93)
191
+ * await CRS.fromEPSG('EPSG:2154');
192
+ *
193
+ * // Register EPSG:4269 (NAD83)
194
+ * await CRS.fromEPSG('EPSG:4269');
169
195
  */
170
- export const defs = (code, proj4def) => proj4.defs(code, proj4def);
196
+ export async function fromEPSG(crs) {
197
+ const def = proj4.defs(crs);
198
+ if (def) {
199
+ return def;
200
+ }
201
+ const code = crs.replace(/^EPSG:/i, '');
202
+ const response = await fetch(`https://epsg.io/${code}.proj4`);
203
+ if (!response.ok) {
204
+ throw new Error(`Failed to fetch EPSG:${code} from epsg.io: ${response.status}`);
205
+ }
206
+ const proj4def = await response.text();
207
+ proj4.defs(crs, proj4def);
208
+ return proj4.defs(crs);
209
+ }
210
+ export function defsFromWkt(wkt) {
211
+ proj4.defs('unknown', wkt);
212
+ const proj4Defs = proj4.defs;
213
+ let projCS;
214
+ if (proj4Defs('unknown').type === 'COMPD_CS') {
215
+ console.warn('Compound coordinate system is not yet supported.');
216
+ projCS = proj4Defs('unknown').PROJCS;
217
+ } else {
218
+ projCS = proj4Defs('unknown');
219
+ }
220
+ const crsAlias = projCS.title || projCS.name || 'EPSG:XXXX';
221
+ if (!(crsAlias in proj4.defs)) {
222
+ proj4.defs(crsAlias, projCS);
223
+ }
224
+ delete proj4Defs.unknown;
225
+ return crsAlias;
226
+ }