nakarte

Source code of https://map.sikmir.ru (fork)
git clone git://git.sikmir.ru/nakarte
Log | Files | Refs | LICENSE

commit 9c057604c8b760645e811acb5aaedcea5954d2f6
parent 06a8ff7db0f25cbec71115ea27658e5b1f64b04c
Author: Sergej Orlov <wladimirych@gmail.com>
Date:   Tue, 20 Jun 2017 20:16:56 +0300

[mapillary] added mvt parser

Diffstat:
Asrc/lib/leaflet.layer.mapillary/lib/mvt.js | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 149 insertions(+), 0 deletions(-)

diff --git a/src/lib/leaflet.layer.mapillary/lib/mvt.js b/src/lib/leaflet.layer.mapillary/lib/mvt.js @@ -0,0 +1,148 @@ +import Pbf from 'pbf'; +import {Tile as TileProto} from './vector_tile_proto'; + +function simlifyTagValue(obj) { + for (let v of Object.values(obj)) { + if (v) { + return v; + } + } +} + +function decodeCoordinate(x) { + return ((x >> 1) ^ (-(x & 1))); +} + +function parseGeometry(geometryType, ints, coordinatesScale) { + if (geometryType !== TileProto.GeomType.POINT && geometryType !== TileProto.GeomType.LINESTRING && + geometryType !== TileProto.GeomType.POLYGON) { + throw new Error(`Unknown feature geometry type ${geometryType}`); + } + const len = ints.length; + let pos = 0; + const lineStrings = []; + let line; + let x = 0, y = 0; + while (pos < len) { + let i = ints[pos]; + let cmd = i & 0x7; + let cmdRepeat = i >> 3; + switch (cmd) { + case 1: // MoveTo + if (cmdRepeat !== 1) { + throw new Error(`repeat=${cmdRepeat} for command MoveTo`); + } + if (pos + 2 > len) { + throw new Error('Not enough elements for MoveTo arguments'); + } + if (line) { + lineStrings.push(line); + } + x += decodeCoordinate(ints[pos + 1]); + y += decodeCoordinate(ints[pos + 2]); + line = [[x * coordinatesScale, y * coordinatesScale]]; + pos += 3; + break; + case 2: // LineTo + if (cmdRepeat < 1) { + throw new Error(`repeat=${cmdRepeat} for command LineTo`); + } + if (!line) { + throw new Error('LineTo with empty linestring') + } + pos +=1; + for (let cmdN = 0; cmdN < cmdRepeat; cmdN++){ + if (pos + 2 > len) { + throw new Error('Not enough elements for LineTo arguments'); + } + x += decodeCoordinate(ints[pos]); + y += decodeCoordinate(ints[pos + 1]); + line.push([x * coordinatesScale, y * coordinatesScale]) + pos += 2; + } + break; + case 7: // ClosePath + if (geometryType !== TileProto.GeomType.POLYGON) { + throw new Error(`ClosePath command for non-polygon type ${geometryType}`); + } + if (!line) { + throw new Error('ClosePath with empty linestring') + } + if (cmdRepeat !== 1) { + throw new Error(`ClosePath repeats ${cmdRepeat} times`); + } + line.push(line[0]); + pos += 1; + break; + default: + throw new Error(`Unknown command ${i} & 0x7 = ${cmd}`); + } + } + if (line) { + lineStrings.push(line); + } + const geometry = {}; + switch (geometryType) { + case TileProto.GeomType.POINT: + if (lineStrings.length !== 1 || lineStrings[0].length !== 1) { + console.log(lineStrings); + throw new Error('Invalid coordinates number for point'); + } + geometry.type = 'Point'; + geometry.coordinats = lineStrings[0][0]; + break; + case TileProto.GeomType.LINESTRING: + if (lineStrings.length !== 1) { + throw new Error('Invalid linestrings number for line'); + } + geometry.type = 'LineString'; + geometry.coordinats = lineStrings[0]; + break; + case TileProto.GeomType.POLYGON: + geometry.type = 'Polygon'; + geometry.coordinats = lineStrings; + break; + } + return geometry; +} + +function parseFeatureTags(tagsInts, layerKeys, layerValues) { + const tags = {}; + if (tagsInts) { + let i = 0; + while (i < tagsInts.length) { + let key = layerKeys[tagsInts[i]]; + let value = layerValues[tagsInts[i + 1]]; + tags[key] = value; + i +=2; + } + } + return tags; +} + +function parseFeatures(layer, coordinatesScale) { + const features = []; + const tagValues = layer.values.map(simlifyTagValue); + for (let feature of layer.features) { + const properties = parseFeatureTags(feature.tags, layer.keys, tagValues); + const geometry = parseGeometry(feature.type, feature.geometry, coordinatesScale); + features.push({properties, geometry}); + } + return features; +} + +function decodeMvt(ar, tileExtent=256) { + const + pbf = new Pbf(new Uint8Array(ar)), + tileData = TileProto.read(pbf); + const layers = tileData.layers.map((layer) => { + const scale = tileExtent / layer.extent; + return { + name: layer.name, + features: parseFeatures(layer, scale) + } + }); + return layers; +} + +export {decodeMvt}; +\ No newline at end of file