mvt.js (4258B)
1 import Pbf from 'pbf'; 2 import {Tile as TileProto} from './vector_tile_pb'; 3 4 function decodeCoordinate(x) { 5 return ((x >> 1) ^ (-(x & 1))); 6 } 7 8 function parseGeometry(geometryType, ints, coordinatesScale) { // eslint-disable-line complexity 9 if (geometryType !== TileProto.GeomType.POINT && geometryType !== TileProto.GeomType.LINESTRING && 10 geometryType !== TileProto.GeomType.POLYGON) { 11 throw new Error(`Unknown feature geometry type ${geometryType}`); 12 } 13 const len = ints.length; 14 let pos = 0; 15 const lineStrings = []; 16 let line; 17 let x = 0, 18 y = 0; 19 while (pos < len) { 20 let i = ints[pos]; 21 let cmd = i & 0x7; 22 let cmdRepeat = i >> 3; 23 switch (cmd) { 24 case 1: // MoveTo 25 if (cmdRepeat !== 1) { 26 throw new Error(`repeat=${cmdRepeat} for command MoveTo`); 27 } 28 if (pos + 2 > len) { 29 throw new Error('Not enough elements for MoveTo arguments'); 30 } 31 if (line) { 32 lineStrings.push(line); 33 } 34 x += decodeCoordinate(ints[pos + 1]); 35 y += decodeCoordinate(ints[pos + 2]); 36 line = [[x * coordinatesScale, y * coordinatesScale]]; 37 pos += 3; 38 break; 39 case 2: // LineTo 40 if (cmdRepeat < 1) { 41 throw new Error(`repeat=${cmdRepeat} for command LineTo`); 42 } 43 if (!line) { 44 throw new Error('LineTo with empty linestring'); 45 } 46 pos += 1; 47 for (let cmdN = 0; cmdN < cmdRepeat; cmdN++) { 48 if (pos + 2 > len) { 49 throw new Error('Not enough elements for LineTo arguments'); 50 } 51 x += decodeCoordinate(ints[pos]); 52 y += decodeCoordinate(ints[pos + 1]); 53 line.push([x * coordinatesScale, y * coordinatesScale]); 54 pos += 2; 55 } 56 break; 57 case 7: // ClosePath 58 if (geometryType !== TileProto.GeomType.POLYGON) { 59 throw new Error(`ClosePath command for non-polygon type ${geometryType}`); 60 } 61 if (!line) { 62 throw new Error('ClosePath with empty linestring'); 63 } 64 if (cmdRepeat !== 1) { 65 throw new Error(`ClosePath repeats ${cmdRepeat} times`); 66 } 67 line.push(line[0]); 68 pos += 1; 69 break; 70 default: 71 throw new Error(`Unknown command ${i} & 0x7 = ${cmd}`); 72 } 73 } 74 if (line) { 75 lineStrings.push(line); 76 } 77 const geometry = {}; 78 switch (geometryType) { 79 case TileProto.GeomType.POINT: 80 if (lineStrings.length !== 1 || lineStrings[0].length !== 1) { 81 throw new Error('Invalid coordinates number for point'); 82 } 83 geometry.type = 'Point'; 84 geometry.coordinates = lineStrings[0][0]; 85 break; 86 case TileProto.GeomType.LINESTRING: 87 geometry.type = 'MultiLineString'; 88 geometry.coordinates = lineStrings; 89 break; 90 case TileProto.GeomType.POLYGON: 91 geometry.type = 'Polygon'; 92 geometry.coordinates = lineStrings; 93 break; 94 default: 95 } 96 return geometry; 97 } 98 99 function parseFeatures(layer, coordinatesScale) { 100 const features = []; 101 for (let feature of layer.features) { 102 const geometry = parseGeometry(feature.type, feature.geometry, coordinatesScale); 103 features.push({geometry}); 104 } 105 return features; 106 } 107 108 function decodeMvt(ar, tileExtent = 256) { 109 const 110 pbf = new Pbf(new Uint8Array(ar)), 111 tileData = TileProto.read(pbf); 112 const parsedLayers = []; 113 for (let layer of tileData.layers) { 114 let scale = tileExtent / layer.extent; 115 parsedLayers.push({ 116 name: layer.name, 117 features: parseFeatures(layer, scale) 118 }); 119 } 120 return parsedLayers; 121 } 122 123 export {decodeMvt};