Skip to content

Commit

Permalink
preliminary 3D feature reading
Browse files Browse the repository at this point in the history
  • Loading branch information
Lauren Budorick committed Apr 25, 2018
1 parent 58df1e9 commit 68e53b0
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 12 deletions.
6 changes: 4 additions & 2 deletions README.md
Expand Up @@ -79,6 +79,7 @@ An object that contains the data for a single vector tile layer.
- **name** (`String) `— layer name
- **extent** (`Number`, default: `4096`) — tile extent size
- **length** (`Number`) — number of features in the layer
- **dimensions** (`Number`, default: `2`) — number of dimensions in the layer

#### Methods

Expand All @@ -99,6 +100,7 @@ An object that contains the data for a single feature.
#### Methods

- **loadGeometry()** — parses feature geometry and returns an array of
[Point](https://github.com/mapbox/point-geometry) arrays (with each point having `x` and `y` properties)
- **bbox()** — calculates and returns the bounding box of the feature in the form `[x1, y1, x2, y2]`
[Point](https://github.com/mapbox/point-geometry) arrays (with each point having `x` and `y` properties,
and `z` properties if the layer's `dimension` property is set to `3`)
- **bbox()** — calculates and returns the bounding box of the feature in the form `[x1, y1, x2, y2, z1?, z2?]`
- **toGeoJSON(x, y, z)** — returns a GeoJSON representation of the feature. (`x`, `y`, and `z` refer to the containing tile's index.)
2 changes: 2 additions & 0 deletions fixtures.js
Expand Up @@ -2,6 +2,8 @@ var mapnik = require('mapnik');
var path = require('path');
var fs = require('fs');

// TODO since node-mapnik will not support vt3 (?), this won't work to generate vt3 fixtures

mapnik.register_datasource(path.join(mapnik.settings.paths.input_plugins, 'geojson.input'));

var fixtures = {
Expand Down
25 changes: 21 additions & 4 deletions lib/vectortilefeature.js
Expand Up @@ -4,10 +4,11 @@ var Point = require('@mapbox/point-geometry');

module.exports = VectorTileFeature;

function VectorTileFeature(pbf, end, extent, keys, values) {
function VectorTileFeature(pbf, end, extent, dimensions, keys, values) {
// Public
this.properties = {};
this.extent = extent;
this.dimensions = dimensions;
this.type = 0;

// Private
Expand Down Expand Up @@ -47,6 +48,7 @@ VectorTileFeature.prototype.loadGeometry = function() {
length = 0,
x = 0,
y = 0,
z = 0,
lines = [],
line;

Expand All @@ -63,12 +65,15 @@ VectorTileFeature.prototype.loadGeometry = function() {
x += pbf.readSVarint();
y += pbf.readSVarint();

if (this.dimensions === 3) z += pbf.readSVarint();

if (cmd === 1) { // moveTo
if (line) lines.push(line);
line = [];
}

line.push(new Point(x, y));
if (this.dimensions === 2) line.push(new Point(x, y));
else if (this.dimensions === 3) line.push(new Point(x, y, z));

} else if (cmd === 7) {

Expand Down Expand Up @@ -99,7 +104,9 @@ VectorTileFeature.prototype.bbox = function() {
x1 = Infinity,
x2 = -Infinity,
y1 = Infinity,
y2 = -Infinity;
y2 = -Infinity,
z1 = Infinity,
z2 = -Infinity;

while (pbf.pos < end) {
if (length <= 0) {
Expand All @@ -118,24 +125,33 @@ VectorTileFeature.prototype.bbox = function() {
if (y < y1) y1 = y;
if (y > y2) y2 = y;

if (this.dimensions === 3) {
z += pbf.readSVarint();
if (z < z1) z1 = z;
if (z > z2) z2 = z;
}

} else if (cmd !== 7) {
throw new Error('unknown command ' + cmd);
}
}

return [x1, y1, x2, y2];
if (this.dimensions === 2) return [x1, y1, x2, y2];
else if (this.dimensions === 3) return [x1, y1, x2, y2, z1, z2];
};

VectorTileFeature.prototype.toGeoJSON = function(x, y, z) {
var size = this.extent * Math.pow(2, z),
x0 = this.extent * x,
y0 = this.extent * y,
z0 = this.extent * z,
coords = this.loadGeometry(),
type = VectorTileFeature.types[this.type],
i, j;

function project(line) {
for (var j = 0; j < line.length; j++) {
// TODO project z coord
var p = line[j], y2 = 180 - (p.y + y0) * 360 / size;
line[j] = [
(p.x + x0) * 360 / size - 180,
Expand All @@ -161,6 +177,7 @@ VectorTileFeature.prototype.toGeoJSON = function(x, y, z) {
break;

case 3:
// TODO polygons w 3 dimensions should be against spec rules, but should that be enforced here? in creation? ???
coords = classifyRings(coords);
for (i = 0; i < coords.length; i++) {
for (j = 0; j < coords[i].length; j++) {
Expand Down
4 changes: 3 additions & 1 deletion lib/vectortilelayer.js
Expand Up @@ -10,6 +10,7 @@ function VectorTileLayer(pbf, end) {
this.name = null;
this.extent = 4096;
this.length = 0;
this.dimensions = 2;

// Private
this._pbf = pbf;
Expand All @@ -29,6 +30,7 @@ function readLayer(tag, layer, pbf) {
else if (tag === 2) layer._features.push(pbf.pos);
else if (tag === 3) layer._keys.push(pbf.readString());
else if (tag === 4) layer._values.push(readValueMessage(pbf));
else if (tag === 6) layer.dimensions = pbf.readVarint();
}

function readValueMessage(pbf) {
Expand Down Expand Up @@ -57,5 +59,5 @@ VectorTileLayer.prototype.feature = function(i) {
this._pbf.pos = this._features[i];

var end = this._pbf.readVarint() + this._pbf.pos;
return new VectorTileFeature(this._pbf, end, this.extent, this._keys, this._values);
return new VectorTileFeature(this._pbf, end, this.extent, this.dimensions, this._keys, this._values);
};
14 changes: 9 additions & 5 deletions proto/vector_tile.proto
Expand Up @@ -37,10 +37,11 @@ message tile {
// Contains a stream of commands and parameters (vertices). The
// repeat count is shifted to the left by 3 bits. This means
// that the command has 3 bits (0-7). The repeat count
// indicates how often this command is to be repeated. Defined
// commands are:
// - MoveTo: 1 (2 parameters follow)
// - LineTo: 2 (2 parameters follow)
// indicates how often this command is to be repeated. MoveTo and LineTo
// commands should be followed by 2 parameters unless `dimensions` is set
// on the enclosing layer to 3. Defined commands are:
// - MoveTo: 1 (2-3 parameters follow)
// - LineTo: 2 (2-3 parameters follow)
// - ClosePath: 7 (no parameters follow)
//
// Ex.: MoveTo(3, 6), LineTo(8, 12), LineTo(20, 34), ClosePath
Expand All @@ -54,7 +55,7 @@ message tile {
// Commands are encoded as uint32 varints, vertex parameters are
// encoded as sint32 varints (zigzag). Vertex parameters are
// also encoded as deltas to the previous position. The original
// position is (0,0)
// position is (0,0) (or (0, 0, 0) in 3 dimensions)
repeated uint32 geometry = 4 [ packed = true ];
}

Expand All @@ -79,6 +80,9 @@ message tile {
// The bounding box in this tile spans from 0..4095 units
optional uint32 extent = 5 [ default = 4096 ];

// TODO update with official proto file when finalized
optional uint32 dimensions = 6 [ default = 2 ];

extensions 16 to max;
}

Expand Down

0 comments on commit 68e53b0

Please sign in to comment.