vtt 0.0.1

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.
Files changed (4) hide show
  1. package/LICENSE +24 -0
  2. package/package.json +35 -0
  3. package/readme.md +72 -0
  4. package/vtt.js +240 -0
package/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "vtt",
3
+ "version": "0.0.1",
4
+ "description": "Vector Tile Transform",
5
+ "main": "vtt.js",
6
+ "author": "yetzt <node@yetzt.me>",
7
+ "license": "Unlicense",
8
+ "dependencies": {
9
+ "pbf": "^3"
10
+ },
11
+ "keywords": [
12
+ "vt",
13
+ "vector",
14
+ "tile",
15
+ "tiles",
16
+ "maps",
17
+ "map",
18
+ "pbf",
19
+ "mvt",
20
+ "stream",
21
+ "vector-tile",
22
+ "vectortile",
23
+ "modify",
24
+ "edit",
25
+ "change"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/yetzt/node-vtt.git"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/yetzt/node-vtt/issues"
33
+ },
34
+ "homepage": "https://github.com/yetzt/node-vtt#readme"
35
+ }
package/readme.md ADDED
@@ -0,0 +1,72 @@
1
+ # Vector Tile Transformer
2
+
3
+ Vector Tile Transformer lets you change [Vector Tiles](https://github.com/mapbox/vector-tile-spec) in a stream.
4
+
5
+ ## API
6
+
7
+ ### `stream = vtt(function(data, fn){ /* ... */ })`
8
+
9
+ A transform stream passing the unpacked vector tile data to a function for modification.
10
+
11
+ #### Example
12
+
13
+ ``` js
14
+
15
+ const fs = require("fs");
16
+
17
+ const src = fs.createReadStream("tile-in.pbf");
18
+ const dest = fs.createWriteStream("tile-out.pbf");
19
+
20
+ const vtt = require("vtt");
21
+
22
+ src.pipe(vtt(function(layers, done){
23
+
24
+ // filter layers by name
25
+ layers = layers.filter(function(layer){
26
+ return (layer.name !== "keepme");
27
+ });
28
+
29
+ // add a property to all features in a layer
30
+ layers[0] = layers[0].map(function(feature){
31
+ feature.properties.modified = true;
32
+ return feature;
33
+ });
34
+
35
+ done(null, layers); // err, data
36
+
37
+ })).pipe(dest);
38
+
39
+ ```
40
+
41
+ #### `data`
42
+
43
+ ``` js
44
+
45
+ [{
46
+ version: 2,
47
+ name: "name",
48
+ extent: 4096,
49
+ features: [{
50
+ id: 1,
51
+ type: 1,
52
+ properties: {
53
+ key: "value",
54
+ },
55
+ geometry: (...geometry),
56
+ }],
57
+ }]
58
+
59
+ ```
60
+
61
+
62
+ ### `data = vtt.unpack(buffer);`
63
+
64
+ Sync convenience method to turn an uncompressed vector tile into an object
65
+
66
+ ### `buffer = vtt.pack(data);`
67
+
68
+ Sync convenience method to turn an object back into an uncompressed vector tile
69
+
70
+ ## License
71
+
72
+ [UNLICENSE](https://unlicense.org/)
package/vtt.js ADDED
@@ -0,0 +1,240 @@
1
+ const pbf = require("pbf");
2
+ const stream = require("stream");
3
+
4
+ const vtt = module.exports = function vtt(){
5
+ if (!(this instanceof vtt)) return new vtt(...arguments);
6
+ const fn = Array.from(arguments).find(function(arg){ return (typeof arg === "function") });
7
+ // data = args.find(function(arg){ return (typeof arg === "object" && Buffer.isBuffer(arg)) });
8
+
9
+ let buf = [];
10
+ return new stream.Transform({
11
+ transform: function(chunk, encoding, done) {
12
+ buf.push(chunk);
13
+ done();
14
+ },
15
+ flush: function(done) {
16
+ const s = this;
17
+ if (!fn) return s.emit("error", new Error("Missing Modify Function")), done();
18
+ fn(unpack(Buffer.concat(buf)), function(err, data){ // FIXME error?
19
+ if (err) return s.emit("error", new Error(err)), done();
20
+ s.emit("data", pack(data)), done();
21
+ });
22
+ }
23
+ });
24
+
25
+ };
26
+
27
+ const unpack = module.exports.unpack = function unpack(buf){
28
+ return (new pbf(buf)).readFields(function(tag, layers, pb){
29
+ if (tag === 0x3) {
30
+
31
+ const layer = {
32
+ version: 1,
33
+ name: null,
34
+ extent: 4096,
35
+ features: [],
36
+ keys: [],
37
+ values: [],
38
+ };
39
+
40
+ pb.readFields(function(tag, l, pb) {
41
+ switch (tag) {
42
+ case 0xf: l.version = pb.readVarint(); break;
43
+ case 0x1: l.name = pb.readString(); break;
44
+ case 0x5: l.extent = pb.readVarint(); break;
45
+ case 0x2:
46
+ const feature = { type: 0, properties: [], geometry: -1 };
47
+ pb.readFields(function(tag, f, pb) {
48
+ let end;
49
+ switch (tag) {
50
+ case 0x1: f.id = pb.readVarint(); break;
51
+ case 0x3: f.type = pb.readVarint(); break;
52
+ case 0x2: // read properties
53
+ end = pb.readVarint() + pb.pos;
54
+ while (pb.pos < end) f.properties.push([ pb.readVarint(), pb.readVarint() ]);
55
+ break;
56
+ case 0x4: // read geometry
57
+ f.geometry = [];
58
+ end = pb.readVarint() + pb.pos;
59
+ let cmd = 1;
60
+ let len = 0;
61
+ let x = 0;
62
+ let y = 0;
63
+ let ring = [];
64
+ let inst;
65
+ while (pb.pos < end) {
66
+ if (len-- <= 0) inst = pb.readVarint(), cmd = inst & 0x7, len = (inst >> 3) - 1;
67
+ switch (cmd) {
68
+ case 0x1: // moveto; new ring
69
+ if (ring.length > 0) f.geometry.push(ring), ring = [];
70
+ case 0x2: // lineto, fill ring
71
+ ring.push([ x += pb.readSVarint(), y += pb.readSVarint() ]);
72
+ break;
73
+ case 0x7: // close, close polygon
74
+ if (ring.length > 0) ring.push([ ring[0][0], ring[0][1] ]); // close polygon
75
+ break;
76
+ default:
77
+ throw new Error("unknown command "+cmd);
78
+ break;
79
+ }
80
+ }
81
+ if (ring.length > 0) f.geometry.push(ring);
82
+ break;
83
+ }
84
+ }, feature, pb.readVarint()+pb.pos);
85
+ l.features.push(feature);
86
+ break;
87
+ case 0x3: l.keys.push(pb.readString()); break;
88
+ case 0x4:
89
+ let end = (pb.readVarint()+pb.pos);
90
+ while (pb.pos < end) {
91
+ switch (pb.readVarint() >> 3) {
92
+ case 0x1: l.values.push(pb.readString()); break;
93
+ case 0x2: l.values.push(pb.readFloat()); break;
94
+ case 0x3: l.values.push(pb.readDouble()); break;
95
+ case 0x4: l.values.push(pb.readVarint64()); break; // deprecated
96
+ case 0x5: l.values.push(pb.readVarint()); break;
97
+ case 0x6: l.values.push(pb.readSVarint()); break;
98
+ case 0x7: l.values.push(pb.readBoolean()); break;
99
+ };
100
+ };
101
+ break;
102
+ };
103
+ }, layer, pb.readVarint()+pb.pos);
104
+
105
+ // map keys and values
106
+ layer.features = layer.features.map(function(f){
107
+ return f.properties = f.properties.reduce(function(p,v){
108
+ return p[layer.keys[v[0]]]=layer.values[v[1]],p;
109
+ },{}), f;
110
+ });
111
+
112
+ layers.push(layer);
113
+ }
114
+
115
+ }, []);
116
+ };
117
+
118
+ const pack = module.exports.pack = function js2pbf(tile){
119
+
120
+ const pb = new pbf();
121
+
122
+ tile.map(function(layer){
123
+
124
+ // destruct properties
125
+ const keys = {};
126
+ const values = {};
127
+ let kidx = 0;
128
+ let vidx = 0;
129
+
130
+ layer.features = layer.features.map(function(feature){
131
+
132
+ feature.properties = Object.entries(feature.properties).filter(function([ k, v ]){
133
+ return (v === null || typeof v !== undefined);
134
+ }).map(function([ k, v ]){
135
+
136
+ // create a unique string for value, good enough™
137
+ const vk = (typeof v)+":"+v.toString();
138
+
139
+ if (!keys.hasOwnProperty(k)) keys[k] = kidx++;
140
+ if (!values.hasOwnProperty(vk)) values[vk] = [ vidx++, v ];
141
+ return [ keys[k], values[vk][0] ];
142
+
143
+ });
144
+
145
+ return feature;
146
+
147
+ });
148
+
149
+ // flatten keys and values
150
+ layer.keys = Object.entries(keys).reduce(function(l, [ k, v ]){ return l[v]=k,l },[]);
151
+ layer.values = Object.entries(values).reduce(function(l, [ k, v ]){ return l[v[0]]=v[1],l },[]);
152
+
153
+ return layer;
154
+
155
+ }).forEach(function(layer){
156
+
157
+ // construct protobuf
158
+ pb.writeMessage(3, function(l, p){
159
+
160
+ // write version, name and extent
161
+ pb.writeVarintField(15, layer.version || 1);
162
+ pb.writeStringField(1, layer.name || "");
163
+ pb.writeVarintField(5, layer.extent || 4096);
164
+
165
+ // write keys
166
+ layer.keys.forEach(function(k){
167
+ pb.writeStringField(3, k);
168
+ });
169
+
170
+ // write values
171
+ layer.values.forEach(function(v){
172
+ pb.writeMessage(4, function(v, pb){
173
+ switch (typeof v) {
174
+ case "string": pb.writeStringField(1, v); break;
175
+ case "boolean": pb.writeBooleanField(7, v); break;
176
+ case "number":
177
+ if (v % 1 !== 0) return pb.writeDoubleField(3, v); // in js all floats are doubles regardless
178
+ if (v < 0) return pb.writeSVarintField(6, v);
179
+ pb.writeVarintField(5, v);
180
+ break;
181
+ case "bigint":
182
+ pb.writeVarintField(4, v);
183
+ break;
184
+ default:
185
+ throw new Error("Property value has invalid type: "+(typeof v));
186
+ break;
187
+ }
188
+ }, v);
189
+
190
+ });
191
+
192
+ // write features
193
+ layer.features.forEach(function(feature){
194
+
195
+ // write feature
196
+ pb.writeMessage(2, function(){
197
+
198
+ // write id and type
199
+ if (feature.id !== undefined) pb.writeVarintField(1, feature.id);
200
+ pb.writeVarintField(3, feature.type);
201
+
202
+ // write properties
203
+ pb.writeMessage(2, function(properties,pb){
204
+ feature.properties.forEach(function(property){
205
+ pb.writeVarint(property[0]); // key-id
206
+ pb.writeVarint(property[1]); // value-id
207
+ });
208
+ }, feature.properties);
209
+
210
+ // write geometry — https://github.com/mapbox/vector-tile-spec/blob/master/2.1/README.md#43-geometry-encoding
211
+ pb.writeMessage(4, function(feature, pb){
212
+ let x = 0, y = 0;
213
+ feature.geometry.forEach(function(ring){
214
+ let dx = 0, dy = 0;
215
+ // write length 1 and MoveTo first coordinate pair
216
+
217
+ pb.writeVarint((1<<3)+0x1);
218
+ pb.writeSVarint(dx = ring[0][0] - x);
219
+ pb.writeSVarint(dy = ring[0][1] - y);
220
+ if (feature.type > 0x1) { // only lines and polygons
221
+ let len = ring.length+2-feature.type; // omit last coordinate pair for polygons
222
+ pb.writeVarint(((len-1)<<3)+0x2); // length (without first element) and LineTo
223
+ for (let i = 1; i < len; i++) { // write remaining coordinates, start with second coordinate
224
+ pb.writeSVarint(dx = ring[i][0] - (x += dx));
225
+ pb.writeSVarint(dy = ring[i][1] - (y += dy));
226
+ };
227
+ // in case of more rings:
228
+ x += dx, y += dy;
229
+ };
230
+ if (feature.type === 0x3) pb.writeVarint((1<<3)+0x7); // ClosePath for polygons
231
+ });
232
+ }, feature);
233
+ }, feature);
234
+ });
235
+ }, layer);
236
+ });
237
+
238
+ return pb.finish();
239
+
240
+ };