summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorKonstantin Käfer <github@kkaefer.com>2014-01-08 12:10:46 +0100
committerKonstantin Käfer <github@kkaefer.com>2014-01-08 12:10:46 +0100
commit4f8556a6b80c41df8f8842a36ce13274c27b9ceb (patch)
tree51ead2bbe1e83bc7ba6d2fdcb355e131d1b1871b /bin
parent528eaf475e084508cb2d2d26df7d4136f1eb5e04 (diff)
downloadqtlocation-mapboxgl-4f8556a6b80c41df8f8842a36ce13274c27b9ceb.tar.gz
add protobuf encoding of style
Diffstat (limited to 'bin')
-rwxr-xr-xbin/convert-style.js140
-rw-r--r--bin/protobuf.js312
-rw-r--r--bin/style.js127
3 files changed, 579 insertions, 0 deletions
diff --git a/bin/convert-style.js b/bin/convert-style.js
new file mode 100755
index 0000000000..aeabbfe86a
--- /dev/null
+++ b/bin/convert-style.js
@@ -0,0 +1,140 @@
+#!/usr/bin/env node
+
+var style = require('./style.js');
+var Protobuf = require('./protobuf.js');
+
+var fs = require('fs');
+
+var pbf = new Protobuf();
+
+// enum
+var bucket_type = {
+ fill: 1,
+ line: 2,
+ point: 3
+};
+
+// enum
+var cap_type = {
+ round: 1,
+}
+
+// enum
+var join_type = {
+ butt: 1,
+ bevel: 2
+}
+
+function createBucket(bucket, name) {
+ var pbf = new Protobuf();
+ pbf.writeTaggedString(1 /* name */, name);
+ pbf.writeTaggedVarint(2 /* type */, bucket_type[bucket.type]);
+
+ pbf.writeTaggedString(3 /* source_name */, bucket.datasource);
+ pbf.writeTaggedString(4 /* source_layer */, bucket.layer);
+ if (bucket.field) {
+ pbf.writeTaggedString(5 /* source_field */, bucket.field);
+ var values = Array.isArray(bucket.value) ? bucket.value : [bucket.value];
+ for (var i = 0; i < values.length; i++) {
+ pbf.writeTaggedString(6 /* source_values */, values[i]);
+ }
+ }
+ if (bucket.cap) {
+ pbf.writeTaggedVarint(7 /* cap */, cap_type[bucket.cap]);
+ }
+ if (bucket.join) {
+ pbf.writeTaggedVarint(8 /* join */, join_type[bucket.join]);
+ }
+
+ // TODO: font stuff
+
+ return pbf;
+}
+
+function createStructure(structure) {
+ var pbf = new Protobuf();
+ pbf.writeTaggedString(1 /* name */, structure.name);
+ if (structure.bucket) {
+ pbf.writeTaggedString(2 /* bucket_name */, structure.bucket);
+ } else if (structure.layers) {
+ for (var i = 0; i < structure.layers.length; i++) {
+ pbf.writeMessage(3 /* child_layers */, createStructure(structure.layers[i]));
+ }
+ }
+ return pbf;
+}
+
+function createWidth(width) {
+ var pbf = new Protobuf();
+ var values = [];
+ if (Array.isArray(width)) {
+ pbf.writeTaggedString(1 /* scaling */, width[0]);
+ for (var i = 1; i < width.length; i++) {
+ if (width[0] === 'stops') {
+ values.push(width[i].z, width[i].val);
+ } else {
+ values.push(width[i]);
+ }
+ }
+ } else {
+ values.push(width);
+ }
+ pbf.writePackedFloats(2 /* values */, values);
+
+ return pbf;
+}
+
+function createLayer(layer, name) {
+ var pbf = new Protobuf();
+ pbf.writeTaggedString(1 /* layer_name */, name);
+
+ if ('color' in layer) {
+ var color = layer.color.match(/^#([0-9a-f]{6})$/i);
+ if (!color) {
+ console.warn('invalid color');
+ } else {
+ pbf.writeTaggedUInt32(2 /* color */, parseInt(color[1] + 'ff', 16));
+ }
+ }
+
+ if ('antialias' in layer) {
+ pbf.writeTaggedBoolean(3 /* antialias */, layer.antialias);
+ }
+
+ if ('width' in layer) {
+ pbf.writeMessage(4 /* width */, createWidth(layer.width));
+ }
+
+ return pbf;
+}
+
+
+function createClass(klass) {
+ var pbf = new Protobuf();
+ pbf.writeTaggedString(1 /* name */, klass.name);
+ for (var name in klass.layers) {
+ pbf.writeMessage(2 /* layers */, createLayer(klass.layers[name], name));
+ }
+ return pbf;
+}
+
+
+
+
+
+for (var name in style.buckets) {
+ var bucket = style.buckets[name];
+ pbf.writeMessage(1 /* buckets */, createBucket(bucket, name));
+}
+
+for (var i = 0; i < style.structure.length; i++) {
+ var structure = style.structure[i];
+ pbf.writeMessage(2 /* structure */, createStructure(structure));
+}
+
+for (var i = 0; i < style.classes.length; i++) {
+ var klass = style.classes[i];
+ pbf.writeMessage(3 /* classes */, createClass(klass));
+}
+
+process.stdout.write(pbf.finish());
diff --git a/bin/protobuf.js b/bin/protobuf.js
new file mode 100644
index 0000000000..fb181c548f
--- /dev/null
+++ b/bin/protobuf.js
@@ -0,0 +1,312 @@
+module.exports = Protobuf;
+function Protobuf(buf, pos) {
+ if (Buffer.isBuffer(buf)) {
+ this.buf = buf;
+ } else {
+ this.buf = new Buffer(buf || 128);
+ }
+ this.pos = pos || 0;
+ this.length = this.buf.length;
+}
+
+// === READING =================================================================
+
+Protobuf.prototype.readUInt32 = function() {
+ var val = this.readUInt32LE(this.pos);
+ this.pos += 4;
+ return val;
+};
+
+Protobuf.prototype.readUInt64 = function() {
+ var val = this.readUInt64LE(this.pos);
+ this.pos += 4;
+ return val;
+};
+
+Protobuf.prototype.readVarint = function() {
+ // TODO: bounds checking
+ var pos = this.pos;
+ if (this.buf[pos] <= 0x7f) {
+ this.pos++;
+ return this.buf[pos];
+ } else if (this.buf[pos + 1] <= 0x7f) {
+ this.pos += 2;
+ return (this.buf[pos] & 0x7f) | (this.buf[pos + 1] << 7);
+ } else if (this.buf[pos + 2] <= 0x7f) {
+ this.pos += 3;
+ return (this.buf[pos] & 0x7f) | (this.buf[pos + 1] & 0x7f) << 7 | (this.buf[pos + 2]) << 14;
+ } else if (this.buf[pos + 3] <= 0x7f) {
+ this.pos += 4;
+ return (this.buf[pos] & 0x7f) | (this.buf[pos + 1] & 0x7f) << 7 | (this.buf[pos + 2] & 0x7f) << 14 | (this.buf[pos + 3]) << 21;
+ } else if (this.buf[pos + 4] <= 0x7f) {
+ this.pos += 5;
+ return ((this.buf[pos] & 0x7f) | (this.buf[pos + 1] & 0x7f) << 7 | (this.buf[pos + 2] & 0x7f) << 14 | (this.buf[pos + 3]) << 21) + (this.buf[pos + 4] * 268435456);
+ } else {
+ throw new Error("TODO: Handle 6+ byte varints");
+ }
+};
+
+Protobuf.prototype.readSVarint = function() {
+ var num = this.readVarint();
+ if (num > 2147483647) throw new Error('TODO: Handle numbers >= 2^30');
+ // zigzag encoding
+ return ((num >> 1) ^ -(num & 1));
+};
+
+Protobuf.prototype.readString = function() {
+ var bytes = this.readVarint();
+ // Node.js
+ var str = this.buf.toString('utf8', this.pos, this.pos + bytes);
+ // Client side JS code:
+ // // TODO: bounds checking
+ // var chr = String.fromCharCode;
+ // var b = this.buf;
+ // var p = this.pos;
+ // var end = this.pos;
+ // var str = '';
+ // while (p < end) {
+ // if (b[p] <= 0x7F) str += chr(b[p++]);
+ // else if (b[p] <= 0xBF) throw new Error('Invalid UTF-8 codepoint: ' + b[p]);
+ // else if (b[p] <= 0xDF) str += chr((b[p++] & 0x1F) << 6 | (b[p++] & 0x3F));
+ // else if (b[p] <= 0xEF) str += chr((b[p++] & 0x1F) << 12 | (b[p++] & 0x3F) << 6 | (b[p++] & 0x3F));
+ // else if (b[p] <= 0xF7) p += 4; // We can't handle these codepoints in JS, so skip.
+ // else if (b[p] <= 0xFB) p += 5;
+ // else if (b[p] <= 0xFD) p += 6;
+ // else throw new Error('Invalid UTF-8 codepoint: ' + b[p]);
+ // }
+ this.pos += bytes;
+ return str;
+};
+
+Protobuf.prototype.readPacked = function(type) {
+ // TODO: bounds checking
+ var bytes = this.readVarint();
+ var end = this.pos + bytes;
+ var array = [];
+ while (this.pos < end) {
+ array.push(this['read' + type]());
+ }
+ return array;
+};
+
+Protobuf.prototype.skip = function(val) {
+ // TODO: bounds checking
+ var type = val & 0x7;
+ switch (type) {
+ /* varint */ case 0: while (this.buf[this.pos++] > 0x7f); break;
+ /* 64 bit */ case 1: this.pos += 8; break;
+ /* length */ case 2: var bytes = this.readVarint(); this.pos += bytes; break;
+ /* 32 bit */ case 5: this.pos += 4; break;
+ default: throw new Error('Unimplemented type: ' + type);
+ }
+};
+
+// Generic read function
+Protobuf.prototype.read = function(spec, end) {
+ var ref, tag;
+ var obj = {};
+ for (tag in spec) {
+ ref = spec[tag];
+ if ('default' in ref) {
+ obj[ref.name] = ref['default'];
+ }
+
+ if ('repeated' in ref) {
+ obj[ref.name] = [];
+ }
+ }
+
+ var begin = this.pos;
+ if (typeof end == 'undefined') {
+ var bytes = this.readVarint();
+ end = this.pos + bytes;
+ }
+
+ while (this.pos < end) {
+ var val = this.readVarint();
+ tag = val >> 3;
+ ref = spec[tag];
+ if (ref) {
+ if (ref.packed) {
+ obj[ref.name] = this.readPacked(ref.type);
+ }
+ else if (ref.repeated) {
+ if (!obj[ref.name]) obj[ref.name] = [];
+ obj[ref.name].push(this['read' + ref.type]());
+ } else {
+ obj[ref.name] = this['read' + ref.type]();
+ }
+ } else {
+ this.skip(val);
+ }
+ }
+
+ if (this._debug) {
+ obj._begin = begin;
+ obj._end = end;
+ obj._bytes = obj._end - obj._begin;
+ }
+
+ for (tag in spec) {
+ ref = spec[tag];
+ if (!(ref.name in obj) && ('required' in ref)) {
+ throw new Error('Field ' + ref.name + ' is required');
+ }
+ }
+
+ return obj;
+};
+
+// === WRITING =================================================================
+
+Protobuf.Varint = 0;
+Protobuf.Int64 = 1;
+Protobuf.Message = 2;
+Protobuf.String = 2;
+Protobuf.Packed = 2;
+Protobuf.Int32 = 5;
+
+Protobuf.prototype.writeTag = function(tag, type) {
+ this.writeVarint((tag << 3) | type);
+};
+
+Protobuf.prototype.writeUInt32 = function(val) {
+ while (this.length < this.pos + 4) this.realloc();
+ this.buf.writeUInt32LE(val, this.pos);
+ this.pos += 4;
+};
+
+Protobuf.prototype.writeTaggedUInt32 = function(tag, val) {
+ this.writeTag(tag, Protobuf.Int32);
+ this.writeUInt32(val);
+};
+
+Protobuf.prototype.writeVarint = function(val) {
+ val = Number(val);
+ if (isNaN(val)) {
+ val = 0;
+ }
+
+ if (val <= 0x7f) {
+ while (this.length < this.pos + 1) this.realloc();
+ this.buf[this.pos++] = val;
+ } else if (val <= 0x3fff) {
+ while (this.length < this.pos + 2) this.realloc();
+ this.buf[this.pos++] = 0x80 | ((val >>> 0) & 0x7f);
+ this.buf[this.pos++] = 0x00 | ((val >>> 7) & 0x7f);
+ } else if (val <= 0x1ffffff) {
+ while (this.length < this.pos + 3) this.realloc();
+ this.buf[this.pos++] = 0x80 | ((val >>> 0) & 0x7f);
+ this.buf[this.pos++] = 0x80 | ((val >>> 7) & 0x7f);
+ this.buf[this.pos++] = 0x00 | ((val >>> 14) & 0x7f);
+ } else if (val <= 0xfffffff) {
+ while (this.length < this.pos + 4) this.realloc();
+ this.buf[this.pos++] = 0x80 | ((val >>> 0) & 0x7f);
+ this.buf[this.pos++] = 0x80 | ((val >>> 7) & 0x7f);
+ this.buf[this.pos++] = 0x80 | ((val >>> 14) & 0x7f);
+ this.buf[this.pos++] = 0x00 | ((val >>> 21) & 0x7f);
+ } else {
+ throw new Error("TODO: Handle 5+ byte varints (" + val + ")");
+ }
+};
+
+Protobuf.prototype.writeTaggedVarint = function(tag, val) {
+ this.writeTag(tag, Protobuf.Varint);
+ this.writeVarint(val);
+};
+
+Protobuf.prototype.writeBoolean = function(val) {
+ this.writeVarint(Boolean(val));
+};
+
+Protobuf.prototype.writeTaggedBoolean = function(tag, val) {
+ this.writeTaggedVarint(tag, Boolean(val));
+};
+
+Protobuf.prototype.writeString = function(str) {
+ str = String(str);
+ var bytes = Buffer.byteLength(str);
+ this.writeVarint(bytes);
+ while (this.length < this.pos + bytes) this.realloc();
+ this.buf.write(str, this.pos);
+ this.pos += bytes;
+};
+
+Protobuf.prototype.writeTaggedString = function(tag, str) {
+ this.writeTag(tag, Protobuf.String);
+ this.writeString(str);
+};
+
+Protobuf.prototype.writeFloat = function(val) {
+ while (this.length < this.pos + 4) this.realloc();
+ this.buf.writeFloatLE(val, this.pos);
+ this.pos += 4;
+};
+
+Protobuf.prototype.writeTaggedFloat = function(tag, val) {
+ this.writeTag(tag, Protobuf.Int32);
+ this.writeFloat(val);
+};
+
+Protobuf.prototype.writeDouble = function(val) {
+ while (this.length < this.pos + 8) this.realloc();
+ this.buf.writeDoubleLE(val, this.pos);
+ this.pos += 8;
+};
+
+Protobuf.prototype.writeTaggedDouble = function(tag, val) {
+ this.writeTag(tag, Protobuf.Int64);
+ this.writeDouble(val);
+};
+
+Protobuf.prototype.writeBuffer = function(buffer) {
+ var bytes = buffer.length;
+ this.writeVarint(bytes);
+ while (this.length < this.pos + bytes) this.realloc();
+ buffer.copy(this.buf, this.pos);
+ this.pos += bytes;
+};
+
+Protobuf.prototype.writeMessage = function(tag, protobuf) {
+ var buffer = protobuf.finish();
+ this.writeTag(tag, Protobuf.Message);
+ this.writeBuffer(buffer);
+};
+
+Protobuf.prototype.writeRepeated = function(type, tag, items) {
+ for (var i = 0; i < items.length; i++) {
+ this['write' + type](tag, items[i]);
+ }
+};
+
+Protobuf.prototype.writePacked = function(type, tag, items) {
+ if (!items.length) return;
+
+ var message = new Protobuf();
+ for (var i = 0; i < items.length; i++) {
+ message['write' + type](items[i]);
+ }
+ var data = message.finish();
+
+ this.writeTag(tag, Protobuf.Packed);
+ this.writeBuffer(data);
+};
+
+Protobuf.prototype.writePackedVarints = function(tag, items) {
+ this.writePacked('Varint', tag, items);
+};
+
+Protobuf.prototype.writePackedFloats = function(tag, items) {
+ this.writePacked('Float', tag, items);
+};
+
+Protobuf.prototype.realloc = function() {
+ var buf = new Buffer(this.buf.length * 2);
+ this.buf.copy(buf);
+ this.buf = buf;
+ this.length = this.buf.length;
+};
+
+Protobuf.prototype.finish = function() {
+ return this.buf.slice(0, this.pos);
+};
diff --git a/bin/style.js b/bin/style.js
new file mode 100644
index 0000000000..d218e125c1
--- /dev/null
+++ b/bin/style.js
@@ -0,0 +1,127 @@
+"use strict";
+
+module.exports = {
+ "buckets": {
+ "water": {
+ "datasource": "streets",
+ "layer": "water",
+ "type": "fill"
+ },
+ "road_large": {
+ "datasource": "streets",
+ "layer": "road", "field": "class", "value": ["motorway", "main"],
+ "type": "line", "cap": "round", "join": "bevel"
+ },
+ "road_regular": {
+ "datasource": "streets",
+ "layer": "road", "field": "class", "value": "street",
+ "type": "line", "cap": "round", "join": "bevel"
+ },
+ "road_limited": {
+ "datasource": "streets",
+ "layer": "road", "field": "class", "value": "street_limited",
+ "type": "line", "cap": "round", "join": "bevel"
+ },
+ "park": {
+ "datasource": "streets",
+ "layer": "landuse", "field": "class", "value": "park",
+ "type": "fill"
+ },
+ "wood": {
+ "datasource": "streets",
+ "layer": "landuse", "field": "class", "value": "wood",
+ "type": "fill"
+ },
+ "school": {
+ "datasource": "streets",
+ "layer": "landuse", "field": "class", "value": "school",
+ "type": "fill"
+ },
+ "cemetery": {
+ "datasource": "streets",
+ "layer": "landuse", "field": "class", "value": "cemetery",
+ "type": "fill"
+ },
+ "industrial": {
+ "datasource": "streets",
+ "layer": "landuse", "field": "class", "value": "industrial",
+ "type": "fill"
+ },
+ "building": {
+ "datasource": "streets",
+ "layer": "building",
+ "type": "fill"
+ },
+ "alcohol": {
+ "datasource": "streets",
+ "layer": "poi_label",
+ "field": "type",
+ "value": ["Alcohol"],
+ "type": "point"
+ }
+ },
+ "sprite": "img/maki-sprite",
+ "structure": [
+ { "name": "park", "bucket": "park" },
+ { "name": "wood", "bucket": "wood" },
+ { "name": "water", "bucket": "water" },
+ { "name": "road_limited", "bucket": "road_limited" },
+ { "name": "road_regular", "bucket": "road_regular" },
+ { "name": "road_large", "bucket": "road_large" },
+ { "name": "alcohol", "bucket": "alcohol" }
+ ],
+ "classes": [
+ {
+ "name": "default",
+ "layers": {
+ "background": {
+ "color": "#FFFFFF"
+ },
+ "park": {
+ "color": "#c8df9f",
+ "antialias": true
+ },
+ "wood": {
+ "color": "#33AA66",
+ "antialias": true
+ },
+ "water": {
+ "color": "#73b6e6",
+ "antialias": true
+ },
+ "road_limited": {
+ "color": "#BBBBBB",
+ "width": [
+ "stops",
+ { z: 0, val: 1 },
+ { z: 20, val: 1 }
+ ]
+ },
+ "road_regular": {
+ "color": "#999999",
+ "width": [
+ "stops",
+ { z: 0, val: 0.5 },
+ { z: 13, val: 0.5 },
+ { z: 16, val: 2 },
+ { z: 20, val: 32 }
+ ],
+ },
+ "road_large": {
+ "color": "#666666",
+ "width": [
+ "stops",
+ { z: 0, val: 0.5 },
+ { z: 11, val: 0.5 },
+ { z: 13, val: 1 },
+ { z: 16, val: 4 },
+ { z: 20, val: 64 }
+ ],
+ },
+ "alcohol": {
+ "image": "alcohol-shop"
+ }
+ }
+ }
+ ]
+};