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