-
-
Save kkaefer/378bd150fe52de8e74b1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Show hidden characters
{ | |
"node": true, | |
"globalstrict": true | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
module.exports = Geometry; | |
function Geometry() { | |
this.values = []; | |
this.pos = { | |
x: 0, | |
y: 0 | |
}; | |
} | |
Geometry.prototype.encodeCommand = function(cmd, length) { | |
var cmd_bits = 3; | |
var cmd_mask = (1 << cmd_bits) - 1; | |
this.values.push((length << cmd_bits) | (cmd & cmd_mask)); | |
}; | |
Geometry.prototype.encodePoint = function(pt) { | |
// Compute delta to the previous coordinate. | |
var dx = pt.x - this.pos.x; | |
var dy = pt.y - this.pos.y; | |
// Manual zigzag encoding. | |
this.values.push((dx << 1) ^ (dx >> 31)); | |
this.values.push((dy << 1) ^ (dy >> 31)); | |
this.pos.x = pt.x; | |
this.pos.y = pt.y; | |
}; | |
Geometry.prototype.addPoint = function(point) { | |
this.encodeCommand(1, 1); | |
this.encodePoint(point); | |
}; | |
Geometry.prototype.addPoints = function(points) { | |
this.encodeCommand(1, points.length); | |
for (var i = 0; i < points.length; i++) { | |
this.encodePoint(points[i]); | |
} | |
}; | |
Geometry.prototype.addLine = function(line) { | |
// Encode moveTo first point in the polygon. | |
this.encodeCommand(1, 1); | |
this.encodePoint(line[0]); | |
// Encode lineTo for the rest of the points. | |
this.encodeCommand(2, line.length - 1); | |
for (var j = 1; j < line.length; j++) { | |
this.encodePoint(line[j]); | |
} | |
}; | |
Geometry.prototype.addPolygon = function(polygon) { | |
this.addLine(polygon); | |
// Encode closepolygon | |
this.encodeCommand(7, 1); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
var Protobuf = require('./protobuf.js'); | |
var Geometry = require('./geometry.js'); | |
module.exports = Layer; | |
function Layer(name, extent) { | |
this.pbf = new Protobuf(); | |
this.pbf.writeTaggedVarint(15, 1); | |
this.pbf.writeTaggedString(1, name); | |
if (extent) { | |
this.pbf.writeTaggedVarint(5, extent); | |
} | |
this.keys = []; | |
this.values = []; | |
} | |
var geom_type = { | |
unknown: 0, | |
point: 1, | |
line: 2, | |
polygon: 3 | |
}; | |
Layer.prototype.addFeature = function(type, geometry, properties) { | |
if (!geom_type[type]) { | |
throw new Error('type must be either point, line or polygon'); | |
} | |
var feature_pbf = new Protobuf(); | |
if (typeof properties !== 'object') properties = {}; | |
feature_pbf.writeTaggedVarint(3, geom_type[type]); | |
// Write tags. | |
var tags = []; | |
for (var key in properties) { | |
if (key === 'id') { | |
feature_pbf.writeTaggedVarint(1, properties.id); | |
} else { | |
var key_pos = this.addKey(key); | |
var val_pos = this.addValue(properties[key]); | |
tags.push(key_pos, val_pos); | |
} | |
} | |
feature_pbf.writePackedVarints(2, tags); | |
// Write geometry. | |
if (!(geometry instanceof Geometry)) { | |
var value = geometry; | |
geometry = new Geometry(); | |
if (type == 'point') geometry.addPoint(value); | |
else if (type == 'line') geometry.addLine(value); | |
else if (type == 'polygon') geometry.addPolygon(value); | |
else throw new Error('unknown geometry type'); | |
} | |
feature_pbf.writePackedVarints(4, geometry.values); | |
this.pbf.writeMessage(2, feature_pbf); | |
}; | |
Layer.prototype.addKey = function(key) { | |
var key_pos = this.keys.indexOf(key); | |
if (key_pos < 0) { | |
this.pbf.writeTaggedString(3, key); | |
key_pos = this.keys.length; | |
this.keys.push(key); | |
} | |
return key_pos; | |
}; | |
Layer.prototype.addValue = function(value) { | |
var val_pos = this.values.indexOf(value); | |
if (val_pos < 0) { | |
var value_pbf = new Protobuf(); | |
if (typeof value === 'number') { | |
if (Math.floor(value) === value) { | |
value_pbf.writeTaggedVarint(4, value); | |
} else { | |
value_pbf.writeTaggedDouble(3, value); | |
} | |
} else if (typeof value == 'boolean') { | |
value_pbf.writeTaggedBoolean(7, value); | |
} else { | |
value_pbf.writeTaggedString(1, value); | |
} | |
this.pbf.writeMessage(4, value_pbf); | |
val_pos = this.values.length; | |
this.values.push(value); | |
} | |
return val_pos; | |
}; | |
Layer.prototype.finish = function() { | |
return this.pbf.finish(); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
module.exports = Protobuf; | |
function Protobuf() { | |
this.buf = new Buffer(128); | |
this.pos = 0; | |
this.length = this.buf.length; | |
} | |
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); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
var Protobuf = require('./protobuf.js'); | |
var Layer = require('./layer.js'); | |
module.exports = Tile; | |
function Tile() { | |
this.pbf = new Protobuf(); | |
} | |
Tile.prototype.addLayer = function(layer) { | |
if (!(layer instanceof Layer)) { | |
throw new Error('layer must be a Layer object'); | |
} | |
this.pbf.writeMessage(3, layer); | |
}; | |
Tile.prototype.finish = function() { | |
return this.pbf.finish(); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Protocol Version 1 | |
package llmr.vector; | |
option optimize_for = LITE_RUNTIME; | |
enum geom_type { | |
Unknown = 0; | |
Point = 1; | |
LineString = 2; | |
Polygon = 3; | |
} | |
// Variant type encoding | |
message value { | |
// Exactly one of these values may be present in a valid message | |
optional string string_value = 1; | |
optional float float_value = 2; | |
optional double double_value = 3; | |
optional int64 int_value = 4; | |
optional uint64 uint_value = 5; | |
optional sint64 sint_value = 6; | |
optional bool bool_value = 7; | |
extensions 8 to max; | |
} | |
message feature { | |
optional uint64 id = 1; | |
// Tags of this feature. Even numbered values refer to the nth | |
// value in the keys list on the tile message, odd numbered | |
// values refer to the nth value in the values list on the tile | |
// message. | |
repeated uint32 tags = 2 [ packed = true ]; | |
// The type of geometry stored in this feature. | |
optional geom_type type = 3 [ default = Unknown ]; | |
// Contains a stream of commands and parameters (vertices). The | |
// repeat count is shifted to the left by 3 bits. This means | |
// that the command has 3 bits (0-15). The repeat count | |
// indicates how often this command is to be repeated. Defined | |
// commands are: | |
// - MoveTo: 1 (2 parameters follow) | |
// - LineTo: 2 (2 parameters follow) | |
// - ClosePath: 15 (no parameters follow) | |
// | |
// Ex.: MoveTo(3, 6), LineTo(8, 12), LineTo(20, 34), ClosePath | |
// Encoded as: [ 3 6 18 5 6 12 22 15 ] | |
// == command type 15 (ClosePath) | |
// ===== relative LineTo(+12, +22) == LineTo(20, 34) | |
// === relative LineTo(+5, +6) == LineTo(8, 12) | |
// == [00010 010] = command type 2 (LineTo), length 2 | |
// === relative MoveTo(+3, +6) | |
// = implicit command type 1 (MoveTo), length 1 | |
// Commands are encoded as uint32 varints, vertex parameters are | |
// encoded as sint32 varints (zigzag). Vertex parameters are | |
// also encoded as deltas to the previous position. The original | |
// position is (0,0) | |
repeated uint32 geometry = 4 [ packed = true ]; | |
// A list of indices to the geometry array that specify a triangulation of | |
// this geometry. This must only exist if this feature is a polygon. | |
// These are the valid indices for the example above: | |
// 0 ==> (3/6) | |
// 1 ==> (8/12) | |
// 2 ==> (20/34) | |
// Indices beyond 2 are invalid, as the total number of vertices is 3. | |
repeated sint32 triangulation = 5 [ packed = true ]; | |
// The total number of vertices encoded in the geometry field. This is can | |
// be deduced by manually iterating through the geometry field, but we can | |
// just as well store the number to avoid the overhead on parsing. | |
optional uint32 vertex_count = 6; | |
} | |
// Stores a glyph with metrics and optional SDF bitmap information. | |
message glyph { | |
required uint32 id = 1; | |
// A signed distance field of the glyph with a border of 3 pixels. | |
optional bytes bitmap = 2; | |
// Glyph metrics. | |
required uint32 width = 3; | |
required uint32 height = 4; | |
required sint32 left = 5; | |
required sint32 top = 6; | |
required uint32 advance = 7; | |
} | |
// Stores font face information and a list of glyphs. | |
message face { | |
required string family = 1; | |
required string style = 2; | |
repeated glyph glyphs = 5; | |
} | |
// Stores the shaping information for the label with a given text | |
message label { | |
// The original value ID this shaping information is for. | |
required uint32 text = 1; | |
// References the index of the font stack in the layer's fontstack array. | |
required uint32 stack = 2; | |
// Parallel arrays of face ID, glyph ID and position. | |
repeated uint32 faces = 3 [packed = true]; | |
repeated uint32 glyphs = 4 [packed = true]; | |
repeated uint32 x = 5 [packed = true]; | |
repeated uint32 y = 6 [packed = true]; | |
} | |
message layer { | |
// Any compliant implementation must first read the version | |
// number encoded in this message and choose the correct | |
// implementation for this version number before proceeding to | |
// decode other parts of this message. | |
required uint32 version = 15 [ default = 1 ]; | |
required string name = 1; | |
// The actual features in this tile. | |
repeated feature features = 2; | |
// Dictionary encoding for keys | |
repeated string keys = 3; | |
// Dictionary encoding for values | |
repeated value values = 4; | |
// The bounding box in this tile spans from 0..4095 units | |
optional uint32 extent = 5 [ default = 4096 ]; | |
// Total vertex count in this layer. | |
optional uint32 vertex_count = 6; | |
// Shaping information for labels contained in this tile. | |
repeated string faces = 7; | |
repeated label labels = 8; | |
repeated string stacks = 9; | |
extensions 16 to max; | |
} | |
message tile { | |
repeated layer layers = 3; | |
repeated face faces = 4; | |
extensions 16 to 8191; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
var Layer = require('./layer.js'); | |
var Tile = require('./tile.js'); | |
var poly_coords = 0; | |
var tile = new Tile(); | |
var water = new Layer('water'); | |
for (var y = 0; y < 4096; y += 32) { | |
for (var x = 0; x < 4096; x += 32) { | |
var poly = []; | |
poly.push({ x: x, y: y }); | |
// poly.push({ x: x + 8, y: y }); | |
poly.push({ x: x + 10, y: y + 3 }); | |
// poly.push({ x: x + 10, y: y + 8 }); | |
poly.push({ x: x + 12, y: y + 12 }); | |
// poly.push({ x: x + 8, y: y + 10 }); | |
// poly.push({ x: x + 2, y: y + 8 }); | |
poly.push({ x: x + 1, y: y + 3 }); | |
water.addFeature('polygon', poly); | |
poly_coords += poly.length; | |
} | |
} | |
tile.addLayer(water); | |
process.stdout.write(tile.finish()); | |
console.warn('poly coords: %d', poly_coords); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment