wkt-parse-and-geojson 1.0.5 → 1.0.7
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/dist/index.cjs.js +34 -248
- package/dist/index.esm.js +34 -248
- package/dist/index.umd.js +34 -248
- package/package.json +2 -7
- package/dist/geojson-builder.d.ts +0 -92
- package/dist/geojson-to-wkt.d.ts +0 -32
- package/dist/index.d.ts +0 -8
- package/dist/namespace.d.ts +0 -34
- package/dist/types.d.ts +0 -51
- package/dist/validate.d.ts +0 -29
- package/dist/wkt-builder.d.ts +0 -17
- package/dist/wkt-parser.d.ts +0 -33
- package/dist/wkt-to-geojson.d.ts +0 -35
package/dist/index.cjs.js
CHANGED
|
@@ -4,7 +4,6 @@ var types = /*#__PURE__*/Object.freeze({
|
|
|
4
4
|
__proto__: null
|
|
5
5
|
});
|
|
6
6
|
|
|
7
|
-
// Precompiled regex for better performance
|
|
8
7
|
const RE_WHITESPACE = /\s/;
|
|
9
8
|
const RE_NUMBER_START = /[0-9\-]/;
|
|
10
9
|
const RE_NUMBER_BODY = /[0-9\.\-eE\+]/;
|
|
@@ -30,7 +29,6 @@ class Lexer {
|
|
|
30
29
|
return RE_NUMBER_BODY.test(c);
|
|
31
30
|
}
|
|
32
31
|
nextToken() {
|
|
33
|
-
// skip whitespace
|
|
34
32
|
while (this.pos < this.input.length && this.isWhitespace(this.peek())) {
|
|
35
33
|
this.advance();
|
|
36
34
|
}
|
|
@@ -50,7 +48,6 @@ class Lexer {
|
|
|
50
48
|
this.advance();
|
|
51
49
|
return { type: 'COMMA', value: ',' };
|
|
52
50
|
}
|
|
53
|
-
// Number: starts with digit or minus
|
|
54
51
|
if (this.isNumberStart(c)) {
|
|
55
52
|
const start = this.pos;
|
|
56
53
|
while (this.pos < this.input.length && this.isNumberBody(this.peek())) {
|
|
@@ -58,7 +55,6 @@ class Lexer {
|
|
|
58
55
|
}
|
|
59
56
|
return { type: 'NUMBER', value: this.input.slice(start, this.pos) };
|
|
60
57
|
}
|
|
61
|
-
// Word (geometry type or EMPTY/Z/M keyword)
|
|
62
58
|
const start = this.pos;
|
|
63
59
|
while (this.pos < this.input.length && RE_WORD_CHAR.test(this.peek())) {
|
|
64
60
|
this.pos++;
|
|
@@ -82,7 +78,6 @@ class WKTParser {
|
|
|
82
78
|
}
|
|
83
79
|
this.tokens.push({ type: 'EOF', value: '' });
|
|
84
80
|
const geometry = this.parseGeometry();
|
|
85
|
-
// 校验尾部无多余字符(防止静默忽略垃圾输入)
|
|
86
81
|
if (this.peek().type !== 'EOF') {
|
|
87
82
|
throw new Error(`Unexpected trailing token after geometry: "${this.peek().value}"`);
|
|
88
83
|
}
|
|
@@ -94,7 +89,6 @@ class WKTParser {
|
|
|
94
89
|
advance() {
|
|
95
90
|
return this.tokens[this.pos++];
|
|
96
91
|
}
|
|
97
|
-
/** 消费当前 token 并返回,若类型不匹配则抛出错误 */
|
|
98
92
|
consume(type) {
|
|
99
93
|
const token = this.peek();
|
|
100
94
|
if (token.type !== type) {
|
|
@@ -135,14 +129,12 @@ class WKTParser {
|
|
|
135
129
|
throw new Error(`Unknown geometry type: ${token.value}`);
|
|
136
130
|
}
|
|
137
131
|
}
|
|
138
|
-
// ── 消费可选的维度修饰符(Z / M / ZM)
|
|
139
132
|
skipDimensionKeyword() {
|
|
140
133
|
const t = this.peek();
|
|
141
134
|
if (t.type === 'WORD' && (t.value === 'Z' || t.value === 'M' || t.value === 'ZM')) {
|
|
142
135
|
this.advance();
|
|
143
136
|
}
|
|
144
137
|
}
|
|
145
|
-
// ── 判断并消费 EMPTY 关键字
|
|
146
138
|
isEmptyGeometry() {
|
|
147
139
|
const t = this.peek();
|
|
148
140
|
if (t.type === 'WORD' && t.value === 'EMPTY') {
|
|
@@ -151,13 +143,10 @@ class WKTParser {
|
|
|
151
143
|
}
|
|
152
144
|
return false;
|
|
153
145
|
}
|
|
154
|
-
// ── POINT ────────────────────────────────────────────────────────
|
|
155
146
|
parsePoint() {
|
|
156
|
-
this.advance();
|
|
147
|
+
this.advance();
|
|
157
148
|
this.skipDimensionKeyword();
|
|
158
149
|
if (this.isEmptyGeometry()) {
|
|
159
|
-
// GeoJSON 无法表示空点:POINT EMPTY 在 GeoJSON 中应用 null geometry Feature,
|
|
160
|
-
// 此处直接抛出,由调用方决定如何处理。
|
|
161
150
|
throw new Error('POINT EMPTY cannot be represented as a GeoJSON Point. ' +
|
|
162
151
|
'Consider using wktToFeature() and checking Feature.geometry === null.');
|
|
163
152
|
}
|
|
@@ -166,9 +155,8 @@ class WKTParser {
|
|
|
166
155
|
this.consume('RPAREN');
|
|
167
156
|
return { type: 'Point', coordinates: coords };
|
|
168
157
|
}
|
|
169
|
-
// ── LINESTRING ───────────────────────────────────────────────────
|
|
170
158
|
parseLineString() {
|
|
171
|
-
this.advance();
|
|
159
|
+
this.advance();
|
|
172
160
|
this.skipDimensionKeyword();
|
|
173
161
|
if (this.isEmptyGeometry()) {
|
|
174
162
|
return { type: 'LineString', coordinates: [] };
|
|
@@ -176,9 +164,8 @@ class WKTParser {
|
|
|
176
164
|
const coords = this.parseCoordinatesList();
|
|
177
165
|
return { type: 'LineString', coordinates: coords };
|
|
178
166
|
}
|
|
179
|
-
// ── POLYGON ──────────────────────────────────────────────────────
|
|
180
167
|
parsePolygon() {
|
|
181
|
-
this.advance();
|
|
168
|
+
this.advance();
|
|
182
169
|
this.skipDimensionKeyword();
|
|
183
170
|
if (this.isEmptyGeometry()) {
|
|
184
171
|
return { type: 'Polygon', coordinates: [] };
|
|
@@ -192,24 +179,21 @@ class WKTParser {
|
|
|
192
179
|
this.consume('RPAREN');
|
|
193
180
|
return { type: 'Polygon', coordinates: rings };
|
|
194
181
|
}
|
|
195
|
-
// ── MULTIPOINT ───────────────────────────────────────────────────
|
|
196
182
|
parseMultiPoint() {
|
|
197
|
-
this.advance();
|
|
183
|
+
this.advance();
|
|
198
184
|
this.skipDimensionKeyword();
|
|
199
185
|
if (this.isEmptyGeometry() || this.peek().type !== 'LPAREN') {
|
|
200
186
|
return { type: 'MultiPoint', coordinates: [] };
|
|
201
187
|
}
|
|
202
|
-
this.advance();
|
|
188
|
+
this.advance();
|
|
203
189
|
const coords = [];
|
|
204
190
|
while (!this.isDone()) {
|
|
205
191
|
if (this.peek().type === 'LPAREN') {
|
|
206
|
-
|
|
207
|
-
this.advance(); // consume (
|
|
192
|
+
this.advance();
|
|
208
193
|
coords.push(this.parseCoordinates());
|
|
209
194
|
this.consume('RPAREN');
|
|
210
195
|
}
|
|
211
196
|
else {
|
|
212
|
-
// 非标准写法: MULTIPOINT (x y, x y)
|
|
213
197
|
coords.push(this.parseCoordinates());
|
|
214
198
|
}
|
|
215
199
|
this.skipComma();
|
|
@@ -217,14 +201,13 @@ class WKTParser {
|
|
|
217
201
|
this.consume('RPAREN');
|
|
218
202
|
return { type: 'MultiPoint', coordinates: coords };
|
|
219
203
|
}
|
|
220
|
-
// ── MULTILINESTRING ──────────────────────────────────────────────
|
|
221
204
|
parseMultiLineString() {
|
|
222
|
-
this.advance();
|
|
205
|
+
this.advance();
|
|
223
206
|
this.skipDimensionKeyword();
|
|
224
207
|
if (this.isEmptyGeometry() || this.peek().type !== 'LPAREN') {
|
|
225
208
|
return { type: 'MultiLineString', coordinates: [] };
|
|
226
209
|
}
|
|
227
|
-
this.advance();
|
|
210
|
+
this.advance();
|
|
228
211
|
const lines = [];
|
|
229
212
|
while (!this.isDone()) {
|
|
230
213
|
lines.push(this.parseCoordinatesList());
|
|
@@ -233,9 +216,8 @@ class WKTParser {
|
|
|
233
216
|
this.consume('RPAREN');
|
|
234
217
|
return { type: 'MultiLineString', coordinates: lines };
|
|
235
218
|
}
|
|
236
|
-
// ── MULTIPOLYGON ─────────────────────────────────────────────────
|
|
237
219
|
parseMultiPolygon() {
|
|
238
|
-
this.advance();
|
|
220
|
+
this.advance();
|
|
239
221
|
this.skipDimensionKeyword();
|
|
240
222
|
if (this.isEmptyGeometry()) {
|
|
241
223
|
return { type: 'MultiPolygon', coordinates: [] };
|
|
@@ -249,14 +231,13 @@ class WKTParser {
|
|
|
249
231
|
this.consume('RPAREN');
|
|
250
232
|
return { type: 'MultiPolygon', coordinates: polys };
|
|
251
233
|
}
|
|
252
|
-
// ── GEOMETRYCOLLECTION ───────────────────────────────────────────
|
|
253
234
|
parseGeometryCollection() {
|
|
254
|
-
this.advance();
|
|
235
|
+
this.advance();
|
|
255
236
|
this.skipDimensionKeyword();
|
|
256
237
|
if (this.isEmptyGeometry() || this.peek().type !== 'LPAREN') {
|
|
257
238
|
return { type: 'GeometryCollection', geometries: [] };
|
|
258
239
|
}
|
|
259
|
-
this.advance();
|
|
240
|
+
this.advance();
|
|
260
241
|
const geometries = [];
|
|
261
242
|
while (!this.isDone()) {
|
|
262
243
|
geometries.push(this.parseGeometry());
|
|
@@ -265,11 +246,6 @@ class WKTParser {
|
|
|
265
246
|
this.consume('RPAREN');
|
|
266
247
|
return { type: 'GeometryCollection', geometries };
|
|
267
248
|
}
|
|
268
|
-
// ── 坐标解析辅助方法 ──────────────────────────────────────────────
|
|
269
|
-
/**
|
|
270
|
-
* 读取一个坐标点(自动检测维度:X Y 或 X Y Z)
|
|
271
|
-
* 读完 X、Y 后,若下一个 token 仍是 NUMBER,则继续读 Z
|
|
272
|
-
*/
|
|
273
249
|
parseCoordinates() {
|
|
274
250
|
const xStr = this.consume('NUMBER').value;
|
|
275
251
|
const yStr = this.consume('NUMBER').value;
|
|
@@ -279,7 +255,6 @@ class WKTParser {
|
|
|
279
255
|
throw new Error(`Invalid coordinate value: "${xStr}"`);
|
|
280
256
|
if (isNaN(y))
|
|
281
257
|
throw new Error(`Invalid coordinate value: "${yStr}"`);
|
|
282
|
-
// 动态检测 Z 坐标
|
|
283
258
|
if (this.peek().type === 'NUMBER') {
|
|
284
259
|
const zStr = this.advance().value;
|
|
285
260
|
const z = parseFloat(zStr);
|
|
@@ -289,7 +264,6 @@ class WKTParser {
|
|
|
289
264
|
}
|
|
290
265
|
return [x, y];
|
|
291
266
|
}
|
|
292
|
-
/** 解析带括号的坐标序列:( x y, x y, ... ) */
|
|
293
267
|
parseCoordinatesList() {
|
|
294
268
|
this.consume('LPAREN');
|
|
295
269
|
const coords = [];
|
|
@@ -300,11 +274,10 @@ class WKTParser {
|
|
|
300
274
|
this.consume('RPAREN');
|
|
301
275
|
return coords;
|
|
302
276
|
}
|
|
303
|
-
/** 解析环列表(Polygon 级别):( (...), (...) ) */
|
|
304
277
|
parseCoordinateListList() {
|
|
305
278
|
if (this.peek().type !== 'LPAREN')
|
|
306
279
|
return [];
|
|
307
|
-
this.advance();
|
|
280
|
+
this.advance();
|
|
308
281
|
const lists = [];
|
|
309
282
|
while (!this.isDone()) {
|
|
310
283
|
lists.push(this.parseCoordinatesList());
|
|
@@ -314,7 +287,6 @@ class WKTParser {
|
|
|
314
287
|
return lists;
|
|
315
288
|
}
|
|
316
289
|
}
|
|
317
|
-
/** 将 WKT 字符串解析为 GeoJSON Geometry 对象 */
|
|
318
290
|
function parse(wkt) {
|
|
319
291
|
const parser = new WKTParser();
|
|
320
292
|
return parser.parse(wkt);
|
|
@@ -326,10 +298,6 @@ var wktParser = /*#__PURE__*/Object.freeze({
|
|
|
326
298
|
parse: parse
|
|
327
299
|
});
|
|
328
300
|
|
|
329
|
-
/**
|
|
330
|
-
* 将坐标数值格式化为字符串,避免科学计数法(WKT 不支持)。
|
|
331
|
-
* 例:1e-7 → "0.0000001",1.50000 → "1.5",1.0 → "1"
|
|
332
|
-
*/
|
|
333
301
|
function formatNumber(v) {
|
|
334
302
|
if (v % 1 !== 0) {
|
|
335
303
|
return Number(v.toFixed(15)).toString();
|
|
@@ -342,7 +310,6 @@ function positionToWkt(pos) {
|
|
|
342
310
|
function coordsToWkt(coords) {
|
|
343
311
|
return coords.map(positionToWkt).join(', ');
|
|
344
312
|
}
|
|
345
|
-
// 检查坐标是否包含 Z(3个分量)
|
|
346
313
|
function hasZ(coordinates) {
|
|
347
314
|
if (!Array.isArray(coordinates))
|
|
348
315
|
return false;
|
|
@@ -360,7 +327,6 @@ function hasZ(coordinates) {
|
|
|
360
327
|
}
|
|
361
328
|
return false;
|
|
362
329
|
}
|
|
363
|
-
// 获取 Z 后缀字符串
|
|
364
330
|
function zSuffix(coordinates) {
|
|
365
331
|
return hasZ(coordinates) ? ' Z' : '';
|
|
366
332
|
}
|
|
@@ -399,10 +365,6 @@ class WKTBuilder {
|
|
|
399
365
|
const ringStr = geom.coordinates.map(ring => `(${coordsToWkt(ring)})`).join(', ');
|
|
400
366
|
return `POLYGON${zSuffix(geom.coordinates)} (${ringStr})`;
|
|
401
367
|
}
|
|
402
|
-
/**
|
|
403
|
-
* 按 OGC/ISO WKT 标准,MULTIPOINT 每个点用括号包裹:
|
|
404
|
-
* MULTIPOINT ((0 0), (1 1), (2 2))
|
|
405
|
-
*/
|
|
406
368
|
buildMultiPoint(geom) {
|
|
407
369
|
if (geom.coordinates.length === 0)
|
|
408
370
|
return 'MULTIPOINT EMPTY';
|
|
@@ -431,10 +393,10 @@ class WKTBuilder {
|
|
|
431
393
|
return `GEOMETRYCOLLECTION (${geoms})`;
|
|
432
394
|
}
|
|
433
395
|
}
|
|
434
|
-
/** 将 GeoJSON Geometry 对象转换为 WKT 字符串 */
|
|
435
396
|
function build(geometry) {
|
|
436
|
-
return
|
|
397
|
+
return WKT_BUILDER.build(geometry);
|
|
437
398
|
}
|
|
399
|
+
const WKT_BUILDER = new WKTBuilder();
|
|
438
400
|
|
|
439
401
|
var wktBuilder = /*#__PURE__*/Object.freeze({
|
|
440
402
|
__proto__: null,
|
|
@@ -442,15 +404,11 @@ var wktBuilder = /*#__PURE__*/Object.freeze({
|
|
|
442
404
|
build: build
|
|
443
405
|
});
|
|
444
406
|
|
|
445
|
-
// ─── 内部工具:判断是否为 Position([number, number] 或 [number, number, number])
|
|
446
407
|
function isPosition(v) {
|
|
447
408
|
return (Array.isArray(v) &&
|
|
448
409
|
(v.length === 2 || v.length === 3) &&
|
|
449
410
|
v.every((n) => typeof n === 'number'));
|
|
450
411
|
}
|
|
451
|
-
/**
|
|
452
|
-
* GeoJSON 几何对象构建器(类形式,方便组合使用)
|
|
453
|
-
*/
|
|
454
412
|
class GeoJSONBuilder {
|
|
455
413
|
createPoint(x, y, z) {
|
|
456
414
|
return z !== undefined
|
|
@@ -460,130 +418,56 @@ class GeoJSONBuilder {
|
|
|
460
418
|
createLineString(coordinates) {
|
|
461
419
|
return { type: 'LineString', coordinates };
|
|
462
420
|
}
|
|
463
|
-
/**
|
|
464
|
-
* 创建 Polygon。
|
|
465
|
-
* - 传入 `Position[]`:视为单个外环,自动包装为 `[ring]`
|
|
466
|
-
* - 传入 `Position[][]`:视为完整的环列表(外环 + 内环/空洞)
|
|
467
|
-
*/
|
|
468
421
|
createPolygon(coordinates) {
|
|
469
422
|
const rings = isPosition(coordinates[0])
|
|
470
|
-
? [coordinates]
|
|
471
|
-
: coordinates;
|
|
423
|
+
? [coordinates]
|
|
424
|
+
: coordinates;
|
|
472
425
|
return { type: 'Polygon', coordinates: rings };
|
|
473
426
|
}
|
|
474
|
-
/**
|
|
475
|
-
* 创建 MultiPoint。
|
|
476
|
-
* - 传入 `Position`:视为单个点,自动包装为 `[point]`
|
|
477
|
-
* - 传入 `Position[]`:视为多个点
|
|
478
|
-
*/
|
|
479
427
|
createMultiPoint(coordinates) {
|
|
480
428
|
const pts = isPosition(coordinates)
|
|
481
|
-
? [coordinates]
|
|
482
|
-
: coordinates;
|
|
429
|
+
? [coordinates]
|
|
430
|
+
: coordinates;
|
|
483
431
|
return { type: 'MultiPoint', coordinates: pts };
|
|
484
432
|
}
|
|
485
|
-
/**
|
|
486
|
-
* 创建 MultiLineString。
|
|
487
|
-
* - 传入 `Position[]`:视为单条线,自动包装为 `[line]`
|
|
488
|
-
* - 传入 `Position[][]`:视为多条线
|
|
489
|
-
*/
|
|
490
433
|
createMultiLineString(coordinates) {
|
|
491
434
|
const lines = isPosition(coordinates[0])
|
|
492
|
-
? [coordinates]
|
|
493
|
-
: coordinates;
|
|
435
|
+
? [coordinates]
|
|
436
|
+
: coordinates;
|
|
494
437
|
return { type: 'MultiLineString', coordinates: lines };
|
|
495
438
|
}
|
|
496
|
-
/**
|
|
497
|
-
* 创建 MultiPolygon。
|
|
498
|
-
* - 传入 `Position[][]`:视为单个多边形(环列表),自动包装为 `[polygon]`
|
|
499
|
-
* - 传入 `Position[][][]`:视为多个多边形
|
|
500
|
-
*/
|
|
501
439
|
createMultiPolygon(coordinates) {
|
|
502
|
-
// 判断:若第一个元素是 Position[](环),则整体是单个 polygon
|
|
503
440
|
const firstElem = coordinates[0];
|
|
504
441
|
const isSinglePolygon = Array.isArray(firstElem) && Array.isArray(firstElem[0]) && isPosition(firstElem[0]);
|
|
505
442
|
const polys = isSinglePolygon
|
|
506
|
-
? [coordinates]
|
|
507
|
-
: coordinates;
|
|
443
|
+
? [coordinates]
|
|
444
|
+
: coordinates;
|
|
508
445
|
return { type: 'MultiPolygon', coordinates: polys };
|
|
509
446
|
}
|
|
510
|
-
/**
|
|
511
|
-
* 创建 GeometryCollection。
|
|
512
|
-
* - 传入单个 `Geometry`:自动包装为 `[geometry]`
|
|
513
|
-
* - 传入 `Geometry[]`:直接使用
|
|
514
|
-
*/
|
|
515
447
|
createGeometryCollection(geometries) {
|
|
516
448
|
const geoms = Array.isArray(geometries) ? geometries : [geometries];
|
|
517
449
|
return { type: 'GeometryCollection', geometries: geoms };
|
|
518
450
|
}
|
|
519
451
|
}
|
|
520
|
-
// 单例,避免重复实例化
|
|
521
452
|
const _builder = new GeoJSONBuilder();
|
|
522
|
-
/** 创建 Point */
|
|
523
453
|
function createPoint(x, y, z) {
|
|
524
454
|
return _builder.createPoint(x, y, z);
|
|
525
455
|
}
|
|
526
|
-
/** 创建 LineString */
|
|
527
456
|
function createLineString(coordinates) {
|
|
528
457
|
return _builder.createLineString(coordinates);
|
|
529
458
|
}
|
|
530
|
-
/**
|
|
531
|
-
* 创建 Polygon。
|
|
532
|
-
* - 传入 `Position[]`:单个外环,自动包装
|
|
533
|
-
* - 传入 `Position[][]`:外环 + 内环(空洞)
|
|
534
|
-
*
|
|
535
|
-
* @example
|
|
536
|
-
* createPolygon([[0,0],[1,0],[1,1],[0,1],[0,0]])
|
|
537
|
-
* createPolygon([[[0,0],[10,0],[10,10],[0,10],[0,0]], [[2,2],[4,2],[4,4],[2,4],[2,2]]])
|
|
538
|
-
*/
|
|
539
459
|
function createPolygon(coordinates) {
|
|
540
460
|
return _builder.createPolygon(coordinates);
|
|
541
461
|
}
|
|
542
|
-
/**
|
|
543
|
-
* 创建 MultiPoint。
|
|
544
|
-
* - 传入 `Position`:单个点
|
|
545
|
-
* - 传入 `Position[]`:多个点
|
|
546
|
-
*
|
|
547
|
-
* @example
|
|
548
|
-
* createMultiPoint([0, 0])
|
|
549
|
-
* createMultiPoint([[0,0],[1,1],[2,2]])
|
|
550
|
-
*/
|
|
551
462
|
function createMultiPoint(coordinates) {
|
|
552
463
|
return _builder.createMultiPoint(coordinates);
|
|
553
464
|
}
|
|
554
|
-
/**
|
|
555
|
-
* 创建 MultiLineString。
|
|
556
|
-
* - 传入 `Position[]`:单条线
|
|
557
|
-
* - 传入 `Position[][]`:多条线
|
|
558
|
-
*
|
|
559
|
-
* @example
|
|
560
|
-
* createMultiLineString([[0,0],[1,1]])
|
|
561
|
-
* createMultiLineString([[[0,0],[1,1]], [[2,2],[3,3]]])
|
|
562
|
-
*/
|
|
563
465
|
function createMultiLineString(coordinates) {
|
|
564
466
|
return _builder.createMultiLineString(coordinates);
|
|
565
467
|
}
|
|
566
|
-
/**
|
|
567
|
-
* 创建 MultiPolygon。
|
|
568
|
-
* - 传入 `Position[][]`:单个多边形(环列表)
|
|
569
|
-
* - 传入 `Position[][][]`:多个多边形
|
|
570
|
-
*
|
|
571
|
-
* @example
|
|
572
|
-
* createMultiPolygon([[[0,0],[1,0],[1,1],[0,1],[0,0]]])
|
|
573
|
-
* createMultiPolygon([[[[0,0],[1,0],[1,1],[0,1],[0,0]]], [[[2,2],[3,2],[3,3],[2,3],[2,2]]]])
|
|
574
|
-
*/
|
|
575
468
|
function createMultiPolygon(coordinates) {
|
|
576
469
|
return _builder.createMultiPolygon(coordinates);
|
|
577
470
|
}
|
|
578
|
-
/**
|
|
579
|
-
* 创建 GeometryCollection。
|
|
580
|
-
* - 传入单个 `Geometry`:自动包装
|
|
581
|
-
* - 传入 `Geometry[]`:直接使用
|
|
582
|
-
*
|
|
583
|
-
* @example
|
|
584
|
-
* createGeometryCollection(createPoint(0, 0))
|
|
585
|
-
* createGeometryCollection([createPoint(0,0), createLineString([[0,0],[1,1]])])
|
|
586
|
-
*/
|
|
587
471
|
function createGeometryCollection(geometries) {
|
|
588
472
|
return _builder.createGeometryCollection(geometries);
|
|
589
473
|
}
|
|
@@ -600,30 +484,9 @@ var geojsonBuilder = /*#__PURE__*/Object.freeze({
|
|
|
600
484
|
createPolygon: createPolygon
|
|
601
485
|
});
|
|
602
486
|
|
|
603
|
-
/**
|
|
604
|
-
* 将 WKT 字符串转换为 GeoJSON Geometry 对象。
|
|
605
|
-
*
|
|
606
|
-
* @example
|
|
607
|
-
* wktToGeoJSON('POINT (30.5 40.5)')
|
|
608
|
-
* // → { type: 'Point', coordinates: [30.5, 40.5] }
|
|
609
|
-
*
|
|
610
|
-
* wktToGeoJSON('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))')
|
|
611
|
-
* // → { type: 'Polygon', coordinates: [[[0,0],[1,0],[1,1],[0,1],[0,0]]] }
|
|
612
|
-
*/
|
|
613
487
|
function wktToGeoJSON(wkt) {
|
|
614
488
|
return parse(wkt);
|
|
615
489
|
}
|
|
616
|
-
/**
|
|
617
|
-
* 将 WKT 字符串转换为 GeoJSON Feature 对象。
|
|
618
|
-
*
|
|
619
|
-
* @param wkt WKT 字符串
|
|
620
|
-
* @param properties 可选的 Feature 属性对象
|
|
621
|
-
* @param id 可选的 Feature ID
|
|
622
|
-
*
|
|
623
|
-
* @example
|
|
624
|
-
* wktToFeature('POINT (30.5 40.5)', { name: '北京' })
|
|
625
|
-
* // → { type: 'Feature', geometry: { type: 'Point', ... }, properties: { name: '北京' } }
|
|
626
|
-
*/
|
|
627
490
|
function wktToFeature(wkt, properties = null, id) {
|
|
628
491
|
const geometry = parse(wkt);
|
|
629
492
|
const feature = {
|
|
@@ -636,16 +499,6 @@ function wktToFeature(wkt, properties = null, id) {
|
|
|
636
499
|
}
|
|
637
500
|
return feature;
|
|
638
501
|
}
|
|
639
|
-
/**
|
|
640
|
-
* 将多个 WKT 字符串批量转换为 GeoJSON FeatureCollection。
|
|
641
|
-
*
|
|
642
|
-
* @param wkts WKT 字符串数组
|
|
643
|
-
* @param properties 可选,每个 Feature 的属性数组(长度应与 wkts 一致)
|
|
644
|
-
*
|
|
645
|
-
* @example
|
|
646
|
-
* wktToFeatureCollection(['POINT (0 0)', 'POINT (1 1)'])
|
|
647
|
-
* // → { type: 'FeatureCollection', features: [...] }
|
|
648
|
-
*/
|
|
649
502
|
function wktToFeatureCollection(wkts, properties) {
|
|
650
503
|
const features = wkts.map((wkt, i) => wktToFeature(wkt, properties ? (properties[i] ?? null) : null));
|
|
651
504
|
return { type: 'FeatureCollection', features };
|
|
@@ -658,43 +511,15 @@ var wktToGeojson = /*#__PURE__*/Object.freeze({
|
|
|
658
511
|
wktToGeoJSON: wktToGeoJSON
|
|
659
512
|
});
|
|
660
513
|
|
|
661
|
-
/**
|
|
662
|
-
* 将 GeoJSON Geometry 对象转换为 WKT 字符串。
|
|
663
|
-
*
|
|
664
|
-
* @example
|
|
665
|
-
* geojsonToWkt({ type: 'Point', coordinates: [30.5, 40.5] })
|
|
666
|
-
* // → 'POINT (30.5 40.5)'
|
|
667
|
-
*
|
|
668
|
-
* geojsonToWkt({ type: 'Polygon', coordinates: [[[0,0],[1,0],[1,1],[0,1],[0,0]]] })
|
|
669
|
-
* // → 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))'
|
|
670
|
-
*/
|
|
671
514
|
function geojsonToWkt(geojson) {
|
|
672
515
|
return build(geojson);
|
|
673
516
|
}
|
|
674
|
-
/**
|
|
675
|
-
* 将 GeoJSON Feature 对象转换为 WKT 字符串(取 geometry 部分)。
|
|
676
|
-
*
|
|
677
|
-
* @throws 若 Feature.geometry 为 null,则抛出错误
|
|
678
|
-
*
|
|
679
|
-
* @example
|
|
680
|
-
* featureToWkt({ type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] }, properties: null })
|
|
681
|
-
* // → 'POINT (0 0)'
|
|
682
|
-
*/
|
|
683
517
|
function featureToWkt(feature) {
|
|
684
518
|
if (!feature.geometry) {
|
|
685
519
|
throw new Error('Feature.geometry is null, cannot convert to WKT');
|
|
686
520
|
}
|
|
687
521
|
return build(feature.geometry);
|
|
688
522
|
}
|
|
689
|
-
/**
|
|
690
|
-
* 将 GeoJSON FeatureCollection 中所有 Feature 转换为 WKT 字符串数组。
|
|
691
|
-
*
|
|
692
|
-
* geometry 为 null 的 Feature 会被跳过(返回数组中对应位置为 null)。
|
|
693
|
-
*
|
|
694
|
-
* @example
|
|
695
|
-
* featureCollectionToWkt({ type: 'FeatureCollection', features: [...] })
|
|
696
|
-
* // → ['POINT (0 0)', 'LINESTRING (0 0, 1 1)', ...]
|
|
697
|
-
*/
|
|
698
523
|
function featureCollectionToWkt(fc) {
|
|
699
524
|
return fc.features.map((f) => {
|
|
700
525
|
if (!f.geometry)
|
|
@@ -710,9 +535,11 @@ var geojsonToWkt$1 = /*#__PURE__*/Object.freeze({
|
|
|
710
535
|
geojsonToWkt: geojsonToWkt
|
|
711
536
|
});
|
|
712
537
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
538
|
+
const VALID_GEOMETRY_TYPES = [
|
|
539
|
+
'Point', 'LineString', 'Polygon',
|
|
540
|
+
'MultiPoint', 'MultiLineString', 'MultiPolygon',
|
|
541
|
+
'GeometryCollection'
|
|
542
|
+
];
|
|
716
543
|
function validateWKT(wkt) {
|
|
717
544
|
if (!wkt || typeof wkt !== 'string') {
|
|
718
545
|
return { valid: false, error: 'WKT must be a non-empty string' };
|
|
@@ -729,28 +556,18 @@ function validateWKT(wkt) {
|
|
|
729
556
|
return { valid: false, error: e.message };
|
|
730
557
|
}
|
|
731
558
|
}
|
|
732
|
-
/**
|
|
733
|
-
* 校验 GeoJSON Geometry 对象是否合法
|
|
734
|
-
*/
|
|
735
559
|
function validateGeoJSON(geojson) {
|
|
736
560
|
if (!geojson || typeof geojson !== 'object') {
|
|
737
561
|
return { valid: false, error: 'GeoJSON must be an object' };
|
|
738
562
|
}
|
|
739
563
|
const obj = geojson;
|
|
740
|
-
// 检查 type 字段
|
|
741
564
|
if (!obj.type || typeof obj.type !== 'string') {
|
|
742
565
|
return { valid: false, error: 'GeoJSON must have a "type" property' };
|
|
743
566
|
}
|
|
744
567
|
const type = obj.type;
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
'MultiPoint', 'MultiLineString', 'MultiPolygon',
|
|
748
|
-
'GeometryCollection'
|
|
749
|
-
];
|
|
750
|
-
if (!validTypes.includes(type)) {
|
|
751
|
-
return { valid: false, error: `Invalid geometry type: "${type}". Must be one of: ${validTypes.join(', ')}` };
|
|
568
|
+
if (!VALID_GEOMETRY_TYPES.includes(type)) {
|
|
569
|
+
return { valid: false, error: `Invalid geometry type: "${type}". Must be one of: ${VALID_GEOMETRY_TYPES.join(', ')}` };
|
|
752
570
|
}
|
|
753
|
-
// GeometryCollection 特殊处理
|
|
754
571
|
if (type === 'GeometryCollection') {
|
|
755
572
|
if (!obj.geometries || !Array.isArray(obj.geometries)) {
|
|
756
573
|
return { valid: false, error: 'GeometryCollection must have a "geometries" array' };
|
|
@@ -763,11 +580,9 @@ function validateGeoJSON(geojson) {
|
|
|
763
580
|
}
|
|
764
581
|
return { valid: true };
|
|
765
582
|
}
|
|
766
|
-
// 其他几何类型必须要有 coordinates
|
|
767
583
|
if (obj.coordinates === undefined) {
|
|
768
584
|
return { valid: false, error: `${type} must have "coordinates"` };
|
|
769
585
|
}
|
|
770
|
-
// 校验坐标格式
|
|
771
586
|
return validateCoordinates(type, obj.coordinates);
|
|
772
587
|
}
|
|
773
588
|
function validateCoordinates(type, coords) {
|
|
@@ -842,28 +657,21 @@ function validatePosition(pos) {
|
|
|
842
657
|
}
|
|
843
658
|
return { valid: true };
|
|
844
659
|
}
|
|
845
|
-
/**
|
|
846
|
-
* 尝试从可能不规范的 WKT 中恢复出有效结果
|
|
847
|
-
* 主要处理尾部多余字符的情况
|
|
848
|
-
*/
|
|
849
660
|
function tryFixWKT(wkt) {
|
|
850
661
|
const trimmed = wkt.trim();
|
|
851
662
|
if (!trimmed) {
|
|
852
663
|
return { fixed: wkt, changed: false };
|
|
853
664
|
}
|
|
854
|
-
// 先尝试直接解析,如果成功则不需要修复
|
|
855
665
|
try {
|
|
856
666
|
parse(trimmed);
|
|
857
667
|
return { fixed: trimmed, changed: false };
|
|
858
668
|
}
|
|
859
669
|
catch {
|
|
860
|
-
// 解析失败,尝试修复
|
|
861
670
|
}
|
|
862
|
-
// 尝试找到最后一个有效的 geometry 结束位置
|
|
863
671
|
const patterns = [
|
|
864
|
-
/\)\s*[A-Z]/i,
|
|
865
|
-
/EMPTY\s+[A-Z]/i,
|
|
866
|
-
/\)\s*$/,
|
|
672
|
+
/\)\s*[A-Z]/i,
|
|
673
|
+
/EMPTY\s+[A-Z]/i,
|
|
674
|
+
/\)\s*$/,
|
|
867
675
|
];
|
|
868
676
|
for (const pattern of patterns) {
|
|
869
677
|
const match = trimmed.match(pattern);
|
|
@@ -874,11 +682,9 @@ function tryFixWKT(wkt) {
|
|
|
874
682
|
return { fixed, changed: true };
|
|
875
683
|
}
|
|
876
684
|
catch {
|
|
877
|
-
// 这个修复方案不行,尝试下一个
|
|
878
685
|
}
|
|
879
686
|
}
|
|
880
687
|
}
|
|
881
|
-
// 尝试去除尾部垃圾字符
|
|
882
688
|
const lastValidIndex = findLastValidPosition(trimmed);
|
|
883
689
|
if (lastValidIndex > 0) {
|
|
884
690
|
const fixed = trimmed.slice(0, lastValidIndex + 1);
|
|
@@ -887,13 +693,11 @@ function tryFixWKT(wkt) {
|
|
|
887
693
|
return { fixed, changed: true };
|
|
888
694
|
}
|
|
889
695
|
catch {
|
|
890
|
-
// 修复失败
|
|
891
696
|
}
|
|
892
697
|
}
|
|
893
698
|
return { fixed: wkt, changed: false };
|
|
894
699
|
}
|
|
895
700
|
function findLastValidPosition(wkt) {
|
|
896
|
-
// 从后往前找第一个有效的右括号位置
|
|
897
701
|
let depth = 0;
|
|
898
702
|
for (let i = wkt.length - 1; i >= 0; i--) {
|
|
899
703
|
const c = wkt[i];
|
|
@@ -901,34 +705,20 @@ function findLastValidPosition(wkt) {
|
|
|
901
705
|
depth++;
|
|
902
706
|
else if (c === '(')
|
|
903
707
|
depth--;
|
|
904
|
-
else if (c === ' ' && depth === 0 &&
|
|
905
|
-
|
|
906
|
-
const afterSpace = wkt.slice(i + 1).trim();
|
|
907
|
-
if (!afterSpace)
|
|
908
|
-
continue;
|
|
909
|
-
if (!/^[A-Z]/.test(afterSpace))
|
|
910
|
-
continue;
|
|
911
|
-
// 如果空格后面是字母开头,可能是垃圾字符
|
|
912
|
-
if (i > 5 && /[A-Z]$/.test(wkt.slice(0, i).trim())) {
|
|
708
|
+
else if (c === ' ' && depth === 0 && /[A-Z]/.test(wkt.slice(i + 1))) {
|
|
709
|
+
if (wkt.slice(0, i).trimEnd().match(/[A-Z]\s*$/)) {
|
|
913
710
|
return i - 1;
|
|
914
711
|
}
|
|
915
712
|
}
|
|
916
713
|
}
|
|
917
714
|
return wkt.length - 1;
|
|
918
715
|
}
|
|
919
|
-
/**
|
|
920
|
-
* 深度克隆 GeoJSON 对象(用于避免意外修改原对象)
|
|
921
|
-
*/
|
|
922
716
|
function cloneGeometry(geometry) {
|
|
923
717
|
return JSON.parse(JSON.stringify(geometry));
|
|
924
718
|
}
|
|
925
|
-
/**
|
|
926
|
-
* 判断两个几何对象是否相等(坐标对比)
|
|
927
|
-
*/
|
|
928
719
|
function geometryEquals(a, b) {
|
|
929
720
|
if (a.type !== b.type)
|
|
930
721
|
return false;
|
|
931
|
-
// Point 比较最常见,单独优化
|
|
932
722
|
if (a.type === 'Point') {
|
|
933
723
|
const aCoords = a.coordinates;
|
|
934
724
|
const bCoords = b.coordinates;
|
|
@@ -937,7 +727,6 @@ function geometryEquals(a, b) {
|
|
|
937
727
|
aCoords[1] === bCoords[1] &&
|
|
938
728
|
(aCoords.length === 2 || aCoords[2] === bCoords[2]);
|
|
939
729
|
}
|
|
940
|
-
// 其他类型使用 JSON.stringify(缓存 key 优化可后续添加)
|
|
941
730
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
942
731
|
}
|
|
943
732
|
|
|
@@ -950,9 +739,6 @@ var validate = /*#__PURE__*/Object.freeze({
|
|
|
950
739
|
validateWKT: validateWKT
|
|
951
740
|
});
|
|
952
741
|
|
|
953
|
-
// 命名空间导出 - 所有公共 API 汇总
|
|
954
|
-
// 使用方式: import WKT from 'wkt-parse-and-geojson';
|
|
955
|
-
// WKT.parse(...), WKT.build(...), etc.
|
|
956
742
|
const WKT = {
|
|
957
743
|
...types,
|
|
958
744
|
...wktParser,
|