nakarte

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

jnx-encoder.js (6026B)


      1 import L from 'leaflet';
      2 import {BinStream} from '~/lib/binary-stream';
      3 
      4 function jnxCoordinates(extents) {
      5     function toJnx(x) {
      6         return Math.round(x / 180.0 * 0x7fffffff);
      7     }
      8 
      9     return [toJnx(extents.north), toJnx(extents.east), toJnx(extents.south), toJnx(extents.west)];
     10 }
     11 
     12 const JnxWriter = L.Class.extend({
     13         initialize: function(productName, productId, zOrder, scaleMultiplier = 1) {
     14             this.tiles = {};
     15             this.productName = productName || 'Raster map';
     16             this.productId = productId || 0;
     17             this.zOrder = zOrder;
     18             this.scaleMultiplier = scaleMultiplier;
     19         },
     20 
     21         addTile: function(tileData, level, latLngBounds) {
     22             this.tiles[level] = this.tiles[level] || [];
     23             tileData = new Blob([tileData]).slice(2);
     24             const extents = {
     25                 west: latLngBounds.getWest(),
     26                 north: latLngBounds.getNorth(),
     27                 south: latLngBounds.getSouth(),
     28                 east: latLngBounds.getEast(),
     29 
     30             };
     31             this.tiles[level].push({data: tileData, extents: extents});
     32         },
     33 
     34         getJnx: function() {
     35             const HEADER_SIZE = 52,
     36                 LEVEL_INFO_SIZE = 17,
     37                 TILE_INFO_SIZE = 28;
     38 
     39             let totalTilesCount = 0;
     40             for (let levelTiles of Object.values(this.tiles)) {
     41                 totalTilesCount += levelTiles.length;
     42             }
     43             if (totalTilesCount === 0) {
     44                 throw new Error('No tiles collected, JNX is empty');
     45             }
     46 
     47             let west = 1e10,
     48                 east = -1e10,
     49                 north = -1e10,
     50                 south = 1e10,
     51                 levels_n = Object.keys(this.tiles).length,
     52                 level, tiles, extents,
     53                 i, tile;
     54             for (let levelTiles of Object.values(this.tiles)) {
     55                 for (let tile of levelTiles) {
     56                     west = (west < tile.extents.west) ? west : tile.extents.west;
     57                     east = (east > tile.extents.east) ? east : tile.extents.east;
     58                     north = (north > tile.extents.north) ? north : tile.extents.north;
     59                     south = (south < tile.extents.south) ? south : tile.extents.south;
     60                 }
     61             }
     62             const stream = new BinStream(true);
     63             // header
     64             stream.writeInt32(4); // version
     65             stream.writeInt32(0); // device id
     66             extents = jnxCoordinates({south: south, north: north, west: west, east: east});
     67             stream.writeInt32(extents[0]); // north
     68             stream.writeInt32(extents[1]); // west
     69             stream.writeInt32(extents[2]); // south
     70             stream.writeInt32(extents[3]); // east
     71             stream.writeInt32(levels_n); // number of zoom levels
     72             stream.writeInt32(0); // expiration date
     73             stream.writeInt32(this.productId);
     74             stream.writeInt32(0); // tiles CRC32
     75             stream.writeInt32(0); // signature version
     76             stream.writeUint32(0); // signature offset
     77             stream.writeInt32(this.zOrder);
     78             stream.seek(HEADER_SIZE + LEVEL_INFO_SIZE * levels_n);
     79             // map description
     80             stream.writeInt32(9); // section version
     81             stream.writeString('12345678-1234-1234-1234-123456789ABC', true); // GUID
     82             stream.writeString(this.productName, true);
     83             stream.writeString('', true);
     84             stream.writeInt16(this.productId);
     85             stream.writeString(this.productName, true);
     86             stream.writeInt32(levels_n);
     87             // levels descriptions
     88             for (level of Object.keys(this.tiles)) {
     89                 stream.writeString('', true);
     90                 stream.writeString('', true);
     91                 stream.writeString('', true);
     92                 stream.writeInt32(level);
     93             }
     94             let tileDescriptorOffset = stream.tell();
     95             // level info
     96             let jnxScale;
     97             stream.seek(HEADER_SIZE);
     98             for (level of Object.keys(this.tiles)) {
     99                 level = parseInt(level, 10);
    100                 stream.writeInt32(this.tiles[level].length);
    101                 stream.writeUint32(tileDescriptorOffset);
    102                 jnxScale =
    103                     34115555 /
    104                     (2 ** level) *
    105                     Math.cos((north + south) / 2 / 180 * Math.PI) /
    106                     1.1 * this.scaleMultiplier;
    107                 stream.writeInt32(jnxScale);
    108                 stream.writeInt32(2);
    109                 stream.writeUint8(0);
    110                 tileDescriptorOffset += TILE_INFO_SIZE * this.tiles[level].length;
    111             }
    112             // tiles descriptors
    113             stream.seek(stream.size);
    114             let tileDataOffset = tileDescriptorOffset;
    115             for (level of Object.keys(this.tiles)) {
    116                 tiles = this.tiles[level];
    117                 for (i = 0; i < tiles.length; i++) {
    118                     tile = tiles[i];
    119                     extents = jnxCoordinates(tile.extents);
    120                     stream.writeInt32(extents[0]); // north
    121                     stream.writeInt32(extents[1]); // west
    122                     stream.writeInt32(extents[2]); // south
    123                     stream.writeInt32(extents[3]); // east
    124                     stream.writeInt16(256); // width
    125                     stream.writeInt16(256); // height
    126                     stream.writeInt32(tile.data.size);
    127                     stream.writeUint32(tileDataOffset);
    128                     tileDataOffset += tile.data.size;
    129                 }
    130             }
    131 
    132             const blob = [];
    133             blob.push(stream.getBuffer());
    134             for (level of Object.keys(this.tiles)) {
    135                 tiles = this.tiles[level];
    136                 for (i = 0; i < tiles.length; i++) {
    137                     tile = tiles[i];
    138                     blob.push(tile.data);
    139                 }
    140             }
    141 
    142             blob.push('BirdsEye');
    143             return new Blob(blob);
    144         }
    145     }
    146 );
    147 
    148 export {JnxWriter};