@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 +0 -0
- package/geojson-vt-dev.js +1063 -0
- package/geojson-vt.js +1 -0
- package/package.json +1 -1
- package/src/convert.js +2 -6
- package/src/index.js +78 -18
- package/src/tile.js +4 -4
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
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
173
|
-
|
|
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])
|
|
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
|
-
|
|
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
|
|
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:
|
|
15
|
-
minY:
|
|
16
|
-
maxX: -
|
|
17
|
-
maxY:
|
|
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);
|