nakarte

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

jnx-maker.js (4074B)


      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(srcLayer, layerName, maxZoomLevel, latLngBounds, progress) {
     59     const jnxProductId = L.stamp(srcLayer);
     60     const jnxZOrder = Math.min(jnxProductId, 100);
     61     const writer = new JnxWriter(layerName, jnxProductId, jnxZOrder);
     62     const xhrQueue = new XHRQueue();
     63     let doStop = false;
     64     let error;
     65     const minZoomLevel = minZoom(maxZoomLevel);
     66     let progressWeight = 1;
     67     for (let zoom = maxZoomLevel; zoom >= minZoomLevel; zoom--) {
     68         let pixelBounds = L.bounds(
     69             L.CRS.EPSG3857.latLngToPoint(latLngBounds.getNorthWest(), zoom).round(),
     70             L.CRS.EPSG3857.latLngToPoint(latLngBounds.getSouthEast(), zoom).round()
     71         );
     72 
     73         let promises = [];
     74         let layer = srcLayer.cloneForPrint({xhrQueue});
     75         let tempMap = getTempMap(zoom, layer._rasterizeNeedsFullSizeMap, pixelBounds);
     76         tempMap.addLayer(layer);
     77         let {iterateTilePromises, count: tilesCount} = await layer.getTilesInfo({
     78                 xhrOptions: defaultXHROptions,
     79                 pixelBounds,
     80                 rawData: true
     81             }
     82         );
     83         for (let tilePromiseRec of iterateTilePromises()) {
     84             promises.push(tilePromiseRec);
     85         }
     86         for (let {tilePromise} of promises) {
     87             let imageRec;
     88             try {
     89                 imageRec = await tilePromise;
     90             } catch (e) {
     91                 error = e;
     92                 doStop = true;
     93                 break;
     94             }
     95             let xhr = imageRec.image;
     96 
     97             if (xhr === null || xhr.status !== 200 || !xhr.response || !xhr.response.byteLength) {
     98                 continue;
     99             }
    100             let image = await ensureImageJpg(xhr.response);
    101             if (!image) {
    102                 error = new Error('Tile image invalid');
    103                 doStop = true;
    104                 break;
    105             }
    106             writer.addTile(image, zoom, imageRec.latLngBounds);
    107             progress(progressWeight / tilesCount, 4 / 3);
    108         }
    109         disposeMap(tempMap);
    110         if (doStop) {
    111             promises.forEach((promiseRec) => promiseRec.abortLoading());
    112             throw error;
    113         }
    114         progressWeight /= 4;
    115     }
    116 
    117     return writer.getJnx();
    118 }
    119 
    120 export {makeJnxFromLayer, minZoom};