nakarte

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

jnx-encoder.js (6036B)


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