diff --git a/README.md b/README.md index d3f1201..2d771d3 100644 --- a/README.md +++ b/README.md @@ -101,4 +101,4 @@ An object that contains the data for a single feature. - **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]` -- **toGeoJSON(x, y, z)** — returns a GeoJSON representation of the feature. (`x`, `y`, and `z` refer to the containing tile's index.) +- **toGeoJSON(x, y, z, project)** — returns a GeoJSON representation of the feature. `x`, `y`, and `z` refer to the containing tile's index. An optional project function can be used if the projection is not EPSG:3857, it will receive coordinates in the form `[x, y]` with `x` from 0.0 to 1.0 and `y` depending on the projection used, and it must return `[x, y]` in EPSG:4326. diff --git a/lib/vectortilefeature.js b/lib/vectortilefeature.js index b9880a1..c570a91 100644 --- a/lib/vectortilefeature.js +++ b/lib/vectortilefeature.js @@ -126,21 +126,28 @@ VectorTileFeature.prototype.bbox = function() { return [x1, y1, x2, y2]; }; -VectorTileFeature.prototype.toGeoJSON = function(x, y, z) { +VectorTileFeature.prototype.toGeoJSON = function(x, y, z, project) { var size = this.extent * Math.pow(2, z), x0 = this.extent * x, y0 = this.extent * y, coords = this.loadGeometry(), type = VectorTileFeature.types[this.type], - i, j; + i, j, + projectPoint = project || projectMercator; + + function projectMercator(point) { + return [ + point[0] * 360 - 180, + 360 / Math.PI * Math.atan(Math.exp((180 - point[1] * 360) * Math.PI / 180)) - 90 + ]; + } - function project(line) { + function projectLine(line) { for (var j = 0; j < line.length; j++) { - var p = line[j], y2 = 180 - (p.y + y0) * 360 / size; - line[j] = [ - (p.x + x0) * 360 / size - 180, - 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90 - ]; + line[j] = projectPoint([ + (line[j].x + x0) / size, + (line[j].y + y0) / size + ]); } } @@ -151,12 +158,12 @@ VectorTileFeature.prototype.toGeoJSON = function(x, y, z) { points[i] = coords[i][0]; } coords = points; - project(coords); + projectLine(coords); break; case 2: for (i = 0; i < coords.length; i++) { - project(coords[i]); + projectLine(coords[i]); } break; @@ -164,7 +171,7 @@ VectorTileFeature.prototype.toGeoJSON = function(x, y, z) { coords = classifyRings(coords); for (i = 0; i < coords.length; i++) { for (j = 0; j < coords[i].length; j++) { - project(coords[i][j]); + projectLine(coords[i][j]); } } break; diff --git a/test/parse.test.js b/test/parse.test.js index 156bedb..3db8d6c 100644 --- a/test/parse.test.js +++ b/test/parse.test.js @@ -138,6 +138,64 @@ test('parsing vector tiles', function(t) { } })); + function projectMercator(point) { + return [ + point[0] * 360 - 180, + 360 / Math.PI * Math.atan(Math.exp((180 - point[1] * 360) * Math.PI / 180)) - 90 + ]; + } + + t.ok(approximateDeepEqual(tile.layers.poi_label.feature(11).toGeoJSON(8801, 5371, 14, projectMercator), { + type: 'Feature', + id: 3000003150561, + 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' + }, + geometry: { + type: 'Point', + coordinates: [13.402258157730103, 52.54398925380624] + } + })); + + t.ok(approximateDeepEqual(tile.layers.bridge.feature(0).toGeoJSON(8801, 5371, 14, projectMercator), { + type: 'Feature', + id: 238162948, + properties: { + class: 'service', + oneway: 0, + osm_id: 238162948, + type: 'service' + }, + geometry: { + type: 'LineString', + coordinates: [[13.399457931518555, 52.546334844036416], [13.399441838264465, 52.546504478525016]] + } + })); + + t.ok(approximateDeepEqual(tile.layers.building.feature(0).toGeoJSON(8801, 5371, 14, projectMercator), { + type: 'Feature', + id: 1000267229912, + properties: { + osm_id: 1000267229912 + }, + geometry: { + type: 'Polygon', + coordinates: [[[13.392285704612732, 52.54974045706258], [13.392264246940613, 52.549737195107554], + [13.392248153686523, 52.549737195107554], [13.392248153686523, 52.54974045706258], + [13.392285704612732, 52.54974045706258]]] + } + })); + function geoJSONFromFixture(name) { var tile = new VectorTile(new Protobuf(fs.readFileSync(__dirname + '/fixtures/' + name + '.pbf'))); return tile.layers.geojson.feature(0).toGeoJSON(0, 0, 0);