nakarte

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

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};