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