geojson-vt
Advanced tools
Comparing version 1.1.0 to 2.0.0
@@ -9,8 +9,28 @@ | ||
var tileIndex = geojsonvt(data, { | ||
maxZoom: 14, | ||
debug: 1 | ||
}); | ||
console.time('drill down'); | ||
for (var i = 0; i < 10; i++) { | ||
for (var j = 0; j < 10; j++) { | ||
tileIndex.getTile(7, 30 + i, 45 + j); | ||
} | ||
} | ||
for (var i = 0; i < 10; i++) { | ||
for (var j = 0; j < 10; j++) { | ||
tileIndex.getTile(8, 60 + i, 90 + j); | ||
} | ||
} | ||
for (var i = 0; i < 10; i++) { | ||
for (var j = 0; j < 10; j++) { | ||
tileIndex.getTile(10, 240 + i, 360 + j); | ||
} | ||
} | ||
console.timeEnd('drill down'); | ||
console.log('tiles generated:', tileIndex.total, JSON.stringify(tileIndex.stats)); | ||
// tileIndex.maxZoom = 14; | ||
// tileIndex.getTile(14, 4100, 6200); | ||
// tileIndex.getTile(14, 4100, 6000); |
{ | ||
"name": "geojson-vt", | ||
"version": "1.1.0", | ||
"version": "2.0.0", | ||
"description": "Slice GeoJSON data into vector tiles efficiently", | ||
@@ -20,49 +20,15 @@ "homepage": "https://github.com/mapbox/geojson-vt", | ||
"benchmark": "^1.0.0", | ||
"browserify": "^7.0.0", | ||
"eslint": "^0.15.1", | ||
"browserify": "^9.0.3", | ||
"eslint": "^0.17.1", | ||
"faucet": "0.0.1", | ||
"tape": "^3.0.3", | ||
"uglify-js": "^2.4.16", | ||
"watchify": "^2.1.1" | ||
"tape": "^3.5.0", | ||
"uglify-js": "^2.4.17", | ||
"watchify": "^2.4.0" | ||
}, | ||
"scripts": { | ||
"test": "eslint src/*.js test/test-*.js && tape test/test-*.js | faucet", | ||
"test": "eslint src/*.js test/test-*.js debug/viz.js && tape test/test-*.js | faucet", | ||
"build-min": "browserify src/index.js -s geojsonvt | uglifyjs -c -m -o geojson-vt.js", | ||
"build-dev": "browserify -d src/index.js -s geojsonvt -o geojson-vt-dev.js", | ||
"watch": "watchify -v -d src/index.js -s geojsonvt -o geojson-vt-dev.js" | ||
}, | ||
"eslintConfig": { | ||
"rules": { | ||
"no-use-before-define": [ | ||
2, | ||
"nofunc" | ||
], | ||
"camelcase": 2, | ||
"space-after-function-name": 2, | ||
"space-in-parens": 2, | ||
"space-before-blocks": 2, | ||
"space-after-keywords": 2, | ||
"comma-style": 2, | ||
"no-lonely-if": 2, | ||
"no-else-return": 2, | ||
"new-cap": 2, | ||
"no-empty": 2, | ||
"no-new": 2, | ||
"key-spacing": 2, | ||
"no-multi-spaces": 0, | ||
"space-in-brackets": 2, | ||
"brace-style": 2, | ||
"indent": 2, | ||
"quotes": [ | ||
2, | ||
"single" | ||
], | ||
"curly": 0, | ||
"no-constant-condition": 0 | ||
}, | ||
"env": { | ||
"node": true, | ||
"browser": true | ||
} | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
### geojson-vt — GeoJSON Vector Tiles | ||
## geojson-vt — GeoJSON Vector Tiles | ||
@@ -18,3 +18,3 @@ A highly efficient JavaScript library for **slicing GeoJSON data into vector tiles on the fly**, | ||
#### Demo | ||
### Demo | ||
@@ -33,3 +33,3 @@ Here's **geojson-vt** action in [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js), | ||
#### Usage | ||
### Usage | ||
@@ -44,3 +44,3 @@ ```js | ||
#### Options | ||
### Options | ||
@@ -52,13 +52,14 @@ You can fine-tune the results with an options object, | ||
var tileIndes = geojsonvt(data, { | ||
baseZoom: 14, // max zoom to preserve detail on | ||
maxZoom: 4, // zoom to slice down to on first pass | ||
maxPoints: 100, // stop slicing each tile below this number of points | ||
tolerance: 3, // simplification tolerance (higher means simpler) | ||
extent: 4096, // tile extent (both width and height) | ||
buffer: 64, // tile buffer on each side | ||
debug: 0 // logging level (0 to disable, 1 or 2) | ||
maxZoom: 14, // max zoom to preserve detail on | ||
tolerance: 3, // simplification tolerance (higher means simpler) | ||
extent: 4096, // tile extent (both width and height) | ||
buffer: 64, // tile buffer on each side | ||
debug: 0 // logging level (0 to disable, 1 or 2) | ||
indexMaxZoom: 4, // max zoom in the initial tile index | ||
indexMaxPoints: 100000, // max number of points per tile in the index | ||
}); | ||
``` | ||
#### Browser builds | ||
### Browser builds | ||
@@ -70,1 +71,19 @@ ```bash | ||
``` | ||
### Changelog | ||
##### 2.0.0 (Mar 20, 2015) | ||
- **Breaking**: `maxZoom` renamed to `indexMaxZoom`, `maxPoints` to `indexMaxPoints`, `baseZoom` to `maxZoom`. | ||
- Improved performance of both indexing and on-demand tile requests. | ||
- Improved memory footprint. | ||
- Better indexing defaults. | ||
- Fixed a bug where unnecessary memory was retained in some cases. | ||
##### 1.1.0 (Mar 2, 2015) | ||
- Add `buffer` and `extent` options. | ||
##### 1.0.0 (Dec 8, 2014) | ||
- Initial release. |
128
src/index.js
@@ -21,3 +21,3 @@ 'use strict'; | ||
var z2 = 1 << options.baseZoom, // 2^z | ||
var z2 = 1 << options.maxZoom, // 2^z | ||
features = convert(data, options.tolerance / (z2 * options.extent)); | ||
@@ -29,3 +29,4 @@ | ||
console.timeEnd('preprocess data'); | ||
console.time('generate tiles up to z' + options.maxZoom); | ||
console.log('index: maxZoom: %d, maxPoints: %d', options.indexMaxZoom, options.indexMaxPoints); | ||
console.time('generate tiles'); | ||
this.stats = {}; | ||
@@ -40,3 +41,3 @@ this.total = 0; | ||
console.log('features: %d, points: %d', this.tiles[0].numFeatures, this.tiles[0].numPoints); | ||
console.timeEnd('generate tiles up to z' + options.maxZoom); | ||
console.timeEnd('generate tiles'); | ||
console.log('tiles generated:', this.total, JSON.stringify(this.stats)); | ||
@@ -47,9 +48,9 @@ } | ||
GeoJSONVT.prototype.options = { | ||
baseZoom: 14, // max zoom to preserve detail on | ||
maxZoom: 4, // zoom to slice down to on first pass | ||
maxPoints: 100, // stop slicing a tile below this number of points | ||
tolerance: 3, // simplification tolerance (higher means simpler) | ||
extent: 4096, // tile extent | ||
buffer: 64, // tile buffer on each side | ||
debug: 0 // logging level (0, 1 or 2) | ||
maxZoom: 14, // max zoom to preserve detail on | ||
indexMaxZoom: 5, // max zoom in the tile index | ||
indexMaxPoints: 100000, // max number of points per tile in the tile index | ||
tolerance: 3, // simplification tolerance (higher means simpler) | ||
extent: 4096, // tile extent | ||
buffer: 64, // tile buffer on each side | ||
debug: 0 // logging level (0, 1 or 2) | ||
}; | ||
@@ -75,3 +76,3 @@ | ||
tile = this.tiles[id], | ||
tileTolerance = z === options.baseZoom ? 0 : options.tolerance / (z2 * extent); | ||
tileTolerance = z === options.maxZoom ? 0 : options.tolerance / (z2 * extent); | ||
@@ -81,3 +82,3 @@ if (!tile) { | ||
tile = this.tiles[id] = createTile(features, z2, x, y, tileTolerance, extent, z === options.baseZoom); | ||
tile = this.tiles[id] = createTile(features, z2, x, y, tileTolerance, z === options.maxZoom); | ||
@@ -90,3 +91,3 @@ if (debug) { | ||
} | ||
var key = 'z' + z + ':'; | ||
var key = 'z' + z; | ||
this.stats[key] = (this.stats[key] || 0) + 1; | ||
@@ -97,10 +98,25 @@ this.total++; | ||
if (!cz && (z === options.maxZoom || tile.numPoints <= options.maxPoints || | ||
isClippedSquare(tile.features, extent, buffer)) || z === options.baseZoom || z === cz) { | ||
tile.source = features; | ||
continue; // stop tiling | ||
// save reference to original geometry in tile so that we can drill down later if we stop now | ||
tile.source = features; | ||
// stop tiling if the tile is degenerate | ||
if (isClippedSquare(tile.features, extent, buffer)) continue; | ||
// if it's the first-pass tiling | ||
if (!cz) { | ||
// stop tiling if we reached max zoom, or if the tile is too simple | ||
if (z === options.indexMaxZoom || tile.numPoints <= options.indexMaxPoints) continue; | ||
// if a drilldown to a specific tile | ||
} else { | ||
// stop tiling if we reached base zoom or our target tile zoom | ||
if (z === options.maxZoom || z === cz) continue; | ||
// stop tiling if it's not an ancestor of the target tile | ||
var m = 1 << (cz - z); | ||
if (x !== Math.floor(cx / m) && y !== Math.floor(cy / m)) continue; | ||
} | ||
if (cz) tile.source = features; | ||
else tile.source = null; | ||
// if we slice further down, no need to keep source geometry | ||
tile.source = null; | ||
@@ -114,25 +130,17 @@ if (debug > 1) console.time('clipping'); | ||
k4 = 1 + k1, | ||
tl, bl, tr, br, left, right; | ||
tl, bl, tr, br, left, right, | ||
m, goLeft, goTop; | ||
tl = bl = tr = br = null; | ||
if (cz) { // if we have a specific tile to drill down to, calculate where to go | ||
m = 1 << (cz - z); | ||
goLeft = cx / m - x < 0.5; | ||
goTop = cy / m - y < 0.5; | ||
} | ||
left = clip(features, z2, x - k1, x + k3, 0, intersectX); | ||
right = clip(features, z2, x + k2, x + k4, 0, intersectX); | ||
tl = bl = tr = br = left = right = null; | ||
if (!cz || goLeft) left = clip(features, z2, x - k1, x + k3, 0, intersectX); | ||
if (!cz || !goLeft) right = clip(features, z2, x + k2, x + k4, 0, intersectX); | ||
if (left) { | ||
if (!cz || goTop) tl = clip(left, z2, y - k1, y + k3, 1, intersectY); | ||
if (!cz || !goTop) bl = clip(left, z2, y + k2, y + k4, 1, intersectY); | ||
tl = clip(left, z2, y - k1, y + k3, 1, intersectY); | ||
bl = clip(left, z2, y + k2, y + k4, 1, intersectY); | ||
} | ||
if (right) { | ||
if (!cz || goTop) tr = clip(right, z2, y - k1, y + k3, 1, intersectY); | ||
if (!cz || !goTop) br = clip(right, z2, y + k2, y + k4, 1, intersectY); | ||
tr = clip(right, z2, y - k1, y + k3, 1, intersectY); | ||
br = clip(right, z2, y + k2, y + k4, 1, intersectY); | ||
} | ||
@@ -150,8 +158,9 @@ | ||
GeoJSONVT.prototype.getTile = function (z, x, y) { | ||
var id = toID(z, x, y); | ||
if (this.tiles[id]) return this.tiles[id]; | ||
var options = this.options, | ||
extent = options.extent, | ||
debug = options.debug; | ||
var id = toID(z, x, y); | ||
if (this.tiles[id]) return transformTile(this.tiles[id], extent); | ||
if (debug > 1) console.log('drilling down to z%d-%d-%d', z, x, y); | ||
@@ -175,12 +184,47 @@ | ||
if (parent.source) { | ||
if (isClippedSquare(parent.features, options.extent, options.buffer)) return parent; | ||
if (isClippedSquare(parent.features, options.extent, options.buffer)) return transformTile(parent, extent); | ||
if (debug) console.time('drilling down'); | ||
if (debug > 1) console.time('drilling down'); | ||
this.splitTile(parent.source, z0, x0, y0, z, x, y); | ||
if (debug) console.timeEnd('drilling down'); | ||
if (debug > 1) console.timeEnd('drilling down'); | ||
} | ||
return this.tiles[id]; | ||
return transformTile(this.tiles[id], extent); | ||
}; | ||
function transformTile(tile, extent) { | ||
if (!tile || tile.transformed) return tile; | ||
var z2 = tile.z2, | ||
tx = tile.x, | ||
ty = tile.y, | ||
i, j, k; | ||
for (i = 0; i < tile.features.length; i++) { | ||
var feature = tile.features[i], | ||
geom = feature.geometry, | ||
type = feature.type; | ||
if (type === 1) { | ||
for (j = 0; j < geom.length; j++) geom[j] = transformPoint(geom[j], extent, z2, tx, ty); | ||
} else { | ||
for (j = 0; j < geom.length; j++) { | ||
var ring = geom[j]; | ||
for (k = 0; k < ring.length; k++) ring[k] = transformPoint(ring[k], extent, z2, tx, ty); | ||
} | ||
} | ||
} | ||
tile.transformed = true; | ||
return tile; | ||
} | ||
function transformPoint(p, extent, z2, tx, ty) { | ||
var x = Math.round(extent * (p[0] * z2 - tx)), | ||
y = Math.round(extent * (p[1] * z2 - ty)); | ||
return [x, y]; | ||
} | ||
// checks whether a tile is a whole-area fill after clipping; if it is, there's no sense slicing it further | ||
@@ -187,0 +231,0 @@ function isClippedSquare(features, extent, buffer) { |
@@ -36,7 +36,10 @@ 'use strict'; | ||
points[index][2] = maxSqDist; // save the point importance in squared pixels as a z coordinate | ||
stack.push(first, index, index, last); | ||
stack.push(first); | ||
stack.push(index); | ||
first = index; | ||
} else { | ||
last = stack.pop(); | ||
first = stack.pop(); | ||
} | ||
last = stack.pop(); | ||
first = stack.pop(); | ||
} | ||
@@ -43,0 +46,0 @@ } |
@@ -5,3 +5,3 @@ 'use strict'; | ||
function createTile(features, z2, tx, ty, tolerance, extent, noSimplify) { | ||
function createTile(features, z2, tx, ty, tolerance, noSimplify) { | ||
var tile = { | ||
@@ -12,7 +12,11 @@ features: [], | ||
numFeatures: 0, | ||
source: null | ||
source: null, | ||
x: tx, | ||
y: ty, | ||
z2: z2, | ||
transformed: false | ||
}; | ||
for (var i = 0; i < features.length; i++) { | ||
tile.numFeatures++; | ||
addFeature(tile, features[i], z2, tx, ty, tolerance, extent, noSimplify); | ||
addFeature(tile, features[i], tolerance, noSimplify); | ||
} | ||
@@ -22,7 +26,7 @@ return tile; | ||
function addFeature(tile, feature, z2, tx, ty, tolerance, extent, noSimplify) { | ||
function addFeature(tile, feature, tolerance, noSimplify) { | ||
var geom = feature.geometry, | ||
type = feature.type, | ||
transformed = [], | ||
simplified = [], | ||
sqTolerance = tolerance * tolerance, | ||
@@ -33,3 +37,3 @@ i, j, ring, p; | ||
for (i = 0; i < geom.length; i++) { | ||
transformed.push(transformPoint(geom[i], z2, tx, ty, extent)); | ||
simplified.push(geom[i]); | ||
tile.numPoints++; | ||
@@ -52,3 +56,3 @@ tile.numSimplified++; | ||
var transformedRing = []; | ||
var simplifiedRing = []; | ||
@@ -59,3 +63,3 @@ for (j = 0; j < ring.length; j++) { | ||
if (noSimplify || p[2] > sqTolerance) { | ||
transformedRing.push(transformPoint(p, z2, tx, ty, extent)); | ||
simplifiedRing.push(p); | ||
tile.numSimplified++; | ||
@@ -66,9 +70,9 @@ } | ||
transformed.push(transformedRing); | ||
simplified.push(simplifiedRing); | ||
} | ||
} | ||
if (transformed.length) { | ||
if (simplified.length) { | ||
tile.features.push({ | ||
geometry: transformed, | ||
geometry: simplified, | ||
type: type, | ||
@@ -79,7 +83,1 @@ tags: feature.tags || null | ||
} | ||
function transformPoint(p, z2, tx, ty, extent) { | ||
var x = Math.round(extent * (p[0] * z2 - tx)), | ||
y = Math.round(extent * (p[1] * z2 - ty)); | ||
return [x, y]; | ||
} |
Sorry, the diff of this file is not supported yet
1509044
19
6638
85