giser-convert-crs 1.0.0 → 1.0.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.
@@ -1,454 +1,624 @@
1
- const PI = Math.PI;
2
- const X_PI = PI * 3000.0 / 180.0;
3
- const SEMI_MAJOR_AXIS = 6378245.0;
4
- const ECCENTRICITY_SQUARED = 0.00669342162296594323;
5
-
6
- const GPS = {
7
- PI,
8
- x_pi: X_PI,
9
-
10
- delta(lat, lon) {
11
- let dLat = this.transformLat(lon - 105.0, lat - 35.0);
12
- let dLon = this.transformLon(lon - 105.0, lat - 35.0);
13
- const radLat = lat / 180.0 * PI;
14
- const sinLat = Math.sin(radLat);
15
- const magic = 1 - ECCENTRICITY_SQUARED * sinLat * sinLat;
16
- const sqrtMagic = Math.sqrt(magic);
17
-
18
- dLat = (dLat * 180.0) / ((SEMI_MAJOR_AXIS * (1 - ECCENTRICITY_SQUARED)) / (magic * sqrtMagic) * PI);
19
- dLon = (dLon * 180.0) / (SEMI_MAJOR_AXIS / sqrtMagic * Math.cos(radLat) * PI);
20
-
21
- return { lat: dLat, lon: dLon };
22
- },
23
-
24
- wgs84_gcj02(wgsLon, wgsLat) {
25
- this._assertLonLat(wgsLon, wgsLat);
26
- if (this.outOfChina(wgsLat, wgsLon)) return [wgsLon, wgsLat];
27
-
28
- const d = this.delta(wgsLat, wgsLon);
29
- return [wgsLon + d.lon, wgsLat + d.lat];
30
- },
31
-
32
- gcj02_wgs84(gcjLon, gcjLat) {
33
- this._assertLonLat(gcjLon, gcjLat);
34
- if (this.outOfChina(gcjLat, gcjLon)) return [gcjLon, gcjLat];
35
-
36
- const d = this.delta(gcjLat, gcjLon);
37
- return [gcjLon - d.lon, gcjLat - d.lat];
38
- },
39
-
40
- gcj02_wgs84_precise(gcjLon, gcjLat) {
41
- this._assertLonLat(gcjLon, gcjLat);
42
- if (this.outOfChina(gcjLat, gcjLon)) return [gcjLon, gcjLat];
43
-
44
- const threshold = 0.000000001;
45
- let minLat = gcjLat - 0.01;
46
- let minLon = gcjLon - 0.01;
47
- let maxLat = gcjLat + 0.01;
48
- let maxLon = gcjLon + 0.01;
49
- let wgsLat = gcjLat;
50
- let wgsLon = gcjLon;
51
-
52
- for (let i = 0; i < 10000; i++) {
53
- wgsLat = (minLat + maxLat) / 2;
54
- wgsLon = (minLon + maxLon) / 2;
55
- const [tmpLon, tmpLat] = this.wgs84_gcj02(wgsLon, wgsLat);
56
- const dLat = tmpLat - gcjLat;
57
- const dLon = tmpLon - gcjLon;
58
-
59
- if (Math.abs(dLat) < threshold && Math.abs(dLon) < threshold) break;
60
- if (dLat > 0) maxLat = wgsLat;
61
- else minLat = wgsLat;
62
- if (dLon > 0) maxLon = wgsLon;
63
- else minLon = wgsLon;
64
- }
65
-
66
- return [wgsLon, wgsLat];
67
- },
68
-
69
- gcj02_bd09ll(gcjLon, gcjLat) {
70
- this._assertLonLat(gcjLon, gcjLat);
71
- const z = Math.sqrt(gcjLon * gcjLon + gcjLat * gcjLat) + 0.00002 * Math.sin(gcjLat * X_PI);
72
- const theta = Math.atan2(gcjLat, gcjLon) + 0.000003 * Math.cos(gcjLon * X_PI);
73
- return [z * Math.cos(theta) + 0.0065, z * Math.sin(theta) + 0.006];
74
- },
75
-
76
- bd09ll_gcj02(bdLon, bdLat) {
77
- this._assertLonLat(bdLon, bdLat);
78
- const x = bdLon - 0.0065;
79
- const y = bdLat - 0.006;
80
- const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI);
81
- const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI);
82
- return [z * Math.cos(theta), z * Math.sin(theta)];
83
- },
84
-
85
- wgs84_bd09ll(wgsLon, wgsLat) {
86
- const gcj = this.wgs84_gcj02(wgsLon, wgsLat);
87
- return this.gcj02_bd09ll(gcj[0], gcj[1]);
88
- },
89
-
90
- bd09ll_wgs84(bdLon, bdLat) {
91
- const gcj = this.bd09ll_gcj02(bdLon, bdLat);
92
- return this.gcj02_wgs84(gcj[0], gcj[1]);
93
- },
94
-
95
- bd09ll_wgs84_precise(bdLon, bdLat) {
96
- const gcj = this.bd09ll_gcj02(bdLon, bdLat);
97
- return this.gcj02_wgs84_precise(gcj[0], gcj[1]);
98
- },
99
-
100
- outOfChina(lat, lon) {
101
- return lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271;
102
- },
103
-
104
- transformLat(x, y) {
105
- let ret = -100 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
106
- ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
107
- ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;
108
- ret += (160.0 * Math.sin(y / 12.0 * PI) + 320.0 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;
109
- return ret;
110
- },
111
-
112
- transformLon(x, y) {
113
- let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
114
- ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
115
- ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;
116
- ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;
117
- return ret;
118
- },
119
-
120
- gcj_encrypt(wgsLat, wgsLon) {
121
- const [lon, lat] = this.wgs84_gcj02(wgsLon, wgsLat);
122
- return { lat, lon };
123
- },
124
-
125
- _assertLonLat(lon, lat) {
126
- if (!Number.isFinite(lon) || !Number.isFinite(lat)) {
127
- throw new Error('Invalid coordinate: longitude and latitude must be finite numbers.');
128
- }
129
- }
1
+ const PI = Math.PI;
2
+ const X_PI = PI * 3000.0 / 180.0;
3
+ const SEMI_MAJOR_AXIS = 6378245.0;
4
+ const ECCENTRICITY_SQUARED = 0.00669342162296594323;
5
+ const EPSG3857_RADIUS = 6378137.0;
6
+ const EPSG3857_MAX_LAT = 85.0511287798066;
7
+
8
+ const GPS = {
9
+ PI,
10
+ x_pi: X_PI,
11
+ EARTH_RADIUS: EPSG3857_RADIUS,
12
+
13
+ delta(lat, lon) {
14
+ let dLat = this.transformLat(lon - 105.0, lat - 35.0);
15
+ let dLon = this.transformLon(lon - 105.0, lat - 35.0);
16
+ const radLat = lat / 180.0 * PI;
17
+ const sinLat = Math.sin(radLat);
18
+ const magic = 1 - ECCENTRICITY_SQUARED * sinLat * sinLat;
19
+ const sqrtMagic = Math.sqrt(magic);
20
+
21
+ dLat = (dLat * 180.0) / ((SEMI_MAJOR_AXIS * (1 - ECCENTRICITY_SQUARED)) / (magic * sqrtMagic) * PI);
22
+ dLon = (dLon * 180.0) / (SEMI_MAJOR_AXIS / sqrtMagic * Math.cos(radLat) * PI);
23
+
24
+ return { lat: dLat, lon: dLon };
25
+ },
26
+
27
+ wgs84_gcj02(wgsLon, wgsLat) {
28
+ this._assertLonLat(wgsLon, wgsLat);
29
+ if (this.outOfChina(wgsLat, wgsLon)) return [wgsLon, wgsLat];
30
+
31
+ const d = this.delta(wgsLat, wgsLon);
32
+ return [wgsLon + d.lon, wgsLat + d.lat];
33
+ },
34
+
35
+ gcj02_wgs84(gcjLon, gcjLat) {
36
+ this._assertLonLat(gcjLon, gcjLat);
37
+ if (this.outOfChina(gcjLat, gcjLon)) return [gcjLon, gcjLat];
38
+
39
+ const d = this.delta(gcjLat, gcjLon);
40
+ return [gcjLon - d.lon, gcjLat - d.lat];
41
+ },
42
+
43
+ gcj02_wgs84_precise(gcjLon, gcjLat) {
44
+ this._assertLonLat(gcjLon, gcjLat);
45
+ if (this.outOfChina(gcjLat, gcjLon)) return [gcjLon, gcjLat];
46
+
47
+ let wgsLon = gcjLon;
48
+ let wgsLat = gcjLat;
49
+
50
+ for (let i = 0; i < 5; i++) {
51
+ const [currLon, currLat] = this.wgs84_gcj02(wgsLon, wgsLat);
52
+ const dLon = currLon - gcjLon;
53
+ const dLat = currLat - gcjLat;
54
+ wgsLon -= dLon;
55
+ wgsLat -= dLat;
56
+ if (Math.abs(dLon) < 1e-11 && Math.abs(dLat) < 1e-11) break;
57
+ }
58
+
59
+ return [wgsLon, wgsLat];
60
+ },
61
+
62
+ gcj02_bd09ll(gcjLon, gcjLat) {
63
+ this._assertLonLat(gcjLon, gcjLat);
64
+ const z = Math.sqrt(gcjLon * gcjLon + gcjLat * gcjLat) + 0.00002 * Math.sin(gcjLat * X_PI);
65
+ const theta = Math.atan2(gcjLat, gcjLon) + 0.000003 * Math.cos(gcjLon * X_PI);
66
+ return [z * Math.cos(theta) + 0.0065, z * Math.sin(theta) + 0.006];
67
+ },
68
+
69
+ bd09ll_gcj02(bdLon, bdLat) {
70
+ this._assertLonLat(bdLon, bdLat);
71
+ const x = bdLon - 0.0065;
72
+ const y = bdLat - 0.006;
73
+ const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI);
74
+ const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI);
75
+ return [z * Math.cos(theta), z * Math.sin(theta)];
76
+ },
77
+
78
+ bd09ll_gcj02_precise(bdLon, bdLat) {
79
+ let [gLon, gLat] = this.bd09ll_gcj02(bdLon, bdLat);
80
+ for (let i = 0; i < 2; i++) {
81
+ const [tBdLon, tBdLat] = this.gcj02_bd09ll(gLon, gLat);
82
+ gLon -= (tBdLon - bdLon);
83
+ gLat -= (tBdLat - bdLat);
84
+ }
85
+ return [gLon, gLat];
86
+ },
87
+
88
+ wgs84_bd09ll(wgsLon, wgsLat) {
89
+ const gcj = this.wgs84_gcj02(wgsLon, wgsLat);
90
+ return this.gcj02_bd09ll(gcj[0], gcj[1]);
91
+ },
92
+
93
+ bd09ll_wgs84(bdLon, bdLat) {
94
+ const gcj = this.bd09ll_gcj02(bdLon, bdLat);
95
+ return this.gcj02_wgs84(gcj[0], gcj[1]);
96
+ },
97
+
98
+ bd09ll_wgs84_precise(bdLon, bdLat) {
99
+ const gcj = this.bd09ll_gcj02_precise(bdLon, bdLat);
100
+ return this.gcj02_wgs84_precise(gcj[0], gcj[1]);
101
+ },
102
+
103
+ wgs84_epsg3857(lon, lat) {
104
+ this._assertLonLat(lon, lat);
105
+ const clampedLat = Math.max(-EPSG3857_MAX_LAT, Math.min(EPSG3857_MAX_LAT, lat));
106
+ const x = lon * EPSG3857_RADIUS * PI / 180.0;
107
+ const radLat = clampedLat * PI / 180.0;
108
+ const y = EPSG3857_RADIUS * Math.log(Math.tan(PI / 4.0 + radLat / 2.0));
109
+ return [x, y];
110
+ },
111
+
112
+ epsg3857_wgs84(x, y) {
113
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
114
+ throw new Error('Invalid coordinate: x and y must be finite numbers.');
115
+ }
116
+ const lon = (x / (EPSG3857_RADIUS * PI)) * 180.0;
117
+ const lat = (2.0 * Math.atan(Math.exp(y / EPSG3857_RADIUS)) - PI / 2.0) * 180.0 / PI;
118
+ return [lon, lat];
119
+ },
120
+
121
+ gcj02_epsg3857(lon, lat) {
122
+ const wgs = this.gcj02_wgs84_precise(lon, lat);
123
+ return this.wgs84_epsg3857(wgs[0], wgs[1]);
124
+ },
125
+
126
+ epsg3857_gcj02(x, y) {
127
+ const wgs = this.epsg3857_wgs84(x, y);
128
+ return this.wgs84_gcj02(wgs[0], wgs[1]);
129
+ },
130
+
131
+ bd09ll_epsg3857(lon, lat) {
132
+ const wgs = this.bd09ll_wgs84_precise(lon, lat);
133
+ return this.wgs84_epsg3857(wgs[0], wgs[1]);
134
+ },
135
+
136
+ epsg3857_bd09ll(x, y) {
137
+ const wgs = this.epsg3857_wgs84(x, y);
138
+ return this.wgs84_bd09ll(wgs[0], wgs[1]);
139
+ },
140
+
141
+ outOfChina(lat, lon) {
142
+ if (Array.isArray(lat)) {
143
+ return this.outOfChina(lat[1], lat[0]);
144
+ }
145
+ return lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271;
146
+ },
147
+
148
+ transformLat(x, y) {
149
+ let ret = -100 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
150
+ ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
151
+ ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;
152
+ ret += (160.0 * Math.sin(y / 12.0 * PI) + 320.0 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;
153
+ return ret;
154
+ },
155
+
156
+ transformLon(x, y) {
157
+ let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
158
+ ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
159
+ ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;
160
+ ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;
161
+ return ret;
162
+ },
163
+
164
+ gcj_encrypt(wgsLat, wgsLon) {
165
+ const [lon, lat] = this.wgs84_gcj02(wgsLon, wgsLat);
166
+ return { lat, lon };
167
+ },
168
+
169
+ _assertLonLat(lon, lat) {
170
+ if (!Number.isFinite(lon) || !Number.isFinite(lat)) {
171
+ throw new Error('Invalid coordinate: longitude and latitude must be finite numbers.');
172
+ }
173
+ }
130
174
  };
131
175
 
132
- const crs = {
133
- bd09ll: '+proj=longlat +datum=BD09',
134
- gcj02: '+proj=longlat +datum=GCJ02',
135
- wgs84: '+proj=longlat +datum=WGS84 +no_defs'
136
- };
137
-
138
- const CRS_ALIASES = {
139
- 'bd-09': 'bd09ll',
140
- 'bd09': 'bd09ll',
141
- 'bd09ll': 'bd09ll',
142
- 'epsg:4326': 'wgs84',
143
- 'gcj-02': 'gcj02',
144
- 'gcj02': 'gcj02',
145
- 'wgs-84': 'wgs84',
146
- 'wgs84': 'wgs84'
147
- };
148
-
149
- const ProjectionTransform = {
150
- crs,
151
-
152
- transform(source, fromCRS, toCRS) {
153
- if (source == null) return null;
154
-
155
- const from = this._normalizeCRS(fromCRS);
156
- const to = this._normalizeCRS(toCRS);
157
-
158
- if (this._isCoord(source)) {
159
- return this._transformCoordinate(source, from, to);
160
- }
161
-
162
- if (Array.isArray(source)) {
163
- return this._transformCoordinateTree(source, from, to);
164
- }
165
-
166
- return this._transformGeoJSON(source, from, to);
167
- },
168
-
169
- _transformGeoJSON(geoJSON, fromCRS, toCRS) {
170
- if (!geoJSON || typeof geoJSON !== 'object') {
171
- throw new Error('Invalid GeoJSON object.');
172
- }
173
-
174
- if (geoJSON.type === 'Feature') {
175
- return {
176
- ...geoJSON,
177
- geometry: geoJSON.geometry ? this.transform(geoJSON.geometry, fromCRS, toCRS) : null
178
- };
179
- }
180
-
181
- if (geoJSON.type === 'FeatureCollection') {
182
- if (!Array.isArray(geoJSON.features)) throw new Error('Invalid FeatureCollection: features must be an array.');
183
- return {
184
- ...geoJSON,
185
- features: geoJSON.features.map(feature => this.transform(feature, fromCRS, toCRS))
186
- };
187
- }
188
-
189
- if (geoJSON.type === 'GeometryCollection') {
190
- if (!Array.isArray(geoJSON.geometries)) throw new Error('Invalid GeometryCollection: geometries must be an array.');
191
- return {
192
- ...geoJSON,
193
- geometries: geoJSON.geometries.map(geometry => this.transform(geometry, fromCRS, toCRS))
194
- };
195
- }
196
-
197
- if ('coordinates' in geoJSON) {
198
- return {
199
- ...geoJSON,
200
- coordinates: this._transformCoordinateTree(geoJSON.coordinates, fromCRS, toCRS)
201
- };
202
- }
203
-
204
- throw new Error(`Unsupported GeoJSON type: ${geoJSON.type || 'unknown'}.`);
205
- },
206
-
207
- _transformCoordinateTree(coordinates, fromCRS, toCRS) {
208
- if (coordinates == null) return coordinates;
209
- if (this._isCoord(coordinates)) return this._transformCoordinate(coordinates, fromCRS, toCRS);
210
- if (!Array.isArray(coordinates)) throw new Error('Invalid coordinates: expected coordinate array.');
211
- return coordinates.map(item => this._transformCoordinateTree(item, fromCRS, toCRS));
212
- },
213
-
214
- _transformCoordinate(coordinate, fromCRS, toCRS) {
215
- const from = this._normalizeCRS(fromCRS);
216
- const to = this._normalizeCRS(toCRS);
217
- const lon = Number(coordinate[0]);
218
- const lat = Number(coordinate[1]);
219
-
220
- if (!Number.isFinite(lon) || !Number.isFinite(lat)) {
221
- throw new Error('Invalid coordinate: longitude and latitude must be finite numbers.');
222
- }
223
-
224
- if (from === to) return [lon, lat, ...coordinate.slice(2)];
225
-
226
- const method = `${from}_${to}`;
227
- if (typeof GPS[method] !== 'function') {
228
- throw new Error(`Unsupported CRS transform: ${fromCRS} -> ${toCRS}.`);
229
- }
230
-
231
- const transformed = GPS[method](lon, lat);
232
- return [transformed[0], transformed[1], ...coordinate.slice(2)];
233
- },
234
-
235
- _normalizeCRS(value) {
236
- if (!value) throw new Error('must provide a valid fromCRS and toCRS.');
237
-
238
- if (typeof value === 'object') {
239
- if (value.type === 'proj4' && value.properties && value.properties.proj) {
240
- const crsName = this._toCRS(value.properties.proj);
241
- if (crsName) return crsName;
242
- }
243
- throw new Error('Unsupported CRS object.');
244
- }
245
-
246
- const key = String(value).trim().toLowerCase();
247
- const normalized = CRS_ALIASES[key];
248
- if (!normalized) throw new Error(`Unsupported CRS: ${value}.`);
249
- return normalized;
250
- },
251
-
252
- _toCRS(proj) {
253
- for (const key in this.crs) {
254
- if (proj === this.crs[key]) return key;
255
- }
256
- return null;
257
- },
258
-
259
- _isCoord(coordinate) {
260
- return Array.isArray(coordinate) &&
261
- coordinate.length >= 2 &&
262
- Number.isFinite(Number(coordinate[0])) &&
263
- Number.isFinite(Number(coordinate[1])) &&
264
- !Array.isArray(coordinate[0]) &&
265
- !Array.isArray(coordinate[1]);
266
- }
176
+ const crs = {
177
+ bd09ll: '+proj=longlat +datum=BD09',
178
+ gcj02: '+proj=longlat +datum=GCJ02',
179
+ wgs84: '+proj=longlat +datum=WGS84 +no_defs',
180
+ epsg3857: '+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs'
267
181
  };
268
182
 
269
- const TYPE_MAP = {
270
- POINT: 'Point',
271
- LINESTRING: 'LineString',
272
- POLYGON: 'Polygon',
273
- MULTIPOINT: 'MultiPoint',
274
- MULTILINESTRING: 'MultiLineString',
275
- MULTIPOLYGON: 'MultiPolygon'
276
- };
277
-
278
- const REVERSE_TYPE_MAP = Object.fromEntries(
279
- Object.entries(TYPE_MAP).map(([wktType, geoJSONType]) => [geoJSONType, wktType])
280
- );
281
-
282
- const WKT = {
283
- parse(wkt) {
284
- if (typeof wkt !== 'string' || !wkt.trim()) {
285
- throw new Error('Invalid WKT string.');
286
- }
287
-
288
- const source = wkt.trim();
289
- const header = source.match(/^([A-Za-z]+)(?:\s+(ZM|Z|M))?\s*/i);
290
- if (!header) throw new Error('Invalid WKT string.');
291
-
292
- const wktType = header[1].toUpperCase();
293
- const geoJSONType = TYPE_MAP[wktType];
294
- if (!geoJSONType) throw new Error(`Unsupported WKT type: ${wktType}.`);
295
-
296
- const body = source.slice(header[0].length).trim();
297
- if (/^EMPTY$/i.test(body)) {
298
- return { type: geoJSONType, coordinates: this._emptyCoordinates(geoJSONType) };
299
- }
300
-
301
- if (!body.startsWith('(') || !body.endsWith(')')) {
302
- throw new Error('Invalid WKT string: missing coordinate parentheses.');
303
- }
304
-
305
- const content = this._stripOuterParens(body);
306
- return {
307
- type: geoJSONType,
308
- coordinates: this._parseCoordinates(content, geoJSONType)
309
- };
310
- },
311
-
312
- stringify(geoJSON) {
313
- if (!geoJSON || typeof geoJSON !== 'object' || !geoJSON.type) {
314
- throw new Error('Invalid GeoJSON.');
315
- }
316
-
317
- const wktType = REVERSE_TYPE_MAP[geoJSON.type];
318
- if (!wktType) throw new Error(`Unsupported geometry type: ${geoJSON.type}.`);
319
-
320
- const coords = geoJSON.coordinates;
321
- if (this._isEmpty(coords)) return `${wktType} EMPTY`;
322
-
323
- return `${wktType}(${this._stringifyCoordinates(coords, geoJSON.type)})`;
324
- },
325
-
326
- _parseCoordinates(content, type) {
327
- switch (type) {
328
- case 'Point':
329
- return this._parsePoint(content);
330
- case 'LineString':
331
- return this._parsePointList(content);
332
- case 'Polygon':
333
- return this._parsePolygon(content);
334
- case 'MultiPoint':
335
- return this._parseMultiPoint(content);
336
- case 'MultiLineString':
337
- return this._splitTopLevel(content).map(line => this._parsePointList(this._stripOptionalOuterParens(line)));
338
- case 'MultiPolygon':
339
- return this._splitTopLevel(content).map(poly => this._parsePolygon(this._stripOptionalOuterParens(poly)));
340
- default:
341
- throw new Error(`Unsupported geometry type: ${type}.`);
342
- }
343
- },
344
-
345
- _parsePoint(value) {
346
- const numbers = value.trim().split(/\s+/).filter(Boolean).map(Number);
347
- if (numbers.length < 2 || numbers.some(n => !Number.isFinite(n))) {
348
- throw new Error(`Invalid WKT coordinate: ${value}.`);
349
- }
350
- return numbers;
351
- },
352
-
353
- _parsePointList(value) {
354
- const points = this._splitTopLevel(value).map(part => this._parsePoint(this._stripOptionalOuterParens(part)));
355
- if (!points.length) throw new Error('Invalid WKT coordinate list.');
356
- return points;
357
- },
358
-
359
- _parsePolygon(value) {
360
- const rings = this._splitTopLevel(value).map(ring => this._parsePointList(this._stripOptionalOuterParens(ring)));
361
- if (!rings.length) throw new Error('Invalid WKT polygon.');
362
- return rings;
363
- },
364
-
365
- _parseMultiPoint(value) {
366
- return this._splitTopLevel(value).map(point => this._parsePoint(this._stripOptionalOuterParens(point)));
367
- },
368
-
369
- _splitTopLevel(value) {
370
- const parts = [];
371
- let depth = 0;
372
- let start = 0;
373
-
374
- for (let i = 0; i < value.length; i++) {
375
- const char = value[i];
376
- if (char === '(') depth++;
377
- else if (char === ')') depth--;
378
-
379
- if (depth < 0) throw new Error('Invalid WKT string: unbalanced parentheses.');
380
-
381
- if (char === ',' && depth === 0) {
382
- parts.push(value.slice(start, i).trim());
383
- start = i + 1;
384
- }
385
- }
386
-
387
- if (depth !== 0) throw new Error('Invalid WKT string: unbalanced parentheses.');
388
- const last = value.slice(start).trim();
389
- if (last) parts.push(last);
390
- return parts;
391
- },
392
-
393
- _stripOuterParens(value) {
394
- const trimmed = value.trim();
395
- if (!trimmed.startsWith('(') || !trimmed.endsWith(')')) {
396
- throw new Error('Invalid WKT string: missing coordinate parentheses.');
397
- }
398
- return trimmed.slice(1, -1).trim();
399
- },
400
-
401
- _stripOptionalOuterParens(value) {
402
- const trimmed = value.trim();
403
- if (!trimmed.startsWith('(')) return trimmed;
404
- return this._stripOuterParens(trimmed);
405
- },
406
-
407
- _stringifyCoordinates(coords, type) {
408
- switch (type) {
409
- case 'Point':
410
- return this._stringifyPoint(coords);
411
- case 'LineString':
412
- return this._stringifyPointList(coords);
413
- case 'Polygon':
414
- return this._stringifyPolygon(coords);
415
- case 'MultiPoint':
416
- return coords.map(point => `(${this._stringifyPoint(point)})`).join(', ');
417
- case 'MultiLineString':
418
- return coords.map(line => `(${this._stringifyPointList(line)})`).join(', ');
419
- case 'MultiPolygon':
420
- return coords.map(poly => `(${this._stringifyPolygon(poly)})`).join(', ');
421
- default:
422
- throw new Error(`Unsupported geometry type: ${type}.`);
423
- }
424
- },
425
-
426
- _stringifyPoint(point) {
427
- if (!Array.isArray(point) || point.length < 2) throw new Error('Invalid coordinate.');
428
- return point.map(value => {
429
- const number = Number(value);
430
- if (!Number.isFinite(number)) throw new Error('Invalid coordinate value.');
431
- return String(number);
432
- }).join(' ');
433
- },
434
-
435
- _stringifyPointList(points) {
436
- if (!Array.isArray(points)) throw new Error('Invalid coordinate list.');
437
- return points.map(point => this._stringifyPoint(point)).join(', ');
438
- },
439
-
440
- _stringifyPolygon(rings) {
441
- if (!Array.isArray(rings)) throw new Error('Invalid polygon coordinates.');
442
- return rings.map(ring => `(${this._stringifyPointList(ring)})`).join(', ');
443
- },
444
-
445
- _emptyCoordinates(type) {
446
- return type === 'Point' ? [] : [];
447
- },
448
-
449
- _isEmpty(coords) {
450
- return Array.isArray(coords) && coords.length === 0;
451
- }
183
+ const CRS_ALIASES = {
184
+ 'bd-09': 'bd09ll',
185
+ 'bd09': 'bd09ll',
186
+ 'bd09ll': 'bd09ll',
187
+ 'bd-09ll': 'bd09ll',
188
+ 'epsg:4326': 'wgs84',
189
+ 'epsg4326': 'wgs84',
190
+ '4326': 'wgs84',
191
+ 'cgcs2000': 'wgs84',
192
+ 'cgcs-2000': 'wgs84',
193
+ 'epsg:4490': 'wgs84',
194
+ 'epsg4490': 'wgs84',
195
+ '4490': 'wgs84',
196
+ 'gcj-02': 'gcj02',
197
+ 'gcj02': 'gcj02',
198
+ 'wgs-84': 'wgs84',
199
+ 'wgs84': 'wgs84',
200
+ 'epsg:3857': 'epsg3857',
201
+ 'epsg3857': 'epsg3857',
202
+ '3857': 'epsg3857',
203
+ 'epsg:900913': 'epsg3857',
204
+ 'epsg900913': 'epsg3857',
205
+ '900913': 'epsg3857',
206
+ 'mercator': 'epsg3857',
207
+ 'webmercator': 'epsg3857'
208
+ };
209
+
210
+ const ProjectionTransform = {
211
+ crs,
212
+
213
+ transform(source, fromCRS, toCRS, options = {}) {
214
+ if (source == null) return null;
215
+
216
+ const from = this._normalizeCRS(fromCRS);
217
+ const to = this._normalizeCRS(toCRS);
218
+ const transformCoord = this._getCoordTransformer(from, to, options);
219
+
220
+ if (this._isCoord(source)) {
221
+ return transformCoord(source);
222
+ }
223
+
224
+ if (Array.isArray(source)) {
225
+ return this._transformCoordinateTree(source, transformCoord, options);
226
+ }
227
+
228
+ return this._transformGeoJSON(source, transformCoord, options);
229
+ },
230
+
231
+ _getCoordTransformer(from, to, options = {}) {
232
+ const isMutate = Boolean(options.mutate);
233
+ const isPrecise = options.precise !== false;
234
+
235
+ if (from === to) {
236
+ return isMutate
237
+ ? coord => coord
238
+ : coord => [Number(coord[0]), Number(coord[1]), ...coord.slice(2)];
239
+ }
240
+
241
+ let method;
242
+ if (isPrecise && (from === 'gcj02' || from === 'bd09ll') && to === 'wgs84') {
243
+ method = `${from}_wgs84_precise`;
244
+ } else {
245
+ method = `${from}_${to}`;
246
+ }
247
+
248
+ const fn = GPS[method];
249
+ if (typeof fn !== 'function') {
250
+ throw new Error(`Unsupported CRS transform: ${from} -> ${to}.`);
251
+ }
252
+
253
+ if (isMutate) {
254
+ return coord => {
255
+ const x = Number(coord[0]);
256
+ const y = Number(coord[1]);
257
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
258
+ throw new Error('Invalid coordinate: longitude and latitude must be finite numbers.');
259
+ }
260
+ const [tx, ty] = fn.call(GPS, x, y);
261
+ coord[0] = tx;
262
+ coord[1] = ty;
263
+ return coord;
264
+ };
265
+ }
266
+
267
+ return coord => {
268
+ const x = Number(coord[0]);
269
+ const y = Number(coord[1]);
270
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
271
+ throw new Error('Invalid coordinate: longitude and latitude must be finite numbers.');
272
+ }
273
+ const [tx, ty] = fn.call(GPS, x, y);
274
+ return [tx, ty, ...coord.slice(2)];
275
+ };
276
+ },
277
+
278
+ _transformGeoJSON(geoJSON, transformCoord, options = {}) {
279
+ if (!geoJSON || typeof geoJSON !== 'object') {
280
+ throw new Error('Invalid GeoJSON object.');
281
+ }
282
+
283
+ const result = options.mutate ? geoJSON : { ...geoJSON };
284
+
285
+ if (Array.isArray(geoJSON.bbox)) {
286
+ result.bbox = this._transformBbox(geoJSON.bbox, transformCoord);
287
+ }
288
+
289
+ if (geoJSON.type === 'Feature') {
290
+ result.geometry = geoJSON.geometry
291
+ ? this._transformGeoJSON(geoJSON.geometry, transformCoord, options)
292
+ : null;
293
+ return result;
294
+ }
295
+
296
+ if (geoJSON.type === 'FeatureCollection') {
297
+ if (!Array.isArray(geoJSON.features)) {
298
+ throw new Error('Invalid FeatureCollection: features must be an array.');
299
+ }
300
+ if (options.mutate) {
301
+ for (let i = 0; i < geoJSON.features.length; i++) {
302
+ geoJSON.features[i] = this._transformGeoJSON(geoJSON.features[i], transformCoord, options);
303
+ }
304
+ result.features = geoJSON.features;
305
+ } else {
306
+ result.features = geoJSON.features.map(f => this._transformGeoJSON(f, transformCoord, options));
307
+ }
308
+ return result;
309
+ }
310
+
311
+ if (geoJSON.type === 'GeometryCollection') {
312
+ if (!Array.isArray(geoJSON.geometries)) {
313
+ throw new Error('Invalid GeometryCollection: geometries must be an array.');
314
+ }
315
+ if (options.mutate) {
316
+ for (let i = 0; i < geoJSON.geometries.length; i++) {
317
+ geoJSON.geometries[i] = geoJSON.geometries[i]
318
+ ? this._transformGeoJSON(geoJSON.geometries[i], transformCoord, options)
319
+ : null;
320
+ }
321
+ result.geometries = geoJSON.geometries;
322
+ } else {
323
+ result.geometries = geoJSON.geometries.map(g => (g ? this._transformGeoJSON(g, transformCoord, options) : null));
324
+ }
325
+ return result;
326
+ }
327
+
328
+ if ('coordinates' in geoJSON) {
329
+ result.coordinates = this._transformCoordinateTree(geoJSON.coordinates, transformCoord, options);
330
+ return result;
331
+ }
332
+
333
+ throw new Error(`Unsupported GeoJSON type: ${geoJSON.type || 'unknown'}.`);
334
+ },
335
+
336
+ _transformCoordinateTree(coordinates, transformCoord, options = {}) {
337
+ if (coordinates == null) return coordinates;
338
+ if (this._isCoord(coordinates)) return transformCoord(coordinates);
339
+ if (!Array.isArray(coordinates)) throw new Error('Invalid coordinates: expected coordinate array.');
340
+
341
+ if (options.mutate) {
342
+ for (let i = 0; i < coordinates.length; i++) {
343
+ coordinates[i] = this._transformCoordinateTree(coordinates[i], transformCoord, options);
344
+ }
345
+ return coordinates;
346
+ }
347
+
348
+ return coordinates.map(item => this._transformCoordinateTree(item, transformCoord, options));
349
+ },
350
+
351
+ _transformBbox(bbox, transformCoord) {
352
+ if (!Array.isArray(bbox) || (bbox.length !== 4 && bbox.length !== 6)) {
353
+ return bbox;
354
+ }
355
+
356
+ if (bbox.length === 4) {
357
+ const [minX, minY, maxX, maxY] = bbox;
358
+ const p1 = transformCoord([minX, minY]);
359
+ const p2 = transformCoord([maxX, maxY]);
360
+ return [
361
+ Math.min(p1[0], p2[0]),
362
+ Math.min(p1[1], p2[1]),
363
+ Math.max(p1[0], p2[0]),
364
+ Math.max(p1[1], p2[1])
365
+ ];
366
+ }
367
+
368
+ const [minX, minY, minZ, maxX, maxY, maxZ] = bbox;
369
+ const p1 = transformCoord([minX, minY, minZ]);
370
+ const p2 = transformCoord([maxX, maxY, maxZ]);
371
+ return [
372
+ Math.min(p1[0], p2[0]),
373
+ Math.min(p1[1], p2[1]),
374
+ minZ,
375
+ Math.max(p1[0], p2[0]),
376
+ Math.max(p1[1], p2[1]),
377
+ maxZ
378
+ ];
379
+ },
380
+
381
+ _normalizeCRS(value) {
382
+ if (!value) throw new Error('must provide a valid fromCRS and toCRS.');
383
+
384
+ if (typeof value === 'object') {
385
+ if (value.type === 'proj4' && value.properties && value.properties.proj) {
386
+ const crsName = this._toCRS(value.properties.proj);
387
+ if (crsName) return crsName;
388
+ }
389
+ throw new Error('Unsupported CRS object.');
390
+ }
391
+
392
+ const key = String(value).trim().toLowerCase();
393
+ const normalized = CRS_ALIASES[key];
394
+ if (!normalized) throw new Error(`Unsupported CRS: ${value}.`);
395
+ return normalized;
396
+ },
397
+
398
+ _toCRS(proj) {
399
+ for (const key in this.crs) {
400
+ if (proj === this.crs[key]) return key;
401
+ }
402
+ return null;
403
+ },
404
+
405
+ _isCoord(coordinate) {
406
+ return Array.isArray(coordinate) &&
407
+ coordinate.length >= 2 &&
408
+ !Array.isArray(coordinate[0]) &&
409
+ !Array.isArray(coordinate[1]) &&
410
+ Number.isFinite(Number(coordinate[0])) &&
411
+ Number.isFinite(Number(coordinate[1]));
412
+ }
413
+ };
414
+
415
+ const TYPE_MAP = {
416
+ POINT: 'Point',
417
+ LINESTRING: 'LineString',
418
+ POLYGON: 'Polygon',
419
+ MULTIPOINT: 'MultiPoint',
420
+ MULTILINESTRING: 'MultiLineString',
421
+ MULTIPOLYGON: 'MultiPolygon',
422
+ GEOMETRYCOLLECTION: 'GeometryCollection'
423
+ };
424
+
425
+ const REVERSE_TYPE_MAP = Object.fromEntries(
426
+ Object.entries(TYPE_MAP).map(([wktType, geoJSONType]) => [geoJSONType, wktType])
427
+ );
428
+
429
+ const WKT = {
430
+ parse(wkt) {
431
+ if (typeof wkt !== 'string' || !wkt.trim()) {
432
+ throw new Error('Invalid WKT string.');
433
+ }
434
+
435
+ const source = wkt.trim();
436
+ const header = source.match(/^([A-Za-z]+)(?:\s+(ZM|Z|M))?\s*/i);
437
+ if (!header) throw new Error('Invalid WKT string.');
438
+
439
+ const wktType = header[1].toUpperCase();
440
+ const geoJSONType = TYPE_MAP[wktType];
441
+ if (!geoJSONType) throw new Error(`Unsupported WKT type: ${wktType}.`);
442
+
443
+ const body = source.slice(header[0].length).trim();
444
+ if (/^EMPTY$/i.test(body)) {
445
+ if (geoJSONType === 'GeometryCollection') {
446
+ return { type: geoJSONType, geometries: [] };
447
+ }
448
+ return { type: geoJSONType, coordinates: this._emptyCoordinates(geoJSONType) };
449
+ }
450
+
451
+ if (!body.startsWith('(') || !body.endsWith(')')) {
452
+ throw new Error('Invalid WKT string: missing coordinate parentheses.');
453
+ }
454
+
455
+ const content = this._stripOuterParens(body);
456
+
457
+ if (geoJSONType === 'GeometryCollection') {
458
+ return {
459
+ type: 'GeometryCollection',
460
+ geometries: this._splitTopLevel(content).map(item => this.parse(item))
461
+ };
462
+ }
463
+
464
+ return {
465
+ type: geoJSONType,
466
+ coordinates: this._parseCoordinates(content, geoJSONType)
467
+ };
468
+ },
469
+
470
+ stringify(geoJSON, options = {}) {
471
+ if (!geoJSON || typeof geoJSON !== 'object' || !geoJSON.type) {
472
+ throw new Error('Invalid GeoJSON.');
473
+ }
474
+
475
+ const wktType = REVERSE_TYPE_MAP[geoJSON.type];
476
+ if (!wktType) throw new Error(`Unsupported geometry type: ${geoJSON.type}.`);
477
+
478
+ const precision = typeof options === 'number' ? options : options?.precision;
479
+
480
+ if (geoJSON.type === 'GeometryCollection') {
481
+ if (!Array.isArray(geoJSON.geometries) || geoJSON.geometries.length === 0) {
482
+ return 'GEOMETRYCOLLECTION EMPTY';
483
+ }
484
+ return `GEOMETRYCOLLECTION(${geoJSON.geometries.map(geom => this.stringify(geom, options)).join(', ')})`;
485
+ }
486
+
487
+ const coords = geoJSON.coordinates;
488
+ if (this._isEmpty(coords)) return `${wktType} EMPTY`;
489
+
490
+ return `${wktType}(${this._stringifyCoordinates(coords, geoJSON.type, precision)})`;
491
+ },
492
+
493
+ _parseCoordinates(content, type) {
494
+ switch (type) {
495
+ case 'Point':
496
+ return this._parsePoint(content);
497
+ case 'LineString':
498
+ return this._parsePointList(content);
499
+ case 'Polygon':
500
+ return this._parsePolygon(content);
501
+ case 'MultiPoint':
502
+ return this._parseMultiPoint(content);
503
+ case 'MultiLineString':
504
+ return this._splitTopLevel(content).map(line => this._parsePointList(this._stripOptionalOuterParens(line)));
505
+ case 'MultiPolygon':
506
+ return this._splitTopLevel(content).map(poly => this._parsePolygon(this._stripOptionalOuterParens(poly)));
507
+ default:
508
+ throw new Error(`Unsupported geometry type: ${type}.`);
509
+ }
510
+ },
511
+
512
+ _parsePoint(value) {
513
+ const numbers = value.trim().split(/\s+/).filter(Boolean).map(Number);
514
+ if (numbers.length < 2 || numbers.some(n => !Number.isFinite(n))) {
515
+ throw new Error(`Invalid WKT coordinate: ${value}.`);
516
+ }
517
+ return numbers;
518
+ },
519
+
520
+ _parsePointList(value) {
521
+ const points = this._splitTopLevel(value).map(part => this._parsePoint(this._stripOptionalOuterParens(part)));
522
+ if (!points.length) throw new Error('Invalid WKT coordinate list.');
523
+ return points;
524
+ },
525
+
526
+ _parsePolygon(value) {
527
+ const rings = this._splitTopLevel(value).map(ring => this._parsePointList(this._stripOptionalOuterParens(ring)));
528
+ if (!rings.length) throw new Error('Invalid WKT polygon.');
529
+ return rings;
530
+ },
531
+
532
+ _parseMultiPoint(value) {
533
+ return this._splitTopLevel(value).map(point => this._parsePoint(this._stripOptionalOuterParens(point)));
534
+ },
535
+
536
+ _splitTopLevel(value) {
537
+ const parts = [];
538
+ let depth = 0;
539
+ let start = 0;
540
+
541
+ for (let i = 0; i < value.length; i++) {
542
+ const char = value[i];
543
+ if (char === '(') depth++;
544
+ else if (char === ')') depth--;
545
+
546
+ if (depth < 0) throw new Error('Invalid WKT string: unbalanced parentheses.');
547
+
548
+ if (char === ',' && depth === 0) {
549
+ parts.push(value.slice(start, i).trim());
550
+ start = i + 1;
551
+ }
552
+ }
553
+
554
+ if (depth !== 0) throw new Error('Invalid WKT string: unbalanced parentheses.');
555
+ const last = value.slice(start).trim();
556
+ if (last) parts.push(last);
557
+ return parts;
558
+ },
559
+
560
+ _stripOuterParens(value) {
561
+ const trimmed = value.trim();
562
+ if (!trimmed.startsWith('(') || !trimmed.endsWith(')')) {
563
+ throw new Error('Invalid WKT string: missing coordinate parentheses.');
564
+ }
565
+ return trimmed.slice(1, -1).trim();
566
+ },
567
+
568
+ _stripOptionalOuterParens(value) {
569
+ const trimmed = value.trim();
570
+ if (!trimmed.startsWith('(')) return trimmed;
571
+ return this._stripOuterParens(trimmed);
572
+ },
573
+
574
+ _stringifyCoordinates(coords, type, precision) {
575
+ switch (type) {
576
+ case 'Point':
577
+ return this._stringifyPoint(coords, precision);
578
+ case 'LineString':
579
+ return this._stringifyPointList(coords, precision);
580
+ case 'Polygon':
581
+ return this._stringifyPolygon(coords, precision);
582
+ case 'MultiPoint':
583
+ return coords.map(point => `(${this._stringifyPoint(point, precision)})`).join(', ');
584
+ case 'MultiLineString':
585
+ return coords.map(line => `(${this._stringifyPointList(line, precision)})`).join(', ');
586
+ case 'MultiPolygon':
587
+ return coords.map(poly => `(${this._stringifyPolygon(poly, precision)})`).join(', ');
588
+ default:
589
+ throw new Error(`Unsupported geometry type: ${type}.`);
590
+ }
591
+ },
592
+
593
+ _stringifyPoint(point, precision) {
594
+ if (!Array.isArray(point) || point.length < 2) throw new Error('Invalid coordinate.');
595
+ return point.map(value => {
596
+ const number = Number(value);
597
+ if (!Number.isFinite(number)) throw new Error('Invalid coordinate value.');
598
+ if (typeof precision === 'number' && precision >= 0) {
599
+ return Number(number.toFixed(precision)).toString();
600
+ }
601
+ return String(number);
602
+ }).join(' ');
603
+ },
604
+
605
+ _stringifyPointList(points, precision) {
606
+ if (!Array.isArray(points)) throw new Error('Invalid coordinate list.');
607
+ return points.map(point => this._stringifyPoint(point, precision)).join(', ');
608
+ },
609
+
610
+ _stringifyPolygon(rings, precision) {
611
+ if (!Array.isArray(rings)) throw new Error('Invalid polygon coordinates.');
612
+ return rings.map(ring => `(${this._stringifyPointList(ring, precision)})`).join(', ');
613
+ },
614
+
615
+ _emptyCoordinates(type) {
616
+ return [];
617
+ },
618
+
619
+ _isEmpty(coords) {
620
+ return Array.isArray(coords) && coords.length === 0;
621
+ }
452
622
  };
453
623
 
454
624
  const giserConvertCrs = {
@@ -456,32 +626,34 @@ const giserConvertCrs = {
456
626
  ProjectionTransform,
457
627
  WKT,
458
628
 
459
- transform(input, fromCRS, toCRS) {
629
+ transform(input, fromCRS, toCRS, options) {
460
630
  if (input == null || input === '') return null;
461
631
 
462
632
  if (typeof input === 'string') {
463
633
  const geoJSON = WKT.parse(input);
464
- return WKT.stringify(ProjectionTransform.transform(geoJSON, fromCRS, toCRS));
634
+ const transformed = ProjectionTransform.transform(geoJSON, fromCRS, toCRS, options);
635
+ return WKT.stringify(transformed, options);
465
636
  }
466
637
 
467
- return ProjectionTransform.transform(input, fromCRS, toCRS);
638
+ return ProjectionTransform.transform(input, fromCRS, toCRS, options);
468
639
  },
469
640
 
470
- transformPoint(coord, fromCRS, toCRS) {
471
- return this.transform(coord, fromCRS, toCRS);
641
+ transformPoint(coord, fromCRS, toCRS, options) {
642
+ return this.transform(coord, fromCRS, toCRS, options);
472
643
  },
473
644
 
474
- transformPoints(points, fromCRS, toCRS) {
475
- return this.transform(points, fromCRS, toCRS);
645
+ transformPoints(points, fromCRS, toCRS, options) {
646
+ return this.transform(points, fromCRS, toCRS, options);
476
647
  },
477
648
 
478
- transformGeoJSON(geoJSON, fromCRS, toCRS) {
479
- return this.transform(geoJSON, fromCRS, toCRS);
649
+ transformGeoJSON(geoJSON, fromCRS, toCRS, options) {
650
+ return this.transform(geoJSON, fromCRS, toCRS, options);
480
651
  },
481
652
 
482
- transformWKT(wkt, fromCRS, toCRS) {
483
- return this.transform(wkt, fromCRS, toCRS);
653
+ transformWKT(wkt, fromCRS, toCRS, options) {
654
+ return this.transform(wkt, fromCRS, toCRS, options);
484
655
  }
485
656
  };
486
657
 
487
658
  export { GPS, ProjectionTransform, WKT, giserConvertCrs as default };
659
+ //# sourceMappingURL=giserConvertCrs.esm.js.map