@mapgis/geojson-vt 16.8.0 → 17.2.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/CHANGELOG ADDED
File without changes
@@ -0,0 +1,1063 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('proj4')) :
3
+ typeof define === 'function' && define.amd ? define(['proj4'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.geojsonvt = factory(global.proj4));
5
+ })(this, (function (proj4) { 'use strict';
6
+
7
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
+
9
+ var proj4__default = /*#__PURE__*/_interopDefaultLegacy(proj4);
10
+
11
+ /**
12
+ * @description: 投影坐标系对象
13
+ */
14
+ var Projection = function Projection(code, def, bounds, width, height) {
15
+ this.bounds = bounds;
16
+ this.width = width;
17
+ this.height = height;
18
+ if (!def) { throw new Error('自定义参考系必须指定参数系信息def'); }
19
+ // 验证proj4库是否存在
20
+ if (!proj4__default["default"]) { throw new Error('proj4对象不存在,自定义投影必须引入proj4对象'); }
21
+ var isP4 = this._isProj4Obj(code);
22
+ this._proj = isP4 ? code : this._projFromCodeDef(code, def);
23
+ };
24
+
25
+ /**
26
+ * @description: 投影方法
27
+ * @param {Array|Object} lnglat
28
+ * @return {*}
29
+ */
30
+ Projection.prototype.project = function project (lnglat) {
31
+ var lng, lat;
32
+ if (Array.isArray(lnglat)) {
33
+ lng = lnglat[0];
34
+ lat = lnglat[1];
35
+ } else {
36
+ lng = lnglat.lng;
37
+ lat = lnglat.lat;
38
+ }
39
+ var coords = this._proj.forward([lng, lat]);
40
+ var bounds = this.bounds;
41
+ var x = (coords[0] - bounds[0]) / this.width;
42
+ var y = (bounds[3] - coords[1]) / this.height;
43
+ return [x, y];
44
+ };
45
+
46
+
47
+ /**
48
+ * @description: 反投影方法
49
+ * @param {Array|Object} point
50
+ * @return {*}
51
+ */
52
+ Projection.prototype.unproject = function unproject (point) {
53
+ var x, y;
54
+ if (Array.isArray(point)) {
55
+ x = point[0];
56
+ y = point[1];
57
+ } else {
58
+ x = point.x;
59
+ y = point.y;
60
+ }
61
+ return this._proj.inverse([x, y]);
62
+ };
63
+
64
+
65
+ /**
66
+ * @description: 定义proj投影
67
+ * @param {*} code
68
+ * @param {*} def
69
+ * @return {*}
70
+ */
71
+ Projection.prototype._projFromCodeDef = function _projFromCodeDef (code, def) {
72
+ if (def) {
73
+ proj4__default["default"].defs(code, def);
74
+ } else if (proj4__default["default"].defs[code] === undefined) {
75
+ var urn = code.split(':');
76
+ if (urn.length > 3) {
77
+ code = (urn[urn.length - 3]) + ":" + (urn[urn.length - 1]);
78
+ }
79
+ if (proj4__default["default"].defs[code] === undefined) {
80
+ throw new Error(("No projection definition for code " + code));
81
+ }
82
+ }
83
+ return proj4__default["default"](code);
84
+ };
85
+
86
+
87
+ /**
88
+ * @description: 判断是否是proj4
89
+ * @param {*} a
90
+ * @return {*}
91
+ */
92
+ Projection.prototype._isProj4Obj = function _isProj4Obj (a) {
93
+ return (typeof a.inverse !== 'undefined' &&
94
+ typeof a.forward !== 'undefined');
95
+ };
96
+
97
+ // calculate simplification data using optimized Douglas-Peucker algorithm
98
+
99
+ function simplify(coords, first, last, sqTolerance) {
100
+ var maxSqDist = sqTolerance;
101
+ var mid = (last - first) >> 1;
102
+ var minPosToMid = last - first;
103
+ var index;
104
+
105
+ var ax = coords[first];
106
+ var ay = coords[first + 1];
107
+ var bx = coords[last];
108
+ var by = coords[last + 1];
109
+
110
+ for (var i = first + 3; i < last; i += 3) {
111
+ var d = getSqSegDist(coords[i], coords[i + 1], ax, ay, bx, by);
112
+
113
+ if (d > maxSqDist) {
114
+ index = i;
115
+ maxSqDist = d;
116
+
117
+ } else if (d === maxSqDist) {
118
+ // a workaround to ensure we choose a pivot close to the middle of the list,
119
+ // reducing recursion depth, for certain degenerate inputs
120
+ // https://github.com/mapbox/geojson-vt/issues/104
121
+ var posToMid = Math.abs(i - mid);
122
+ if (posToMid < minPosToMid) {
123
+ index = i;
124
+ minPosToMid = posToMid;
125
+ }
126
+ }
127
+ }
128
+
129
+ if (maxSqDist > sqTolerance) {
130
+ if (index - first > 3) { simplify(coords, first, index, sqTolerance); }
131
+ coords[index + 2] = maxSqDist;
132
+ if (last - index > 3) { simplify(coords, index, last, sqTolerance); }
133
+ }
134
+ }
135
+
136
+ // square distance from a point to a segment
137
+ function getSqSegDist(px, py, x, y, bx, by) {
138
+
139
+ var dx = bx - x;
140
+ var dy = by - y;
141
+
142
+ if (dx !== 0 || dy !== 0) {
143
+
144
+ var t = ((px - x) * dx + (py - y) * dy) / (dx * dx + dy * dy);
145
+
146
+ if (t > 1) {
147
+ x = bx;
148
+ y = by;
149
+
150
+ } else if (t > 0) {
151
+ x += dx * t;
152
+ y += dy * t;
153
+ }
154
+ }
155
+
156
+ dx = px - x;
157
+ dy = py - y;
158
+
159
+ return dx * dx + dy * dy;
160
+ }
161
+
162
+ function createFeature(id, type, geom, tags) {
163
+ var feature = {
164
+ id: id == null ? null : id,
165
+ type: type,
166
+ geometry: geom,
167
+ tags: tags,
168
+ minX: Infinity,
169
+ minY: Infinity,
170
+ maxX: -Infinity,
171
+ maxY: -Infinity
172
+ };
173
+
174
+ if (type === 'Point' || type === 'MultiPoint' || type === 'LineString') {
175
+ calcLineBBox(feature, geom);
176
+
177
+ } else if (type === 'Polygon') {
178
+ // the outer ring (ie [0]) contains all inner rings
179
+ calcLineBBox(feature, geom[0]);
180
+
181
+ } else if (type === 'MultiLineString') {
182
+ for (var i = 0, list = geom; i < list.length; i += 1) {
183
+ var line = list[i];
184
+
185
+ calcLineBBox(feature, line);
186
+ }
187
+
188
+ } else if (type === 'MultiPolygon') {
189
+ for (var i$1 = 0, list$1 = geom; i$1 < list$1.length; i$1 += 1) {
190
+ // the outer ring (ie [0]) contains all inner rings
191
+ var polygon = list$1[i$1];
192
+
193
+ calcLineBBox(feature, polygon[0]);
194
+ }
195
+ }
196
+
197
+ return feature;
198
+ }
199
+
200
+ function calcLineBBox(feature, geom) {
201
+ for (var i = 0; i < geom.length; i += 3) {
202
+ feature.minX = Math.min(feature.minX, geom[i]);
203
+ feature.minY = Math.min(feature.minY, geom[i + 1]);
204
+ feature.maxX = Math.max(feature.maxX, geom[i]);
205
+ feature.maxY = Math.max(feature.maxY, geom[i + 1]);
206
+ }
207
+ }
208
+
209
+ // converts GeoJSON feature into an intermediate projected JSON vector format with simplification data
210
+
211
+ function convert(data, options) {
212
+ var features = [];
213
+ if (data.type === 'FeatureCollection') {
214
+ for (var i = 0; i < data.features.length; i++) {
215
+ convertFeature(features, data.features[i], options, i);
216
+ }
217
+ } else if (data.type === 'Feature') {
218
+ convertFeature(features, data, options);
219
+ } else {
220
+ // single geometry or a geometry collection
221
+ convertFeature(features, {geometry: data}, options);
222
+ }
223
+
224
+ return features;
225
+ }
226
+
227
+ /**
228
+ * 转换要素,主要是坐标投影变换
229
+ * @param {*} features 传回外部的数据
230
+ * @param {*} geojson 要处理的geojson数据
231
+ * @param {*} options 参数
232
+ * @param {*} options.crs 投影参数, 要区分4326以及3857
233
+ * @param {*} options.projectionParams 详细投影参数
234
+ * @param {*} index 处理的geojson数据的序号
235
+ */
236
+ function convertFeature(features, geojson, options, index) {
237
+ if (!geojson.geometry) { return; }
238
+
239
+ var crs = options.crs;
240
+
241
+ var projectionParams = options.projectionParams;
242
+ var projection;
243
+ if (projectionParams && projectionParams.def) {
244
+ // projectionParams ={
245
+ // "code": "EPSG:4547",
246
+ // "def": "+proj=tmerc +lat_0=0 +lon_0=114 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs +type=crs",
247
+ // "originX": 401635,
248
+ // "originY": 3542709,
249
+ // "width": 216746.66666666663,
250
+ // "height": 216746.6666666665,
251
+ // "bounds": [
252
+ // 401635,
253
+ // 3325962.3333333335,
254
+ // 618381.6666666666,
255
+ // 3542709
256
+ // ]
257
+ // }
258
+ var code = projectionParams.code;
259
+ var def = projectionParams.def;
260
+ var bounds = projectionParams.bounds;
261
+ var width = projectionParams.width;
262
+ var height = projectionParams.height;
263
+ projection = new Projection(code, def, bounds, width, height);
264
+ }
265
+
266
+ var coords = geojson.geometry.coordinates;
267
+ var type = geojson.geometry.type;
268
+ var tolerance = Math.pow(
269
+ options.tolerance / ((1 << options.maxZoom) * options.extent),
270
+ 2
271
+ );
272
+ var geometry = [];
273
+ var id = geojson.id;
274
+ if (options.promoteId) {
275
+ id = geojson.properties[options.promoteId];
276
+ } else if (options.generateId) {
277
+ id = index || 0;
278
+ }
279
+ if (type === 'Point') {
280
+ convertPoint(coords, geometry, crs, projection);
281
+ } else if (type === 'MultiPoint') {
282
+ for (var i = 0, list = coords; i < list.length; i += 1) {
283
+ var p = list[i];
284
+
285
+ convertPoint(p, geometry, crs, projection);
286
+ }
287
+ } else if (type === 'LineString') {
288
+ convertLine(coords, geometry, tolerance, false, crs, projection);
289
+ } else if (type === 'MultiLineString') {
290
+ if (options.lineMetrics) {
291
+ // explode into linestrings to be able to track metrics
292
+ for (var i$1 = 0, list$1 = coords; i$1 < list$1.length; i$1 += 1) {
293
+ var line = list$1[i$1];
294
+
295
+ geometry = [];
296
+ convertLine(line, geometry, tolerance, false, crs, projection);
297
+ features.push(
298
+ createFeature(id, 'LineString', geometry, geojson.properties)
299
+ );
300
+ }
301
+ return;
302
+ } else {
303
+ convertLines(coords, geometry, tolerance, false, crs, projection);
304
+ }
305
+ } else if (type === 'Polygon') {
306
+ convertLines(coords, geometry, tolerance, true, crs, projection);
307
+ } else if (type === 'MultiPolygon') {
308
+ for (var i$2 = 0, list$2 = coords; i$2 < list$2.length; i$2 += 1) {
309
+ var polygon = list$2[i$2];
310
+
311
+ var newPolygon = [];
312
+ convertLines(polygon, newPolygon, tolerance, true, crs, projection);
313
+ geometry.push(newPolygon);
314
+ }
315
+ } else if (type === 'GeometryCollection') {
316
+ for (var i$3 = 0, list$3 = geojson.geometry.geometries; i$3 < list$3.length; i$3 += 1) {
317
+ var singleGeometry = list$3[i$3];
318
+
319
+ convertFeature(
320
+ features,
321
+ {
322
+ id: id,
323
+ geometry: singleGeometry,
324
+ properties: geojson.properties,
325
+ },
326
+ options,
327
+ index
328
+ );
329
+ }
330
+ return;
331
+ } else {
332
+ throw new Error('Input data is not a valid GeoJSON object.');
333
+ }
334
+
335
+ features.push(createFeature(id, type, geometry, geojson.properties));
336
+ }
337
+
338
+ function convertPoint(coords, out, crs, projection) {
339
+ var coord = project(coords, crs, projection);
340
+ out.push(
341
+ coord[0],
342
+ coord[1],
343
+ 0
344
+ );
345
+ }
346
+
347
+ function convertLine(ring, out, tolerance, isPolygon, crs, projection) {
348
+ var x0, y0;
349
+ var size = 0;
350
+
351
+ for (var j = 0; j < ring.length; j++) {
352
+ var ref = project(ring[j], crs, projection);
353
+ var x = ref[0];
354
+ var y = ref[1];
355
+
356
+ out.push(x, y, 0);
357
+
358
+ if (j > 0) {
359
+ if (isPolygon) {
360
+ size += (x0 * y - x * y0) / 2; // area
361
+ } else {
362
+ size += Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)); // length
363
+ }
364
+ }
365
+ x0 = x;
366
+ y0 = y;
367
+ }
368
+
369
+ var last = out.length - 3;
370
+ out[2] = 1;
371
+ simplify(out, 0, last, tolerance);
372
+ out[last + 2] = 1;
373
+
374
+ out.size = Math.abs(size);
375
+ out.start = 0;
376
+ out.end = out.size;
377
+ }
378
+
379
+ function convertLines(rings, out, tolerance, isPolygon, crs, projection) {
380
+ for (var i = 0; i < rings.length; i++) {
381
+ var geom = [];
382
+ convertLine(rings[i], geom, tolerance, isPolygon, crs, projection);
383
+ out.push(geom);
384
+ }
385
+ }
386
+
387
+ /**
388
+ * 投影转换,将经纬度转为矩阵范围
389
+ * @param {Array} lnglatArr
390
+ * @param {*} crs
391
+ * @param {*} projection
392
+ * @returns 返回投影后的坐标,注意这里的坐标不是平面坐标值,而是矩阵值,即 [0 ~ 1]之间
393
+ */
394
+ function project(lnglatArr, crs, projection) {
395
+ var lng = lnglatArr[0];
396
+ var lat = lnglatArr[1];
397
+ var coords = [];
398
+ if (projection) {
399
+ coords = projection.project([lng, lat]);
400
+ } else if (crs === 'EPSG:3857') {
401
+ var x = lnglatArr[0];
402
+ var y = lnglatArr[1];
403
+ var transformX = x / 360 + 0.5;
404
+ var sin = Math.sin((y * Math.PI) / 180);
405
+ var transformY = 0.5 - (0.25 * Math.log((1 + sin) / (1 - sin))) / Math.PI;
406
+ coords = [transformX, transformY];
407
+ } else if (crs === 'EPSG:4326') {
408
+ var x$1 = lnglatArr[0];
409
+ var y$1 = lnglatArr[1];
410
+ var transformX$1 = x$1 / 360 + 0.5;
411
+ var transformY$1 = (90 - y$1) / 360;
412
+ coords = [transformX$1, transformY$1];
413
+ } else {
414
+ throw new Error('未预定义投影转换方法');
415
+ }
416
+ return coords
417
+ }
418
+
419
+ /* clip features between two vertical or horizontal axis-parallel lines:
420
+ * | |
421
+ * ___|___ | /
422
+ * / | \____|____/
423
+ * | |
424
+ *
425
+ * k1 and k2 are the line coordinates
426
+ * axis: 0 for x, 1 for y
427
+ * minAll and maxAll: minimum and maximum coordinate value for all features
428
+ */
429
+ function clip(features, scale, k1, k2, axis, minAll, maxAll, options) {
430
+ k1 /= scale;
431
+ k2 /= scale;
432
+
433
+ if (minAll >= k1 && maxAll < k2) { return features; } // trivial accept
434
+ else if (maxAll < k1 || minAll >= k2) { return null; } // trivial reject
435
+
436
+ var clipped = [];
437
+
438
+ for (var i$2 = 0, list$2 = features; i$2 < list$2.length; i$2 += 1) {
439
+ var feature = list$2[i$2];
440
+
441
+ var geometry = feature.geometry;
442
+ var type = feature.type;
443
+
444
+ var min = axis === 0 ? feature.minX : feature.minY;
445
+ var max = axis === 0 ? feature.maxX : feature.maxY;
446
+
447
+ if (min >= k1 && max < k2) { // trivial accept
448
+ clipped.push(feature);
449
+ continue;
450
+ } else if (max < k1 || min >= k2) { // trivial reject
451
+ continue;
452
+ }
453
+
454
+ var newGeometry = [];
455
+
456
+ if (type === 'Point' || type === 'MultiPoint') {
457
+ clipPoints(geometry, newGeometry, k1, k2, axis);
458
+
459
+ } else if (type === 'LineString') {
460
+ clipLine(geometry, newGeometry, k1, k2, axis, false, options.lineMetrics);
461
+
462
+ } else if (type === 'MultiLineString') {
463
+ clipLines(geometry, newGeometry, k1, k2, axis, false);
464
+
465
+ } else if (type === 'Polygon') {
466
+ clipLines(geometry, newGeometry, k1, k2, axis, true);
467
+
468
+ } else if (type === 'MultiPolygon') {
469
+ for (var i = 0, list = geometry; i < list.length; i += 1) {
470
+ var polygon = list[i];
471
+
472
+ var newPolygon = [];
473
+ clipLines(polygon, newPolygon, k1, k2, axis, true);
474
+ if (newPolygon.length) {
475
+ newGeometry.push(newPolygon);
476
+ }
477
+ }
478
+ }
479
+
480
+ if (newGeometry.length) {
481
+ if (options.lineMetrics && type === 'LineString') {
482
+ for (var i$1 = 0, list$1 = newGeometry; i$1 < list$1.length; i$1 += 1) {
483
+ var line = list$1[i$1];
484
+
485
+ clipped.push(createFeature(feature.id, type, line, feature.tags));
486
+ }
487
+ continue;
488
+ }
489
+
490
+ if (type === 'LineString' || type === 'MultiLineString') {
491
+ if (newGeometry.length === 1) {
492
+ type = 'LineString';
493
+ newGeometry = newGeometry[0];
494
+ } else {
495
+ type = 'MultiLineString';
496
+ }
497
+ }
498
+ if (type === 'Point' || type === 'MultiPoint') {
499
+ type = newGeometry.length === 3 ? 'Point' : 'MultiPoint';
500
+ }
501
+
502
+ clipped.push(createFeature(feature.id, type, newGeometry, feature.tags));
503
+ }
504
+ }
505
+
506
+ return clipped.length ? clipped : null;
507
+ }
508
+
509
+ function clipPoints(geom, newGeom, k1, k2, axis) {
510
+ for (var i = 0; i < geom.length; i += 3) {
511
+ var a = geom[i + axis];
512
+
513
+ if (a >= k1 && a <= k2) {
514
+ addPoint(newGeom, geom[i], geom[i + 1], geom[i + 2]);
515
+ }
516
+ }
517
+ }
518
+
519
+ function clipLine(geom, newGeom, k1, k2, axis, isPolygon, trackMetrics) {
520
+
521
+ var slice = newSlice(geom);
522
+ var intersect = axis === 0 ? intersectX : intersectY;
523
+ var len = geom.start;
524
+ var segLen, t;
525
+
526
+ for (var i = 0; i < geom.length - 3; i += 3) {
527
+ var ax$1 = geom[i];
528
+ var ay$1 = geom[i + 1];
529
+ var az$1 = geom[i + 2];
530
+ var bx = geom[i + 3];
531
+ var by = geom[i + 4];
532
+ var a$1 = axis === 0 ? ax$1 : ay$1;
533
+ var b = axis === 0 ? bx : by;
534
+ var exited = false;
535
+
536
+ if (trackMetrics) { segLen = Math.sqrt(Math.pow(ax$1 - bx, 2) + Math.pow(ay$1 - by, 2)); }
537
+
538
+ if (a$1 < k1) {
539
+ // ---|--> | (line enters the clip region from the left)
540
+ if (b > k1) {
541
+ t = intersect(slice, ax$1, ay$1, bx, by, k1);
542
+ if (trackMetrics) { slice.start = len + segLen * t; }
543
+ }
544
+ } else if (a$1 > k2) {
545
+ // | <--|--- (line enters the clip region from the right)
546
+ if (b < k2) {
547
+ t = intersect(slice, ax$1, ay$1, bx, by, k2);
548
+ if (trackMetrics) { slice.start = len + segLen * t; }
549
+ }
550
+ } else {
551
+ addPoint(slice, ax$1, ay$1, az$1);
552
+ }
553
+ if (b < k1 && a$1 >= k1) {
554
+ // <--|--- | or <--|-----|--- (line exits the clip region on the left)
555
+ t = intersect(slice, ax$1, ay$1, bx, by, k1);
556
+ exited = true;
557
+ }
558
+ if (b > k2 && a$1 <= k2) {
559
+ // | ---|--> or ---|-----|--> (line exits the clip region on the right)
560
+ t = intersect(slice, ax$1, ay$1, bx, by, k2);
561
+ exited = true;
562
+ }
563
+
564
+ if (!isPolygon && exited) {
565
+ if (trackMetrics) { slice.end = len + segLen * t; }
566
+ newGeom.push(slice);
567
+ slice = newSlice(geom);
568
+ }
569
+
570
+ if (trackMetrics) { len += segLen; }
571
+ }
572
+
573
+ // add the last point
574
+ var last = geom.length - 3;
575
+ var ax = geom[last];
576
+ var ay = geom[last + 1];
577
+ var az = geom[last + 2];
578
+ var a = axis === 0 ? ax : ay;
579
+ if (a >= k1 && a <= k2) { addPoint(slice, ax, ay, az); }
580
+
581
+ // close the polygon if its endpoints are not the same after clipping
582
+ last = slice.length - 3;
583
+ if (isPolygon && last >= 3 && (slice[last] !== slice[0] || slice[last + 1] !== slice[1])) {
584
+ addPoint(slice, slice[0], slice[1], slice[2]);
585
+ }
586
+
587
+ // add the final slice
588
+ if (slice.length) {
589
+ newGeom.push(slice);
590
+ }
591
+ }
592
+
593
+ function newSlice(line) {
594
+ var slice = [];
595
+ slice.size = line.size;
596
+ slice.start = line.start;
597
+ slice.end = line.end;
598
+ return slice;
599
+ }
600
+
601
+ function clipLines(geom, newGeom, k1, k2, axis, isPolygon) {
602
+ for (var i = 0, list = geom; i < list.length; i += 1) {
603
+ var line = list[i];
604
+
605
+ clipLine(line, newGeom, k1, k2, axis, isPolygon, false);
606
+ }
607
+ }
608
+
609
+ function addPoint(out, x, y, z) {
610
+ out.push(x, y, z);
611
+ }
612
+
613
+ function intersectX(out, ax, ay, bx, by, x) {
614
+ var t = (x - ax) / (bx - ax);
615
+ addPoint(out, x, ay + (by - ay) * t, 1);
616
+ return t;
617
+ }
618
+
619
+ function intersectY(out, ax, ay, bx, by, y) {
620
+ var t = (y - ay) / (by - ay);
621
+ addPoint(out, ax + (bx - ax) * t, y, 1);
622
+ return t;
623
+ }
624
+
625
+ // Transforms the coordinates of each feature in the given tile from
626
+ // mercator-projected space into (extent x extent) tile space.
627
+ function transformTile(tile, extent) {
628
+ if (tile.transformed) { return tile; }
629
+
630
+ var z2 = 1 << tile.z;
631
+ var tx = tile.x;
632
+ var ty = tile.y;
633
+
634
+ for (var i = 0, list = tile.features; i < list.length; i += 1) {
635
+ var feature = list[i];
636
+
637
+ var geom = feature.geometry;
638
+ var type = feature.type;
639
+
640
+ feature.geometry = [];
641
+
642
+ if (type === 1) {
643
+ for (var j = 0; j < geom.length; j += 2) {
644
+ feature.geometry.push(transformPoint(geom[j], geom[j + 1], extent, z2, tx, ty));
645
+ }
646
+ } else {
647
+ for (var j$1 = 0; j$1 < geom.length; j$1++) {
648
+ var ring = [];
649
+ for (var k = 0; k < geom[j$1].length; k += 2) {
650
+ ring.push(transformPoint(geom[j$1][k], geom[j$1][k + 1], extent, z2, tx, ty));
651
+ }
652
+ feature.geometry.push(ring);
653
+ }
654
+ }
655
+ }
656
+
657
+ tile.transformed = true;
658
+
659
+ return tile;
660
+ }
661
+
662
+ function transformPoint(x, y, extent, z2, tx, ty) {
663
+ return [
664
+ Math.round(extent * (x * z2 - tx)),
665
+ Math.round(extent * (y * z2 - ty))];
666
+ }
667
+
668
+ function createTile(features, z, tx, ty, options) {
669
+ var tolerance = z === options.maxZoom ? 0 : options.tolerance / ((1 << z) * options.extent);
670
+ var tile = {
671
+ features: [],
672
+ numPoints: 0,
673
+ numSimplified: 0,
674
+ numFeatures: features.length,
675
+ source: null,
676
+ x: tx,
677
+ y: ty,
678
+ z: z,
679
+ transformed: false,
680
+ minX: Infinity,
681
+ minY: Infinity,
682
+ maxX: -Infinity,
683
+ maxY: -Infinity
684
+ };
685
+ for (var i = 0, list = features; i < list.length; i += 1) {
686
+ var feature = list[i];
687
+
688
+ addFeature(tile, feature, tolerance, options);
689
+ }
690
+ return tile;
691
+ }
692
+
693
+ function addFeature(tile, feature, tolerance, options) {
694
+ var geom = feature.geometry;
695
+ var type = feature.type;
696
+ var simplified = [];
697
+
698
+ tile.minX = Math.min(tile.minX, feature.minX);
699
+ tile.minY = Math.min(tile.minY, feature.minY);
700
+ tile.maxX = Math.max(tile.maxX, feature.maxX);
701
+ tile.maxY = Math.max(tile.maxY, feature.maxY);
702
+
703
+ if (type === 'Point' || type === 'MultiPoint') {
704
+ for (var i = 0; i < geom.length; i += 3) {
705
+ simplified.push(geom[i], geom[i + 1]);
706
+ tile.numPoints++;
707
+ tile.numSimplified++;
708
+ }
709
+
710
+ } else if (type === 'LineString') {
711
+ addLine(simplified, geom, tile, tolerance, false, false);
712
+
713
+ } else if (type === 'MultiLineString' || type === 'Polygon') {
714
+ for (var i$1 = 0; i$1 < geom.length; i$1++) {
715
+ addLine(simplified, geom[i$1], tile, tolerance, type === 'Polygon', i$1 === 0);
716
+ }
717
+
718
+ } else if (type === 'MultiPolygon') {
719
+
720
+ for (var k = 0; k < geom.length; k++) {
721
+ var polygon = geom[k];
722
+ for (var i$2 = 0; i$2 < polygon.length; i$2++) {
723
+ addLine(simplified, polygon[i$2], tile, tolerance, true, i$2 === 0);
724
+ }
725
+ }
726
+ }
727
+
728
+ if (simplified.length) {
729
+ var tags = feature.tags || null;
730
+
731
+ if (type === 'LineString' && options.lineMetrics) {
732
+ tags = {};
733
+ for (var key in feature.tags) { tags[key] = feature.tags[key]; }
734
+ tags['mapbox_clip_start'] = geom.start / geom.size;
735
+ tags['mapbox_clip_end'] = geom.end / geom.size;
736
+ }
737
+
738
+ var tileFeature = {
739
+ geometry: simplified,
740
+ type: type === 'Polygon' || type === 'MultiPolygon' ? 3 :
741
+ (type === 'LineString' || type === 'MultiLineString' ? 2 : 1),
742
+ tags: tags
743
+ };
744
+ if (feature.id !== null) {
745
+ tileFeature.id = feature.id;
746
+ }
747
+ tile.features.push(tileFeature);
748
+ }
749
+ }
750
+
751
+ function addLine(result, geom, tile, tolerance, isPolygon, isOuter) {
752
+ var sqTolerance = tolerance * tolerance;
753
+
754
+ if (tolerance > 0 && (geom.size < (isPolygon ? sqTolerance : tolerance))) {
755
+ tile.numPoints += geom.length / 3;
756
+ return;
757
+ }
758
+
759
+ var ring = [];
760
+
761
+ for (var i = 0; i < geom.length; i += 3) {
762
+ if (tolerance === 0 || geom[i + 2] > sqTolerance) {
763
+ tile.numSimplified++;
764
+ ring.push(geom[i], geom[i + 1]);
765
+ }
766
+ tile.numPoints++;
767
+ }
768
+
769
+ if (isPolygon) { rewind(ring, isOuter); }
770
+
771
+ result.push(ring);
772
+ }
773
+
774
+ function rewind(ring, clockwise) {
775
+ var area = 0;
776
+ for (var i = 0, len = ring.length, j = len - 2; i < len; j = i, i += 2) {
777
+ area += (ring[i] - ring[j]) * (ring[i + 1] + ring[j + 1]);
778
+ }
779
+ if (area > 0 === clockwise) {
780
+ for (var i$1 = 0, len$1 = ring.length; i$1 < len$1 / 2; i$1 += 2) {
781
+ var x = ring[i$1];
782
+ var y = ring[i$1 + 1];
783
+ ring[i$1] = ring[len$1 - 2 - i$1];
784
+ ring[i$1 + 1] = ring[len$1 - 1 - i$1];
785
+ ring[len$1 - 2 - i$1] = x;
786
+ ring[len$1 - 1 - i$1] = y;
787
+ }
788
+ }
789
+ }
790
+
791
+ var defaultOptions = {
792
+ crs: 'EPSG:3857', // 默认的投影坐标系 3857 4326走经纬度裁图
793
+ maxZoom: 14, // max zoom to preserve detail on
794
+ indexMaxZoom: 5, // max zoom in the tile index
795
+ indexMaxPoints: 100000, // max number of points per tile in the tile index
796
+ tolerance: 3, // simplification tolerance (higher means simpler)
797
+ extent: 4096, // tile extent
798
+ buffer: 64, // tile buffer on each side
799
+ lineMetrics: false, // whether to calculate line metrics
800
+ promoteId: null, // name of a feature property to be promoted to feature.id
801
+ generateId: false, // whether to generate feature ids. Cannot be used with promoteId
802
+ debug: 0, // logging level (0, 1 or 2)
803
+ isWrapData: false // 表示是否对geojson数据进行分段处理。如果为true,则会对超出范围的数据重新计算,保证其在世界范围内显示。如果为false,则不处理。
804
+ };
805
+
806
+ var GeoJSONVT = function GeoJSONVT(data, options) {
807
+ options = this.options = extend(Object.create(defaultOptions), options);
808
+
809
+ var debug = options.debug;
810
+
811
+ if (debug) { console.time('preprocess data'); }
812
+
813
+ if (options.maxZoom < 0 || options.maxZoom > 24) { throw new Error('maxZoom should be in the 0-24 range'); }
814
+ if (options.promoteId && options.generateId) { throw new Error('promoteId and generateId cannot be used together.'); }
815
+
816
+ // projects and adds simplification info
817
+ var features = convert(data, options);
818
+ this.features = features;
819
+ // tiles and tileCoords are part of the public API
820
+ this.tiles = {};
821
+ this.tileCoords = [];
822
+
823
+ if (debug) {
824
+ console.timeEnd('preprocess data');
825
+ console.log('index: maxZoom: %d, maxPoints: %d', options.indexMaxZoom, options.indexMaxPoints);
826
+ console.time('generate tiles');
827
+ this.stats = {};
828
+ this.total = 0;
829
+ }
830
+
831
+ // wraps features (ie extreme west and extreme east)
832
+ // 移除worldCopies操作,目前按屏幕范围取瓦片范围
833
+ // if (options.isWrapData) {
834
+ // features = wrap(features, options);
835
+ // }
836
+ // start slicing from the top tile down
837
+ // if (features.length) this.splitTile(features, 0, 0, 0);
838
+
839
+ // if (debug) {
840
+ // if (features.length) console.log('features: %d, points: %d', this.tiles[0].numFeatures, this.tiles[0].numPoints);
841
+ // console.timeEnd('generate tiles');
842
+ // console.log('tiles generated:', this.total, JSON.stringify(this.stats));
843
+ // }
844
+ };
845
+
846
+ // splits features from a parent tile to sub-tiles.
847
+ // z, x, and y are the coordinates of the parent tile
848
+ // cz, cx, and cy are the coordinates of the target tile
849
+ //
850
+ // If no target tile is specified, splitting stops when we reach the maximum
851
+ // zoom or the number of points is low as specified in the options.
852
+ GeoJSONVT.prototype.splitTile = function splitTile (features, z, x, y, cz, cx, cy) {
853
+
854
+ var stack = [features, z, x, y];
855
+ var options = this.options;
856
+ var debug = options.debug;
857
+
858
+ // avoid recursion by using a processing queue
859
+ while (stack.length) {
860
+ y = stack.pop();
861
+ x = stack.pop();
862
+ z = stack.pop();
863
+ features = stack.pop();
864
+
865
+ var z2 = 1 << z;
866
+ var id = toID(z, x, y);
867
+ var tile = this.tiles[id];
868
+
869
+ if (!tile) {
870
+ if (debug > 1) { console.time('creation'); }
871
+
872
+ tile = this.tiles[id] = createTile(features, z, x, y, options);
873
+ this.tileCoords.push({z: z, x: x, y: y});
874
+
875
+ if (debug) {
876
+ if (debug > 1) {
877
+ console.log('tile z%d-%d-%d (features: %d, points: %d, simplified: %d)',
878
+ z, x, y, tile.numFeatures, tile.numPoints, tile.numSimplified);
879
+ console.timeEnd('creation');
880
+ }
881
+ var key = "z" + z;
882
+ this.stats[key] = (this.stats[key] || 0) + 1;
883
+ this.total++;
884
+ }
885
+ }
886
+
887
+ // save reference to original geometry in tile so that we can drill down later if we stop now
888
+ tile.source = features;
889
+
890
+ // if it's the first-pass tiling
891
+ if (cz == null) {
892
+ // stop tiling if we reached max zoom, or if the tile is too simple
893
+ if (z === options.indexMaxZoom || tile.numPoints <= options.indexMaxPoints) { continue; }
894
+ // if a drilldown to a specific tile
895
+ } else if (z === options.maxZoom || z === cz) {
896
+ // stop tiling if we reached base zoom or our target tile zoom
897
+ continue;
898
+ } else if (cz != null) {
899
+ // stop tiling if it's not an ancestor of the target tile
900
+ var zoomSteps = cz - z;
901
+ if (x !== cx >> zoomSteps || y !== cy >> zoomSteps) { continue; }
902
+ }
903
+
904
+ // if we slice further down, no need to keep source geometry
905
+ tile.source = null;
906
+
907
+ if (features.length === 0) { continue; }
908
+
909
+ if (debug > 1) { console.time('clipping'); }
910
+
911
+ // values we'll use for clipping
912
+ var k1 = 0.5 * options.buffer / options.extent;
913
+ var k2 = 0.5 - k1;
914
+ var k3 = 0.5 + k1;
915
+ var k4 = 1 + k1;
916
+
917
+ var tl = null;
918
+ var bl = null;
919
+ var tr = null;
920
+ var br = null;
921
+
922
+ var left = clip(features, z2, x - k1, x + k3, 0, tile.minX, tile.maxX, options);
923
+ var right = clip(features, z2, x + k2, x + k4, 0, tile.minX, tile.maxX, options);
924
+ features = null;
925
+
926
+ if (left) {
927
+ tl = clip(left, z2, y - k1, y + k3, 1, tile.minY, tile.maxY, options);
928
+ bl = clip(left, z2, y + k2, y + k4, 1, tile.minY, tile.maxY, options);
929
+ left = null;
930
+ }
931
+
932
+ if (right) {
933
+ tr = clip(right, z2, y - k1, y + k3, 1, tile.minY, tile.maxY, options);
934
+ br = clip(right, z2, y + k2, y + k4, 1, tile.minY, tile.maxY, options);
935
+ right = null;
936
+ }
937
+
938
+ if (debug > 1) { console.timeEnd('clipping'); }
939
+
940
+ stack.push(tl || [], z + 1, x * 2, y * 2);
941
+ stack.push(bl || [], z + 1, x * 2, y * 2 + 1);
942
+ stack.push(tr || [], z + 1, x * 2 + 1, y * 2);
943
+ stack.push(br || [], z + 1, x * 2 + 1, y * 2 + 1);
944
+ }
945
+ };
946
+
947
+ /**
948
+ * @description: 创建0级瓦片
949
+ * @param {*} x0
950
+ * @param {*} y0
951
+ * @param {*} options
952
+ * @return {*}
953
+ */
954
+ GeoJSONVT.prototype._createZeroTile = function _createZeroTile (x0, y0, options) {
955
+ var z0 = 0;
956
+ var k1 = 0.5 * options.buffer / options.extent;
957
+ var scale = 1 >> z0;
958
+ var idZero = toID(z0, x0, y0);
959
+
960
+ var xMin = x0 * scale;
961
+ var xMax = (x0 + 1) * scale;
962
+ var yMin = y0 * scale;
963
+ var yMax = (y0 + 1) * scale;
964
+
965
+ var tile;
966
+ // 横向裁剪
967
+ var clipWidth = clip(this.features, scale, xMin - k1, xMax + k1, 0, -Infinity, Infinity, options);
968
+ if (clipWidth) {
969
+ var clipFeatures = clip(
970
+ clipWidth,
971
+ scale,
972
+ yMin - k1,
973
+ yMax + k1,
974
+ 1,
975
+ -Infinity,
976
+ Infinity,
977
+ options
978
+ );
979
+
980
+ if (clipFeatures) {
981
+ this.tiles[idZero] = transformTile(createTile(clipFeatures, z0, x0, y0, options), options.extent);
982
+ this.tiles[idZero].source = clipFeatures;
983
+ tile = this.tiles[idZero];
984
+ }
985
+
986
+ }
987
+ return tile;
988
+ };
989
+
990
+ GeoJSONVT.prototype.getTile = function getTile (z, x, y) {
991
+ z = +z;
992
+ x = +x;
993
+ y = +y;
994
+
995
+ var options = this.options;
996
+ var extent = options.extent;
997
+ var debug = options.debug;
998
+
999
+ if (z < 0 || z > 24) { return null; }
1000
+
1001
+ // 去除warp操作,目前坐标系是无限延伸的,不能warp范围
1002
+ // const z2 = 1 << z;
1003
+ // x = (x + z2) & (z2 - 1); // wrap tile x coordinate
1004
+ var id = toID(z, x, y);
1005
+ if (this.tiles[id]) {
1006
+ return transformTile(this.tiles[id], extent);
1007
+ } else if (z === 0) {
1008
+ return this._createZeroTile(x, y, options);
1009
+ }
1010
+
1011
+ if (debug > 1) { console.log('drilling down to z%d-%d-%d', z, x, y); }
1012
+
1013
+ var z0 = z;
1014
+ var x0 = x;
1015
+ var y0 = y;
1016
+ var parent;
1017
+
1018
+ while (!parent && z0 > 0) {
1019
+ z0--;
1020
+ x0 = x0 >> 1;
1021
+ y0 = y0 >> 1;
1022
+ var idZero = toID(z0, x0, y0);
1023
+ parent = this.tiles[idZero];
1024
+ if (z0 === 0 && !parent) {
1025
+ parent = this._createZeroTile(x0, y0, options);
1026
+ }
1027
+ }
1028
+ if (!parent || !parent.source) { return null; }
1029
+
1030
+ // if we found a parent tile containing the original geometry, we can drill down from it
1031
+ if (debug > 1) {
1032
+ console.log('found parent tile z%d-%d-%d', z0, x0, y0);
1033
+ console.time('drilling down');
1034
+ }
1035
+ this.splitTile(parent.source, z0, x0, y0, z, x, y);
1036
+ if (debug > 1) { console.timeEnd('drilling down'); }
1037
+
1038
+ return this.tiles[id] ? transformTile(this.tiles[id], extent) : null;
1039
+ };
1040
+
1041
+ /**
1042
+ * @description: 瓦片编码方式(此插件默认的编码方式在0级多张瓦片时会导致瓦片重复)
1043
+ * @param {*} z
1044
+ * @param {*} x
1045
+ * @param {*} y
1046
+ * @return {*}
1047
+ */
1048
+ function toID(z, x, y) {
1049
+ return x.toString(36) + y.toString(36) + z.toString(36);
1050
+ }
1051
+
1052
+ function extend(dest, src) {
1053
+ for (var i in src) { dest[i] = src[i]; }
1054
+ return dest;
1055
+ }
1056
+
1057
+ function geojsonvt(data, options) {
1058
+ return new GeoJSONVT(data, options);
1059
+ }
1060
+
1061
+ return geojsonvt;
1062
+
1063
+ }));
package/geojson-vt.js ADDED
@@ -0,0 +1 @@
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("proj4")):"function"==typeof define&&define.amd?define(["proj4"],e):(t="undefined"!=typeof globalThis?globalThis:t||self).geojsonvt=e(t.proj4)}(this,(function(t){"use strict";function e(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=e(t),i=function(t,e,i,r,o){if(this.bounds=i,this.width=r,this.height=o,!e)throw new Error("自定义参考系必须指定参数系信息def");if(!n.default)throw new Error("proj4对象不存在,自定义投影必须引入proj4对象");var a=this._isProj4Obj(t);this._proj=a?t:this._projFromCodeDef(t,e)};function r(t,e,n,i,r,o){var a=r-n,l=o-i;if(0!==a||0!==l){var s=((t-n)*a+(e-i)*l)/(a*a+l*l);s>1?(n=r,i=o):s>0&&(n+=a*s,i+=l*s)}return(a=t-n)*a+(l=e-i)*l}function o(t,e,n,i){var r={id:null==t?null:t,type:e,geometry:n,tags:i,minX:1/0,minY:1/0,maxX:-1/0,maxY:-1/0};if("Point"===e||"MultiPoint"===e||"LineString"===e)a(r,n);else if("Polygon"===e)a(r,n[0]);else if("MultiLineString"===e)for(var o=0,l=n;o<l.length;o+=1){a(r,l[o])}else if("MultiPolygon"===e)for(var s=0,u=n;s<u.length;s+=1){a(r,u[s][0])}return r}function a(t,e){for(var n=0;n<e.length;n+=3)t.minX=Math.min(t.minX,e[n]),t.minY=Math.min(t.minY,e[n+1]),t.maxX=Math.max(t.maxX,e[n]),t.maxY=Math.max(t.maxY,e[n+1])}function l(t,e,n,r){if(e.geometry){var a,h=n.crs,m=n.projectionParams;if(m&&m.def){var g=m.code,p=m.def,d=m.bounds,c=m.width,v=m.height;a=new i(g,p,d,c,v)}var x=e.geometry.coordinates,y=e.geometry.type,M=Math.pow(n.tolerance/((1<<n.maxZoom)*n.extent),2),P=[],w=e.id;if(n.promoteId?w=e.properties[n.promoteId]:n.generateId&&(w=r||0),"Point"===y)s(x,P,h,a);else if("MultiPoint"===y)for(var S=0,b=x;S<b.length;S+=1){s(b[S],P,h,a)}else if("LineString"===y)u(x,P,M,!1,h,a);else if("MultiLineString"===y){if(n.lineMetrics){for(var j=0,Y=x;j<Y.length;j+=1){u(Y[j],P=[],M,!1,h,a),t.push(o(w,"LineString",P,e.properties))}return}f(x,P,M,!1,h,a)}else if("Polygon"===y)f(x,P,M,!0,h,a);else{if("MultiPolygon"!==y){if("GeometryCollection"===y){for(var X=0,L=e.geometry.geometries;X<L.length;X+=1){l(t,{id:w,geometry:L[X],properties:e.properties},n,r)}return}throw new Error("Input data is not a valid GeoJSON object.")}for(var z=0,E=x;z<E.length;z+=1){var Z=[];f(E[z],Z,M,!0,h,a),P.push(Z)}}t.push(o(w,y,P,e.properties))}}function s(t,e,n,i){var r=h(t,n,i);e.push(r[0],r[1],0)}function u(t,e,n,i,o,a){for(var l,s,u=0,f=0;f<t.length;f++){var m=h(t[f],o,a),g=m[0],p=m[1];e.push(g,p,0),f>0&&(u+=i?(l*p-g*s)/2:Math.sqrt(Math.pow(g-l,2)+Math.pow(p-s,2))),l=g,s=p}var d=e.length-3;e[2]=1,function t(e,n,i,o){for(var a,l=o,s=i-n>>1,u=i-n,f=e[n],h=e[n+1],m=e[i],g=e[i+1],p=n+3;p<i;p+=3){var d=r(e[p],e[p+1],f,h,m,g);if(d>l)a=p,l=d;else if(d===l){var c=Math.abs(p-s);c<u&&(a=p,u=c)}}l>o&&(a-n>3&&t(e,n,a,o),e[a+2]=l,i-a>3&&t(e,a,i,o))}(e,0,d,n),e[d+2]=1,e.size=Math.abs(u),e.start=0,e.end=e.size}function f(t,e,n,i,r,o){for(var a=0;a<t.length;a++){var l=[];u(t[a],l,n,i,r,o),e.push(l)}}function h(t,e,n){var i=t[0],r=t[1],o=[];if(n)o=n.project([i,r]);else if("EPSG:3857"===e){var a=t[0],l=t[1],s=a/360+.5,u=Math.sin(l*Math.PI/180);o=[s,.5-.25*Math.log((1+u)/(1-u))/Math.PI]}else{if("EPSG:4326"!==e)throw new Error("未预定义投影转换方法");o=[t[0]/360+.5,(90-t[1])/360]}return o}function m(t,e,n,i,r,a,l,s){if(i/=e,a>=(n/=e)&&l<i)return t;if(l<n||a>=i)return null;for(var u=[],f=0,h=t;f<h.length;f+=1){var m=h[f],d=m.geometry,v=m.type,x=0===r?m.minX:m.minY,y=0===r?m.maxX:m.maxY;if(x>=n&&y<i)u.push(m);else if(!(y<n||x>=i)){var M=[];if("Point"===v||"MultiPoint"===v)g(d,M,n,i,r);else if("LineString"===v)p(d,M,n,i,r,!1,s.lineMetrics);else if("MultiLineString"===v)c(d,M,n,i,r,!1);else if("Polygon"===v)c(d,M,n,i,r,!0);else if("MultiPolygon"===v)for(var P=0,w=d;P<w.length;P+=1){var S=[];c(w[P],S,n,i,r,!0),S.length&&M.push(S)}if(M.length){if(s.lineMetrics&&"LineString"===v){for(var b=0,j=M;b<j.length;b+=1){var Y=j[b];u.push(o(m.id,v,Y,m.tags))}continue}"LineString"!==v&&"MultiLineString"!==v||(1===M.length?(v="LineString",M=M[0]):v="MultiLineString"),"Point"!==v&&"MultiPoint"!==v||(v=3===M.length?"Point":"MultiPoint"),u.push(o(m.id,v,M,m.tags))}}}return u.length?u:null}function g(t,e,n,i,r){for(var o=0;o<t.length;o+=3){var a=t[o+r];a>=n&&a<=i&&v(e,t[o],t[o+1],t[o+2])}}function p(t,e,n,i,r,o,a){for(var l,s,u=d(t),f=0===r?x:y,h=t.start,m=0;m<t.length-3;m+=3){var g=t[m],p=t[m+1],c=t[m+2],M=t[m+3],P=t[m+4],w=0===r?g:p,S=0===r?M:P,b=!1;a&&(l=Math.sqrt(Math.pow(g-M,2)+Math.pow(p-P,2))),w<n?S>n&&(s=f(u,g,p,M,P,n),a&&(u.start=h+l*s)):w>i?S<i&&(s=f(u,g,p,M,P,i),a&&(u.start=h+l*s)):v(u,g,p,c),S<n&&w>=n&&(s=f(u,g,p,M,P,n),b=!0),S>i&&w<=i&&(s=f(u,g,p,M,P,i),b=!0),!o&&b&&(a&&(u.end=h+l*s),e.push(u),u=d(t)),a&&(h+=l)}var j=t.length-3,Y=t[j],X=t[j+1],L=t[j+2],z=0===r?Y:X;z>=n&&z<=i&&v(u,Y,X,L),j=u.length-3,o&&j>=3&&(u[j]!==u[0]||u[j+1]!==u[1])&&v(u,u[0],u[1],u[2]),u.length&&e.push(u)}function d(t){var e=[];return e.size=t.size,e.start=t.start,e.end=t.end,e}function c(t,e,n,i,r,o){for(var a=0,l=t;a<l.length;a+=1){p(l[a],e,n,i,r,o,!1)}}function v(t,e,n,i){t.push(e,n,i)}function x(t,e,n,i,r,o){var a=(o-e)/(i-e);return v(t,o,n+(r-n)*a,1),a}function y(t,e,n,i,r,o){var a=(o-n)/(r-n);return v(t,e+(i-e)*a,o,1),a}function M(t,e){if(t.transformed)return t;for(var n=1<<t.z,i=t.x,r=t.y,o=0,a=t.features;o<a.length;o+=1){var l=a[o],s=l.geometry,u=l.type;if(l.geometry=[],1===u)for(var f=0;f<s.length;f+=2)l.geometry.push(P(s[f],s[f+1],e,n,i,r));else for(var h=0;h<s.length;h++){for(var m=[],g=0;g<s[h].length;g+=2)m.push(P(s[h][g],s[h][g+1],e,n,i,r));l.geometry.push(m)}}return t.transformed=!0,t}function P(t,e,n,i,r,o){return[Math.round(n*(t*i-r)),Math.round(n*(e*i-o))]}function w(t,e,n,i,r){for(var o=e===r.maxZoom?0:r.tolerance/((1<<e)*r.extent),a={features:[],numPoints:0,numSimplified:0,numFeatures:t.length,source:null,x:n,y:i,z:e,transformed:!1,minX:1/0,minY:1/0,maxX:-1/0,maxY:-1/0},l=0,s=t;l<s.length;l+=1){S(a,s[l],o,r)}return a}function S(t,e,n,i){var r=e.geometry,o=e.type,a=[];if(t.minX=Math.min(t.minX,e.minX),t.minY=Math.min(t.minY,e.minY),t.maxX=Math.max(t.maxX,e.maxX),t.maxY=Math.max(t.maxY,e.maxY),"Point"===o||"MultiPoint"===o)for(var l=0;l<r.length;l+=3)a.push(r[l],r[l+1]),t.numPoints++,t.numSimplified++;else if("LineString"===o)b(a,r,t,n,!1,!1);else if("MultiLineString"===o||"Polygon"===o)for(var s=0;s<r.length;s++)b(a,r[s],t,n,"Polygon"===o,0===s);else if("MultiPolygon"===o)for(var u=0;u<r.length;u++)for(var f=r[u],h=0;h<f.length;h++)b(a,f[h],t,n,!0,0===h);if(a.length){var m=e.tags||null;if("LineString"===o&&i.lineMetrics){for(var g in m={},e.tags)m[g]=e.tags[g];m.mapbox_clip_start=r.start/r.size,m.mapbox_clip_end=r.end/r.size}var p={geometry:a,type:"Polygon"===o||"MultiPolygon"===o?3:"LineString"===o||"MultiLineString"===o?2:1,tags:m};null!==e.id&&(p.id=e.id),t.features.push(p)}}function b(t,e,n,i,r,o){var a=i*i;if(i>0&&e.size<(r?a:i))n.numPoints+=e.length/3;else{for(var l=[],s=0;s<e.length;s+=3)(0===i||e[s+2]>a)&&(n.numSimplified++,l.push(e[s],e[s+1])),n.numPoints++;r&&function(t,e){for(var n=0,i=0,r=t.length,o=r-2;i<r;o=i,i+=2)n+=(t[i]-t[o])*(t[i+1]+t[o+1]);if(n>0===e)for(var a=0,l=t.length;a<l/2;a+=2){var s=t[a],u=t[a+1];t[a]=t[l-2-a],t[a+1]=t[l-1-a],t[l-2-a]=s,t[l-1-a]=u}}(l,o),t.push(l)}}i.prototype.project=function(t){var e,n;Array.isArray(t)?(e=t[0],n=t[1]):(e=t.lng,n=t.lat);var i=this._proj.forward([e,n]),r=this.bounds;return[(i[0]-r[0])/this.width,(r[3]-i[1])/this.height]},i.prototype.unproject=function(t){var e,n;return Array.isArray(t)?(e=t[0],n=t[1]):(e=t.x,n=t.y),this._proj.inverse([e,n])},i.prototype._projFromCodeDef=function(t,e){if(e)n.default.defs(t,e);else if(void 0===n.default.defs[t]){var i=t.split(":");if(i.length>3&&(t=i[i.length-3]+":"+i[i.length-1]),void 0===n.default.defs[t])throw new Error("No projection definition for code "+t)}return n.default(t)},i.prototype._isProj4Obj=function(t){return void 0!==t.inverse&&void 0!==t.forward};var j={crs:"EPSG:3857",maxZoom:14,indexMaxZoom:5,indexMaxPoints:1e5,tolerance:3,extent:4096,buffer:64,lineMetrics:!1,promoteId:null,generateId:!1,debug:0,isWrapData:!1},Y=function(t,e){var n=(e=this.options=function(t,e){for(var n in e)t[n]=e[n];return t}(Object.create(j),e)).debug;if(n&&console.time("preprocess data"),e.maxZoom<0||e.maxZoom>24)throw new Error("maxZoom should be in the 0-24 range");if(e.promoteId&&e.generateId)throw new Error("promoteId and generateId cannot be used together.");var i=function(t,e){var n=[];if("FeatureCollection"===t.type)for(var i=0;i<t.features.length;i++)l(n,t.features[i],e,i);else"Feature"===t.type?l(n,t,e):l(n,{geometry:t},e);return n}(t,e);this.features=i,this.tiles={},this.tileCoords=[],n&&(console.timeEnd("preprocess data"),console.log("index: maxZoom: %d, maxPoints: %d",e.indexMaxZoom,e.indexMaxPoints),console.time("generate tiles"),this.stats={},this.total=0)};function X(t,e,n){return e.toString(36)+n.toString(36)+t.toString(36)}return Y.prototype.splitTile=function(t,e,n,i,r,o,a){for(var l=[t,e,n,i],s=this.options,u=s.debug;l.length;){i=l.pop(),n=l.pop(),e=l.pop(),t=l.pop();var f=1<<e,h=X(e,n,i),g=this.tiles[h];if(!g&&(u>1&&console.time("creation"),g=this.tiles[h]=w(t,e,n,i,s),this.tileCoords.push({z:e,x:n,y:i}),u)){u>1&&(console.log("tile z%d-%d-%d (features: %d, points: %d, simplified: %d)",e,n,i,g.numFeatures,g.numPoints,g.numSimplified),console.timeEnd("creation"));var p="z"+e;this.stats[p]=(this.stats[p]||0)+1,this.total++}if(g.source=t,null==r){if(e===s.indexMaxZoom||g.numPoints<=s.indexMaxPoints)continue}else{if(e===s.maxZoom||e===r)continue;if(null!=r){var d=r-e;if(n!==o>>d||i!==a>>d)continue}}if(g.source=null,0!==t.length){u>1&&console.time("clipping");var c=.5*s.buffer/s.extent,v=.5-c,x=.5+c,y=1+c,M=null,P=null,S=null,b=null,j=m(t,f,n-c,n+x,0,g.minX,g.maxX,s),Y=m(t,f,n+v,n+y,0,g.minX,g.maxX,s);t=null,j&&(M=m(j,f,i-c,i+x,1,g.minY,g.maxY,s),P=m(j,f,i+v,i+y,1,g.minY,g.maxY,s),j=null),Y&&(S=m(Y,f,i-c,i+x,1,g.minY,g.maxY,s),b=m(Y,f,i+v,i+y,1,g.minY,g.maxY,s),Y=null),u>1&&console.timeEnd("clipping"),l.push(M||[],e+1,2*n,2*i),l.push(P||[],e+1,2*n,2*i+1),l.push(S||[],e+1,2*n+1,2*i),l.push(b||[],e+1,2*n+1,2*i+1)}}},Y.prototype._createZeroTile=function(t,e,n){var i,r=.5*n.buffer/n.extent,o=X(0,t,e),a=1*t,l=1*(t+1),s=1*e,u=1*(e+1),f=m(this.features,1,a-r,l+r,0,-1/0,1/0,n);if(f){var h=m(f,1,s-r,u+r,1,-1/0,1/0,n);h&&(this.tiles[o]=M(w(h,0,t,e,n),n.extent),this.tiles[o].source=h,i=this.tiles[o])}return i},Y.prototype.getTile=function(t,e,n){t=+t,e=+e,n=+n;var i=this.options,r=i.extent,o=i.debug;if(t<0||t>24)return null;var a=X(t,e,n);if(this.tiles[a])return M(this.tiles[a],r);if(0===t)return this._createZeroTile(e,n,i);o>1&&console.log("drilling down to z%d-%d-%d",t,e,n);for(var l,s=t,u=e,f=n;!l&&s>0;){var h=X(--s,u>>=1,f>>=1);l=this.tiles[h],0!==s||l||(l=this._createZeroTile(u,f,i))}return l&&l.source?(o>1&&(console.log("found parent tile z%d-%d-%d",s,u,f),console.time("drilling down")),this.splitTile(l.source,s,u,f,t,e,n),o>1&&console.timeEnd("drilling down"),this.tiles[a]?M(this.tiles[a],r):null):null},function(t,e){return new Y(t,e)}}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mapgis/geojson-vt",
3
- "version":"16.8.0",
3
+ "version": "17.2.0",
4
4
  "description": "Slice GeoJSON data into vector tiles efficiently",
5
5
  "homepage": "https://github.com/parndeedlit/geojson-vt",
6
6
  "keywords": [
package/src/convert.js CHANGED
@@ -124,7 +124,6 @@ function convertFeature(features, geojson, options, index) {
124
124
  }
125
125
 
126
126
  function convertPoint(coords, out, crs, projection) {
127
- console.log('convertPoint: ', convertPoint);
128
127
  const coord = project(coords, crs, projection)
129
128
  out.push(
130
129
  coord[0],
@@ -183,19 +182,16 @@ function project(lnglatArr, crs, projection) {
183
182
  let coords = [];
184
183
  if (projection) {
185
184
  coords = projection.project([lng, lat]);
186
- coords[1] = coords[1] < 0 ? 0 : coords[1] > 1 ? 1 : coords[1];
187
185
  } else if (crs === 'EPSG:3857') {
188
186
  const [x, y] = lnglatArr
189
187
  const transformX = x / 360 + 0.5;
190
188
  const sin = Math.sin((y * Math.PI) / 180);
191
- let transformY = 0.5 - (0.25 * Math.log((1 + sin) / (1 - sin))) / Math.PI;
192
- transformY = transformY < 0 ? 0 : transformY > 1 ? 1 : transformY;
189
+ const transformY = 0.5 - (0.25 * Math.log((1 + sin) / (1 - sin))) / Math.PI;
193
190
  coords = [transformX, transformY];
194
191
  } else if (crs === 'EPSG:4326') {
195
192
  const [x, y] = lnglatArr
196
193
  const transformX = x / 360 + 0.5;
197
- let transformY = (90 - y) / 360;
198
- transformY = transformY < 0 ? 0 : transformY > 1 ? 1 : transformY;
194
+ const transformY = (90 - y) / 360;
199
195
  coords = [transformX, transformY]
200
196
  } else {
201
197
  throw new Error('未预定义投影转换方法');
package/src/index.js CHANGED
@@ -16,7 +16,8 @@ const defaultOptions = {
16
16
  lineMetrics: false, // whether to calculate line metrics
17
17
  promoteId: null, // name of a feature property to be promoted to feature.id
18
18
  generateId: false, // whether to generate feature ids. Cannot be used with promoteId
19
- debug: 0 // logging level (0, 1 or 2)
19
+ debug: 0, // logging level (0, 1 or 2)
20
+ isWrapData: false // 表示是否对geojson数据进行分段处理。如果为true,则会对超出范围的数据重新计算,保证其在世界范围内显示。如果为false,则不处理。
20
21
  };
21
22
 
22
23
  class GeoJSONVT {
@@ -31,8 +32,8 @@ class GeoJSONVT {
31
32
  if (options.promoteId && options.generateId) throw new Error('promoteId and generateId cannot be used together.');
32
33
 
33
34
  // projects and adds simplification info
34
- let features = convert(data, options);
35
-
35
+ const features = convert(data, options);
36
+ this.features = features;
36
37
  // tiles and tileCoords are part of the public API
37
38
  this.tiles = {};
38
39
  this.tileCoords = [];
@@ -46,16 +47,18 @@ class GeoJSONVT {
46
47
  }
47
48
 
48
49
  // wraps features (ie extreme west and extreme east)
49
- features = wrap(features, options);
50
-
50
+ // 移除worldCopies操作,目前按屏幕范围取瓦片范围
51
+ // if (options.isWrapData) {
52
+ // features = wrap(features, options);
53
+ // }
51
54
  // start slicing from the top tile down
52
- if (features.length) this.splitTile(features, 0, 0, 0);
55
+ // if (features.length) this.splitTile(features, 0, 0, 0);
53
56
 
54
- if (debug) {
55
- if (features.length) console.log('features: %d, points: %d', this.tiles[0].numFeatures, this.tiles[0].numPoints);
56
- console.timeEnd('generate tiles');
57
- console.log('tiles generated:', this.total, JSON.stringify(this.stats));
58
- }
57
+ // if (debug) {
58
+ // if (features.length) console.log('features: %d, points: %d', this.tiles[0].numFeatures, this.tiles[0].numPoints);
59
+ // console.timeEnd('generate tiles');
60
+ // console.log('tiles generated:', this.total, JSON.stringify(this.stats));
61
+ // }
59
62
  }
60
63
 
61
64
  // splits features from a parent tile to sub-tiles.
@@ -159,6 +162,49 @@ class GeoJSONVT {
159
162
  }
160
163
  }
161
164
 
165
+ /**
166
+ * @description: 创建0级瓦片
167
+ * @param {*} x0
168
+ * @param {*} y0
169
+ * @param {*} options
170
+ * @return {*}
171
+ */
172
+ _createZeroTile(x0, y0, options) {
173
+ const z0 = 0;
174
+ const k1 = 0.5 * options.buffer / options.extent;
175
+ const scale = 1 >> z0;
176
+ const idZero = toID(z0, x0, y0);
177
+
178
+ const xMin = x0 * scale;
179
+ const xMax = (x0 + 1) * scale;
180
+ const yMin = y0 * scale;
181
+ const yMax = (y0 + 1) * scale;
182
+
183
+ let tile;
184
+ // 横向裁剪
185
+ const clipWidth = clip(this.features, scale, xMin - k1, xMax + k1, 0, -Infinity, Infinity, options);
186
+ if (clipWidth) {
187
+ const clipFeatures = clip(
188
+ clipWidth,
189
+ scale,
190
+ yMin - k1,
191
+ yMax + k1,
192
+ 1,
193
+ -Infinity,
194
+ Infinity,
195
+ options
196
+ );
197
+
198
+ if (clipFeatures) {
199
+ this.tiles[idZero] = transform(createTile(clipFeatures, z0, x0, y0, options), options.extent)
200
+ this.tiles[idZero].source = clipFeatures;
201
+ tile = this.tiles[idZero];
202
+ }
203
+
204
+ }
205
+ return tile;
206
+ }
207
+
162
208
  getTile(z, x, y) {
163
209
  z = +z;
164
210
  x = +x;
@@ -169,11 +215,15 @@ class GeoJSONVT {
169
215
 
170
216
  if (z < 0 || z > 24) return null;
171
217
 
172
- const z2 = 1 << z;
173
- x = (x + z2) & (z2 - 1); // wrap tile x coordinate
174
-
218
+ // 去除warp操作,目前坐标系是无限延伸的,不能warp范围
219
+ // const z2 = 1 << z;
220
+ // x = (x + z2) & (z2 - 1); // wrap tile x coordinate
175
221
  const id = toID(z, x, y);
176
- if (this.tiles[id]) return transform(this.tiles[id], extent);
222
+ if (this.tiles[id]) {
223
+ return transform(this.tiles[id], extent);
224
+ } else if (z === 0) {
225
+ return this._createZeroTile(x, y, options);
226
+ }
177
227
 
178
228
  if (debug > 1) console.log('drilling down to z%d-%d-%d', z, x, y);
179
229
 
@@ -186,9 +236,12 @@ class GeoJSONVT {
186
236
  z0--;
187
237
  x0 = x0 >> 1;
188
238
  y0 = y0 >> 1;
189
- parent = this.tiles[toID(z0, x0, y0)];
239
+ const idZero = toID(z0, x0, y0);
240
+ parent = this.tiles[idZero];
241
+ if (z0 === 0 && !parent) {
242
+ parent = this._createZeroTile(x0, y0, options);
243
+ }
190
244
  }
191
-
192
245
  if (!parent || !parent.source) return null;
193
246
 
194
247
  // if we found a parent tile containing the original geometry, we can drill down from it
@@ -203,8 +256,15 @@ class GeoJSONVT {
203
256
  }
204
257
  }
205
258
 
259
+ /**
260
+ * @description: 瓦片编码方式(此插件默认的编码方式在0级多张瓦片时会导致瓦片重复)
261
+ * @param {*} z
262
+ * @param {*} x
263
+ * @param {*} y
264
+ * @return {*}
265
+ */
206
266
  function toID(z, x, y) {
207
- return (((1 << z) * y + x) * 32) + z;
267
+ return x.toString(36) + y.toString(36) + z.toString(36);
208
268
  }
209
269
 
210
270
  function extend(dest, src) {
package/src/tile.js CHANGED
@@ -11,10 +11,10 @@ export default function createTile(features, z, tx, ty, options) {
11
11
  y: ty,
12
12
  z,
13
13
  transformed: false,
14
- minX: 2,
15
- minY: 1,
16
- maxX: -1,
17
- maxY: 0
14
+ minX: Infinity,
15
+ minY: Infinity,
16
+ maxX: -Infinity,
17
+ maxY: -Infinity
18
18
  };
19
19
  for (const feature of features) {
20
20
  addFeature(tile, feature, tolerance, options);