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