Comparing version
@@ -5,3 +5,3 @@ var fs = require('fs') | ||
var Pbf = require('pbf') | ||
var VectorTile = require('vector-tile').VectorTile | ||
var VectorTile = require('@mapbox/vector-tile').VectorTile | ||
var Benchmark = require('benchmark') | ||
@@ -20,2 +20,5 @@ var serialize = require('../') | ||
var points = JSON.parse(fs.readFileSync(path.join(__dirname, 'points.geojson'))) | ||
var pointsTile = geojsonVt(points).getTile(14, 3888, 6255) | ||
var suite = new Benchmark.Suite('vt-pbf') | ||
@@ -29,2 +32,5 @@ suite | ||
}) | ||
.add('points', function () { | ||
serialize.fromGeojsonVt({ 'geojsonLayer': pointsTile }) | ||
}) | ||
.add('lots of properties', function () { | ||
@@ -31,0 +37,0 @@ serialize.fromGeojsonVt({ 'geojsonLayer': propertiesTile }) |
163
index.js
var Pbf = require('pbf') | ||
var vtpb = require('./vector-tile-pb') | ||
var GeoJSONWrapper = require('./lib/geojson_wrapper') | ||
@@ -17,9 +16,4 @@ | ||
function fromVectorTileJs (tile) { | ||
var layers = [] | ||
for (var l in tile.layers) { | ||
layers.push(prepareLayer(tile.layers[l])) | ||
} | ||
var out = new Pbf() | ||
vtpb.tile.write({ layers: layers }, out) | ||
writeTile(tile, out) | ||
return out.finish() | ||
@@ -43,49 +37,81 @@ } | ||
/** | ||
* Prepare the given layer to be serialized by the auto-generated pbf | ||
* serializer by encoding the feature geometry and properties. | ||
*/ | ||
function prepareLayer (layer) { | ||
var preparedLayer = { | ||
name: layer.name || '', | ||
version: layer.version || 1, | ||
extent: layer.extent || 4096, | ||
function writeTile (tile, pbf) { | ||
for (var key in tile.layers) { | ||
pbf.writeMessage(3, writeLayer, tile.layers[key]) | ||
} | ||
} | ||
function writeLayer (layer, pbf) { | ||
pbf.writeVarintField(15, layer.version || 1) | ||
pbf.writeStringField(1, layer.name || '') | ||
pbf.writeVarintField(5, layer.extent || 4096) | ||
var i | ||
var context = { | ||
keys: [], | ||
values: [], | ||
features: [] | ||
keycache: {}, | ||
valuecache: {} | ||
} | ||
var keycache = {} | ||
var valuecache = {} | ||
for (i = 0; i < layer.length; i++) { | ||
context.feature = layer.feature(i) | ||
pbf.writeMessage(2, writeFeature, context) | ||
} | ||
for (var i = 0; i < layer.length; i++) { | ||
var feature = layer.feature(i) | ||
feature.geometry = encodeGeometry(feature.loadGeometry()) | ||
var keys = context.keys | ||
for (i = 0; i < keys.length; i++) { | ||
pbf.writeStringField(3, keys[i]) | ||
} | ||
var tags = [] | ||
for (var key in feature.properties) { | ||
var keyIndex = keycache[key] | ||
if (typeof keyIndex === 'undefined') { | ||
preparedLayer.keys.push(key) | ||
keyIndex = preparedLayer.keys.length - 1 | ||
keycache[key] = keyIndex | ||
} | ||
var value = wrapValue(feature.properties[key]) | ||
var valueIndex = valuecache[value.key] | ||
if (typeof valueIndex === 'undefined') { | ||
preparedLayer.values.push(value) | ||
valueIndex = preparedLayer.values.length - 1 | ||
valuecache[value.key] = valueIndex | ||
} | ||
tags.push(keyIndex) | ||
tags.push(valueIndex) | ||
} | ||
var values = context.values | ||
for (i = 0; i < values.length; i++) { | ||
pbf.writeMessage(4, writeValue, values[i]) | ||
} | ||
} | ||
feature.tags = tags | ||
preparedLayer.features.push(feature) | ||
function writeFeature (context, pbf) { | ||
var feature = context.feature | ||
if (feature.id !== undefined) { | ||
pbf.writeVarintField(1, feature.id) | ||
} | ||
return preparedLayer | ||
pbf.writeMessage(2, writeProperties, context) | ||
pbf.writeVarintField(3, feature.type) | ||
pbf.writeMessage(4, writeGeometry, feature) | ||
} | ||
function writeProperties (context, pbf) { | ||
var feature = context.feature | ||
var keys = context.keys | ||
var values = context.values | ||
var keycache = context.keycache | ||
var valuecache = context.valuecache | ||
for (var key in feature.properties) { | ||
var keyIndex = keycache[key] | ||
if (typeof keyIndex === 'undefined') { | ||
keys.push(key) | ||
keyIndex = keys.length - 1 | ||
keycache[key] = keyIndex | ||
} | ||
pbf.writeVarint(keyIndex) | ||
var value = feature.properties[key] | ||
var type = typeof value | ||
if (type !== 'string' && type !== 'boolean' && type !== 'number') { | ||
value = JSON.stringify(value) | ||
} | ||
var valueKey = type + ':' + value | ||
var valueIndex = valuecache[valueKey] | ||
if (typeof valueIndex === 'undefined') { | ||
values.push(value) | ||
valueIndex = values.length - 1 | ||
valuecache[valueKey] = valueIndex | ||
} | ||
pbf.writeVarint(valueIndex) | ||
} | ||
} | ||
function command (cmd, length) { | ||
@@ -99,11 +125,5 @@ return (length << 3) + (cmd & 0x7) | ||
/** | ||
* Encode a polygon's geometry into an array ready to be serialized | ||
* to mapbox vector tile specified geometry data. | ||
* | ||
* @param {Array} Rings, each being an array of [x, y] tile-space coordinates | ||
* @return {Array} encoded geometry | ||
*/ | ||
function encodeGeometry (geometry) { | ||
var encoded = [] | ||
function writeGeometry (feature, pbf) { | ||
var geometry = feature.loadGeometry() | ||
var type = feature.type | ||
var x = 0 | ||
@@ -114,10 +134,15 @@ var y = 0 | ||
var ring = geometry[r] | ||
encoded.push(command(1, 1)) // moveto | ||
var count = 1 | ||
if (type === 1) { | ||
count = ring.length | ||
} | ||
pbf.writeVarint(command(1, count)) // moveto | ||
for (var i = 0; i < ring.length; i++) { | ||
if (i === 1) { | ||
encoded.push(command(2, ring.length - 1)) // lineto | ||
if (i === 1 && type !== 1) { | ||
pbf.writeVarint(command(2, ring.length - 1)) // lineto | ||
} | ||
var dx = ring[i].x - x | ||
var dy = ring[i].y - y | ||
encoded.push(zigzag(dx), zigzag(dy)) | ||
pbf.writeVarint(zigzag(dx)) | ||
pbf.writeVarint(zigzag(dy)) | ||
x += dx | ||
@@ -127,33 +152,19 @@ y += dy | ||
} | ||
return encoded | ||
} | ||
/** | ||
* Wrap a property value according to its type. The returned object | ||
* is of the form { xxxx_value: primitiveValue }, which is what the generated | ||
* protobuf serializer expects. | ||
*/ | ||
function wrapValue (value) { | ||
var result | ||
function writeValue (value, pbf) { | ||
var type = typeof value | ||
if (type === 'string') { | ||
result = { string_value: value } | ||
pbf.writeStringField(1, value) | ||
} else if (type === 'boolean') { | ||
result = { bool_value: value } | ||
pbf.writeBooleanField(7, value) | ||
} else if (type === 'number') { | ||
if (value % 1 !== 0) { | ||
result = { double_value: value } | ||
pbf.writeDoubleField(3, value) | ||
} else if (value < 0) { | ||
result = { sint_value: value } | ||
pbf.writeSVarintField(6, value) | ||
} else { | ||
result = { uint_value: value } | ||
pbf.writeVarintField(5, value) | ||
} | ||
} else { | ||
value = JSON.stringify(value) | ||
result = { string_value: value } | ||
} | ||
result.key = type + ':' + value | ||
return result | ||
} |
{ | ||
"name": "vt-pbf", | ||
"version": "3.0.0", | ||
"version": "3.0.1", | ||
"description": "Serialize mapbox vector tiles to binary protobufs in javascript.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -36,2 +36,26 @@ var fs = require('fs') | ||
test('geojson-vt multipoint', function (t) { | ||
var orig = JSON.parse(fs.readFileSync(__dirname + '/fixtures/multi-points.json')) | ||
var tileindex = geojsonVt(orig) | ||
var tile = tileindex.getTile(1, 0, 0) | ||
var buff = serialize.fromGeojsonVt({ | ||
'multipoint': tile | ||
}) | ||
var tile3 = new VectorTile(new Pbf(buff)) | ||
var layer = tile3.layers['multipoint'] | ||
var features = [] | ||
for (var i = 0; i < layer.length; i++) { | ||
var feat = layer.feature(i).toGeoJSON(0, 0, 1) | ||
features.push(feat) | ||
} | ||
t.plan(orig.features.length) | ||
orig.features.forEach(function (expected) { | ||
var actual = features.shift() | ||
t.ok(eq.compare(actual, expected)) | ||
}) | ||
}) | ||
test('vector-tile-js tiles', function (t) { | ||
@@ -38,0 +62,0 @@ var data = fs.readFileSync(__dirname + '/fixtures/rectangle-1.0.0.pbf') |
5055218
9074.62%19
26.67%357
-8.7%