wkt-parse-and-geojson 1.0.1 → 1.0.3
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/dist/index.cjs.js +48 -48
- package/dist/index.esm.js +48 -48
- package/dist/index.umd.js +48 -48
- package/dist/wkt-parser.d.ts +2 -2
- package/package.json +17 -7
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// Precompiled regex for better performance
|
|
4
|
+
const RE_WHITESPACE = /\s/;
|
|
5
|
+
const RE_NUMBER_START = /[0-9\-]/;
|
|
6
|
+
const RE_NUMBER_BODY = /[0-9\.\-eE\+]/;
|
|
7
|
+
const RE_WORD_CHAR = /[a-zA-Z_]/;
|
|
3
8
|
class Lexer {
|
|
4
9
|
constructor(input) {
|
|
5
10
|
this.pos = 0;
|
|
@@ -12,14 +17,13 @@ class Lexer {
|
|
|
12
17
|
return this.input[this.pos++] || '';
|
|
13
18
|
}
|
|
14
19
|
isWhitespace(c) {
|
|
15
|
-
return
|
|
20
|
+
return RE_WHITESPACE.test(c);
|
|
16
21
|
}
|
|
17
|
-
/** 数字开头字符:数字 或 负号 */
|
|
18
22
|
isNumberStart(c) {
|
|
19
|
-
return
|
|
23
|
+
return RE_NUMBER_START.test(c);
|
|
20
24
|
}
|
|
21
25
|
isNumberBody(c) {
|
|
22
|
-
return
|
|
26
|
+
return RE_NUMBER_BODY.test(c);
|
|
23
27
|
}
|
|
24
28
|
nextToken() {
|
|
25
29
|
// skip whitespace
|
|
@@ -44,18 +48,18 @@ class Lexer {
|
|
|
44
48
|
}
|
|
45
49
|
// Number: starts with digit or minus
|
|
46
50
|
if (this.isNumberStart(c)) {
|
|
47
|
-
|
|
51
|
+
const start = this.pos;
|
|
48
52
|
while (this.pos < this.input.length && this.isNumberBody(this.peek())) {
|
|
49
|
-
|
|
53
|
+
this.pos++;
|
|
50
54
|
}
|
|
51
|
-
return { type: 'NUMBER', value:
|
|
55
|
+
return { type: 'NUMBER', value: this.input.slice(start, this.pos) };
|
|
52
56
|
}
|
|
53
57
|
// Word (geometry type or EMPTY/Z/M keyword)
|
|
54
|
-
|
|
55
|
-
while (this.pos < this.input.length &&
|
|
56
|
-
|
|
58
|
+
const start = this.pos;
|
|
59
|
+
while (this.pos < this.input.length && RE_WORD_CHAR.test(this.peek())) {
|
|
60
|
+
this.pos++;
|
|
57
61
|
}
|
|
58
|
-
return { type: 'WORD', value:
|
|
62
|
+
return { type: 'WORD', value: this.input.slice(start, this.pos).toUpperCase() };
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
65
|
class WKTParser {
|
|
@@ -94,6 +98,15 @@ class WKTParser {
|
|
|
94
98
|
}
|
|
95
99
|
return this.advance();
|
|
96
100
|
}
|
|
101
|
+
skipComma() {
|
|
102
|
+
if (this.peek().type === 'COMMA') {
|
|
103
|
+
this.advance();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
isDone() {
|
|
107
|
+
const t = this.peek();
|
|
108
|
+
return t.type === 'RPAREN' || t.type === 'EOF';
|
|
109
|
+
}
|
|
97
110
|
parseGeometry() {
|
|
98
111
|
const token = this.peek();
|
|
99
112
|
if (token.type !== 'WORD') {
|
|
@@ -166,7 +179,13 @@ class WKTParser {
|
|
|
166
179
|
if (this.isEmptyGeometry()) {
|
|
167
180
|
return { type: 'Polygon', coordinates: [] };
|
|
168
181
|
}
|
|
169
|
-
|
|
182
|
+
this.consume('LPAREN');
|
|
183
|
+
const rings = [];
|
|
184
|
+
while (!this.isDone()) {
|
|
185
|
+
rings.push(this.parseCoordinatesList());
|
|
186
|
+
this.skipComma();
|
|
187
|
+
}
|
|
188
|
+
this.consume('RPAREN');
|
|
170
189
|
return { type: 'Polygon', coordinates: rings };
|
|
171
190
|
}
|
|
172
191
|
// ── MULTIPOINT ───────────────────────────────────────────────────
|
|
@@ -178,7 +197,7 @@ class WKTParser {
|
|
|
178
197
|
}
|
|
179
198
|
this.advance(); // consume outer (
|
|
180
199
|
const coords = [];
|
|
181
|
-
while (this.
|
|
200
|
+
while (!this.isDone()) {
|
|
182
201
|
if (this.peek().type === 'LPAREN') {
|
|
183
202
|
// 标准写法: MULTIPOINT ((x y), (x y))
|
|
184
203
|
this.advance(); // consume (
|
|
@@ -189,9 +208,7 @@ class WKTParser {
|
|
|
189
208
|
// 非标准写法: MULTIPOINT (x y, x y)
|
|
190
209
|
coords.push(this.parseCoordinates());
|
|
191
210
|
}
|
|
192
|
-
|
|
193
|
-
this.advance();
|
|
194
|
-
}
|
|
211
|
+
this.skipComma();
|
|
195
212
|
}
|
|
196
213
|
this.consume('RPAREN');
|
|
197
214
|
return { type: 'MultiPoint', coordinates: coords };
|
|
@@ -205,11 +222,9 @@ class WKTParser {
|
|
|
205
222
|
}
|
|
206
223
|
this.advance(); // consume outer (
|
|
207
224
|
const lines = [];
|
|
208
|
-
while (this.
|
|
225
|
+
while (!this.isDone()) {
|
|
209
226
|
lines.push(this.parseCoordinatesList());
|
|
210
|
-
|
|
211
|
-
this.advance();
|
|
212
|
-
}
|
|
227
|
+
this.skipComma();
|
|
213
228
|
}
|
|
214
229
|
this.consume('RPAREN');
|
|
215
230
|
return { type: 'MultiLineString', coordinates: lines };
|
|
@@ -221,7 +236,13 @@ class WKTParser {
|
|
|
221
236
|
if (this.isEmptyGeometry()) {
|
|
222
237
|
return { type: 'MultiPolygon', coordinates: [] };
|
|
223
238
|
}
|
|
224
|
-
|
|
239
|
+
this.consume('LPAREN');
|
|
240
|
+
const polys = [];
|
|
241
|
+
while (!this.isDone()) {
|
|
242
|
+
polys.push(this.parseCoordinateListList());
|
|
243
|
+
this.skipComma();
|
|
244
|
+
}
|
|
245
|
+
this.consume('RPAREN');
|
|
225
246
|
return { type: 'MultiPolygon', coordinates: polys };
|
|
226
247
|
}
|
|
227
248
|
// ── GEOMETRYCOLLECTION ───────────────────────────────────────────
|
|
@@ -233,11 +254,9 @@ class WKTParser {
|
|
|
233
254
|
}
|
|
234
255
|
this.advance(); // consume (
|
|
235
256
|
const geometries = [];
|
|
236
|
-
while (this.
|
|
257
|
+
while (!this.isDone()) {
|
|
237
258
|
geometries.push(this.parseGeometry());
|
|
238
|
-
|
|
239
|
-
this.advance();
|
|
240
|
-
}
|
|
259
|
+
this.skipComma();
|
|
241
260
|
}
|
|
242
261
|
this.consume('RPAREN');
|
|
243
262
|
return { type: 'GeometryCollection', geometries };
|
|
@@ -270,11 +289,9 @@ class WKTParser {
|
|
|
270
289
|
parseCoordinatesList() {
|
|
271
290
|
this.consume('LPAREN');
|
|
272
291
|
const coords = [];
|
|
273
|
-
while (this.
|
|
292
|
+
while (!this.isDone()) {
|
|
274
293
|
coords.push(this.parseCoordinates());
|
|
275
|
-
|
|
276
|
-
this.advance();
|
|
277
|
-
}
|
|
294
|
+
this.skipComma();
|
|
278
295
|
}
|
|
279
296
|
this.consume('RPAREN');
|
|
280
297
|
return coords;
|
|
@@ -285,26 +302,9 @@ class WKTParser {
|
|
|
285
302
|
return [];
|
|
286
303
|
this.advance(); // consume outer (
|
|
287
304
|
const lists = [];
|
|
288
|
-
while (this.
|
|
305
|
+
while (!this.isDone()) {
|
|
289
306
|
lists.push(this.parseCoordinatesList());
|
|
290
|
-
|
|
291
|
-
this.advance();
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
this.consume('RPAREN');
|
|
295
|
-
return lists;
|
|
296
|
-
}
|
|
297
|
-
/** 解析多边形列表(MultiPolygon 级别):( ((...)), ((...)) ) */
|
|
298
|
-
parseCoordinateListListList() {
|
|
299
|
-
if (this.peek().type !== 'LPAREN')
|
|
300
|
-
return [];
|
|
301
|
-
this.advance(); // consume outer (
|
|
302
|
-
const lists = [];
|
|
303
|
-
while (this.peek().type !== 'RPAREN' && this.peek().type !== 'EOF') {
|
|
304
|
-
lists.push(this.parseCoordinateListList());
|
|
305
|
-
if (this.peek().type === 'COMMA') {
|
|
306
|
-
this.advance();
|
|
307
|
-
}
|
|
307
|
+
this.skipComma();
|
|
308
308
|
}
|
|
309
309
|
this.consume('RPAREN');
|
|
310
310
|
return lists;
|
package/dist/index.esm.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// Precompiled regex for better performance
|
|
2
|
+
const RE_WHITESPACE = /\s/;
|
|
3
|
+
const RE_NUMBER_START = /[0-9\-]/;
|
|
4
|
+
const RE_NUMBER_BODY = /[0-9\.\-eE\+]/;
|
|
5
|
+
const RE_WORD_CHAR = /[a-zA-Z_]/;
|
|
1
6
|
class Lexer {
|
|
2
7
|
constructor(input) {
|
|
3
8
|
this.pos = 0;
|
|
@@ -10,14 +15,13 @@ class Lexer {
|
|
|
10
15
|
return this.input[this.pos++] || '';
|
|
11
16
|
}
|
|
12
17
|
isWhitespace(c) {
|
|
13
|
-
return
|
|
18
|
+
return RE_WHITESPACE.test(c);
|
|
14
19
|
}
|
|
15
|
-
/** 数字开头字符:数字 或 负号 */
|
|
16
20
|
isNumberStart(c) {
|
|
17
|
-
return
|
|
21
|
+
return RE_NUMBER_START.test(c);
|
|
18
22
|
}
|
|
19
23
|
isNumberBody(c) {
|
|
20
|
-
return
|
|
24
|
+
return RE_NUMBER_BODY.test(c);
|
|
21
25
|
}
|
|
22
26
|
nextToken() {
|
|
23
27
|
// skip whitespace
|
|
@@ -42,18 +46,18 @@ class Lexer {
|
|
|
42
46
|
}
|
|
43
47
|
// Number: starts with digit or minus
|
|
44
48
|
if (this.isNumberStart(c)) {
|
|
45
|
-
|
|
49
|
+
const start = this.pos;
|
|
46
50
|
while (this.pos < this.input.length && this.isNumberBody(this.peek())) {
|
|
47
|
-
|
|
51
|
+
this.pos++;
|
|
48
52
|
}
|
|
49
|
-
return { type: 'NUMBER', value:
|
|
53
|
+
return { type: 'NUMBER', value: this.input.slice(start, this.pos) };
|
|
50
54
|
}
|
|
51
55
|
// Word (geometry type or EMPTY/Z/M keyword)
|
|
52
|
-
|
|
53
|
-
while (this.pos < this.input.length &&
|
|
54
|
-
|
|
56
|
+
const start = this.pos;
|
|
57
|
+
while (this.pos < this.input.length && RE_WORD_CHAR.test(this.peek())) {
|
|
58
|
+
this.pos++;
|
|
55
59
|
}
|
|
56
|
-
return { type: 'WORD', value:
|
|
60
|
+
return { type: 'WORD', value: this.input.slice(start, this.pos).toUpperCase() };
|
|
57
61
|
}
|
|
58
62
|
}
|
|
59
63
|
class WKTParser {
|
|
@@ -92,6 +96,15 @@ class WKTParser {
|
|
|
92
96
|
}
|
|
93
97
|
return this.advance();
|
|
94
98
|
}
|
|
99
|
+
skipComma() {
|
|
100
|
+
if (this.peek().type === 'COMMA') {
|
|
101
|
+
this.advance();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
isDone() {
|
|
105
|
+
const t = this.peek();
|
|
106
|
+
return t.type === 'RPAREN' || t.type === 'EOF';
|
|
107
|
+
}
|
|
95
108
|
parseGeometry() {
|
|
96
109
|
const token = this.peek();
|
|
97
110
|
if (token.type !== 'WORD') {
|
|
@@ -164,7 +177,13 @@ class WKTParser {
|
|
|
164
177
|
if (this.isEmptyGeometry()) {
|
|
165
178
|
return { type: 'Polygon', coordinates: [] };
|
|
166
179
|
}
|
|
167
|
-
|
|
180
|
+
this.consume('LPAREN');
|
|
181
|
+
const rings = [];
|
|
182
|
+
while (!this.isDone()) {
|
|
183
|
+
rings.push(this.parseCoordinatesList());
|
|
184
|
+
this.skipComma();
|
|
185
|
+
}
|
|
186
|
+
this.consume('RPAREN');
|
|
168
187
|
return { type: 'Polygon', coordinates: rings };
|
|
169
188
|
}
|
|
170
189
|
// ── MULTIPOINT ───────────────────────────────────────────────────
|
|
@@ -176,7 +195,7 @@ class WKTParser {
|
|
|
176
195
|
}
|
|
177
196
|
this.advance(); // consume outer (
|
|
178
197
|
const coords = [];
|
|
179
|
-
while (this.
|
|
198
|
+
while (!this.isDone()) {
|
|
180
199
|
if (this.peek().type === 'LPAREN') {
|
|
181
200
|
// 标准写法: MULTIPOINT ((x y), (x y))
|
|
182
201
|
this.advance(); // consume (
|
|
@@ -187,9 +206,7 @@ class WKTParser {
|
|
|
187
206
|
// 非标准写法: MULTIPOINT (x y, x y)
|
|
188
207
|
coords.push(this.parseCoordinates());
|
|
189
208
|
}
|
|
190
|
-
|
|
191
|
-
this.advance();
|
|
192
|
-
}
|
|
209
|
+
this.skipComma();
|
|
193
210
|
}
|
|
194
211
|
this.consume('RPAREN');
|
|
195
212
|
return { type: 'MultiPoint', coordinates: coords };
|
|
@@ -203,11 +220,9 @@ class WKTParser {
|
|
|
203
220
|
}
|
|
204
221
|
this.advance(); // consume outer (
|
|
205
222
|
const lines = [];
|
|
206
|
-
while (this.
|
|
223
|
+
while (!this.isDone()) {
|
|
207
224
|
lines.push(this.parseCoordinatesList());
|
|
208
|
-
|
|
209
|
-
this.advance();
|
|
210
|
-
}
|
|
225
|
+
this.skipComma();
|
|
211
226
|
}
|
|
212
227
|
this.consume('RPAREN');
|
|
213
228
|
return { type: 'MultiLineString', coordinates: lines };
|
|
@@ -219,7 +234,13 @@ class WKTParser {
|
|
|
219
234
|
if (this.isEmptyGeometry()) {
|
|
220
235
|
return { type: 'MultiPolygon', coordinates: [] };
|
|
221
236
|
}
|
|
222
|
-
|
|
237
|
+
this.consume('LPAREN');
|
|
238
|
+
const polys = [];
|
|
239
|
+
while (!this.isDone()) {
|
|
240
|
+
polys.push(this.parseCoordinateListList());
|
|
241
|
+
this.skipComma();
|
|
242
|
+
}
|
|
243
|
+
this.consume('RPAREN');
|
|
223
244
|
return { type: 'MultiPolygon', coordinates: polys };
|
|
224
245
|
}
|
|
225
246
|
// ── GEOMETRYCOLLECTION ───────────────────────────────────────────
|
|
@@ -231,11 +252,9 @@ class WKTParser {
|
|
|
231
252
|
}
|
|
232
253
|
this.advance(); // consume (
|
|
233
254
|
const geometries = [];
|
|
234
|
-
while (this.
|
|
255
|
+
while (!this.isDone()) {
|
|
235
256
|
geometries.push(this.parseGeometry());
|
|
236
|
-
|
|
237
|
-
this.advance();
|
|
238
|
-
}
|
|
257
|
+
this.skipComma();
|
|
239
258
|
}
|
|
240
259
|
this.consume('RPAREN');
|
|
241
260
|
return { type: 'GeometryCollection', geometries };
|
|
@@ -268,11 +287,9 @@ class WKTParser {
|
|
|
268
287
|
parseCoordinatesList() {
|
|
269
288
|
this.consume('LPAREN');
|
|
270
289
|
const coords = [];
|
|
271
|
-
while (this.
|
|
290
|
+
while (!this.isDone()) {
|
|
272
291
|
coords.push(this.parseCoordinates());
|
|
273
|
-
|
|
274
|
-
this.advance();
|
|
275
|
-
}
|
|
292
|
+
this.skipComma();
|
|
276
293
|
}
|
|
277
294
|
this.consume('RPAREN');
|
|
278
295
|
return coords;
|
|
@@ -283,26 +300,9 @@ class WKTParser {
|
|
|
283
300
|
return [];
|
|
284
301
|
this.advance(); // consume outer (
|
|
285
302
|
const lists = [];
|
|
286
|
-
while (this.
|
|
303
|
+
while (!this.isDone()) {
|
|
287
304
|
lists.push(this.parseCoordinatesList());
|
|
288
|
-
|
|
289
|
-
this.advance();
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
this.consume('RPAREN');
|
|
293
|
-
return lists;
|
|
294
|
-
}
|
|
295
|
-
/** 解析多边形列表(MultiPolygon 级别):( ((...)), ((...)) ) */
|
|
296
|
-
parseCoordinateListListList() {
|
|
297
|
-
if (this.peek().type !== 'LPAREN')
|
|
298
|
-
return [];
|
|
299
|
-
this.advance(); // consume outer (
|
|
300
|
-
const lists = [];
|
|
301
|
-
while (this.peek().type !== 'RPAREN' && this.peek().type !== 'EOF') {
|
|
302
|
-
lists.push(this.parseCoordinateListList());
|
|
303
|
-
if (this.peek().type === 'COMMA') {
|
|
304
|
-
this.advance();
|
|
305
|
-
}
|
|
305
|
+
this.skipComma();
|
|
306
306
|
}
|
|
307
307
|
this.consume('RPAREN');
|
|
308
308
|
return lists;
|
package/dist/index.umd.js
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.WKTGeoJSON = {}));
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
|
+
// Precompiled regex for better performance
|
|
8
|
+
const RE_WHITESPACE = /\s/;
|
|
9
|
+
const RE_NUMBER_START = /[0-9\-]/;
|
|
10
|
+
const RE_NUMBER_BODY = /[0-9\.\-eE\+]/;
|
|
11
|
+
const RE_WORD_CHAR = /[a-zA-Z_]/;
|
|
7
12
|
class Lexer {
|
|
8
13
|
constructor(input) {
|
|
9
14
|
this.pos = 0;
|
|
@@ -16,14 +21,13 @@
|
|
|
16
21
|
return this.input[this.pos++] || '';
|
|
17
22
|
}
|
|
18
23
|
isWhitespace(c) {
|
|
19
|
-
return
|
|
24
|
+
return RE_WHITESPACE.test(c);
|
|
20
25
|
}
|
|
21
|
-
/** 数字开头字符:数字 或 负号 */
|
|
22
26
|
isNumberStart(c) {
|
|
23
|
-
return
|
|
27
|
+
return RE_NUMBER_START.test(c);
|
|
24
28
|
}
|
|
25
29
|
isNumberBody(c) {
|
|
26
|
-
return
|
|
30
|
+
return RE_NUMBER_BODY.test(c);
|
|
27
31
|
}
|
|
28
32
|
nextToken() {
|
|
29
33
|
// skip whitespace
|
|
@@ -48,18 +52,18 @@
|
|
|
48
52
|
}
|
|
49
53
|
// Number: starts with digit or minus
|
|
50
54
|
if (this.isNumberStart(c)) {
|
|
51
|
-
|
|
55
|
+
const start = this.pos;
|
|
52
56
|
while (this.pos < this.input.length && this.isNumberBody(this.peek())) {
|
|
53
|
-
|
|
57
|
+
this.pos++;
|
|
54
58
|
}
|
|
55
|
-
return { type: 'NUMBER', value:
|
|
59
|
+
return { type: 'NUMBER', value: this.input.slice(start, this.pos) };
|
|
56
60
|
}
|
|
57
61
|
// Word (geometry type or EMPTY/Z/M keyword)
|
|
58
|
-
|
|
59
|
-
while (this.pos < this.input.length &&
|
|
60
|
-
|
|
62
|
+
const start = this.pos;
|
|
63
|
+
while (this.pos < this.input.length && RE_WORD_CHAR.test(this.peek())) {
|
|
64
|
+
this.pos++;
|
|
61
65
|
}
|
|
62
|
-
return { type: 'WORD', value:
|
|
66
|
+
return { type: 'WORD', value: this.input.slice(start, this.pos).toUpperCase() };
|
|
63
67
|
}
|
|
64
68
|
}
|
|
65
69
|
class WKTParser {
|
|
@@ -98,6 +102,15 @@
|
|
|
98
102
|
}
|
|
99
103
|
return this.advance();
|
|
100
104
|
}
|
|
105
|
+
skipComma() {
|
|
106
|
+
if (this.peek().type === 'COMMA') {
|
|
107
|
+
this.advance();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
isDone() {
|
|
111
|
+
const t = this.peek();
|
|
112
|
+
return t.type === 'RPAREN' || t.type === 'EOF';
|
|
113
|
+
}
|
|
101
114
|
parseGeometry() {
|
|
102
115
|
const token = this.peek();
|
|
103
116
|
if (token.type !== 'WORD') {
|
|
@@ -170,7 +183,13 @@
|
|
|
170
183
|
if (this.isEmptyGeometry()) {
|
|
171
184
|
return { type: 'Polygon', coordinates: [] };
|
|
172
185
|
}
|
|
173
|
-
|
|
186
|
+
this.consume('LPAREN');
|
|
187
|
+
const rings = [];
|
|
188
|
+
while (!this.isDone()) {
|
|
189
|
+
rings.push(this.parseCoordinatesList());
|
|
190
|
+
this.skipComma();
|
|
191
|
+
}
|
|
192
|
+
this.consume('RPAREN');
|
|
174
193
|
return { type: 'Polygon', coordinates: rings };
|
|
175
194
|
}
|
|
176
195
|
// ── MULTIPOINT ───────────────────────────────────────────────────
|
|
@@ -182,7 +201,7 @@
|
|
|
182
201
|
}
|
|
183
202
|
this.advance(); // consume outer (
|
|
184
203
|
const coords = [];
|
|
185
|
-
while (this.
|
|
204
|
+
while (!this.isDone()) {
|
|
186
205
|
if (this.peek().type === 'LPAREN') {
|
|
187
206
|
// 标准写法: MULTIPOINT ((x y), (x y))
|
|
188
207
|
this.advance(); // consume (
|
|
@@ -193,9 +212,7 @@
|
|
|
193
212
|
// 非标准写法: MULTIPOINT (x y, x y)
|
|
194
213
|
coords.push(this.parseCoordinates());
|
|
195
214
|
}
|
|
196
|
-
|
|
197
|
-
this.advance();
|
|
198
|
-
}
|
|
215
|
+
this.skipComma();
|
|
199
216
|
}
|
|
200
217
|
this.consume('RPAREN');
|
|
201
218
|
return { type: 'MultiPoint', coordinates: coords };
|
|
@@ -209,11 +226,9 @@
|
|
|
209
226
|
}
|
|
210
227
|
this.advance(); // consume outer (
|
|
211
228
|
const lines = [];
|
|
212
|
-
while (this.
|
|
229
|
+
while (!this.isDone()) {
|
|
213
230
|
lines.push(this.parseCoordinatesList());
|
|
214
|
-
|
|
215
|
-
this.advance();
|
|
216
|
-
}
|
|
231
|
+
this.skipComma();
|
|
217
232
|
}
|
|
218
233
|
this.consume('RPAREN');
|
|
219
234
|
return { type: 'MultiLineString', coordinates: lines };
|
|
@@ -225,7 +240,13 @@
|
|
|
225
240
|
if (this.isEmptyGeometry()) {
|
|
226
241
|
return { type: 'MultiPolygon', coordinates: [] };
|
|
227
242
|
}
|
|
228
|
-
|
|
243
|
+
this.consume('LPAREN');
|
|
244
|
+
const polys = [];
|
|
245
|
+
while (!this.isDone()) {
|
|
246
|
+
polys.push(this.parseCoordinateListList());
|
|
247
|
+
this.skipComma();
|
|
248
|
+
}
|
|
249
|
+
this.consume('RPAREN');
|
|
229
250
|
return { type: 'MultiPolygon', coordinates: polys };
|
|
230
251
|
}
|
|
231
252
|
// ── GEOMETRYCOLLECTION ───────────────────────────────────────────
|
|
@@ -237,11 +258,9 @@
|
|
|
237
258
|
}
|
|
238
259
|
this.advance(); // consume (
|
|
239
260
|
const geometries = [];
|
|
240
|
-
while (this.
|
|
261
|
+
while (!this.isDone()) {
|
|
241
262
|
geometries.push(this.parseGeometry());
|
|
242
|
-
|
|
243
|
-
this.advance();
|
|
244
|
-
}
|
|
263
|
+
this.skipComma();
|
|
245
264
|
}
|
|
246
265
|
this.consume('RPAREN');
|
|
247
266
|
return { type: 'GeometryCollection', geometries };
|
|
@@ -274,11 +293,9 @@
|
|
|
274
293
|
parseCoordinatesList() {
|
|
275
294
|
this.consume('LPAREN');
|
|
276
295
|
const coords = [];
|
|
277
|
-
while (this.
|
|
296
|
+
while (!this.isDone()) {
|
|
278
297
|
coords.push(this.parseCoordinates());
|
|
279
|
-
|
|
280
|
-
this.advance();
|
|
281
|
-
}
|
|
298
|
+
this.skipComma();
|
|
282
299
|
}
|
|
283
300
|
this.consume('RPAREN');
|
|
284
301
|
return coords;
|
|
@@ -289,26 +306,9 @@
|
|
|
289
306
|
return [];
|
|
290
307
|
this.advance(); // consume outer (
|
|
291
308
|
const lists = [];
|
|
292
|
-
while (this.
|
|
309
|
+
while (!this.isDone()) {
|
|
293
310
|
lists.push(this.parseCoordinatesList());
|
|
294
|
-
|
|
295
|
-
this.advance();
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
this.consume('RPAREN');
|
|
299
|
-
return lists;
|
|
300
|
-
}
|
|
301
|
-
/** 解析多边形列表(MultiPolygon 级别):( ((...)), ((...)) ) */
|
|
302
|
-
parseCoordinateListListList() {
|
|
303
|
-
if (this.peek().type !== 'LPAREN')
|
|
304
|
-
return [];
|
|
305
|
-
this.advance(); // consume outer (
|
|
306
|
-
const lists = [];
|
|
307
|
-
while (this.peek().type !== 'RPAREN' && this.peek().type !== 'EOF') {
|
|
308
|
-
lists.push(this.parseCoordinateListList());
|
|
309
|
-
if (this.peek().type === 'COMMA') {
|
|
310
|
-
this.advance();
|
|
311
|
-
}
|
|
311
|
+
this.skipComma();
|
|
312
312
|
}
|
|
313
313
|
this.consume('RPAREN');
|
|
314
314
|
return lists;
|
package/dist/wkt-parser.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export declare class WKTParser {
|
|
|
7
7
|
private advance;
|
|
8
8
|
/** 消费当前 token 并返回,若类型不匹配则抛出错误 */
|
|
9
9
|
private consume;
|
|
10
|
+
private skipComma;
|
|
11
|
+
private isDone;
|
|
10
12
|
private parseGeometry;
|
|
11
13
|
private skipDimensionKeyword;
|
|
12
14
|
private isEmptyGeometry;
|
|
@@ -26,8 +28,6 @@ export declare class WKTParser {
|
|
|
26
28
|
private parseCoordinatesList;
|
|
27
29
|
/** 解析环列表(Polygon 级别):( (...), (...) ) */
|
|
28
30
|
private parseCoordinateListList;
|
|
29
|
-
/** 解析多边形列表(MultiPolygon 级别):( ((...)), ((...)) ) */
|
|
30
|
-
private parseCoordinateListListList;
|
|
31
31
|
}
|
|
32
32
|
/** 将 WKT 字符串解析为 GeoJSON Geometry 对象 */
|
|
33
33
|
export declare function parse(wkt: string): Geometry;
|
package/package.json
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wkt-parse-and-geojson",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Zero-dependency WKT parser/builder and WKT↔GeoJSON converter",
|
|
6
6
|
"main": "dist/index.cjs.js",
|
|
7
7
|
"module": "dist/index.esm.js",
|
|
8
|
+
"browser": "dist/index.umd.js",
|
|
8
9
|
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.esm.js",
|
|
13
|
+
"require": "./dist/index.cjs.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
9
17
|
"files": [
|
|
10
|
-
"dist"
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
11
21
|
],
|
|
12
22
|
"scripts": {
|
|
13
23
|
"build": "rollup -c",
|
|
@@ -19,7 +29,10 @@
|
|
|
19
29
|
"geojson",
|
|
20
30
|
"geometry",
|
|
21
31
|
"parser",
|
|
22
|
-
"
|
|
32
|
+
"builder",
|
|
33
|
+
"gis",
|
|
34
|
+
"ogc",
|
|
35
|
+
"spatial"
|
|
23
36
|
],
|
|
24
37
|
"author": "",
|
|
25
38
|
"license": "MIT",
|
|
@@ -28,8 +41,5 @@
|
|
|
28
41
|
"rollup": "^4.40.0",
|
|
29
42
|
"tslib": "^2.8.1",
|
|
30
43
|
"typescript": "^5.8.3"
|
|
31
|
-
},
|
|
32
|
-
"dependencies": {
|
|
33
|
-
"wkt-parse-and-geojson": "^1.0.0"
|
|
34
44
|
}
|
|
35
45
|
}
|