nakarte

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

jnx-maker.js (4199B)


      1 import L from 'leaflet';
      2 import {JnxWriter} from './jnx-encoder';
      3 import {getTempMap, disposeMap} from '~/lib/leaflet.layer.rasterize';
      4 import {XHRQueue} from '~/lib/xhr-promise';
      5 import {arrayBufferToString, stringToArrayBuffer} from '~/lib/binary-strings';
      6 
      7 const defaultXHROptions = {
      8     responseType: 'arraybuffer',
      9     timeout: 20000,
     10     isResponseSuccess: (xhr) => xhr.status === 200 || xhr.status === 404
     11 };
     12 
     13 function minZoom(maxZoom) {
     14     return Math.max(maxZoom - 4, 0);
     15 }
     16 
     17 function imageFromarrayBuffer(arr) {
     18     const dataUrl = 'data:image/png;base64,' + btoa(arrayBufferToString(arr));
     19     const image = new Image();
     20     return new Promise(function(resolve, reject) {
     21             image.onload = () => resolve(image);
     22             image.onerror = () => reject(new Error('Tile image corrupt'));
     23             image.src = dataUrl;
     24         }
     25     );
     26 }
     27 
     28 async function convertToJpeg(image) {
     29     try {
     30         image = await imageFromarrayBuffer(image);
     31     } catch (e) {
     32         return null;
     33     }
     34     const canvas = document.createElement("canvas");
     35     canvas.width = image.width;
     36     canvas.height = image.height;
     37     const ctx = canvas.getContext("2d");
     38     ctx.drawImage(image, 0, 0);
     39     const dataURL = canvas.toDataURL("image/jpeg");
     40     const s = atob(dataURL.replace(/^data:image\/jpeg;base64,/u, ""));
     41     return stringToArrayBuffer(s);
     42 }
     43 
     44 function ensureImageJpg(image) {
     45     if (!image) {
     46         return null;
     47     }
     48     if (arrayBufferToString(image.slice(0, 4)) === '\x89PNG' &&
     49         arrayBufferToString(image.slice(-8)) === 'IEND\xae\x42\x60\x82') {
     50         return convertToJpeg(image);
     51     } else if (arrayBufferToString(image.slice(0, 2)) === '\xff\xd8' &&
     52         arrayBufferToString(image.slice(-2)) === '\xff\xd9') {
     53         return Promise.resolve(image);
     54     }
     55     return null;
     56 }
     57 
     58 async function makeJnxFromLayer(
     59     srcLayer, layerName, maxZoomLevel, latLngBounds, correctZoom, progress
     60 ) {
     61     const jnxProductId = L.stamp(srcLayer);
     62     const jnxZOrder = Math.min(jnxProductId, 100);
     63     // scale multiplier for GPSMAP 67
     64     const scaleMultiplier = correctZoom ? 3.5 : 1;
     65     const writer = new JnxWriter(layerName, jnxProductId, jnxZOrder, scaleMultiplier);
     66     const xhrQueue = new XHRQueue();
     67     let doStop = false;
     68     let error;
     69     const minZoomLevel = minZoom(maxZoomLevel);
     70     let progressWeight = 1;
     71     for (let zoom = maxZoomLevel; zoom >= minZoomLevel; zoom--) {
     72         let pixelBounds = L.bounds(
     73             L.CRS.EPSG3857.latLngToPoint(latLngBounds.getNorthWest(), zoom).round(),
     74             L.CRS.EPSG3857.latLngToPoint(latLngBounds.getSouthEast(), zoom).round()
     75         );
     76 
     77         let promises = [];
     78         let layer = srcLayer.cloneForPrint({xhrQueue});
     79         let tempMap = getTempMap(zoom, layer._rasterizeNeedsFullSizeMap, pixelBounds);
     80         tempMap.addLayer(layer);
     81         let {iterateTilePromises, count: tilesCount} = await layer.getTilesInfo({
     82                 xhrOptions: defaultXHROptions,
     83                 pixelBounds,
     84                 rawData: true
     85             }
     86         );
     87         for (let tilePromiseRec of iterateTilePromises()) {
     88             promises.push(tilePromiseRec);
     89         }
     90         for (let {tilePromise} of promises) {
     91             let imageRec;
     92             try {
     93                 imageRec = await tilePromise;
     94             } catch (e) {
     95                 error = e;
     96                 doStop = true;
     97                 break;
     98             }
     99             let xhr = imageRec.image;
    100 
    101             if (xhr === null || xhr.status !== 200 || !xhr.response || !xhr.response.byteLength) {
    102                 continue;
    103             }
    104             let image = await ensureImageJpg(xhr.response);
    105             if (!image) {
    106                 error = new Error('Tile image invalid');
    107                 doStop = true;
    108                 break;
    109             }
    110             writer.addTile(image, zoom, imageRec.latLngBounds);
    111             progress(progressWeight / tilesCount, 4 / 3);
    112         }
    113         disposeMap(tempMap);
    114         if (doStop) {
    115             promises.forEach((promiseRec) => promiseRec.abortLoading());
    116             throw error;
    117         }
    118         progressWeight /= 4;
    119     }
    120 
    121     return writer.getJnx();
    122 }
    123 
    124 export {makeJnxFromLayer, minZoom};