wkt-parse-and-geojson 1.0.0

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 ADDED
@@ -0,0 +1,656 @@
1
+ # wkt-parse-and-geojson
2
+
3
+ 一个零依赖、轻量级的 TypeScript/JavaScript 库,用于:
4
+ - **WKT 解析**:将 WKT 字符串解析为 GeoJSON Geometry 对象
5
+ - **WKT 构建**:将 GeoJSON Geometry 对象序列化为 WKT 字符串
6
+ - **WKT ↔ GeoJSON 互转**:在 WKT 与 GeoJSON Geometry / Feature / FeatureCollection 之间自由转换
7
+ - **GeoJSON 工厂方法**:快速创建各种 GeoJSON 几何对象
8
+
9
+ ## 目录
10
+
11
+ - [支持的几何类型](#支持的几何类型)
12
+ - [安装与构建](#安装与构建)
13
+ - [快速上手](#快速上手)
14
+ - [API 文档](#api-文档)
15
+ - [parse(wkt)](#1-parsewkt)
16
+ - [build(geometry)](#2-buildgeometry)
17
+ - [wktToGeoJSON(wkt)](#3-wkttogeojsonwkt)
18
+ - [wktToFeature(wkt, properties?, id?)](#4-wkttofeaturewkt-properties-id)
19
+ - [wktToFeatureCollection(wkts, properties?)](#5-wkttofeaturecollectionwkts-properties)
20
+ - [geojsonToWkt(geometry)](#6-geojsontowktgeometry)
21
+ - [featureToWkt(feature)](#7-featuretowktfeature)
22
+ - [featureCollectionToWkt(fc)](#8-featurecollectiontowktfc)
23
+ - [GeoJSON 工厂方法](#9-geojson-工厂方法)
24
+ - [类型定义](#类型定义)
25
+ - [注意事项与边界行为](#注意事项与边界行为)
26
+ - [本地调试](#本地调试)
27
+
28
+ ---
29
+
30
+ ## 支持的几何类型
31
+
32
+ | WKT 类型 | GeoJSON 类型 | 说明 |
33
+ |---------|-------------|------|
34
+ | `POINT` | `Point` | 点 |
35
+ | `LINESTRING` | `LineString` | 线 |
36
+ | `POLYGON` | `Polygon` | 多边形(支持空洞) |
37
+ | `MULTIPOINT` | `MultiPoint` | 点集合 |
38
+ | `MULTILINESTRING` | `MultiLineString` | 线集合 |
39
+ | `MULTIPOLYGON` | `MultiPolygon` | 多边形集合 |
40
+ | `GEOMETRYCOLLECTION` | `GeometryCollection` | 几何集合 |
41
+
42
+ 支持以下 WKT 扩展语法:
43
+ - **Z 坐标**:`POINT (x y z)` / `POINT Z (x y z)`
44
+ - **维度修饰符**:`Z`、`M`、`ZM`(`M` 值会被忽略,仅保留 XYZ)
45
+ - **EMPTY**:`LINESTRING EMPTY`、`POLYGON EMPTY` 等(`POINT EMPTY` 会抛出错误,见[注意事项](#注意事项与边界行为))
46
+
47
+ ---
48
+
49
+ ## 安装与构建
50
+
51
+ ```bash
52
+ # 安装依赖
53
+ npm install
54
+
55
+ # 构建(生成 dist/ 目录)
56
+ npm run build
57
+
58
+ # 仅做 TypeScript 类型检查(不生成文件)
59
+ npm run typecheck
60
+ ```
61
+
62
+ 构建产物:
63
+
64
+ | 文件 | 格式 | 用途 |
65
+ |------|------|------|
66
+ | `dist/index.esm.js` | ES Module | 浏览器 / 现代打包工具 |
67
+ | `dist/index.cjs.js` | CommonJS | Node.js |
68
+ | `dist/index.umd.js` | UMD | `<script>` 标签直接引入 |
69
+ | `dist/index.d.ts` | TypeScript 声明 | IDE 类型提示 |
70
+
71
+ ---
72
+
73
+ ## 快速上手
74
+
75
+ ### Node.js
76
+
77
+ ```javascript
78
+ // CommonJS
79
+ const { parse, build, wktToFeature } = require('./dist/index.cjs.js');
80
+
81
+ // ES Module
82
+ import { parse, build, wktToFeature } from './dist/index.esm.js';
83
+ ```
84
+
85
+ ### 浏览器 (script 标签)
86
+
87
+ ```html
88
+ <!-- UMD 方式:通过 script 标签直接引入,全局变量 WKTGeoJSON -->
89
+ <script src="./dist/index.umd.js"></script>
90
+ <script>
91
+ const geom = WKTGeoJSON.parse('POINT (116.39 39.91)');
92
+ console.log(geom);
93
+ // → { type: 'Point', coordinates: [116.39, 39.91] }
94
+
95
+ const wkt = WKTGeoJSON.build(geom);
96
+ console.log(wkt);
97
+ // → "POINT (116.39 39.91)"
98
+ </script>
99
+ ```
100
+
101
+ ### 浏览器 (ES Module)
102
+
103
+ ```html
104
+ <script type="module">
105
+ import { parse, build } from '../dist/index.esm.js';
106
+
107
+ const geom = parse('POINT (116.39 39.91)');
108
+ console.log(geom);
109
+ // → { type: 'Point', coordinates: [116.39, 39.91] }
110
+
111
+ const wkt = build(geom);
112
+ console.log(wkt);
113
+ // → "POINT (116.39 39.91)"
114
+ </script>
115
+ ```
116
+
117
+ ---
118
+
119
+ ## API 文档
120
+
121
+ ### 1. `parse(wkt)`
122
+
123
+ 将 WKT 字符串解析为 GeoJSON Geometry 对象。
124
+
125
+ **参数:**
126
+ - `wkt` `string` — WKT 格式的字符串
127
+
128
+ **返回:** `Geometry`
129
+
130
+ **抛出:** 输入格式错误、未知几何类型、坐标值无效时抛出 `Error`
131
+
132
+ ```javascript
133
+ // ── POINT ─────────────────────────────────────────────────────────────
134
+ parse('POINT (116.39 39.91)')
135
+ // → { type: 'Point', coordinates: [116.39, 39.91] }
136
+
137
+ // 带 Z 坐标
138
+ parse('POINT (116.39 39.91 50)')
139
+ // → { type: 'Point', coordinates: [116.39, 39.91, 50] }
140
+
141
+ // 带维度修饰符(Z 关键字)
142
+ parse('POINT Z (116.39 39.91 50)')
143
+ // → { type: 'Point', coordinates: [116.39, 39.91, 50] }
144
+
145
+ // ── LINESTRING ────────────────────────────────────────────────────────
146
+ parse('LINESTRING (0 0, 1 1, 2 0)')
147
+ // → { type: 'LineString', coordinates: [[0,0],[1,1],[2,0]] }
148
+
149
+ // ── POLYGON ───────────────────────────────────────────────────────────
150
+ // 无空洞
151
+ parse('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))')
152
+ // → { type: 'Polygon', coordinates: [[[0,0],[10,0],[10,10],[0,10],[0,0]]] }
153
+
154
+ // 带空洞(外环 + 内环)
155
+ parse('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 4 2, 4 4, 2 4, 2 2))')
156
+ // → { type: 'Polygon', coordinates: [
157
+ // [[0,0],[10,0],[10,10],[0,10],[0,0]], ← 外环
158
+ // [[2,2],[4,2],[4,4],[2,4],[2,2]] ← 内环(空洞)
159
+ // ]}
160
+
161
+ // ── MULTIPOINT ────────────────────────────────────────────────────────
162
+ // 标准写法(每个点用括号包裹)
163
+ parse('MULTIPOINT ((0 0), (1 1), (2 2))')
164
+ // → { type: 'MultiPoint', coordinates: [[0,0],[1,1],[2,2]] }
165
+
166
+ // 非标准写法(兼容)
167
+ parse('MULTIPOINT (0 0, 1 1, 2 2)')
168
+ // → { type: 'MultiPoint', coordinates: [[0,0],[1,1],[2,2]] }
169
+
170
+ // ── MULTILINESTRING ───────────────────────────────────────────────────
171
+ parse('MULTILINESTRING ((0 0, 1 1), (2 2, 3 3))')
172
+ // → { type: 'MultiLineString', coordinates: [[[0,0],[1,1]], [[2,2],[3,3]]] }
173
+
174
+ // ── MULTIPOLYGON ──────────────────────────────────────────────────────
175
+ parse('MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 0)), ((2 2, 3 2, 3 3, 2 3, 2 2)))')
176
+ // → { type: 'MultiPolygon', coordinates: [
177
+ // [[[0,0],[1,0],[1,1],[0,1],[0,0]]],
178
+ // [[[2,2],[3,2],[3,3],[2,3],[2,2]]]
179
+ // ]}
180
+
181
+ // ── GEOMETRYCOLLECTION ────────────────────────────────────────────────
182
+ parse('GEOMETRYCOLLECTION (POINT (0 0), LINESTRING (0 0, 1 1))')
183
+ // → { type: 'GeometryCollection', geometries: [
184
+ // { type: 'Point', coordinates: [0,0] },
185
+ // { type: 'LineString', coordinates: [[0,0],[1,1]] }
186
+ // ]}
187
+
188
+ // ── EMPTY ─────────────────────────────────────────────────────────────
189
+ parse('LINESTRING EMPTY')
190
+ // → { type: 'LineString', coordinates: [] }
191
+
192
+ parse('POLYGON EMPTY')
193
+ // → { type: 'Polygon', coordinates: [] }
194
+ ```
195
+
196
+ ---
197
+
198
+ ### 2. `build(geometry)`
199
+
200
+ 将 GeoJSON Geometry 对象转换为 WKT 字符串。
201
+
202
+ **参数:**
203
+ - `geometry` `Geometry` — GeoJSON Geometry 对象
204
+
205
+ **返回:** `string`
206
+
207
+ ```javascript
208
+ build({ type: 'Point', coordinates: [116.39, 39.91] })
209
+ // → 'POINT (116.39 39.91)'
210
+
211
+ build({ type: 'Point', coordinates: [116.39, 39.91, 50] })
212
+ // → 'POINT Z (116.39 39.91 50)'
213
+
214
+ build({ type: 'LineString', coordinates: [[0,0],[1,1],[2,0]] })
215
+ // → 'LINESTRING (0 0, 1 1, 2 0)'
216
+
217
+ build({ type: 'Polygon', coordinates: [[[0,0],[10,0],[10,10],[0,10],[0,0]]] })
218
+ // → 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))'
219
+
220
+ // 带空洞的多边形
221
+ build({
222
+ type: 'Polygon',
223
+ coordinates: [
224
+ [[0,0],[10,0],[10,10],[0,10],[0,0]],
225
+ [[2,2],[4,2],[4,4],[2,4],[2,2]]
226
+ ]
227
+ })
228
+ // → 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 4 2, 4 4, 2 4, 2 2))'
229
+
230
+ // MULTIPOINT 输出符合 OGC 标准(每个点带括号)
231
+ build({ type: 'MultiPoint', coordinates: [[0,0],[1,1],[2,2]] })
232
+ // → 'MULTIPOINT ((0 0), (1 1), (2 2))'
233
+
234
+ // 空几何
235
+ build({ type: 'LineString', coordinates: [] })
236
+ // → 'LINESTRING EMPTY'
237
+
238
+ build({ type: 'GeometryCollection', geometries: [] })
239
+ // → 'GEOMETRYCOLLECTION EMPTY'
240
+ ```
241
+
242
+ ---
243
+
244
+ ### 3. `wktToGeoJSON(wkt)`
245
+
246
+ 将 WKT 字符串转换为 GeoJSON Geometry 对象(`parse` 的语义化别名)。
247
+
248
+ ```javascript
249
+ import { wktToGeoJSON } from './dist/index.esm.js';
250
+
251
+ const geom = wktToGeoJSON('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))');
252
+ // → { type: 'Polygon', coordinates: [[[0,0],[1,0],[1,1],[0,1],[0,0]]] }
253
+ ```
254
+
255
+ ---
256
+
257
+ ### 4. `wktToFeature(wkt, properties?, id?)`
258
+
259
+ 将 WKT 字符串转换为 GeoJSON **Feature** 对象。
260
+
261
+ **参数:**
262
+ - `wkt` `string` — WKT 字符串
263
+ - `properties` `Record<string, unknown> | null`(可选)— Feature 属性,默认 `null`
264
+ - `id` `string | number`(可选)— Feature ID
265
+
266
+ **返回:** `Feature`
267
+
268
+ ```javascript
269
+ import { wktToFeature } from './dist/index.esm.js';
270
+
271
+ // 带属性
272
+ wktToFeature('POINT (116.39 39.91)', { name: '北京', pop: 21540000 })
273
+ // → {
274
+ // type: 'Feature',
275
+ // geometry: { type: 'Point', coordinates: [116.39, 39.91] },
276
+ // properties: { name: '北京', pop: 21540000 }
277
+ // }
278
+
279
+ // 带 ID
280
+ wktToFeature('LINESTRING (0 0, 1 1)', null, 42)
281
+ // → { type: 'Feature', geometry: {...}, properties: null, id: 42 }
282
+
283
+ // 无属性
284
+ wktToFeature('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))')
285
+ // → { type: 'Feature', geometry: {...}, properties: null }
286
+ ```
287
+
288
+ ---
289
+
290
+ ### 5. `wktToFeatureCollection(wkts, properties?)`
291
+
292
+ 将多个 WKT 字符串批量转换为 GeoJSON **FeatureCollection**。
293
+
294
+ **参数:**
295
+ - `wkts` `string[]` — WKT 字符串数组
296
+ - `properties` `Array<Record<string, unknown> | null>`(可选)— 每个 Feature 的属性数组
297
+
298
+ **返回:** `FeatureCollection`
299
+
300
+ ```javascript
301
+ import { wktToFeatureCollection } from './dist/index.esm.js';
302
+
303
+ const fc = wktToFeatureCollection(
304
+ ['POINT (116.39 39.91)', 'POINT (121.47 31.23)', 'POINT (113.26 23.13)'],
305
+ [{ name: '北京' }, { name: '上海' }, { name: '广州' }]
306
+ );
307
+ // → {
308
+ // type: 'FeatureCollection',
309
+ // features: [
310
+ // { type: 'Feature', geometry: { type: 'Point', ... }, properties: { name: '北京' } },
311
+ // { type: 'Feature', geometry: { type: 'Point', ... }, properties: { name: '上海' } },
312
+ // { type: 'Feature', geometry: { type: 'Point', ... }, properties: { name: '广州' } }
313
+ // ]
314
+ // }
315
+
316
+ // 不传属性
317
+ const fc2 = wktToFeatureCollection(['POINT (0 0)', 'LINESTRING (0 0, 1 1)']);
318
+ ```
319
+
320
+ ---
321
+
322
+ ### 6. `geojsonToWkt(geometry)`
323
+
324
+ 将 GeoJSON Geometry 对象转换为 WKT 字符串(`build` 的语义化别名)。
325
+
326
+ ```javascript
327
+ import { geojsonToWkt } from './dist/index.esm.js';
328
+
329
+ geojsonToWkt({ type: 'Point', coordinates: [116.39, 39.91] })
330
+ // → 'POINT (116.39 39.91)'
331
+ ```
332
+
333
+ ---
334
+
335
+ ### 7. `featureToWkt(feature)`
336
+
337
+ 将 GeoJSON **Feature** 转换为 WKT 字符串(取 `geometry` 部分)。
338
+
339
+ **抛出:** 若 `Feature.geometry` 为 `null`,则抛出 `Error`
340
+
341
+ ```javascript
342
+ import { featureToWkt } from './dist/index.esm.js';
343
+
344
+ featureToWkt({
345
+ type: 'Feature',
346
+ geometry: { type: 'Point', coordinates: [116.39, 39.91] },
347
+ properties: { name: '北京' }
348
+ })
349
+ // → 'POINT (116.39 39.91)'
350
+ ```
351
+
352
+ ---
353
+
354
+ ### 8. `featureCollectionToWkt(fc)`
355
+
356
+ 将 GeoJSON **FeatureCollection** 中所有 Feature 转换为 WKT 字符串数组。`geometry` 为 `null` 的 Feature 对应位置返回 `null`。
357
+
358
+ **返回:** `Array<string | null>`
359
+
360
+ ```javascript
361
+ import { featureCollectionToWkt } from './dist/index.esm.js';
362
+
363
+ featureCollectionToWkt({
364
+ type: 'FeatureCollection',
365
+ features: [
366
+ { type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] }, properties: null },
367
+ { type: 'Feature', geometry: { type: 'LineString', coordinates: [[0,0],[1,1]] }, properties: null },
368
+ { type: 'Feature', geometry: null, properties: null } // ← null geometry
369
+ ]
370
+ })
371
+ // → ['POINT (0 0)', 'LINESTRING (0 0, 1 1)', null]
372
+ ```
373
+
374
+ ---
375
+
376
+ ### 9. GeoJSON 工厂方法
377
+
378
+ 快速创建 GeoJSON Geometry 对象。所有工厂方法均支持**简化输入**(自动包装)和**完整输入**两种形式。
379
+
380
+ #### `createPoint(x, y, z?)`
381
+
382
+ ```javascript
383
+ createPoint(116.39, 39.91)
384
+ // → { type: 'Point', coordinates: [116.39, 39.91] }
385
+
386
+ createPoint(116.39, 39.91, 50)
387
+ // → { type: 'Point', coordinates: [116.39, 39.91, 50] }
388
+ ```
389
+
390
+ #### `createLineString(coordinates)`
391
+
392
+ ```javascript
393
+ createLineString([[0,0],[1,1],[2,0]])
394
+ // → { type: 'LineString', coordinates: [[0,0],[1,1],[2,0]] }
395
+ ```
396
+
397
+ #### `createPolygon(coordinates)`
398
+
399
+ 支持两种输入:
400
+
401
+ ```javascript
402
+ // ① 传入单个外环 Position[](自动包装)
403
+ createPolygon([[0,0],[1,0],[1,1],[0,1],[0,0]])
404
+ // → { type: 'Polygon', coordinates: [[[0,0],[1,0],[1,1],[0,1],[0,0]]] }
405
+
406
+ // ② 传入完整环列表 Position[][](外环 + 内环/空洞)
407
+ createPolygon([
408
+ [[0,0],[10,0],[10,10],[0,10],[0,0]], // 外环
409
+ [[2,2],[4,2],[4,4],[2,4],[2,2]] // 内环(空洞)
410
+ ])
411
+ // → { type: 'Polygon', coordinates: [[...外环...], [...内环...]] }
412
+ ```
413
+
414
+ #### `createMultiPoint(coordinates)`
415
+
416
+ 支持两种输入:
417
+
418
+ ```javascript
419
+ // ① 传入单个点 Position(自动包装)
420
+ createMultiPoint([0, 0])
421
+ // → { type: 'MultiPoint', coordinates: [[0,0]] }
422
+
423
+ // ② 传入多个点 Position[]
424
+ createMultiPoint([[0,0],[1,1],[2,2]])
425
+ // → { type: 'MultiPoint', coordinates: [[0,0],[1,1],[2,2]] }
426
+ ```
427
+
428
+ #### `createMultiLineString(coordinates)`
429
+
430
+ 支持两种输入:
431
+
432
+ ```javascript
433
+ // ① 传入单条线 Position[](自动包装)
434
+ createMultiLineString([[0,0],[1,1],[2,0]])
435
+ // → { type: 'MultiLineString', coordinates: [[[0,0],[1,1],[2,0]]] }
436
+
437
+ // ② 传入多条线 Position[][]
438
+ createMultiLineString([[[0,0],[1,1]], [[2,2],[3,3]]])
439
+ // → { type: 'MultiLineString', coordinates: [[[0,0],[1,1]], [[2,2],[3,3]]] }
440
+ ```
441
+
442
+ #### `createMultiPolygon(coordinates)`
443
+
444
+ 支持两种输入:
445
+
446
+ ```javascript
447
+ // ① 传入单个多边形的环列表 Position[][](自动包装)
448
+ createMultiPolygon([[[0,0],[1,0],[1,1],[0,1],[0,0]]])
449
+ // → { type: 'MultiPolygon', coordinates: [[[[0,0],[1,0],[1,1],[0,1],[0,0]]]] }
450
+
451
+ // ② 传入多个多边形 Position[][][]
452
+ createMultiPolygon([
453
+ [[[0,0],[1,0],[1,1],[0,1],[0,0]]],
454
+ [[[2,2],[3,2],[3,3],[2,3],[2,2]]]
455
+ ])
456
+ // → { type: 'MultiPolygon', coordinates: [...] }
457
+ ```
458
+
459
+ #### `createGeometryCollection(geometries)`
460
+
461
+ 支持两种输入:
462
+
463
+ ```javascript
464
+ // ① 传入单个 Geometry(自动包装)
465
+ createGeometryCollection(createPoint(0, 0))
466
+ // → { type: 'GeometryCollection', geometries: [{ type: 'Point', coordinates: [0,0] }] }
467
+
468
+ // ② 传入 Geometry[]
469
+ createGeometryCollection([
470
+ createPoint(0, 0),
471
+ createLineString([[0,0],[1,1]]),
472
+ createPolygon([[0,0],[10,0],[10,10],[0,10],[0,0]])
473
+ ])
474
+ // → { type: 'GeometryCollection', geometries: [...] }
475
+ ```
476
+
477
+ ---
478
+
479
+ ## 类型定义
480
+
481
+ ```typescript
482
+ // 坐标点(二维或三维)
483
+ type Position = [number, number] | [number, number, number];
484
+
485
+ // 几何类型
486
+ type Geometry =
487
+ | Point // { type: 'Point'; coordinates: Position }
488
+ | LineString // { type: 'LineString'; coordinates: Position[] }
489
+ | Polygon // { type: 'Polygon'; coordinates: Position[][] }
490
+ | MultiPoint // { type: 'MultiPoint'; coordinates: Position[] }
491
+ | MultiLineString // { type: 'MultiLineString'; coordinates: Position[][] }
492
+ | MultiPolygon // { type: 'MultiPolygon'; coordinates: Position[][][] }
493
+ | GeometryCollection // { type: 'GeometryCollection'; geometries: Geometry[] }
494
+
495
+ // Feature:包含一个 Geometry 和任意属性
496
+ interface Feature<G extends Geometry = Geometry> {
497
+ type: 'Feature';
498
+ geometry: G | null;
499
+ properties: Record<string, unknown> | null;
500
+ id?: string | number;
501
+ }
502
+
503
+ // FeatureCollection:包含多个 Feature
504
+ interface FeatureCollection {
505
+ type: 'FeatureCollection';
506
+ features: Feature[];
507
+ }
508
+
509
+ // 所有 GeoJSON 对象的联合类型
510
+ type GeoJSONObject = Geometry | Feature | FeatureCollection;
511
+ ```
512
+
513
+ ---
514
+
515
+ ## 注意事项与边界行为
516
+
517
+ ### POINT EMPTY
518
+
519
+ `POINT EMPTY` 在 GeoJSON 规范中没有对应表示(GeoJSON Point 不允许 `null` 坐标)。
520
+ 解析时会**抛出错误**,建议改用 `Feature` 的 `null geometry`:
521
+
522
+ ```javascript
523
+ // ❌ 会抛出错误
524
+ parse('POINT EMPTY');
525
+
526
+ // ✅ 推荐方式:用 null geometry Feature 表示空点
527
+ const emptyFeature = {
528
+ type: 'Feature',
529
+ geometry: null,
530
+ properties: null
531
+ };
532
+ ```
533
+
534
+ ### LINESTRING / POLYGON 等 EMPTY
535
+
536
+ 其他 EMPTY 几何会解析为空坐标数组,并在构建时输出 `EMPTY`:
537
+
538
+ ```javascript
539
+ parse('LINESTRING EMPTY')
540
+ // → { type: 'LineString', coordinates: [] }
541
+
542
+ build({ type: 'LineString', coordinates: [] })
543
+ // → 'LINESTRING EMPTY'
544
+ ```
545
+
546
+ ### 科学计数法坐标
547
+
548
+ WKT 标准不支持科学计数法(如 `1e-7`)。`build()` 内部已做格式化处理,确保输出为标准十进制:
549
+
550
+ ```javascript
551
+ build({ type: 'Point', coordinates: [0.0000001, 1.0000000] })
552
+ // → 'POINT (0.0000001 1)' 而非 'POINT (1e-7 1)'
553
+ ```
554
+
555
+ ### MULTIPOINT 标准格式
556
+
557
+ `build()` 输出符合 OGC/ISO 标准格式(每个点用括号包裹),可被 PostGIS、QGIS 等工具正确识别:
558
+
559
+ ```javascript
560
+ build({ type: 'MultiPoint', coordinates: [[0,0],[1,1]] })
561
+ // → 'MULTIPOINT ((0 0), (1 1))' ✅ 标准
562
+ // 不是:'MULTIPOINT (0 0, 1 1)' ❌ 非标准
563
+ ```
564
+
565
+ ### 尾部垃圾字符检测
566
+
567
+ 解析器会严格校验输入,发现几何体后的多余字符时抛出错误:
568
+
569
+ ```javascript
570
+ parse('POINT (0 0) garbage')
571
+ // → Error: Unexpected trailing token after geometry: "garbage"
572
+ ```
573
+
574
+ ---
575
+
576
+ ## 完整示例
577
+
578
+ ### WKT → GeoJSON Feature → 回写 WKT
579
+
580
+ ```javascript
581
+ import { wktToFeature, featureToWkt } from './dist/index.esm.js';
582
+
583
+ const wkt = 'POLYGON ((116 39, 117 39, 117 40, 116 40, 116 39))';
584
+
585
+ // 解析为 Feature
586
+ const feature = wktToFeature(wkt, { name: '某区域', area: 12345 });
587
+
588
+ // 修改属性
589
+ feature.properties.verified = true;
590
+
591
+ // 取回 WKT
592
+ const outputWkt = featureToWkt(feature);
593
+ console.log(outputWkt);
594
+ // → 'POLYGON ((116 39, 117 39, 117 40, 116 40, 116 39))'
595
+ ```
596
+
597
+ ### 批量城市点构建 FeatureCollection
598
+
599
+ ```javascript
600
+ import { wktToFeatureCollection } from './dist/index.esm.js';
601
+
602
+ const cities = [
603
+ { wkt: 'POINT (116.39 39.91)', props: { name: '北京', code: 'BJ' } },
604
+ { wkt: 'POINT (121.47 31.23)', props: { name: '上海', code: 'SH' } },
605
+ { wkt: 'POINT (113.26 23.13)', props: { name: '广州', code: 'GZ' } },
606
+ ];
607
+
608
+ const fc = wktToFeatureCollection(
609
+ cities.map(c => c.wkt),
610
+ cities.map(c => c.props)
611
+ );
612
+
613
+ // 直接输出为 GeoJSON 字符串
614
+ console.log(JSON.stringify(fc, null, 2));
615
+ ```
616
+
617
+ ### 使用工厂方法组合复杂几何
618
+
619
+ ```javascript
620
+ import { createPoint, createLineString, createPolygon, createGeometryCollection, build } from './dist/index.esm.js';
621
+
622
+ const collection = createGeometryCollection([
623
+ createPoint(0, 0),
624
+ createPoint(10, 10),
625
+ createLineString([[0,0],[5,5],[10,0]]),
626
+ createPolygon([[20,0],[30,0],[30,10],[20,10],[20,0]]) // 单环,自动包装
627
+ ]);
628
+
629
+ console.log(build(collection));
630
+ // → 'GEOMETRYCOLLECTION (POINT (0 0), POINT (10 10), LINESTRING (0 0, 5 5, 10 0), POLYGON ((20 0, 30 0, 30 10, 20 10, 20 0)))'
631
+ ```
632
+
633
+ ---
634
+
635
+ ## 本地调试
636
+
637
+ 启动 HTTP 服务,在浏览器中测试 debug 页面:
638
+
639
+ ```bash
640
+ # 方式一:npx serve
641
+ npx serve debug
642
+
643
+ # 方式二:Python
644
+ cd debug && python -m http.server 8080
645
+
646
+ # 方式三:http-server
647
+ npx http-server debug -p 8080
648
+ ```
649
+
650
+ 然后访问 `http://localhost:8080/index.html`,即可在页面中交互测试所有 API。
651
+
652
+ ---
653
+
654
+ ## License
655
+
656
+ MIT