@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 +3 -0
- package/.travis.yml +7 -0
- package/CHANGELOG +0 -0
- package/CHANGELOG.md +41 -0
- package/LICENSE.txt +28 -0
- package/README.md +104 -0
- package/fixtures.js +157 -0
- package/index.js +3 -0
- package/lib/vectortile.js +17 -0
- package/lib/vectortilefeature.js +245 -0
- package/lib/vectortilelayer.js +61 -0
- package/package.json +33 -0
- package/proto/vector_tile.proto +88 -0
- package/test/bench.js +36 -0
- package/test/fixtures/12-1143-1497.vector.pbf +0 -0
- package/test/fixtures/14-8801-5371.vector.pbf +0 -0
- package/test/fixtures/lots-of-tags.vector.pbf +0 -0
- package/test/fixtures/multi-line.pbf +4 -0
- package/test/fixtures/multi-point.pbf +2 -0
- package/test/fixtures/multi-polygon.pbf +0 -0
- package/test/fixtures/multipolygon-with-closepath.pbf +0 -0
- package/test/fixtures/multipolygon.pbf +0 -0
- package/test/fixtures/polygon-with-inner.pbf +0 -0
- package/test/fixtures/singleton-multi-line.pbf +3 -0
- package/test/fixtures/singleton-multi-point.pbf +2 -0
- package/test/fixtures/singleton-multi-polygon.pbf +0 -0
- package/test/fixtures/stacked-multipolygon.pbf +0 -0
- package/test/fixtures/zero-line.pbf +0 -0
- package/test/fixtures/zero-point.pbf +0 -0
- package/test/fixtures/zero-polygon.pbf +0 -0
- package/test/parse.test.js +225 -0
package/.eslintrc
ADDED
package/.travis.yml
ADDED
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
|
+
[](http://travis-ci.org/mapbox/vector-tile-js) [](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) — 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) `— layer name
|
|
80
|
+
- **extent** (`Number`, default: `4096`) — tile extent size
|
|
81
|
+
- **length** (`Number`) — number of features in the layer
|
|
82
|
+
|
|
83
|
+
#### Methods
|
|
84
|
+
|
|
85
|
+
- **feature(i)** — 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`) — type of the feature (also see `VectorTileFeature.types`)
|
|
95
|
+
- **extent** (`Number`) — feature extent size
|
|
96
|
+
- **id** (`Number`) — feature identifier, if present
|
|
97
|
+
- **properties** (`Object`) — object literal with feature properties
|
|
98
|
+
|
|
99
|
+
#### Methods
|
|
100
|
+
|
|
101
|
+
- **loadGeometry()** — 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()** — calculates and returns the bounding box of the feature in the form `[x1, y1, x2, y2]`
|
|
104
|
+
- **toGeoJSON(x, y, z)** — 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,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
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
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
|
+
});
|