geojson-vt
Version:
Slice GeoJSON data into vector tiles efficiently
140 lines (118 loc) • 3.87 kB
JavaScript
'use strict';
module.exports = convert;
var simplify = require('./simplify');
// converts GeoJSON feature into an intermediate JSON vector format with projection & simplification
function convert(data, tolerance) {
var features = [];
if (data.type === 'FeatureCollection') {
for (var i = 0; i < data.features.length; i++) {
convertFeature(features, data.features[i], tolerance);
}
} else if (data.type === 'Feature') {
convertFeature(features, data, tolerance);
} else {
convertFeature(features, {geometry: data}, tolerance);
}
return features;
}
function convertFeature(features, feature, tolerance) {
var geom = feature.geometry,
type = geom.type,
coords = geom.coordinates,
tags = feature.properties,
i, j, rings;
if (type === 'Point') {
features.push(create(tags, 1, [projectPoint(coords)]));
} else if (type === 'MultiPoint') {
features.push(create(tags, 1, project(coords)));
} else if (type === 'LineString') {
features.push(create(tags, 2, [project(coords, tolerance)]));
} else if (type === 'MultiLineString' || type === 'Polygon') {
rings = [];
for (i = 0; i < coords.length; i++) {
rings.push(project(coords[i], tolerance));
}
features.push(create(tags, type === 'Polygon' ? 3 : 2, rings));
} else if (type === 'MultiPolygon') {
rings = [];
for (i = 0; i < coords.length; i++) {
for (j = 0; j < coords[i].length; j++) {
rings.push(project(coords[i][j], tolerance));
}
}
features.push(create(tags, 3, rings));
} else if (type === 'GeometryCollection') {
for (i = 0; i < geom.geometries.length; i++) {
convertFeature(features, {
geometry: geom.geometries[i],
properties: tags
}, tolerance);
}
} else {
console.warn('Unsupported GeoJSON type: ' + geom.type);
}
}
function create(tags, type, geometry) {
var feature = {
geometry: geometry,
type: type,
tags: tags || null,
min: [1, 1],
max: [0, 0]
};
calcBBox(feature);
return feature;
}
function project(lonlats, tolerance) {
var projected = [];
for (var i = 0; i < lonlats.length; i++) {
projected.push(projectPoint(lonlats[i]));
}
if (tolerance) {
simplify(projected, tolerance);
calcSize(projected);
}
return projected;
}
function projectPoint(p) {
var sin = Math.sin(p[1] * Math.PI / 180),
x = (p[0] / 360 + 0.5),
y = (0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI);
return [x, y, 0];
}
// calculate area and length of the poly
function calcSize(points) {
var sum = 0,
dist = 0;
for (var i = 0, a, b; i < points.length - 1; i++) {
a = b || points[i];
b = points[i + 1];
sum += a[0] * b[1] - b[0] * a[1];
dist += Math.abs(b[0] - a[0]) + Math.abs(b[1] - a[1]); // Manhattan distance
}
points.area = Math.abs(sum / 2);
points.dist = dist;
}
// calculate the feature bounding box for faster clipping later
function calcBBox(feature) {
var geometry = feature.geometry,
min = feature.min,
max = feature.max;
if (feature.type === 1) {
calcRingBBOX(min, max, geometry);
} else {
for (var i = 0; i < geometry.length; i++) {
calcRingBBOX(min, max, geometry[i]);
}
}
return feature;
}
function calcRingBBOX(min, max, points) {
for (var i = 0, p; i < points.length; i++) {
p = points[i];
min[0] = Math.min(p[0], min[0]);
max[0] = Math.max(p[0], max[0]);
min[1] = Math.min(p[1], min[1]);
max[1] = Math.max(p[1], max[1]);
}
}