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