@mapgis/vector-tile 16.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "eslint-config-unstyled"
3
+ }
package/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: node_js
2
+ sudo: false
3
+ node_js:
4
+ - 4
5
+ script:
6
+ - npm test
7
+ - npm run cov
package/CHANGELOG ADDED
File without changes
package/CHANGELOG.md ADDED
@@ -0,0 +1,41 @@
1
+ ## vector-tile-js changelog
2
+
3
+ ### 1.3.1 (2017-03-02)
4
+
5
+ - Fix bug causing infinite loop when parsing ClosePath command (#61) h/t @sanjin-saric
6
+ - Pin node-mapnik dependency to `~3.6.0` (#62, see also https://github.com/mapnik/node-mapnik/issues/848)
7
+
8
+ ### 1.3.0 (2016-07-18)
9
+
10
+ - Added "id" property to VectorTileFeature (#43)
11
+
12
+ ### 1.2.1 (2016-05-18)
13
+
14
+ - Fixed geometry structure of MultiPoints, Polygons, and MultiPolygons in toGeoJSON()
15
+
16
+ ### 1.2.0 (2015-12-10)
17
+
18
+ - Added "id" property to toGeoJSON() output
19
+
20
+ ### 1.1.3 (2015-06-15)
21
+
22
+ - Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
23
+
24
+ ### 1.1.2 (2015-03-05)
25
+
26
+ - Fixed decoding of negative values in feature properties
27
+
28
+ ### 1.1.1 (2015-02-25)
29
+
30
+ - Remove sphericalmercator dependency
31
+ - Correctly handle MultiPoint and MultiLineString features in toGeoJSON()
32
+
33
+ ### 1.1.0 (2015-02-21)
34
+
35
+ - Added VectorTileFeature#toGeoJSON()
36
+
37
+ ### 1.0.0 (2014-12-26)
38
+
39
+ ### 0.0.1 (2014-04-13)
40
+
41
+ - Initial release
package/LICENSE.txt ADDED
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2014, Mapbox
2
+
3
+
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without modification,
7
+ are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice,
10
+ this list of conditions and the following disclaimer.
11
+ * Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+ * Neither the name of Mapbox nor the names of its contributors
15
+ may be used to endorse or promote products derived from this software
16
+ without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # vector-tile
2
+
3
+ [![build status](https://secure.travis-ci.org/mapbox/vector-tile-js.svg)](http://travis-ci.org/mapbox/vector-tile-js) [![Coverage Status](https://coveralls.io/repos/mapbox/vector-tile-js/badge.svg)](https://coveralls.io/r/mapbox/vector-tile-js)
4
+
5
+ This library reads [Mapbox Vector Tiles](https://github.com/mapbox/vector-tile-spec) and allows access to the layers and features.
6
+
7
+ ## Example
8
+
9
+ ```js
10
+ var VectorTile = require('@mapbox/vector-tile').VectorTile;
11
+ var Protobuf = require('pbf');
12
+
13
+ var tile = new VectorTile(new Protobuf(data));
14
+
15
+ // Contains a map of all layers
16
+ tile.layers;
17
+
18
+ var landuse = tile.layers.landuse;
19
+
20
+ // Amount of features in this layer
21
+ landuse.length;
22
+
23
+ // Returns the first feature
24
+ landuse.feature(0);
25
+ ```
26
+
27
+ Vector tiles contained in [serialtiles-spec](https://github.com/mapbox/serialtiles-spec)
28
+ are gzip-encoded, so a complete example of parsing them with the native
29
+ zlib module would be:
30
+
31
+ ```js
32
+ var VectorTile = require('vector-tile').VectorTile;
33
+ var Protobuf = require('pbf');
34
+ var zlib = require('zlib');
35
+
36
+ zlib.gunzip(data, function(err, buffer) {
37
+ var tile = new VectorTile(new Protobuf(buffer));
38
+ });
39
+ ```
40
+
41
+ ## Depends
42
+
43
+ - Node.js v0.10.x or v0.8.x
44
+
45
+
46
+ ## Install
47
+
48
+ To install:
49
+
50
+ npm install @mapbox/vector-tile
51
+
52
+
53
+ ## API Reference
54
+
55
+
56
+ ### VectorTile
57
+
58
+ An object that parses vector tile data and makes it readable.
59
+
60
+ #### Constructor
61
+
62
+ - **new VectorTile(protobuf[, end])** —
63
+ parses the vector tile data contained in the given [Protobuf](https://github.com/mapbox/pbf) object,
64
+ saving resulting layers in the created object as a `layers` property. Optionally accepts end index.
65
+
66
+ #### Properties
67
+
68
+ - **layers** (Object) &mdash; an object containing parsed layers in the form of `{<name>: <layer>, ...}`,
69
+ where each layer is a `VectorTileLayer` object.
70
+
71
+
72
+ ### VectorTileLayer
73
+
74
+ An object that contains the data for a single vector tile layer.
75
+
76
+ #### Properties
77
+
78
+ - **version** (`Number`, default: `1`)
79
+ - **name** (`String) `&mdash; layer name
80
+ - **extent** (`Number`, default: `4096`) &mdash; tile extent size
81
+ - **length** (`Number`) &mdash; number of features in the layer
82
+
83
+ #### Methods
84
+
85
+ - **feature(i)** &mdash; get a feature (`VectorTileFeature`) by the given index from the layer.
86
+
87
+
88
+ ### VectorTileFeature
89
+
90
+ An object that contains the data for a single feature.
91
+
92
+ #### Properties
93
+
94
+ - **type** (`Number`) &mdash; type of the feature (also see `VectorTileFeature.types`)
95
+ - **extent** (`Number`) &mdash; feature extent size
96
+ - **id** (`Number`) &mdash; feature identifier, if present
97
+ - **properties** (`Object`) &mdash; object literal with feature properties
98
+
99
+ #### Methods
100
+
101
+ - **loadGeometry()** &mdash; parses feature geometry and returns an array of
102
+ [Point](https://github.com/mapbox/point-geometry) arrays (with each point having `x` and `y` properties)
103
+ - **bbox()** &mdash; calculates and returns the bounding box of the feature in the form `[x1, y1, x2, y2]`
104
+ - **toGeoJSON(x, y, z)** &mdash; returns a GeoJSON representation of the feature. (`x`, `y`, and `z` refer to the containing tile's index.)
package/fixtures.js ADDED
@@ -0,0 +1,157 @@
1
+ var mapnik = require('mapnik');
2
+ var path = require('path');
3
+ var fs = require('fs');
4
+
5
+ mapnik.register_datasource(path.join(mapnik.settings.paths.input_plugins, 'geojson.input'));
6
+
7
+ var fixtures = {
8
+ "zero-point": {
9
+ "type": "FeatureCollection",
10
+ "features": [
11
+ {
12
+ "type": "Feature",
13
+ "geometry": {
14
+ "type": "MultiPoint",
15
+ "coordinates": []
16
+ },
17
+ "properties": {}
18
+ }
19
+ ]
20
+ },
21
+ "zero-line": {
22
+ "type": "FeatureCollection",
23
+ "features": [
24
+ {
25
+ "type": "Feature",
26
+ "geometry": {
27
+ "type": "MultiLineString",
28
+ "coordinates": []
29
+ },
30
+ "properties": {}
31
+ }
32
+ ]
33
+ },
34
+ "zero-polygon": {
35
+ "type": "FeatureCollection",
36
+ "features": [
37
+ {
38
+ "type": "Feature",
39
+ "geometry": {
40
+ "type": "MultiPolygon",
41
+ "coordinates": []
42
+ },
43
+ "properties": {}
44
+ }
45
+ ]
46
+ },
47
+ "singleton-multi-point": {
48
+ "type": "FeatureCollection",
49
+ "features": [
50
+ {
51
+ "type": "Feature",
52
+ "geometry": {
53
+ "type": "MultiPoint",
54
+ "coordinates": [[1, 2]]
55
+ },
56
+ "properties": {}
57
+ }
58
+ ]
59
+ },
60
+ "singleton-multi-line": {
61
+ "type": "FeatureCollection",
62
+ "features": [
63
+ {
64
+ "type": "Feature",
65
+ "geometry": {
66
+ "type": "MultiLineString",
67
+ "coordinates": [[[1, 2], [3, 4]]]
68
+ },
69
+ "properties": {}
70
+ }
71
+ ]
72
+ },
73
+ "singleton-multi-polygon": {
74
+ "type": "FeatureCollection",
75
+ "features": [
76
+ {
77
+ "type": "Feature",
78
+ "geometry": {
79
+ "type": "MultiPolygon",
80
+ "coordinates": [[[[0, 0], [1, 0], [1, 1], [0, 0]]]]
81
+ },
82
+ "properties": {}
83
+ }
84
+ ]
85
+ },
86
+ "multi-point": {
87
+ "type": "FeatureCollection",
88
+ "features": [
89
+ {
90
+ "type": "Feature",
91
+ "geometry": {
92
+ "type": "MultiPoint",
93
+ "coordinates": [[1, 2], [3, 4]]
94
+ },
95
+ "properties": {}
96
+ }
97
+ ]
98
+ },
99
+ "multi-line": {
100
+ "type": "FeatureCollection",
101
+ "features": [
102
+ {
103
+ "type": "Feature",
104
+ "geometry": {
105
+ "type": "MultiLineString",
106
+ "coordinates": [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
107
+ },
108
+ "properties": {}
109
+ }
110
+ ]
111
+ },
112
+ "multi-polygon": {
113
+ "type": "FeatureCollection",
114
+ "features": [
115
+ {
116
+ "type": "Feature",
117
+ "geometry": {
118
+ "type": "MultiPolygon",
119
+ "coordinates": [[[[0, 0], [1, 0], [1, 1], [0, 0]]], [[[0, 0], [-1, 0], [-1, -1], [0, 0]]]]
120
+ },
121
+ "properties": {}
122
+ }
123
+ ]
124
+ },
125
+ "polygon-with-inner": {
126
+ "type": "FeatureCollection",
127
+ "features": [
128
+ {
129
+ "type": "Feature",
130
+ "geometry": {
131
+ "type": "Polygon",
132
+ "coordinates": [[[-2, 2], [2, 2], [2, -2], [-2, -2], [-2, 2]], [[-1, 1], [1, 1], [1, -1], [-1, -1], [-1, 1]]]
133
+ },
134
+ "properties": {}
135
+ }
136
+ ]
137
+ },
138
+ "stacked-multipolygon": {
139
+ "type": "FeatureCollection",
140
+ "features": [
141
+ {
142
+ "type": "Feature",
143
+ "geometry": {
144
+ "type": "MultiPolygon",
145
+ "coordinates": [[[[-2, 2], [2, 2], [2, -2], [-2, -2], [-2, 2]]], [[[-1, 1], [1, 1], [1, -1], [-1, -1], [-1, 1]]]]
146
+ },
147
+ "properties": {}
148
+ }
149
+ ]
150
+ }
151
+ }
152
+
153
+ for (var fixture in fixtures) {
154
+ var vtile = new mapnik.VectorTile(0, 0, 0);
155
+ vtile.addGeoJSON(JSON.stringify(fixtures[fixture]), "geojson");
156
+ fs.writeFileSync('./test/fixtures/' + fixture + '.pbf', vtile.getData());
157
+ }
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ module.exports.VectorTile = require('./lib/vectortile.js');
2
+ module.exports.VectorTileFeature = require('./lib/vectortilefeature.js');
3
+ module.exports.VectorTileLayer = require('./lib/vectortilelayer.js');
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ var VectorTileLayer = require('./vectortilelayer');
4
+
5
+ module.exports = VectorTile;
6
+
7
+ function VectorTile(pbf, end) {
8
+ this.layers = pbf.readFields(readTile, {}, end);
9
+ }
10
+
11
+ function readTile(tag, layers, pbf) {
12
+ if (tag === 3) {
13
+ var layer = new VectorTileLayer(pbf, pbf.readVarint() + pbf.pos);
14
+ if (layer.length) layers[layer.name] = layer;
15
+ }
16
+ }
17
+
@@ -0,0 +1,245 @@
1
+ 'use strict';
2
+
3
+ var Point = require('@mapbox/point-geometry');
4
+
5
+ module.exports = VectorTileFeature;
6
+
7
+ function VectorTileFeature(pbf, end, extent, keys, values) {
8
+ // Public
9
+ this.properties = {};
10
+ this.extent = extent;
11
+ this.type = 0;
12
+
13
+ // Private
14
+ this._pbf = pbf;
15
+ this._geometry = -1;
16
+ this._keys = keys;
17
+ this._values = values;
18
+
19
+ pbf.readFields(readFeature, this, end);
20
+ }
21
+
22
+ function readFeature(tag, feature, pbf) {
23
+ if (tag == 1) feature.id = pbf.readVarint();
24
+ else if (tag == 2) readTag(pbf, feature);
25
+ else if (tag == 3) feature.type = pbf.readVarint();
26
+ else if (tag == 4) feature._geometry = pbf.pos;
27
+ }
28
+
29
+ function readTag(pbf, feature) {
30
+ var end = pbf.readVarint() + pbf.pos;
31
+
32
+ while (pbf.pos < end) {
33
+ var key = feature._keys[pbf.readVarint()],
34
+ value = feature._values[pbf.readVarint()];
35
+ feature.properties[key] = value;
36
+ }
37
+ }
38
+
39
+ VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
40
+
41
+ VectorTileFeature.prototype.loadGeometry = function() {
42
+ var pbf = this._pbf;
43
+ pbf.pos = this._geometry;
44
+
45
+ var end = pbf.readVarint() + pbf.pos,
46
+ cmd = 1,
47
+ length = 0,
48
+ x = 0,
49
+ y = 0,
50
+ lines = [],
51
+ line;
52
+
53
+ while (pbf.pos < end) {
54
+ if (length <= 0) {
55
+ var cmdLen = pbf.readVarint();
56
+ cmd = cmdLen & 0x7;
57
+ length = cmdLen >> 3;
58
+ }
59
+
60
+ length--;
61
+
62
+ if (cmd === 1 || cmd === 2) {
63
+ x += pbf.readSVarint();
64
+ y += pbf.readSVarint();
65
+
66
+ if (cmd === 1) { // moveTo
67
+ if (line) lines.push(line);
68
+ line = [];
69
+ }
70
+
71
+ line.push(new Point(x, y));
72
+
73
+ } else if (cmd === 7) {
74
+
75
+ // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
76
+ if (line) {
77
+ line.push(line[0].clone()); // closePolygon
78
+ }
79
+
80
+ } else {
81
+ throw new Error('unknown command ' + cmd);
82
+ }
83
+ }
84
+
85
+ if (line) lines.push(line);
86
+
87
+ return lines;
88
+ };
89
+
90
+ VectorTileFeature.prototype.bbox = function() {
91
+ var pbf = this._pbf;
92
+ pbf.pos = this._geometry;
93
+
94
+ var end = pbf.readVarint() + pbf.pos,
95
+ cmd = 1,
96
+ length = 0,
97
+ x = 0,
98
+ y = 0,
99
+ x1 = Infinity,
100
+ x2 = -Infinity,
101
+ y1 = Infinity,
102
+ y2 = -Infinity;
103
+
104
+ while (pbf.pos < end) {
105
+ if (length <= 0) {
106
+ var cmdLen = pbf.readVarint();
107
+ cmd = cmdLen & 0x7;
108
+ length = cmdLen >> 3;
109
+ }
110
+
111
+ length--;
112
+
113
+ if (cmd === 1 || cmd === 2) {
114
+ x += pbf.readSVarint();
115
+ y += pbf.readSVarint();
116
+ if (x < x1) x1 = x;
117
+ if (x > x2) x2 = x;
118
+ if (y < y1) y1 = y;
119
+ if (y > y2) y2 = y;
120
+
121
+ } else if (cmd !== 7) {
122
+ throw new Error('unknown command ' + cmd);
123
+ }
124
+ }
125
+
126
+ return [x1, y1, x2, y2];
127
+ };
128
+
129
+ VectorTileFeature.prototype.toGeoJSON = function(x, y, z, epsg) {
130
+ console.log('epsg', epsg);
131
+ epsg = epsg || 'EPSG:3857' // EPSG:4326 or EPSG:3857
132
+ var size = this.extent * Math.pow(2, z),
133
+ x0 = this.extent * x,
134
+ y0 = this.extent * y,
135
+ coords = this.loadGeometry(),
136
+ type = VectorTileFeature.types[this.type],
137
+ i, j;
138
+
139
+ function project(line) {
140
+ if (epsg === 'EPSG:3857') {
141
+ for (var j = 0; j < line.length; j++) {
142
+ var p = line[j], y2 = 180 - (p.y + y0) * 360 / size;
143
+ line[j] = [
144
+ (p.x + x0) * 360 / size - 180,
145
+ 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90
146
+ ];
147
+ }
148
+ } else if (epsg === 'EPSG:4326') {
149
+ for (var j = 0; j < line.length; j++) {
150
+ var p = line[j]
151
+ line[j] = [
152
+ (p.x + x0) * 360 / size - 180,
153
+ 90 - (p.y + y0) * 360 / size ,
154
+ ];
155
+ }
156
+ }
157
+ }
158
+
159
+ switch (this.type) {
160
+ case 1:
161
+ var points = [];
162
+ for (i = 0; i < coords.length; i++) {
163
+ points[i] = coords[i][0];
164
+ }
165
+ coords = points;
166
+ project(coords);
167
+ break;
168
+
169
+ case 2:
170
+ for (i = 0; i < coords.length; i++) {
171
+ project(coords[i]);
172
+ }
173
+ break;
174
+
175
+ case 3:
176
+ coords = classifyRings(coords);
177
+ for (i = 0; i < coords.length; i++) {
178
+ for (j = 0; j < coords[i].length; j++) {
179
+ project(coords[i][j]);
180
+ }
181
+ }
182
+ break;
183
+ }
184
+
185
+ if (coords.length === 1) {
186
+ coords = coords[0];
187
+ } else {
188
+ type = 'Multi' + type;
189
+ }
190
+
191
+ var result = {
192
+ type: "Feature",
193
+ geometry: {
194
+ type: type,
195
+ coordinates: coords
196
+ },
197
+ properties: this.properties
198
+ };
199
+
200
+ if ('id' in this) {
201
+ result.id = this.id;
202
+ }
203
+
204
+ return result;
205
+ };
206
+
207
+ // classifies an array of rings into polygons with outer rings and holes
208
+
209
+ function classifyRings(rings) {
210
+ var len = rings.length;
211
+
212
+ if (len <= 1) return [rings];
213
+
214
+ var polygons = [],
215
+ polygon,
216
+ ccw;
217
+
218
+ for (var i = 0; i < len; i++) {
219
+ var area = signedArea(rings[i]);
220
+ if (area === 0) continue;
221
+
222
+ if (ccw === undefined) ccw = area < 0;
223
+
224
+ if (ccw === area < 0) {
225
+ if (polygon) polygons.push(polygon);
226
+ polygon = [rings[i]];
227
+
228
+ } else {
229
+ polygon.push(rings[i]);
230
+ }
231
+ }
232
+ if (polygon) polygons.push(polygon);
233
+
234
+ return polygons;
235
+ }
236
+
237
+ function signedArea(ring) {
238
+ var sum = 0;
239
+ for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
240
+ p1 = ring[i];
241
+ p2 = ring[j];
242
+ sum += (p2.x - p1.x) * (p1.y + p2.y);
243
+ }
244
+ return sum;
245
+ }
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ var VectorTileFeature = require('./vectortilefeature.js');
4
+
5
+ module.exports = VectorTileLayer;
6
+
7
+ function VectorTileLayer(pbf, end) {
8
+ // Public
9
+ this.version = 1;
10
+ this.name = null;
11
+ this.extent = 4096;
12
+ this.length = 0;
13
+
14
+ // Private
15
+ this._pbf = pbf;
16
+ this._keys = [];
17
+ this._values = [];
18
+ this._features = [];
19
+
20
+ pbf.readFields(readLayer, this, end);
21
+
22
+ this.length = this._features.length;
23
+ }
24
+
25
+ function readLayer(tag, layer, pbf) {
26
+ if (tag === 15) layer.version = pbf.readVarint();
27
+ else if (tag === 1) layer.name = pbf.readString();
28
+ else if (tag === 5) layer.extent = pbf.readVarint();
29
+ else if (tag === 2) layer._features.push(pbf.pos);
30
+ else if (tag === 3) layer._keys.push(pbf.readString());
31
+ else if (tag === 4) layer._values.push(readValueMessage(pbf));
32
+ }
33
+
34
+ function readValueMessage(pbf) {
35
+ var value = null,
36
+ end = pbf.readVarint() + pbf.pos;
37
+
38
+ while (pbf.pos < end) {
39
+ var tag = pbf.readVarint() >> 3;
40
+
41
+ value = tag === 1 ? pbf.readString() :
42
+ tag === 2 ? pbf.readFloat() :
43
+ tag === 3 ? pbf.readDouble() :
44
+ tag === 4 ? pbf.readVarint64() :
45
+ tag === 5 ? pbf.readVarint() :
46
+ tag === 6 ? pbf.readSVarint() :
47
+ tag === 7 ? pbf.readBoolean() : null;
48
+ }
49
+
50
+ return value;
51
+ }
52
+
53
+ // return feature `i` from this layer as a `VectorTileFeature`
54
+ VectorTileLayer.prototype.feature = function(i) {
55
+ if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds');
56
+
57
+ this._pbf.pos = this._features[i];
58
+
59
+ var end = this._pbf.readVarint() + this._pbf.pos;
60
+ return new VectorTileFeature(this._pbf, end, this.extent, this._keys, this._values);
61
+ };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@mapgis/vector-tile",
3
+ "description": "Parses vector tiles",
4
+ "repository": "https://github.com/mapbox/vector-tile-js.git",
5
+ "version":"16.6.0",
6
+ "license": "BSD-3-Clause",
7
+ "main": "index.js",
8
+ "dependencies": {
9
+ "@mapbox/point-geometry": "~0.1.0"
10
+ },
11
+ "devDependencies": {
12
+ "benchmark": "^1.0.0",
13
+ "coveralls": "~2.11.2",
14
+ "istanbul": "~0.3.6",
15
+ "mapnik": "~3.6.0",
16
+ "jshint": "^2.6.3",
17
+ "pbf": "^1.3.2",
18
+ "tape": "~3.5.0",
19
+ "eslint": "~1.00.0",
20
+ "eslint-config-unstyled": "^1.1.0"
21
+ },
22
+ "jshintConfig": {
23
+ "trailing": true,
24
+ "undef": true,
25
+ "unused": true,
26
+ "indent": 4,
27
+ "node": true
28
+ },
29
+ "scripts": {
30
+ "test": "eslint lib index.js && jshint lib && tape test/parse.test.js",
31
+ "cov": "istanbul cover ./node_modules/.bin/tape test/parse.test.js && coveralls < ./coverage/lcov.info"
32
+ }
33
+ }
@@ -0,0 +1,88 @@
1
+ // Protocol Version 1
2
+
3
+ message tile {
4
+ enum GeomType {
5
+ Unknown = 0;
6
+ Point = 1;
7
+ LineString = 2;
8
+ Polygon = 3;
9
+ }
10
+
11
+ // Variant type encoding
12
+ message value {
13
+ // Exactly one of these values may be present in a valid message
14
+ optional string string_value = 1;
15
+ optional float float_value = 2;
16
+ optional double double_value = 3;
17
+ optional int64 int_value = 4;
18
+ optional uint64 uint_value = 5;
19
+ optional sint64 sint_value = 6;
20
+ optional bool bool_value = 7;
21
+
22
+ extensions 8 to max;
23
+ }
24
+
25
+ message feature {
26
+ optional uint64 id = 1;
27
+
28
+ // Tags of this feature. Even numbered values refer to the nth
29
+ // value in the keys list on the tile message, odd numbered
30
+ // values refer to the nth value in the values list on the tile
31
+ // message.
32
+ repeated uint32 tags = 2 [ packed = true ];
33
+
34
+ // The type of geometry stored in this feature.
35
+ optional GeomType type = 3 [ default = Unknown ];
36
+
37
+ // Contains a stream of commands and parameters (vertices). The
38
+ // repeat count is shifted to the left by 3 bits. This means
39
+ // that the command has 3 bits (0-7). The repeat count
40
+ // indicates how often this command is to be repeated. Defined
41
+ // commands are:
42
+ // - MoveTo: 1 (2 parameters follow)
43
+ // - LineTo: 2 (2 parameters follow)
44
+ // - ClosePath: 7 (no parameters follow)
45
+ //
46
+ // Ex.: MoveTo(3, 6), LineTo(8, 12), LineTo(20, 34), ClosePath
47
+ // Encoded as: [ 9 3 6 18 5 6 12 22 15 ]
48
+ // == command type 7 (ClosePath), length 1
49
+ // ===== relative LineTo(+12, +22) == LineTo(20, 34)
50
+ // === relative LineTo(+5, +6) == LineTo(8, 12)
51
+ // == [00010 010] = command type 2 (LineTo), length 2
52
+ // === relative MoveTo(+3, +6)
53
+ // == [00001 001] = command type 1 (MoveTo), length 1
54
+ // Commands are encoded as uint32 varints, vertex parameters are
55
+ // encoded as sint32 varints (zigzag). Vertex parameters are
56
+ // also encoded as deltas to the previous position. The original
57
+ // position is (0,0)
58
+ repeated uint32 geometry = 4 [ packed = true ];
59
+ }
60
+
61
+ message layer {
62
+ // Any compliant implementation must first read the version
63
+ // number encoded in this message and choose the correct
64
+ // implementation for this version number before proceeding to
65
+ // decode other parts of this message.
66
+ required uint32 version = 15 [ default = 1 ];
67
+
68
+ required string name = 1;
69
+
70
+ // The actual features in this tile.
71
+ repeated feature features = 2;
72
+
73
+ // Dictionary encoding for keys
74
+ repeated string keys = 3;
75
+
76
+ // Dictionary encoding for values
77
+ repeated value values = 4;
78
+
79
+ // The bounding box in this tile spans from 0..4095 units
80
+ optional uint32 extent = 5 [ default = 4096 ];
81
+
82
+ extensions 16 to max;
83
+ }
84
+
85
+ repeated layer layers = 3;
86
+
87
+ extensions 16 to 8191;
88
+ }
package/test/bench.js ADDED
@@ -0,0 +1,36 @@
1
+ var Pbf = require('pbf'),
2
+ VectorTile = require('..').VectorTile,
3
+ Benchmark = require('benchmark'),
4
+ fs = require('fs');
5
+
6
+ var suite = new Benchmark.Suite(),
7
+ data = fs.readFileSync(__dirname + '/fixtures/14-8801-5371.vector.Pbf');
8
+
9
+ readTile(); // output any errors before running the suite
10
+ readTile(true);
11
+
12
+ suite
13
+ .add('read tile with geometries', function() {
14
+ readTile(true);
15
+ })
16
+ .add('read tile without geometries', function() {
17
+ readTile();
18
+ })
19
+ .on('cycle', function(event) {
20
+ console.log(String(event.target));
21
+ })
22
+ .run();
23
+
24
+
25
+ function readTile(loadGeom, loadPacked) {
26
+ var buf = new Pbf(data),
27
+ vt = new VectorTile(buf);
28
+
29
+ for (var id in vt.layers) {
30
+ var layer = vt.layers[id];
31
+ for (var i = 0; i < layer.length; i++) {
32
+ var feature = layer.feature(i);
33
+ if (loadGeom) feature.loadGeometry();
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,4 @@
1
+ $x
2
+ geojson(� " � �
3
+ .- .+
4
+ .-
@@ -0,0 +1,2 @@
1
+ x
2
+ geojson(� 
Binary file
Binary file
@@ -0,0 +1,3 @@
1
+ x
2
+ geojson(� " � �
3
+ .-
@@ -0,0 +1,2 @@
1
+ x
2
+ geojson(�  " � �
File without changes
File without changes
File without changes
@@ -0,0 +1,225 @@
1
+ var test = require('tape'),
2
+ fs = require('fs'),
3
+ Protobuf = require('pbf'),
4
+ VectorTile = require('..').VectorTile,
5
+ VectorTileLayer = require('..').VectorTileLayer,
6
+ VectorTileFeature = require('..').VectorTileFeature;
7
+
8
+ function approximateDeepEqual(a, b, epsilon) {
9
+ epsilon = epsilon || 1e-6;
10
+
11
+ if (typeof a !== typeof b)
12
+ return false;
13
+ if (typeof a === 'number')
14
+ return Math.abs(a - b) < epsilon;
15
+ if (a === null || typeof a !== 'object')
16
+ return a === b;
17
+
18
+ var ka = Object.keys(a);
19
+ var kb = Object.keys(b);
20
+
21
+ if (ka.length != kb.length)
22
+ return false;
23
+
24
+ ka.sort();
25
+ kb.sort();
26
+
27
+ for (var i = 0; i < ka.length; i++)
28
+ if (ka[i] != kb[i] || !approximateDeepEqual(a[ka[i]], b[ka[i]], epsilon))
29
+ return false;
30
+
31
+ return true;
32
+ }
33
+
34
+ test('parsing vector tiles', function(t) {
35
+ var data = fs.readFileSync(__dirname + '/fixtures/14-8801-5371.vector.pbf');
36
+
37
+ t.test('should have all layers', function(t) {
38
+ var tile = new VectorTile(new Protobuf(data));
39
+
40
+ t.deepEqual(Object.keys(tile.layers), [
41
+ 'landuse', 'waterway', 'water', 'barrier_line', 'building',
42
+ 'landuse_overlay', 'tunnel', 'road', 'bridge', 'place_label',
43
+ 'water_label', 'poi_label', 'road_label', 'waterway_label' ]);
44
+
45
+ t.end();
46
+ });
47
+
48
+ t.test('should extract the tags of a feature', function(t) {
49
+ var tile = new VectorTile(new Protobuf(data));
50
+
51
+ t.equal(tile.layers.poi_label.length, 558);
52
+
53
+ var park = tile.layers.poi_label.feature(11);
54
+
55
+ t.deepEqual(park.bbox(), [ 3898, 1731, 3898, 1731 ]);
56
+
57
+ t.throws(function() {
58
+ var park = tile.layers.poi_label.feature(1e9);
59
+ }, 'throws on reading a feature out of bounds');
60
+
61
+ t.equal(park.id, 3000003150561);
62
+
63
+ t.equal(park.properties.name, 'Mauerpark');
64
+ t.equal(park.properties.type, 'Park');
65
+
66
+ // Check point geometry
67
+ t.deepEqual(park.loadGeometry(), [ [ { x: 3898, y: 1731 } ] ]);
68
+
69
+ // Check line geometry
70
+ t.deepEqual(tile.layers.road.feature(656).loadGeometry(), [ [ { x: 1988, y: 306 }, { x: 1808, y: 321 }, { x: 1506, y: 347 } ] ]);
71
+ t.end();
72
+ });
73
+
74
+ t.test('changing first point of a polygon should not change last point', function(t) {
75
+ var tile = new VectorTile(new Protobuf(data));
76
+
77
+ var building = tile.layers.building.feature(0).loadGeometry();
78
+ t.deepEqual(building, [ [ { x: 2039, y: -32 }, { x: 2035, y: -31 }, { x: 2032, y: -31 }, { x: 2032, y: -32 }, { x: 2039, y: -32 } ] ]);
79
+ building[0][0].x = 1;
80
+ building[0][0].y = 2;
81
+ building[0][1].x = 3;
82
+ building[0][1].y = 4;
83
+ t.deepEqual(building, [ [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 2032, y: -31 }, { x: 2032, y: -32 }, { x: 2039, y: -32 } ] ]);
84
+ t.end();
85
+ });
86
+
87
+ t.test('toGeoJSON', function(t) {
88
+ var tile = new VectorTile(new Protobuf(data));
89
+
90
+ t.ok(approximateDeepEqual(tile.layers.poi_label.feature(11).toGeoJSON(8801, 5371, 14), {
91
+ type: 'Feature',
92
+ id: 3000003150561,
93
+ properties: {
94
+ localrank: 1,
95
+ maki: 'park',
96
+ name: 'Mauerpark',
97
+ name_de: 'Mauerpark',
98
+ name_en: 'Mauerpark',
99
+ name_es: 'Mauerpark',
100
+ name_fr: 'Mauerpark',
101
+ osm_id: 3000003150561,
102
+ ref: '',
103
+ scalerank: 2,
104
+ type: 'Park'
105
+ },
106
+ geometry: {
107
+ type: 'Point',
108
+ coordinates: [13.402258157730103, 52.54398925380624]
109
+ }
110
+ }));
111
+
112
+ t.ok(approximateDeepEqual(tile.layers.bridge.feature(0).toGeoJSON(8801, 5371, 14), {
113
+ type: 'Feature',
114
+ id: 238162948,
115
+ properties: {
116
+ class: 'service',
117
+ oneway: 0,
118
+ osm_id: 238162948,
119
+ type: 'service'
120
+ },
121
+ geometry: {
122
+ type: 'LineString',
123
+ coordinates: [[13.399457931518555, 52.546334844036416], [13.399441838264465, 52.546504478525016]]
124
+ }
125
+ }));
126
+
127
+ t.ok(approximateDeepEqual(tile.layers.building.feature(0).toGeoJSON(8801, 5371, 14), {
128
+ type: 'Feature',
129
+ id: 1000267229912,
130
+ properties: {
131
+ osm_id: 1000267229912
132
+ },
133
+ geometry: {
134
+ type: 'Polygon',
135
+ coordinates: [[[13.392285704612732, 52.54974045706258], [13.392264246940613, 52.549737195107554],
136
+ [13.392248153686523, 52.549737195107554], [13.392248153686523, 52.54974045706258],
137
+ [13.392285704612732, 52.54974045706258]]]
138
+ }
139
+ }));
140
+
141
+ function geoJSONFromFixture(name) {
142
+ var tile = new VectorTile(new Protobuf(fs.readFileSync(__dirname + '/fixtures/' + name + '.pbf')));
143
+ return tile.layers.geojson.feature(0).toGeoJSON(0, 0, 0);
144
+ }
145
+
146
+ // https://github.com/mapbox/vector-tile-spec/issues/30
147
+ t.ok(approximateDeepEqual(geoJSONFromFixture("singleton-multi-point").geometry, {
148
+ type: 'Point',
149
+ coordinates: [1, 2]
150
+ }, 1e-1));
151
+ t.ok(approximateDeepEqual(geoJSONFromFixture("singleton-multi-line").geometry, {
152
+ type: 'LineString',
153
+ coordinates: [[1, 2], [3, 4]]
154
+ }, 1e-1));
155
+ t.ok(approximateDeepEqual(geoJSONFromFixture("singleton-multi-polygon").geometry, {
156
+ type: 'Polygon',
157
+ coordinates: [[[1, 0], [0, 0], [1, 1], [1, 0]]]
158
+ }, 1e-1));
159
+
160
+ t.ok(approximateDeepEqual(geoJSONFromFixture("multi-point").geometry, {
161
+ type: 'MultiPoint',
162
+ coordinates: [[1, 2], [3, 4]]
163
+ }, 1e-1));
164
+ t.ok(approximateDeepEqual(geoJSONFromFixture("multi-line").geometry, {
165
+ type: 'MultiLineString',
166
+ coordinates: [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
167
+ }, 1e-1));
168
+ t.ok(approximateDeepEqual(geoJSONFromFixture("multi-polygon").geometry, {
169
+ type: 'MultiPolygon',
170
+ coordinates: [[[[1, 0], [0, 0], [1, 1], [1, 0]]], [[[-1, -1], [-1, 0], [0, 0], [-1, -1]]]]
171
+ }, 1e-1));
172
+
173
+ // https://github.com/mapbox/vector-tile-js/issues/32
174
+ t.ok(approximateDeepEqual(geoJSONFromFixture("polygon-with-inner").geometry, {
175
+ type: 'Polygon',
176
+ coordinates: [[[2, -2], [-2, -2], [-2, 2], [2, 2], [2, -2]], [[-1, 1], [-1, -1], [1, -1], [1, 1], [-1, 1]]]
177
+ }, 1e-1));
178
+ t.ok(approximateDeepEqual(geoJSONFromFixture("stacked-multipolygon").geometry, {
179
+ type: 'MultiPolygon',
180
+ coordinates: [[[[2, -2], [-2, -2], [-2, 2], [2, 2], [2, -2]]], [[[1, -1], [-1, -1], [-1, 1], [1, 1], [1, -1]]]]
181
+ }, 1e-1));
182
+
183
+ t.end();
184
+ })
185
+ });
186
+
187
+ test('VectorTileLayer', function(t) {
188
+ var emptyLayer = new VectorTileLayer(new Protobuf(new Buffer([])));
189
+ t.ok(emptyLayer, 'can be created with no values');
190
+ t.end();
191
+ });
192
+
193
+ test('VectorTileFeature', function(t) {
194
+ var emptyFeature = new VectorTileFeature(new Protobuf(new Buffer([])));
195
+ t.ok(emptyFeature, 'can be created with no values');
196
+ t.ok(Array.isArray(VectorTileFeature.types));
197
+ t.deepEqual(VectorTileFeature.types, ['Unknown', 'Point', 'LineString', 'Polygon']);
198
+ t.end();
199
+ });
200
+
201
+ test('https://github.com/mapbox/vector-tile-js/issues/15', function(t) {
202
+ var data = fs.readFileSync(__dirname + '/fixtures/lots-of-tags.vector.pbf');
203
+ var tile = new VectorTile(new Protobuf(data));
204
+ t.ok(tile.layers["stuttgart-rails"].feature(0));
205
+ t.end();
206
+ });
207
+
208
+ test('https://github.com/mapbox/mapbox-gl-js/issues/1019', function(t) {
209
+ var data = fs.readFileSync(__dirname + '/fixtures/12-1143-1497.vector.pbf');
210
+ var tile = new VectorTile(new Protobuf(data));
211
+ t.ok(tile.layers["water"].feature(1).loadGeometry());
212
+ t.end();
213
+ });
214
+
215
+ test('https://github.com/mapbox/vector-tile-js/issues/60', function(t) {
216
+ var data = fs.readFileSync(__dirname + '/fixtures/multipolygon-with-closepath.pbf');
217
+ var tile = new VectorTile(new Protobuf(data));
218
+ for (var id in tile.layers) {
219
+ var layer = tile.layers[id];
220
+ for (var i = 0; i < layer.length; i++) {
221
+ layer.feature(i).loadGeometry();
222
+ }
223
+ }
224
+ t.end();
225
+ });