wkt-parse-and-geojson 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -5
- package/dist/index.cjs.js +135 -38
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +135 -39
- package/dist/index.umd.js +135 -38
- package/dist/namespace.d.ts +34 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -83,8 +83,12 @@ npm install wkt-parse-and-geojson
|
|
|
83
83
|
// CommonJS
|
|
84
84
|
const { parse, build, wktToFeature } = require('wkt-parse-and-geojson');
|
|
85
85
|
|
|
86
|
-
// ES Module
|
|
86
|
+
// ES Module - 按需导入
|
|
87
87
|
import { parse, build, wktToFeature } from 'wkt-parse-and-geojson';
|
|
88
|
+
|
|
89
|
+
// ES Module - 命名空间导入(避免命名冲突)
|
|
90
|
+
import WKT from 'wkt-parse-and-geojson';
|
|
91
|
+
const geom = WKT.parse('POINT (116.39 39.91)');
|
|
88
92
|
```
|
|
89
93
|
|
|
90
94
|
### 浏览器 (script 标签)
|
|
@@ -93,13 +97,13 @@ import { parse, build, wktToFeature } from 'wkt-parse-and-geojson';
|
|
|
93
97
|
<!-- UMD 方式:通过 script 标签直接引入,全局变量 WKTGeoJSON -->
|
|
94
98
|
<script src="https://unpkg.com/wkt-parse-and-geojson/dist/index.umd.js"></script>
|
|
95
99
|
<script>
|
|
100
|
+
// 方式一:使用命名空间(推荐,避免命名冲突)
|
|
96
101
|
const geom = WKTGeoJSON.parse('POINT (116.39 39.91)');
|
|
97
102
|
console.log(geom);
|
|
98
|
-
// → { type: 'Point', coordinates: [116.39, 39.91] }
|
|
99
103
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
// 方式二:解构赋值(需注意命名冲突)
|
|
105
|
+
const { parse, build } = WKTGeoJSON;
|
|
106
|
+
const wkt = build(geom);
|
|
103
107
|
</script>
|
|
104
108
|
```
|
|
105
109
|
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var types = /*#__PURE__*/Object.freeze({
|
|
4
|
+
__proto__: null
|
|
5
|
+
});
|
|
6
|
+
|
|
3
7
|
// Precompiled regex for better performance
|
|
4
8
|
const RE_WHITESPACE = /\s/;
|
|
5
9
|
const RE_NUMBER_START = /[0-9\-]/;
|
|
@@ -316,16 +320,21 @@ function parse(wkt) {
|
|
|
316
320
|
return parser.parse(wkt);
|
|
317
321
|
}
|
|
318
322
|
|
|
323
|
+
var wktParser = /*#__PURE__*/Object.freeze({
|
|
324
|
+
__proto__: null,
|
|
325
|
+
WKTParser: WKTParser,
|
|
326
|
+
parse: parse
|
|
327
|
+
});
|
|
328
|
+
|
|
319
329
|
/**
|
|
320
330
|
* 将坐标数值格式化为字符串,避免科学计数法(WKT 不支持)。
|
|
321
331
|
* 例:1e-7 → "0.0000001",1.50000 → "1.5",1.0 → "1"
|
|
322
332
|
*/
|
|
323
333
|
function formatNumber(v) {
|
|
324
|
-
// 有小数则最多保留 15 位有效位,再去掉尾零
|
|
325
334
|
if (v % 1 !== 0) {
|
|
326
|
-
return
|
|
335
|
+
return Number(v.toFixed(15)).toString();
|
|
327
336
|
}
|
|
328
|
-
return v
|
|
337
|
+
return String(v);
|
|
329
338
|
}
|
|
330
339
|
function positionToWkt(pos) {
|
|
331
340
|
return pos.map(formatNumber).join(' ');
|
|
@@ -333,6 +342,28 @@ function positionToWkt(pos) {
|
|
|
333
342
|
function coordsToWkt(coords) {
|
|
334
343
|
return coords.map(positionToWkt).join(', ');
|
|
335
344
|
}
|
|
345
|
+
// 检查坐标是否包含 Z(3个分量)
|
|
346
|
+
function hasZ(coordinates) {
|
|
347
|
+
if (!Array.isArray(coordinates))
|
|
348
|
+
return false;
|
|
349
|
+
if (coordinates.length === 0)
|
|
350
|
+
return false;
|
|
351
|
+
const first = coordinates[0];
|
|
352
|
+
if (Array.isArray(first)) {
|
|
353
|
+
if (typeof first[0] === 'number') {
|
|
354
|
+
return first.length === 3;
|
|
355
|
+
}
|
|
356
|
+
if (Array.isArray(first[0])) {
|
|
357
|
+
const firstRing = first;
|
|
358
|
+
return firstRing.length > 0 && firstRing[0].length === 3;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
// 获取 Z 后缀字符串
|
|
364
|
+
function zSuffix(coordinates) {
|
|
365
|
+
return hasZ(coordinates) ? ' Z' : '';
|
|
366
|
+
}
|
|
336
367
|
class WKTBuilder {
|
|
337
368
|
build(geometry) {
|
|
338
369
|
switch (geometry.type) {
|
|
@@ -355,21 +386,18 @@ class WKTBuilder {
|
|
|
355
386
|
}
|
|
356
387
|
}
|
|
357
388
|
buildPoint(geom) {
|
|
358
|
-
|
|
359
|
-
return `POINT${hasZ ? ' Z' : ''} (${positionToWkt(geom.coordinates)})`;
|
|
389
|
+
return `POINT${zSuffix(geom.coordinates)} (${positionToWkt(geom.coordinates)})`;
|
|
360
390
|
}
|
|
361
391
|
buildLineString(geom) {
|
|
362
392
|
if (geom.coordinates.length === 0)
|
|
363
393
|
return 'LINESTRING EMPTY';
|
|
364
|
-
|
|
365
|
-
return `LINESTRING${hasZ ? ' Z' : ''} (${coordsToWkt(geom.coordinates)})`;
|
|
394
|
+
return `LINESTRING${zSuffix(geom.coordinates)} (${coordsToWkt(geom.coordinates)})`;
|
|
366
395
|
}
|
|
367
396
|
buildPolygon(geom) {
|
|
368
397
|
if (geom.coordinates.length === 0)
|
|
369
398
|
return 'POLYGON EMPTY';
|
|
370
|
-
const hasZ = geom.coordinates[0].length > 0 && geom.coordinates[0][0].length === 3;
|
|
371
399
|
const ringStr = geom.coordinates.map(ring => `(${coordsToWkt(ring)})`).join(', ');
|
|
372
|
-
return `POLYGON${
|
|
400
|
+
return `POLYGON${zSuffix(geom.coordinates)} (${ringStr})`;
|
|
373
401
|
}
|
|
374
402
|
/**
|
|
375
403
|
* 按 OGC/ISO WKT 标准,MULTIPOINT 每个点用括号包裹:
|
|
@@ -378,26 +406,23 @@ class WKTBuilder {
|
|
|
378
406
|
buildMultiPoint(geom) {
|
|
379
407
|
if (geom.coordinates.length === 0)
|
|
380
408
|
return 'MULTIPOINT EMPTY';
|
|
381
|
-
const hasZ = geom.coordinates[0].length === 3;
|
|
382
409
|
const pts = geom.coordinates.map(p => `(${positionToWkt(p)})`).join(', ');
|
|
383
|
-
return `MULTIPOINT${
|
|
410
|
+
return `MULTIPOINT${zSuffix(geom.coordinates)} (${pts})`;
|
|
384
411
|
}
|
|
385
412
|
buildMultiLineString(geom) {
|
|
386
413
|
if (geom.coordinates.length === 0)
|
|
387
414
|
return 'MULTILINESTRING EMPTY';
|
|
388
|
-
const hasZ = geom.coordinates[0].length > 0 && geom.coordinates[0][0].length === 3;
|
|
389
415
|
const lines = geom.coordinates.map(line => `(${coordsToWkt(line)})`).join(', ');
|
|
390
|
-
return `MULTILINESTRING${
|
|
416
|
+
return `MULTILINESTRING${zSuffix(geom.coordinates)} (${lines})`;
|
|
391
417
|
}
|
|
392
418
|
buildMultiPolygon(geom) {
|
|
393
419
|
if (geom.coordinates.length === 0)
|
|
394
420
|
return 'MULTIPOLYGON EMPTY';
|
|
395
|
-
const hasZ = geom.coordinates[0].length > 0 && geom.coordinates[0][0].length > 0 && geom.coordinates[0][0][0].length === 3;
|
|
396
421
|
const polys = geom.coordinates.map(poly => {
|
|
397
422
|
const rings = poly.map(ring => `(${coordsToWkt(ring)})`).join(', ');
|
|
398
423
|
return `(${rings})`;
|
|
399
424
|
}).join(', ');
|
|
400
|
-
return `MULTIPOLYGON${
|
|
425
|
+
return `MULTIPOLYGON${zSuffix(geom.coordinates)} (${polys})`;
|
|
401
426
|
}
|
|
402
427
|
buildGeometryCollection(geom) {
|
|
403
428
|
if (geom.geometries.length === 0)
|
|
@@ -408,8 +433,16 @@ class WKTBuilder {
|
|
|
408
433
|
}
|
|
409
434
|
/** 将 GeoJSON Geometry 对象转换为 WKT 字符串 */
|
|
410
435
|
function build(geometry) {
|
|
411
|
-
return
|
|
436
|
+
return WKT_BUILDER.build(geometry);
|
|
412
437
|
}
|
|
438
|
+
// 单例实例,避免重复创建
|
|
439
|
+
const WKT_BUILDER = new WKTBuilder();
|
|
440
|
+
|
|
441
|
+
var wktBuilder = /*#__PURE__*/Object.freeze({
|
|
442
|
+
__proto__: null,
|
|
443
|
+
WKTBuilder: WKTBuilder,
|
|
444
|
+
build: build
|
|
445
|
+
});
|
|
413
446
|
|
|
414
447
|
// ─── 内部工具:判断是否为 Position([number, number] 或 [number, number, number])
|
|
415
448
|
function isPosition(v) {
|
|
@@ -557,6 +590,18 @@ function createGeometryCollection(geometries) {
|
|
|
557
590
|
return _builder.createGeometryCollection(geometries);
|
|
558
591
|
}
|
|
559
592
|
|
|
593
|
+
var geojsonBuilder = /*#__PURE__*/Object.freeze({
|
|
594
|
+
__proto__: null,
|
|
595
|
+
GeoJSONBuilder: GeoJSONBuilder,
|
|
596
|
+
createGeometryCollection: createGeometryCollection,
|
|
597
|
+
createLineString: createLineString,
|
|
598
|
+
createMultiLineString: createMultiLineString,
|
|
599
|
+
createMultiPoint: createMultiPoint,
|
|
600
|
+
createMultiPolygon: createMultiPolygon,
|
|
601
|
+
createPoint: createPoint,
|
|
602
|
+
createPolygon: createPolygon
|
|
603
|
+
});
|
|
604
|
+
|
|
560
605
|
/**
|
|
561
606
|
* 将 WKT 字符串转换为 GeoJSON Geometry 对象。
|
|
562
607
|
*
|
|
@@ -608,6 +653,13 @@ function wktToFeatureCollection(wkts, properties) {
|
|
|
608
653
|
return { type: 'FeatureCollection', features };
|
|
609
654
|
}
|
|
610
655
|
|
|
656
|
+
var wktToGeojson = /*#__PURE__*/Object.freeze({
|
|
657
|
+
__proto__: null,
|
|
658
|
+
wktToFeature: wktToFeature,
|
|
659
|
+
wktToFeatureCollection: wktToFeatureCollection,
|
|
660
|
+
wktToGeoJSON: wktToGeoJSON
|
|
661
|
+
});
|
|
662
|
+
|
|
611
663
|
/**
|
|
612
664
|
* 将 GeoJSON Geometry 对象转换为 WKT 字符串。
|
|
613
665
|
*
|
|
@@ -653,6 +705,19 @@ function featureCollectionToWkt(fc) {
|
|
|
653
705
|
});
|
|
654
706
|
}
|
|
655
707
|
|
|
708
|
+
var geojsonToWkt$1 = /*#__PURE__*/Object.freeze({
|
|
709
|
+
__proto__: null,
|
|
710
|
+
featureCollectionToWkt: featureCollectionToWkt,
|
|
711
|
+
featureToWkt: featureToWkt,
|
|
712
|
+
geojsonToWkt: geojsonToWkt
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
// 预定义常量,避免重复创建
|
|
716
|
+
const VALID_GEOMETRY_TYPES = [
|
|
717
|
+
'Point', 'LineString', 'Polygon',
|
|
718
|
+
'MultiPoint', 'MultiLineString', 'MultiPolygon',
|
|
719
|
+
'GeometryCollection'
|
|
720
|
+
];
|
|
656
721
|
/**
|
|
657
722
|
* 校验 WKT 字符串格式是否合法
|
|
658
723
|
*/
|
|
@@ -685,13 +750,8 @@ function validateGeoJSON(geojson) {
|
|
|
685
750
|
return { valid: false, error: 'GeoJSON must have a "type" property' };
|
|
686
751
|
}
|
|
687
752
|
const type = obj.type;
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
'MultiPoint', 'MultiLineString', 'MultiPolygon',
|
|
691
|
-
'GeometryCollection'
|
|
692
|
-
];
|
|
693
|
-
if (!validTypes.includes(type)) {
|
|
694
|
-
return { valid: false, error: `Invalid geometry type: "${type}". Must be one of: ${validTypes.join(', ')}` };
|
|
753
|
+
if (!VALID_GEOMETRY_TYPES.includes(type)) {
|
|
754
|
+
return { valid: false, error: `Invalid geometry type: "${type}". Must be one of: ${VALID_GEOMETRY_TYPES.join(', ')}` };
|
|
695
755
|
}
|
|
696
756
|
// GeometryCollection 特殊处理
|
|
697
757
|
if (type === 'GeometryCollection') {
|
|
@@ -794,11 +854,14 @@ function tryFixWKT(wkt) {
|
|
|
794
854
|
if (!trimmed) {
|
|
795
855
|
return { fixed: wkt, changed: false };
|
|
796
856
|
}
|
|
797
|
-
//
|
|
798
|
-
|
|
799
|
-
|
|
857
|
+
// 先尝试直接解析,如果成功则不需要修复
|
|
858
|
+
try {
|
|
859
|
+
parse(trimmed);
|
|
800
860
|
return { fixed: trimmed, changed: false };
|
|
801
861
|
}
|
|
862
|
+
catch {
|
|
863
|
+
// 解析失败,尝试修复
|
|
864
|
+
}
|
|
802
865
|
// 尝试找到最后一个有效的 geometry 结束位置
|
|
803
866
|
const patterns = [
|
|
804
867
|
/\)\s*[A-Z]/i, // 括号后跟字母 (如 POLYGON ((...)) POINT )
|
|
@@ -809,23 +872,30 @@ function tryFixWKT(wkt) {
|
|
|
809
872
|
const match = trimmed.match(pattern);
|
|
810
873
|
if (match) {
|
|
811
874
|
const fixed = trimmed.slice(0, match.index + (match[0].match(/\)/)?.[0].length || 0));
|
|
812
|
-
|
|
875
|
+
try {
|
|
876
|
+
parse(fixed);
|
|
813
877
|
return { fixed, changed: true };
|
|
814
878
|
}
|
|
879
|
+
catch {
|
|
880
|
+
// 这个修复方案不行,尝试下一个
|
|
881
|
+
}
|
|
815
882
|
}
|
|
816
883
|
}
|
|
817
884
|
// 尝试去除尾部垃圾字符
|
|
818
885
|
const lastValidIndex = findLastValidPosition(trimmed);
|
|
819
886
|
if (lastValidIndex > 0) {
|
|
820
887
|
const fixed = trimmed.slice(0, lastValidIndex + 1);
|
|
821
|
-
|
|
888
|
+
try {
|
|
889
|
+
parse(fixed);
|
|
822
890
|
return { fixed, changed: true };
|
|
823
891
|
}
|
|
892
|
+
catch {
|
|
893
|
+
// 修复失败
|
|
894
|
+
}
|
|
824
895
|
}
|
|
825
896
|
return { fixed: wkt, changed: false };
|
|
826
897
|
}
|
|
827
898
|
function findLastValidPosition(wkt) {
|
|
828
|
-
// 从后往前找第一个有效的右括号位置
|
|
829
899
|
let depth = 0;
|
|
830
900
|
for (let i = wkt.length - 1; i >= 0; i--) {
|
|
831
901
|
const c = wkt[i];
|
|
@@ -833,15 +903,9 @@ function findLastValidPosition(wkt) {
|
|
|
833
903
|
depth++;
|
|
834
904
|
else if (c === '(')
|
|
835
905
|
depth--;
|
|
836
|
-
else if (c === ' ' && depth === 0 &&
|
|
837
|
-
//
|
|
838
|
-
|
|
839
|
-
if (!afterSpace)
|
|
840
|
-
continue;
|
|
841
|
-
if (!/^[A-Z]/.test(afterSpace))
|
|
842
|
-
continue;
|
|
843
|
-
// 如果空格后面是字母开头,可能是垃圾字符
|
|
844
|
-
if (i > 5 && /[A-Z]$/.test(wkt.slice(0, i).trim())) {
|
|
906
|
+
else if (c === ' ' && depth === 0 && /[A-Z]/.test(wkt.slice(i + 1))) {
|
|
907
|
+
// 如果空格后面是字母开头,可能是垃圾字符的起点
|
|
908
|
+
if (wkt.slice(0, i).trimEnd().match(/[A-Z]\s*$/)) {
|
|
845
909
|
return i - 1;
|
|
846
910
|
}
|
|
847
911
|
}
|
|
@@ -860,10 +924,43 @@ function cloneGeometry(geometry) {
|
|
|
860
924
|
function geometryEquals(a, b) {
|
|
861
925
|
if (a.type !== b.type)
|
|
862
926
|
return false;
|
|
927
|
+
// Point 比较最常见,单独优化
|
|
928
|
+
if (a.type === 'Point') {
|
|
929
|
+
const aCoords = a.coordinates;
|
|
930
|
+
const bCoords = b.coordinates;
|
|
931
|
+
return aCoords.length === bCoords.length &&
|
|
932
|
+
aCoords[0] === bCoords[0] &&
|
|
933
|
+
aCoords[1] === bCoords[1] &&
|
|
934
|
+
(aCoords.length === 2 || aCoords[2] === bCoords[2]);
|
|
935
|
+
}
|
|
936
|
+
// 其他类型使用 JSON.stringify 比较
|
|
863
937
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
864
938
|
}
|
|
865
939
|
|
|
940
|
+
var validate = /*#__PURE__*/Object.freeze({
|
|
941
|
+
__proto__: null,
|
|
942
|
+
cloneGeometry: cloneGeometry,
|
|
943
|
+
geometryEquals: geometryEquals,
|
|
944
|
+
tryFixWKT: tryFixWKT,
|
|
945
|
+
validateGeoJSON: validateGeoJSON,
|
|
946
|
+
validateWKT: validateWKT
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
// 命名空间导出 - 所有公共 API 汇总
|
|
950
|
+
// 使用方式: import WKT from 'wkt-parse-and-geojson';
|
|
951
|
+
// WKT.parse(...), WKT.build(...), etc.
|
|
952
|
+
const WKT = {
|
|
953
|
+
...types,
|
|
954
|
+
...wktParser,
|
|
955
|
+
...wktBuilder,
|
|
956
|
+
...geojsonBuilder,
|
|
957
|
+
...wktToGeojson,
|
|
958
|
+
...geojsonToWkt$1,
|
|
959
|
+
...validate,
|
|
960
|
+
};
|
|
961
|
+
|
|
866
962
|
exports.GeoJSONBuilder = GeoJSONBuilder;
|
|
963
|
+
exports.WKT = WKT;
|
|
867
964
|
exports.WKTBuilder = WKTBuilder;
|
|
868
965
|
exports.WKTParser = WKTParser;
|
|
869
966
|
exports.build = build;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,3 +5,4 @@ export { createPoint, createLineString, createPolygon, createMultiPoint, createM
|
|
|
5
5
|
export { wktToGeoJSON, wktToFeature, wktToFeatureCollection } from './wkt-to-geojson';
|
|
6
6
|
export { geojsonToWkt, featureToWkt, featureCollectionToWkt } from './geojson-to-wkt';
|
|
7
7
|
export { validateWKT, validateGeoJSON, tryFixWKT, cloneGeometry, geometryEquals, type ValidationResult, } from './validate';
|
|
8
|
+
export { default as WKT } from './namespace';
|
package/dist/index.esm.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
var types = /*#__PURE__*/Object.freeze({
|
|
2
|
+
__proto__: null
|
|
3
|
+
});
|
|
4
|
+
|
|
1
5
|
// Precompiled regex for better performance
|
|
2
6
|
const RE_WHITESPACE = /\s/;
|
|
3
7
|
const RE_NUMBER_START = /[0-9\-]/;
|
|
@@ -314,16 +318,21 @@ function parse(wkt) {
|
|
|
314
318
|
return parser.parse(wkt);
|
|
315
319
|
}
|
|
316
320
|
|
|
321
|
+
var wktParser = /*#__PURE__*/Object.freeze({
|
|
322
|
+
__proto__: null,
|
|
323
|
+
WKTParser: WKTParser,
|
|
324
|
+
parse: parse
|
|
325
|
+
});
|
|
326
|
+
|
|
317
327
|
/**
|
|
318
328
|
* 将坐标数值格式化为字符串,避免科学计数法(WKT 不支持)。
|
|
319
329
|
* 例:1e-7 → "0.0000001",1.50000 → "1.5",1.0 → "1"
|
|
320
330
|
*/
|
|
321
331
|
function formatNumber(v) {
|
|
322
|
-
// 有小数则最多保留 15 位有效位,再去掉尾零
|
|
323
332
|
if (v % 1 !== 0) {
|
|
324
|
-
return
|
|
333
|
+
return Number(v.toFixed(15)).toString();
|
|
325
334
|
}
|
|
326
|
-
return v
|
|
335
|
+
return String(v);
|
|
327
336
|
}
|
|
328
337
|
function positionToWkt(pos) {
|
|
329
338
|
return pos.map(formatNumber).join(' ');
|
|
@@ -331,6 +340,28 @@ function positionToWkt(pos) {
|
|
|
331
340
|
function coordsToWkt(coords) {
|
|
332
341
|
return coords.map(positionToWkt).join(', ');
|
|
333
342
|
}
|
|
343
|
+
// 检查坐标是否包含 Z(3个分量)
|
|
344
|
+
function hasZ(coordinates) {
|
|
345
|
+
if (!Array.isArray(coordinates))
|
|
346
|
+
return false;
|
|
347
|
+
if (coordinates.length === 0)
|
|
348
|
+
return false;
|
|
349
|
+
const first = coordinates[0];
|
|
350
|
+
if (Array.isArray(first)) {
|
|
351
|
+
if (typeof first[0] === 'number') {
|
|
352
|
+
return first.length === 3;
|
|
353
|
+
}
|
|
354
|
+
if (Array.isArray(first[0])) {
|
|
355
|
+
const firstRing = first;
|
|
356
|
+
return firstRing.length > 0 && firstRing[0].length === 3;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
// 获取 Z 后缀字符串
|
|
362
|
+
function zSuffix(coordinates) {
|
|
363
|
+
return hasZ(coordinates) ? ' Z' : '';
|
|
364
|
+
}
|
|
334
365
|
class WKTBuilder {
|
|
335
366
|
build(geometry) {
|
|
336
367
|
switch (geometry.type) {
|
|
@@ -353,21 +384,18 @@ class WKTBuilder {
|
|
|
353
384
|
}
|
|
354
385
|
}
|
|
355
386
|
buildPoint(geom) {
|
|
356
|
-
|
|
357
|
-
return `POINT${hasZ ? ' Z' : ''} (${positionToWkt(geom.coordinates)})`;
|
|
387
|
+
return `POINT${zSuffix(geom.coordinates)} (${positionToWkt(geom.coordinates)})`;
|
|
358
388
|
}
|
|
359
389
|
buildLineString(geom) {
|
|
360
390
|
if (geom.coordinates.length === 0)
|
|
361
391
|
return 'LINESTRING EMPTY';
|
|
362
|
-
|
|
363
|
-
return `LINESTRING${hasZ ? ' Z' : ''} (${coordsToWkt(geom.coordinates)})`;
|
|
392
|
+
return `LINESTRING${zSuffix(geom.coordinates)} (${coordsToWkt(geom.coordinates)})`;
|
|
364
393
|
}
|
|
365
394
|
buildPolygon(geom) {
|
|
366
395
|
if (geom.coordinates.length === 0)
|
|
367
396
|
return 'POLYGON EMPTY';
|
|
368
|
-
const hasZ = geom.coordinates[0].length > 0 && geom.coordinates[0][0].length === 3;
|
|
369
397
|
const ringStr = geom.coordinates.map(ring => `(${coordsToWkt(ring)})`).join(', ');
|
|
370
|
-
return `POLYGON${
|
|
398
|
+
return `POLYGON${zSuffix(geom.coordinates)} (${ringStr})`;
|
|
371
399
|
}
|
|
372
400
|
/**
|
|
373
401
|
* 按 OGC/ISO WKT 标准,MULTIPOINT 每个点用括号包裹:
|
|
@@ -376,26 +404,23 @@ class WKTBuilder {
|
|
|
376
404
|
buildMultiPoint(geom) {
|
|
377
405
|
if (geom.coordinates.length === 0)
|
|
378
406
|
return 'MULTIPOINT EMPTY';
|
|
379
|
-
const hasZ = geom.coordinates[0].length === 3;
|
|
380
407
|
const pts = geom.coordinates.map(p => `(${positionToWkt(p)})`).join(', ');
|
|
381
|
-
return `MULTIPOINT${
|
|
408
|
+
return `MULTIPOINT${zSuffix(geom.coordinates)} (${pts})`;
|
|
382
409
|
}
|
|
383
410
|
buildMultiLineString(geom) {
|
|
384
411
|
if (geom.coordinates.length === 0)
|
|
385
412
|
return 'MULTILINESTRING EMPTY';
|
|
386
|
-
const hasZ = geom.coordinates[0].length > 0 && geom.coordinates[0][0].length === 3;
|
|
387
413
|
const lines = geom.coordinates.map(line => `(${coordsToWkt(line)})`).join(', ');
|
|
388
|
-
return `MULTILINESTRING${
|
|
414
|
+
return `MULTILINESTRING${zSuffix(geom.coordinates)} (${lines})`;
|
|
389
415
|
}
|
|
390
416
|
buildMultiPolygon(geom) {
|
|
391
417
|
if (geom.coordinates.length === 0)
|
|
392
418
|
return 'MULTIPOLYGON EMPTY';
|
|
393
|
-
const hasZ = geom.coordinates[0].length > 0 && geom.coordinates[0][0].length > 0 && geom.coordinates[0][0][0].length === 3;
|
|
394
419
|
const polys = geom.coordinates.map(poly => {
|
|
395
420
|
const rings = poly.map(ring => `(${coordsToWkt(ring)})`).join(', ');
|
|
396
421
|
return `(${rings})`;
|
|
397
422
|
}).join(', ');
|
|
398
|
-
return `MULTIPOLYGON${
|
|
423
|
+
return `MULTIPOLYGON${zSuffix(geom.coordinates)} (${polys})`;
|
|
399
424
|
}
|
|
400
425
|
buildGeometryCollection(geom) {
|
|
401
426
|
if (geom.geometries.length === 0)
|
|
@@ -406,8 +431,16 @@ class WKTBuilder {
|
|
|
406
431
|
}
|
|
407
432
|
/** 将 GeoJSON Geometry 对象转换为 WKT 字符串 */
|
|
408
433
|
function build(geometry) {
|
|
409
|
-
return
|
|
434
|
+
return WKT_BUILDER.build(geometry);
|
|
410
435
|
}
|
|
436
|
+
// 单例实例,避免重复创建
|
|
437
|
+
const WKT_BUILDER = new WKTBuilder();
|
|
438
|
+
|
|
439
|
+
var wktBuilder = /*#__PURE__*/Object.freeze({
|
|
440
|
+
__proto__: null,
|
|
441
|
+
WKTBuilder: WKTBuilder,
|
|
442
|
+
build: build
|
|
443
|
+
});
|
|
411
444
|
|
|
412
445
|
// ─── 内部工具:判断是否为 Position([number, number] 或 [number, number, number])
|
|
413
446
|
function isPosition(v) {
|
|
@@ -555,6 +588,18 @@ function createGeometryCollection(geometries) {
|
|
|
555
588
|
return _builder.createGeometryCollection(geometries);
|
|
556
589
|
}
|
|
557
590
|
|
|
591
|
+
var geojsonBuilder = /*#__PURE__*/Object.freeze({
|
|
592
|
+
__proto__: null,
|
|
593
|
+
GeoJSONBuilder: GeoJSONBuilder,
|
|
594
|
+
createGeometryCollection: createGeometryCollection,
|
|
595
|
+
createLineString: createLineString,
|
|
596
|
+
createMultiLineString: createMultiLineString,
|
|
597
|
+
createMultiPoint: createMultiPoint,
|
|
598
|
+
createMultiPolygon: createMultiPolygon,
|
|
599
|
+
createPoint: createPoint,
|
|
600
|
+
createPolygon: createPolygon
|
|
601
|
+
});
|
|
602
|
+
|
|
558
603
|
/**
|
|
559
604
|
* 将 WKT 字符串转换为 GeoJSON Geometry 对象。
|
|
560
605
|
*
|
|
@@ -606,6 +651,13 @@ function wktToFeatureCollection(wkts, properties) {
|
|
|
606
651
|
return { type: 'FeatureCollection', features };
|
|
607
652
|
}
|
|
608
653
|
|
|
654
|
+
var wktToGeojson = /*#__PURE__*/Object.freeze({
|
|
655
|
+
__proto__: null,
|
|
656
|
+
wktToFeature: wktToFeature,
|
|
657
|
+
wktToFeatureCollection: wktToFeatureCollection,
|
|
658
|
+
wktToGeoJSON: wktToGeoJSON
|
|
659
|
+
});
|
|
660
|
+
|
|
609
661
|
/**
|
|
610
662
|
* 将 GeoJSON Geometry 对象转换为 WKT 字符串。
|
|
611
663
|
*
|
|
@@ -651,6 +703,19 @@ function featureCollectionToWkt(fc) {
|
|
|
651
703
|
});
|
|
652
704
|
}
|
|
653
705
|
|
|
706
|
+
var geojsonToWkt$1 = /*#__PURE__*/Object.freeze({
|
|
707
|
+
__proto__: null,
|
|
708
|
+
featureCollectionToWkt: featureCollectionToWkt,
|
|
709
|
+
featureToWkt: featureToWkt,
|
|
710
|
+
geojsonToWkt: geojsonToWkt
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
// 预定义常量,避免重复创建
|
|
714
|
+
const VALID_GEOMETRY_TYPES = [
|
|
715
|
+
'Point', 'LineString', 'Polygon',
|
|
716
|
+
'MultiPoint', 'MultiLineString', 'MultiPolygon',
|
|
717
|
+
'GeometryCollection'
|
|
718
|
+
];
|
|
654
719
|
/**
|
|
655
720
|
* 校验 WKT 字符串格式是否合法
|
|
656
721
|
*/
|
|
@@ -683,13 +748,8 @@ function validateGeoJSON(geojson) {
|
|
|
683
748
|
return { valid: false, error: 'GeoJSON must have a "type" property' };
|
|
684
749
|
}
|
|
685
750
|
const type = obj.type;
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
'MultiPoint', 'MultiLineString', 'MultiPolygon',
|
|
689
|
-
'GeometryCollection'
|
|
690
|
-
];
|
|
691
|
-
if (!validTypes.includes(type)) {
|
|
692
|
-
return { valid: false, error: `Invalid geometry type: "${type}". Must be one of: ${validTypes.join(', ')}` };
|
|
751
|
+
if (!VALID_GEOMETRY_TYPES.includes(type)) {
|
|
752
|
+
return { valid: false, error: `Invalid geometry type: "${type}". Must be one of: ${VALID_GEOMETRY_TYPES.join(', ')}` };
|
|
693
753
|
}
|
|
694
754
|
// GeometryCollection 特殊处理
|
|
695
755
|
if (type === 'GeometryCollection') {
|
|
@@ -792,11 +852,14 @@ function tryFixWKT(wkt) {
|
|
|
792
852
|
if (!trimmed) {
|
|
793
853
|
return { fixed: wkt, changed: false };
|
|
794
854
|
}
|
|
795
|
-
//
|
|
796
|
-
|
|
797
|
-
|
|
855
|
+
// 先尝试直接解析,如果成功则不需要修复
|
|
856
|
+
try {
|
|
857
|
+
parse(trimmed);
|
|
798
858
|
return { fixed: trimmed, changed: false };
|
|
799
859
|
}
|
|
860
|
+
catch {
|
|
861
|
+
// 解析失败,尝试修复
|
|
862
|
+
}
|
|
800
863
|
// 尝试找到最后一个有效的 geometry 结束位置
|
|
801
864
|
const patterns = [
|
|
802
865
|
/\)\s*[A-Z]/i, // 括号后跟字母 (如 POLYGON ((...)) POINT )
|
|
@@ -807,23 +870,30 @@ function tryFixWKT(wkt) {
|
|
|
807
870
|
const match = trimmed.match(pattern);
|
|
808
871
|
if (match) {
|
|
809
872
|
const fixed = trimmed.slice(0, match.index + (match[0].match(/\)/)?.[0].length || 0));
|
|
810
|
-
|
|
873
|
+
try {
|
|
874
|
+
parse(fixed);
|
|
811
875
|
return { fixed, changed: true };
|
|
812
876
|
}
|
|
877
|
+
catch {
|
|
878
|
+
// 这个修复方案不行,尝试下一个
|
|
879
|
+
}
|
|
813
880
|
}
|
|
814
881
|
}
|
|
815
882
|
// 尝试去除尾部垃圾字符
|
|
816
883
|
const lastValidIndex = findLastValidPosition(trimmed);
|
|
817
884
|
if (lastValidIndex > 0) {
|
|
818
885
|
const fixed = trimmed.slice(0, lastValidIndex + 1);
|
|
819
|
-
|
|
886
|
+
try {
|
|
887
|
+
parse(fixed);
|
|
820
888
|
return { fixed, changed: true };
|
|
821
889
|
}
|
|
890
|
+
catch {
|
|
891
|
+
// 修复失败
|
|
892
|
+
}
|
|
822
893
|
}
|
|
823
894
|
return { fixed: wkt, changed: false };
|
|
824
895
|
}
|
|
825
896
|
function findLastValidPosition(wkt) {
|
|
826
|
-
// 从后往前找第一个有效的右括号位置
|
|
827
897
|
let depth = 0;
|
|
828
898
|
for (let i = wkt.length - 1; i >= 0; i--) {
|
|
829
899
|
const c = wkt[i];
|
|
@@ -831,15 +901,9 @@ function findLastValidPosition(wkt) {
|
|
|
831
901
|
depth++;
|
|
832
902
|
else if (c === '(')
|
|
833
903
|
depth--;
|
|
834
|
-
else if (c === ' ' && depth === 0 &&
|
|
835
|
-
//
|
|
836
|
-
|
|
837
|
-
if (!afterSpace)
|
|
838
|
-
continue;
|
|
839
|
-
if (!/^[A-Z]/.test(afterSpace))
|
|
840
|
-
continue;
|
|
841
|
-
// 如果空格后面是字母开头,可能是垃圾字符
|
|
842
|
-
if (i > 5 && /[A-Z]$/.test(wkt.slice(0, i).trim())) {
|
|
904
|
+
else if (c === ' ' && depth === 0 && /[A-Z]/.test(wkt.slice(i + 1))) {
|
|
905
|
+
// 如果空格后面是字母开头,可能是垃圾字符的起点
|
|
906
|
+
if (wkt.slice(0, i).trimEnd().match(/[A-Z]\s*$/)) {
|
|
843
907
|
return i - 1;
|
|
844
908
|
}
|
|
845
909
|
}
|
|
@@ -858,7 +922,39 @@ function cloneGeometry(geometry) {
|
|
|
858
922
|
function geometryEquals(a, b) {
|
|
859
923
|
if (a.type !== b.type)
|
|
860
924
|
return false;
|
|
925
|
+
// Point 比较最常见,单独优化
|
|
926
|
+
if (a.type === 'Point') {
|
|
927
|
+
const aCoords = a.coordinates;
|
|
928
|
+
const bCoords = b.coordinates;
|
|
929
|
+
return aCoords.length === bCoords.length &&
|
|
930
|
+
aCoords[0] === bCoords[0] &&
|
|
931
|
+
aCoords[1] === bCoords[1] &&
|
|
932
|
+
(aCoords.length === 2 || aCoords[2] === bCoords[2]);
|
|
933
|
+
}
|
|
934
|
+
// 其他类型使用 JSON.stringify 比较
|
|
861
935
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
862
936
|
}
|
|
863
937
|
|
|
864
|
-
|
|
938
|
+
var validate = /*#__PURE__*/Object.freeze({
|
|
939
|
+
__proto__: null,
|
|
940
|
+
cloneGeometry: cloneGeometry,
|
|
941
|
+
geometryEquals: geometryEquals,
|
|
942
|
+
tryFixWKT: tryFixWKT,
|
|
943
|
+
validateGeoJSON: validateGeoJSON,
|
|
944
|
+
validateWKT: validateWKT
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
// 命名空间导出 - 所有公共 API 汇总
|
|
948
|
+
// 使用方式: import WKT from 'wkt-parse-and-geojson';
|
|
949
|
+
// WKT.parse(...), WKT.build(...), etc.
|
|
950
|
+
const WKT = {
|
|
951
|
+
...types,
|
|
952
|
+
...wktParser,
|
|
953
|
+
...wktBuilder,
|
|
954
|
+
...geojsonBuilder,
|
|
955
|
+
...wktToGeojson,
|
|
956
|
+
...geojsonToWkt$1,
|
|
957
|
+
...validate,
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
export { GeoJSONBuilder, WKT, WKTBuilder, WKTParser, build, cloneGeometry, createGeometryCollection, createLineString, createMultiLineString, createMultiPoint, createMultiPolygon, createPoint, createPolygon, featureCollectionToWkt, featureToWkt, geojsonToWkt, geometryEquals, parse, tryFixWKT, validateGeoJSON, validateWKT, wktToFeature, wktToFeatureCollection, wktToGeoJSON };
|
package/dist/index.umd.js
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.WKTGeoJSON = {}));
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
|
+
var types = /*#__PURE__*/Object.freeze({
|
|
8
|
+
__proto__: null
|
|
9
|
+
});
|
|
10
|
+
|
|
7
11
|
// Precompiled regex for better performance
|
|
8
12
|
const RE_WHITESPACE = /\s/;
|
|
9
13
|
const RE_NUMBER_START = /[0-9\-]/;
|
|
@@ -320,16 +324,21 @@
|
|
|
320
324
|
return parser.parse(wkt);
|
|
321
325
|
}
|
|
322
326
|
|
|
327
|
+
var wktParser = /*#__PURE__*/Object.freeze({
|
|
328
|
+
__proto__: null,
|
|
329
|
+
WKTParser: WKTParser,
|
|
330
|
+
parse: parse
|
|
331
|
+
});
|
|
332
|
+
|
|
323
333
|
/**
|
|
324
334
|
* 将坐标数值格式化为字符串,避免科学计数法(WKT 不支持)。
|
|
325
335
|
* 例:1e-7 → "0.0000001",1.50000 → "1.5",1.0 → "1"
|
|
326
336
|
*/
|
|
327
337
|
function formatNumber(v) {
|
|
328
|
-
// 有小数则最多保留 15 位有效位,再去掉尾零
|
|
329
338
|
if (v % 1 !== 0) {
|
|
330
|
-
return
|
|
339
|
+
return Number(v.toFixed(15)).toString();
|
|
331
340
|
}
|
|
332
|
-
return v
|
|
341
|
+
return String(v);
|
|
333
342
|
}
|
|
334
343
|
function positionToWkt(pos) {
|
|
335
344
|
return pos.map(formatNumber).join(' ');
|
|
@@ -337,6 +346,28 @@
|
|
|
337
346
|
function coordsToWkt(coords) {
|
|
338
347
|
return coords.map(positionToWkt).join(', ');
|
|
339
348
|
}
|
|
349
|
+
// 检查坐标是否包含 Z(3个分量)
|
|
350
|
+
function hasZ(coordinates) {
|
|
351
|
+
if (!Array.isArray(coordinates))
|
|
352
|
+
return false;
|
|
353
|
+
if (coordinates.length === 0)
|
|
354
|
+
return false;
|
|
355
|
+
const first = coordinates[0];
|
|
356
|
+
if (Array.isArray(first)) {
|
|
357
|
+
if (typeof first[0] === 'number') {
|
|
358
|
+
return first.length === 3;
|
|
359
|
+
}
|
|
360
|
+
if (Array.isArray(first[0])) {
|
|
361
|
+
const firstRing = first;
|
|
362
|
+
return firstRing.length > 0 && firstRing[0].length === 3;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
// 获取 Z 后缀字符串
|
|
368
|
+
function zSuffix(coordinates) {
|
|
369
|
+
return hasZ(coordinates) ? ' Z' : '';
|
|
370
|
+
}
|
|
340
371
|
class WKTBuilder {
|
|
341
372
|
build(geometry) {
|
|
342
373
|
switch (geometry.type) {
|
|
@@ -359,21 +390,18 @@
|
|
|
359
390
|
}
|
|
360
391
|
}
|
|
361
392
|
buildPoint(geom) {
|
|
362
|
-
|
|
363
|
-
return `POINT${hasZ ? ' Z' : ''} (${positionToWkt(geom.coordinates)})`;
|
|
393
|
+
return `POINT${zSuffix(geom.coordinates)} (${positionToWkt(geom.coordinates)})`;
|
|
364
394
|
}
|
|
365
395
|
buildLineString(geom) {
|
|
366
396
|
if (geom.coordinates.length === 0)
|
|
367
397
|
return 'LINESTRING EMPTY';
|
|
368
|
-
|
|
369
|
-
return `LINESTRING${hasZ ? ' Z' : ''} (${coordsToWkt(geom.coordinates)})`;
|
|
398
|
+
return `LINESTRING${zSuffix(geom.coordinates)} (${coordsToWkt(geom.coordinates)})`;
|
|
370
399
|
}
|
|
371
400
|
buildPolygon(geom) {
|
|
372
401
|
if (geom.coordinates.length === 0)
|
|
373
402
|
return 'POLYGON EMPTY';
|
|
374
|
-
const hasZ = geom.coordinates[0].length > 0 && geom.coordinates[0][0].length === 3;
|
|
375
403
|
const ringStr = geom.coordinates.map(ring => `(${coordsToWkt(ring)})`).join(', ');
|
|
376
|
-
return `POLYGON${
|
|
404
|
+
return `POLYGON${zSuffix(geom.coordinates)} (${ringStr})`;
|
|
377
405
|
}
|
|
378
406
|
/**
|
|
379
407
|
* 按 OGC/ISO WKT 标准,MULTIPOINT 每个点用括号包裹:
|
|
@@ -382,26 +410,23 @@
|
|
|
382
410
|
buildMultiPoint(geom) {
|
|
383
411
|
if (geom.coordinates.length === 0)
|
|
384
412
|
return 'MULTIPOINT EMPTY';
|
|
385
|
-
const hasZ = geom.coordinates[0].length === 3;
|
|
386
413
|
const pts = geom.coordinates.map(p => `(${positionToWkt(p)})`).join(', ');
|
|
387
|
-
return `MULTIPOINT${
|
|
414
|
+
return `MULTIPOINT${zSuffix(geom.coordinates)} (${pts})`;
|
|
388
415
|
}
|
|
389
416
|
buildMultiLineString(geom) {
|
|
390
417
|
if (geom.coordinates.length === 0)
|
|
391
418
|
return 'MULTILINESTRING EMPTY';
|
|
392
|
-
const hasZ = geom.coordinates[0].length > 0 && geom.coordinates[0][0].length === 3;
|
|
393
419
|
const lines = geom.coordinates.map(line => `(${coordsToWkt(line)})`).join(', ');
|
|
394
|
-
return `MULTILINESTRING${
|
|
420
|
+
return `MULTILINESTRING${zSuffix(geom.coordinates)} (${lines})`;
|
|
395
421
|
}
|
|
396
422
|
buildMultiPolygon(geom) {
|
|
397
423
|
if (geom.coordinates.length === 0)
|
|
398
424
|
return 'MULTIPOLYGON EMPTY';
|
|
399
|
-
const hasZ = geom.coordinates[0].length > 0 && geom.coordinates[0][0].length > 0 && geom.coordinates[0][0][0].length === 3;
|
|
400
425
|
const polys = geom.coordinates.map(poly => {
|
|
401
426
|
const rings = poly.map(ring => `(${coordsToWkt(ring)})`).join(', ');
|
|
402
427
|
return `(${rings})`;
|
|
403
428
|
}).join(', ');
|
|
404
|
-
return `MULTIPOLYGON${
|
|
429
|
+
return `MULTIPOLYGON${zSuffix(geom.coordinates)} (${polys})`;
|
|
405
430
|
}
|
|
406
431
|
buildGeometryCollection(geom) {
|
|
407
432
|
if (geom.geometries.length === 0)
|
|
@@ -412,8 +437,16 @@
|
|
|
412
437
|
}
|
|
413
438
|
/** 将 GeoJSON Geometry 对象转换为 WKT 字符串 */
|
|
414
439
|
function build(geometry) {
|
|
415
|
-
return
|
|
440
|
+
return WKT_BUILDER.build(geometry);
|
|
416
441
|
}
|
|
442
|
+
// 单例实例,避免重复创建
|
|
443
|
+
const WKT_BUILDER = new WKTBuilder();
|
|
444
|
+
|
|
445
|
+
var wktBuilder = /*#__PURE__*/Object.freeze({
|
|
446
|
+
__proto__: null,
|
|
447
|
+
WKTBuilder: WKTBuilder,
|
|
448
|
+
build: build
|
|
449
|
+
});
|
|
417
450
|
|
|
418
451
|
// ─── 内部工具:判断是否为 Position([number, number] 或 [number, number, number])
|
|
419
452
|
function isPosition(v) {
|
|
@@ -561,6 +594,18 @@
|
|
|
561
594
|
return _builder.createGeometryCollection(geometries);
|
|
562
595
|
}
|
|
563
596
|
|
|
597
|
+
var geojsonBuilder = /*#__PURE__*/Object.freeze({
|
|
598
|
+
__proto__: null,
|
|
599
|
+
GeoJSONBuilder: GeoJSONBuilder,
|
|
600
|
+
createGeometryCollection: createGeometryCollection,
|
|
601
|
+
createLineString: createLineString,
|
|
602
|
+
createMultiLineString: createMultiLineString,
|
|
603
|
+
createMultiPoint: createMultiPoint,
|
|
604
|
+
createMultiPolygon: createMultiPolygon,
|
|
605
|
+
createPoint: createPoint,
|
|
606
|
+
createPolygon: createPolygon
|
|
607
|
+
});
|
|
608
|
+
|
|
564
609
|
/**
|
|
565
610
|
* 将 WKT 字符串转换为 GeoJSON Geometry 对象。
|
|
566
611
|
*
|
|
@@ -612,6 +657,13 @@
|
|
|
612
657
|
return { type: 'FeatureCollection', features };
|
|
613
658
|
}
|
|
614
659
|
|
|
660
|
+
var wktToGeojson = /*#__PURE__*/Object.freeze({
|
|
661
|
+
__proto__: null,
|
|
662
|
+
wktToFeature: wktToFeature,
|
|
663
|
+
wktToFeatureCollection: wktToFeatureCollection,
|
|
664
|
+
wktToGeoJSON: wktToGeoJSON
|
|
665
|
+
});
|
|
666
|
+
|
|
615
667
|
/**
|
|
616
668
|
* 将 GeoJSON Geometry 对象转换为 WKT 字符串。
|
|
617
669
|
*
|
|
@@ -657,6 +709,19 @@
|
|
|
657
709
|
});
|
|
658
710
|
}
|
|
659
711
|
|
|
712
|
+
var geojsonToWkt$1 = /*#__PURE__*/Object.freeze({
|
|
713
|
+
__proto__: null,
|
|
714
|
+
featureCollectionToWkt: featureCollectionToWkt,
|
|
715
|
+
featureToWkt: featureToWkt,
|
|
716
|
+
geojsonToWkt: geojsonToWkt
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
// 预定义常量,避免重复创建
|
|
720
|
+
const VALID_GEOMETRY_TYPES = [
|
|
721
|
+
'Point', 'LineString', 'Polygon',
|
|
722
|
+
'MultiPoint', 'MultiLineString', 'MultiPolygon',
|
|
723
|
+
'GeometryCollection'
|
|
724
|
+
];
|
|
660
725
|
/**
|
|
661
726
|
* 校验 WKT 字符串格式是否合法
|
|
662
727
|
*/
|
|
@@ -689,13 +754,8 @@
|
|
|
689
754
|
return { valid: false, error: 'GeoJSON must have a "type" property' };
|
|
690
755
|
}
|
|
691
756
|
const type = obj.type;
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
'MultiPoint', 'MultiLineString', 'MultiPolygon',
|
|
695
|
-
'GeometryCollection'
|
|
696
|
-
];
|
|
697
|
-
if (!validTypes.includes(type)) {
|
|
698
|
-
return { valid: false, error: `Invalid geometry type: "${type}". Must be one of: ${validTypes.join(', ')}` };
|
|
757
|
+
if (!VALID_GEOMETRY_TYPES.includes(type)) {
|
|
758
|
+
return { valid: false, error: `Invalid geometry type: "${type}". Must be one of: ${VALID_GEOMETRY_TYPES.join(', ')}` };
|
|
699
759
|
}
|
|
700
760
|
// GeometryCollection 特殊处理
|
|
701
761
|
if (type === 'GeometryCollection') {
|
|
@@ -798,11 +858,14 @@
|
|
|
798
858
|
if (!trimmed) {
|
|
799
859
|
return { fixed: wkt, changed: false };
|
|
800
860
|
}
|
|
801
|
-
//
|
|
802
|
-
|
|
803
|
-
|
|
861
|
+
// 先尝试直接解析,如果成功则不需要修复
|
|
862
|
+
try {
|
|
863
|
+
parse(trimmed);
|
|
804
864
|
return { fixed: trimmed, changed: false };
|
|
805
865
|
}
|
|
866
|
+
catch {
|
|
867
|
+
// 解析失败,尝试修复
|
|
868
|
+
}
|
|
806
869
|
// 尝试找到最后一个有效的 geometry 结束位置
|
|
807
870
|
const patterns = [
|
|
808
871
|
/\)\s*[A-Z]/i, // 括号后跟字母 (如 POLYGON ((...)) POINT )
|
|
@@ -813,23 +876,30 @@
|
|
|
813
876
|
const match = trimmed.match(pattern);
|
|
814
877
|
if (match) {
|
|
815
878
|
const fixed = trimmed.slice(0, match.index + (match[0].match(/\)/)?.[0].length || 0));
|
|
816
|
-
|
|
879
|
+
try {
|
|
880
|
+
parse(fixed);
|
|
817
881
|
return { fixed, changed: true };
|
|
818
882
|
}
|
|
883
|
+
catch {
|
|
884
|
+
// 这个修复方案不行,尝试下一个
|
|
885
|
+
}
|
|
819
886
|
}
|
|
820
887
|
}
|
|
821
888
|
// 尝试去除尾部垃圾字符
|
|
822
889
|
const lastValidIndex = findLastValidPosition(trimmed);
|
|
823
890
|
if (lastValidIndex > 0) {
|
|
824
891
|
const fixed = trimmed.slice(0, lastValidIndex + 1);
|
|
825
|
-
|
|
892
|
+
try {
|
|
893
|
+
parse(fixed);
|
|
826
894
|
return { fixed, changed: true };
|
|
827
895
|
}
|
|
896
|
+
catch {
|
|
897
|
+
// 修复失败
|
|
898
|
+
}
|
|
828
899
|
}
|
|
829
900
|
return { fixed: wkt, changed: false };
|
|
830
901
|
}
|
|
831
902
|
function findLastValidPosition(wkt) {
|
|
832
|
-
// 从后往前找第一个有效的右括号位置
|
|
833
903
|
let depth = 0;
|
|
834
904
|
for (let i = wkt.length - 1; i >= 0; i--) {
|
|
835
905
|
const c = wkt[i];
|
|
@@ -837,15 +907,9 @@
|
|
|
837
907
|
depth++;
|
|
838
908
|
else if (c === '(')
|
|
839
909
|
depth--;
|
|
840
|
-
else if (c === ' ' && depth === 0 &&
|
|
841
|
-
//
|
|
842
|
-
|
|
843
|
-
if (!afterSpace)
|
|
844
|
-
continue;
|
|
845
|
-
if (!/^[A-Z]/.test(afterSpace))
|
|
846
|
-
continue;
|
|
847
|
-
// 如果空格后面是字母开头,可能是垃圾字符
|
|
848
|
-
if (i > 5 && /[A-Z]$/.test(wkt.slice(0, i).trim())) {
|
|
910
|
+
else if (c === ' ' && depth === 0 && /[A-Z]/.test(wkt.slice(i + 1))) {
|
|
911
|
+
// 如果空格后面是字母开头,可能是垃圾字符的起点
|
|
912
|
+
if (wkt.slice(0, i).trimEnd().match(/[A-Z]\s*$/)) {
|
|
849
913
|
return i - 1;
|
|
850
914
|
}
|
|
851
915
|
}
|
|
@@ -864,10 +928,43 @@
|
|
|
864
928
|
function geometryEquals(a, b) {
|
|
865
929
|
if (a.type !== b.type)
|
|
866
930
|
return false;
|
|
931
|
+
// Point 比较最常见,单独优化
|
|
932
|
+
if (a.type === 'Point') {
|
|
933
|
+
const aCoords = a.coordinates;
|
|
934
|
+
const bCoords = b.coordinates;
|
|
935
|
+
return aCoords.length === bCoords.length &&
|
|
936
|
+
aCoords[0] === bCoords[0] &&
|
|
937
|
+
aCoords[1] === bCoords[1] &&
|
|
938
|
+
(aCoords.length === 2 || aCoords[2] === bCoords[2]);
|
|
939
|
+
}
|
|
940
|
+
// 其他类型使用 JSON.stringify 比较
|
|
867
941
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
868
942
|
}
|
|
869
943
|
|
|
944
|
+
var validate = /*#__PURE__*/Object.freeze({
|
|
945
|
+
__proto__: null,
|
|
946
|
+
cloneGeometry: cloneGeometry,
|
|
947
|
+
geometryEquals: geometryEquals,
|
|
948
|
+
tryFixWKT: tryFixWKT,
|
|
949
|
+
validateGeoJSON: validateGeoJSON,
|
|
950
|
+
validateWKT: validateWKT
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
// 命名空间导出 - 所有公共 API 汇总
|
|
954
|
+
// 使用方式: import WKT from 'wkt-parse-and-geojson';
|
|
955
|
+
// WKT.parse(...), WKT.build(...), etc.
|
|
956
|
+
const WKT = {
|
|
957
|
+
...types,
|
|
958
|
+
...wktParser,
|
|
959
|
+
...wktBuilder,
|
|
960
|
+
...geojsonBuilder,
|
|
961
|
+
...wktToGeojson,
|
|
962
|
+
...geojsonToWkt$1,
|
|
963
|
+
...validate,
|
|
964
|
+
};
|
|
965
|
+
|
|
870
966
|
exports.GeoJSONBuilder = GeoJSONBuilder;
|
|
967
|
+
exports.WKT = WKT;
|
|
871
968
|
exports.WKTBuilder = WKTBuilder;
|
|
872
969
|
exports.WKTParser = WKTParser;
|
|
873
970
|
exports.build = build;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as types from './types';
|
|
2
|
+
import * as wktParser from './wkt-parser';
|
|
3
|
+
import * as wktBuilder from './wkt-builder';
|
|
4
|
+
import * as geojsonBuilder from './geojson-builder';
|
|
5
|
+
import * as validate from './validate';
|
|
6
|
+
declare const WKT: {
|
|
7
|
+
validateWKT(wkt: string): validate.ValidationResult;
|
|
8
|
+
validateGeoJSON(geojson: unknown): validate.ValidationResult;
|
|
9
|
+
tryFixWKT(wkt: string): {
|
|
10
|
+
fixed: string;
|
|
11
|
+
changed: boolean;
|
|
12
|
+
};
|
|
13
|
+
cloneGeometry<G extends types.Geometry>(geometry: G): G;
|
|
14
|
+
geometryEquals(a: types.Geometry, b: types.Geometry): boolean;
|
|
15
|
+
geojsonToWkt(geojson: types.Geometry): string;
|
|
16
|
+
featureToWkt(feature: types.Feature): string;
|
|
17
|
+
featureCollectionToWkt(fc: types.FeatureCollection): Array<string | null>;
|
|
18
|
+
wktToGeoJSON(wkt: string): types.Geometry;
|
|
19
|
+
wktToFeature(wkt: string, properties?: Record<string, unknown> | null, id?: string | number): types.Feature;
|
|
20
|
+
wktToFeatureCollection(wkts: string[], properties?: Array<Record<string, unknown> | null>): types.FeatureCollection;
|
|
21
|
+
createPoint(x: number, y: number, z?: number): types.Point;
|
|
22
|
+
createLineString(coordinates: types.Position[]): types.LineString;
|
|
23
|
+
createPolygon(coordinates: types.Position[] | types.Position[][]): types.Polygon;
|
|
24
|
+
createMultiPoint(coordinates: types.Position | types.Position[]): types.MultiPoint;
|
|
25
|
+
createMultiLineString(coordinates: types.Position[] | types.Position[][]): types.MultiLineString;
|
|
26
|
+
createMultiPolygon(coordinates: types.Position[][] | types.Position[][][]): types.MultiPolygon;
|
|
27
|
+
createGeometryCollection(geometries: types.Geometry | types.Geometry[]): types.GeometryCollection;
|
|
28
|
+
GeoJSONBuilder: typeof geojsonBuilder.GeoJSONBuilder;
|
|
29
|
+
build(geometry: types.Geometry): string;
|
|
30
|
+
WKTBuilder: typeof wktBuilder.WKTBuilder;
|
|
31
|
+
parse(wkt: string): types.Geometry;
|
|
32
|
+
WKTParser: typeof wktParser.WKTParser;
|
|
33
|
+
};
|
|
34
|
+
export default WKT;
|