Skip to content

Commit

Permalink
Add feature.toGeoJSON(x, y, z)
Browse files Browse the repository at this point in the history
  • Loading branch information
jfirebaugh committed Feb 21, 2015
1 parent 5b562b9 commit b15016a
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 1 deletion.
33 changes: 33 additions & 0 deletions lib/vectortilefeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,36 @@ VectorTileFeature.prototype.bbox = function() {

return [x1, y1, x2, y2];
};

var SphericalMercator = require('sphericalmercator');

VectorTileFeature.prototype.toGeoJSON = function(x, y, z) {
var merc = new SphericalMercator({size: this.extent}),
x0 = this.extent * x,
y0 = this.extent * y,
coords = this.loadGeometry(),
type = VectorTileFeature.types[this.type];

for (var i = 0; i < coords.length; i++) {
var line = coords[i];
for (var j = 0; j < line.length; j++) {
var p = line[j];
line[j] = merc.ll([p.x + x0, p.y + y0], z);
}
}

if (type === 'Point') {
coords = coords[0][0];
} else if (type === 'LineString') {
coords = coords[0];
}

This comment has been minimized.

Copy link
@mourner

mourner Feb 22, 2015

Member

This will be broken in many cases — do we intentionally keep it simple until we can fully reconstruct GeoJSON after mapnik-vector-tile changes?

This comment has been minimized.

Copy link
@jfirebaugh

jfirebaugh Feb 23, 2015

Author Contributor

Point and LineString cases will always be correct already, right? As I understand it, it's just Polygon where the ring order is undefined, and I expect that the vector tile spec will adopt GeoJSON order.

This comment has been minimized.

Copy link
@mourner

mourner Feb 24, 2015

Member

There's also MultiPoint and MultiLineString, where all but the first point/ring will get ignored. And you have to differentiate between Polygon and MultiPolygon.

This comment has been minimized.

Copy link
@jfirebaugh

jfirebaugh Feb 24, 2015

Author Contributor

Okay, those types are not listed in the GeomType enum. Is there somewhere they are documented? cc @springmeyer

This comment has been minimized.

Copy link
@mourner

mourner Feb 24, 2015

Member

@jfirebaugh VT spec doesn't have the concept of multi-geometries because different parts are encoded using moveTo-lineTo commands. So if you have a MultiPolygon in the original data, it will be encoded as a Polygon with rings of individual polygons mashed together. You often can't reconstruct original geometry in case of Polygon/MultiPolygon because you can't easily classify rings as outer or holes (and what holes belong to what outer rings), and that's the main reason why mapbox/mapnik-vector-tile#59 should be fixed.

This comment has been minimized.

Copy link
@jfirebaugh

jfirebaugh Feb 24, 2015

Author Contributor

Upstream: mapbox/vector-tile-spec#30

I'll add handling for MultiPoint and MultiLineString.


return {
type: "Feature",
geometry: {
type: type,
coordinates: coords
},
properties: this.properties
};
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"license": "BSD",
"main": "index.js",
"dependencies": {
"point-geometry": "0.0.0"
"point-geometry": "0.0.0",
"sphericalmercator": "^1.0.3"
},
"devDependencies": {
"benchmark": "^1.0.0",
Expand Down
58 changes: 58 additions & 0 deletions test/parse.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,64 @@ test('parsing vector tiles', function(t) {
t.deepEqual(building, [ [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 2032, y: -31 }, { x: 2032, y: -32 }, { x: 2039, y: -32 } ] ]);
t.end();
});

t.test('toGeoJSON', function(t) {
var tile = new VectorTile(new Protobuf(data));

function close(actual, expected) {
t.equal(actual.length, expected.length);
for (var i = 0; i < actual.length; i++) {
if (actual[i].length) {
close(actual[i], expected[i]);
} else {
t.ok(Math.abs(actual[i] - expected[i]) < 1e-6);
}
}
}

var point = tile.layers.poi_label.feature(11).toGeoJSON(8801, 5371, 14);
t.deepEqual(point.type, 'Feature');
t.deepEqual(point.properties, {
localrank: 1,
maki: 'park',
name: 'Mauerpark',
name_de: 'Mauerpark',
name_en: 'Mauerpark',
name_es: 'Mauerpark',
name_fr: 'Mauerpark',
osm_id: 3000003150561,
ref: '',
scalerank: 2,
type: 'Park'
});
t.deepEqual(point.geometry.type, 'Point');
close(point.geometry.coordinates, [13.402258157730103, 52.54398925380624]);

var line = tile.layers.bridge.feature(0).toGeoJSON(8801, 5371, 14);
t.deepEqual(line.type, 'Feature');
t.deepEqual(line.properties, {
class: 'service',
oneway: 0,
osm_id: 238162948,
type: 'service'
});
t.deepEqual(line.geometry.type, 'LineString');
close(line.geometry.coordinates,
[[13.399457931518555, 52.546334844036416], [13.399441838264465, 52.546504478525016]]);

var poly = tile.layers.building.feature(0).toGeoJSON(8801, 5371, 14);
t.deepEqual(poly.type, 'Feature');
t.deepEqual(poly.properties, {
osm_id: 1000267229912
});
t.deepEqual(poly.geometry.type, 'Polygon');
close(poly.geometry.coordinates,
[[[13.392285704612732, 52.54974045706258], [13.392264246940613, 52.549737195107554],
[13.392248153686523, 52.549737195107554], [13.392248153686523, 52.54974045706258],
[13.392285704612732, 52.54974045706258]]]);

t.end();
})
});

test('VectorTileLayer', function(t) {
Expand Down

0 comments on commit b15016a

Please sign in to comment.