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 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(); // consume POINT
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(); // consume LINESTRING
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(); // consume POLYGON
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(); // consume MULTIPOINT
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(); // consume outer (
188
+ this.advance();
203
189
  const coords = [];
204
190
  while (!this.isDone()) {
205
191
  if (this.peek().type === 'LPAREN') {
206
- // 标准写法: MULTIPOINT ((x y), (x y))
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(); // consume MULTILINESTRING
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(); // consume outer (
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(); // consume MULTIPOLYGON
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(); // consume GEOMETRYCOLLECTION
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(); // consume (
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(); // consume outer (
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 new WKTBuilder().build(geometry);
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] // 单环:Position[] → Position[][]
471
- : coordinates; // 多环:已是 Position[][]
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] // 单点:Position → Position[]
482
- : coordinates; // 多点:已是 Position[]
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] // 单线:Position[] → Position[][]
493
- : coordinates; // 多线:已是 Position[][]
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] // 单多边形:Position[][] → Position[][][]
507
- : coordinates; // 多多边形:已是 Position[][][]
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
- * 校验 WKT 字符串格式是否合法
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
- const validTypes = [
746
- 'Point', 'LineString', 'Polygon',
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, // 括号后跟字母 (如 POLYGON ((...)) POINT )
865
- /EMPTY\s+[A-Z]/i, // EMPTY 后跟字母
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 && i < wkt.length - 1) {
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,