nakarte

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

commit ec7b6cfe2a32a1ee8639337c0ba7c6418508a171
parent abf2683238e3d9759acd34fa7eaeacee8da78d46
Author: Sergej Orlov <wladimirych@gmail.com>
Date:   Thu,  9 Feb 2017 11:59:12 +0300

[print control] map render; save pdf; hash state; tileLayer, Yandex. Bing

Diffstat:
Msrc/App.js | 4+++-
Msrc/lib/leaflet.control.printPages/control.js | 192+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/lib/leaflet.control.printPages/form.html | 2+-
Msrc/lib/leaflet.control.printPages/map-render.js | 269+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/lib/leaflet.control.printPages/pageFeature.js | 5++++-
Asrc/lib/leaflet.control.printPages/pdf.js | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/leaflet.layer.rasterize/Bing.js | 30++++++++++++++++++++++++++++++
Asrc/lib/leaflet.layer.rasterize/TileLayer.js | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/leaflet.layer.rasterize/Yandex.js | 9+++++++++
Asrc/lib/leaflet.layer.rasterize/imgFromDataString.js | 19+++++++++++++++++++
Asrc/lib/leaflet.layer.rasterize/index.js | 3+++
11 files changed, 683 insertions(+), 57 deletions(-)

diff --git a/src/App.js b/src/App.js @@ -70,7 +70,9 @@ function setUp() { /////////// controls bottom-left corner - new L.Control.PrintPages({position: 'bottomleft'}).addTo(map); + new L.Control.PrintPages({position: 'bottomleft'}) + .addTo(map) + .enableHashState('p'); /////////// controls bottom-right corner diff --git a/src/lib/leaflet.control.printPages/control.js b/src/lib/leaflet.control.printPages/control.js @@ -5,8 +5,13 @@ import 'lib/controls-styles/controls-styles.css'; import './control.css'; import PageFeature from './pageFeature'; import Contextmenu from 'lib/contextmenu'; -import {renderMap} from './map-render' +import {renderPages} from './map-render' import formHtml from './form.html'; +import {notify, notifyXhrError} from 'lib/notifications'; +import {makePdf} from './pdf'; +import {saveAs} from 'browser-filesaver'; +import {blobFromString} from 'lib/binary-strings'; +import 'lib/leaflet.hashState/leaflet.hashState'; ko.extenders.checkNumberRange = function(target, range) { return ko.pureComputed({ @@ -23,9 +28,23 @@ ko.extenders.checkNumberRange = function(target, range) { ).extend({notify: 'always'}); }; +function savePagesPdf(imagesInfo, resolution) { + let pdf = makePdf(imagesInfo, resolution); + pdf = blobFromString(pdf); + saveAs(pdf, 'map.pdf'); +} + +function savePageJpg(page) { + saveAs(blobFromString(page.data), 'map.jpg'); +} + L.Control.PrintPages = L.Control.extend({ options: {position: 'bottomleft'}, + includes: [L.Mixin.Events, L.Mixin.HashState], + + stateChangeEvents: ['change'], + pageSizes: [ {'name': 'A1', width: 594, height: 841}, {'name': 'A2', width: 420, height: 594}, @@ -56,6 +75,12 @@ L.Control.PrintPages = L.Control.extend({ this.scale.subscribe(this.onPageSizeChanged, this); this.resolution.subscribe(this.onPageSizeChanged, this); this.pageSizeDescription = ko.pureComputed(this._displayPageSize, this); + + //hash state notifications + this.scale.subscribe(this.notifyChange, this); + this.printSize.subscribe(this.notifyChange, this); + this.resolution.subscribe(this.notifyChange, this); + this.zoomLevel.subscribe(this.notifyChange, this); }, onAdd: function(map) { @@ -82,27 +107,32 @@ L.Control.PrintPages = L.Control.extend({ L.DomUtil.addClass(this._container, 'minimized'); }, - addPage: function(isLandsacape) { + addPage: function(isLandsacape, center) { let [pageWidth, pageHeight] = this.printSize(); if (isLandsacape) { [pageWidth, pageHeight] = [pageHeight, pageWidth]; } - const page = new PageFeature(this._map.getCenter(), [pageWidth, pageHeight], + if (!center) { + center = this._map.getCenter(); + } + const page = new PageFeature(center, [pageWidth, pageHeight], this.scale(), (this.pages.length + 1).toString() ); + page._rotated = isLandsacape; page.addTo(this._map); this.pages.push(page); let cm = new Contextmenu(this.makePageContexmenuItems.bind(this, page)); page.on('contextmenu', cm.show, cm); page.on('click', this.rotatePage.bind(this, page)); page.on('move', this.updateFormZooms, this); + page.on('moveend', this.notifyChange, this); this.updateFormZooms(); + this.notifyChange(); return page }, addLandscapePage: function() { const page = this.addPage(true); - page._rotated = true; }, addPortraitPage: function() { @@ -116,20 +146,101 @@ L.Control.PrintPages = L.Control.extend({ for (; i < this.pages.length; i++) { this.pages[i].setLabel((i + 1).toString()); } + this.notifyChange(); this.updateFormZooms() }, removePages: function() { this.pages.forEach((page) => page.removeFrom(this._map)); this.pages = []; + this.notifyChange(); this.updateFormZooms(); }, - savePdf: function(data) { + onSavePdfClicked: function() { + if (!this.pages.length) { + notify('Add some pages to print'); + return; + } + this.savePdf(); + }, + + zoomForPrint: function() { + let zoom = this.zoomLevel(); + if (zoom === 'auto') { + zoom = this.suggestZooms() + } else { + zoom = {mapZoom: zoom, satZoom: zoom} + } + return zoom; + }, + + incrementProgress: function(inc, range) { + this.downloadProgressRange(range); + this.downloadProgressDone((this.downloadProgressDone() || 0) + inc); + }, + + savePdf: function() { if (!this._map) { return; } - renderMap(this._map); + this.downloadProgressDone(0); + this.makingPdf(true); + const pages = this.pages.map((page) => { + return { + latLngBounds: page.getLatLngBounds(), + printSize: page.getPrintSize() + } + } + ); + const resolution = this.resolution(); + renderPages({ + map: this._map, + pages, + zooms: this.zoomForPrint(), + resolution, + progressCallback: this.incrementProgress.bind(this) + } + ).then((images) => { + if (images) { + savePagesPdf(images, resolution) + } + } + ).catch((e) => { + if (e.status !== undefined) { + notifyXhrError(e, 'map'); + } else { + notify(e); + } + } + ).then(() => this.makingPdf(false)); + }, + + savePageJpg: function(page) { + const pages = [{ + latLngBounds: page.getLatLngBounds(), + printSize: page.getPrintSize() + }]; + this.downloadProgressDone(0); + this.makingPdf(true); + renderPages({ + map: this._map, + pages, + zooms: this.zoomForPrint(), + resolution: this.resolution(), + progressCallback: this.incrementProgress.bind(this) + } + ) + .then((images) => savePageJpg(images[0])) + .catch((e) => { + // throw e; + if (e.status !== undefined) { + notifyXhrError(e, 'map'); + } else { + notify(e); + } + } + ).then(() => this.makingPdf(false)); }, onPageSizeChanged: function() { @@ -151,7 +262,7 @@ L.Control.PrintPages = L.Control.extend({ '-', {text: 'Delete', callback: this.removePage.bind(this, page)}, '-', - {text: 'Save image', callback: this.savePageJpg.bind(this, page), disabled: true} + {text: 'Save image', callback: this.savePageJpg.bind(this, page)} ]; if (this.pages.length > 1) { items.push({text: 'Change order', separator: true}); @@ -172,10 +283,7 @@ L.Control.PrintPages = L.Control.extend({ rotatePage: function(page) { page._rotated = !page._rotated; page.rotate(); - }, - - savePageJpg: function(page) { - + this.notifyChange(); }, renumberPage: function(page, newIndex) { @@ -185,6 +293,7 @@ L.Control.PrintPages = L.Control.extend({ for (let i = Math.min(oldIndex, newIndex); i < this.pages.length; i++) { this.pages[i].setLabel((i + 1).toString()); } + this.notifyChange(); }, _printSize: function() { @@ -231,6 +340,64 @@ L.Control.PrintPages = L.Control.extend({ } } return `${width} x ${height} mm`; + }, + + notifyChange: function() { + this.fire('change'); + }, + + serializeState: function() { + const pages = this.pages; + let state = null; + if (pages.length) { + state = []; + state.push(this.scale().toString()); + state.push(this.resolution().toString()); + state.push(this.zoomLevel().toString()); + state.push(this.pageWidth().toString()); + state.push(this.pageHeight().toString()); + state.push(this.marginLeft().toString()); + state.push(this.marginRight().toString()); + state.push(this.marginTop().toString()); + state.push(this.marginBottom().toString()); + for (let page of pages) { + let latLng = page.getLatLng(); + state.push(latLng.lat.toFixed(5)); + state.push(latLng.lng.toFixed(5)); + state.push(page._rotated ? '1' : '0'); + } + } + return state; + }, + + unserializeState: function(state) { + if (!state || !state.length) { + return false; + } + this.removePages(); + state = [...state]; + this.scale(state.shift()); + this.resolution(state.shift()); + this.zoomLevel(state.shift()); + this.pageWidth(state.shift()); + this.pageHeight(state.shift()); + this.marginLeft(state.shift()); + this.marginRight(state.shift()); + this.marginTop(state.shift()); + this.marginBottom(state.shift()); + let lat, lng, rotated; + while (state.length >= 3) { + lat = parseFloat(state.shift()); + lng = parseFloat(state.shift()); + rotated = parseInt(state.shift(), 10); + if (isNaN(lat) || isNaN(lng) || lat < -85 || lat > 85 || lng < -180 || lng > 180) { + break; + } + this.addPage(!!rotated, L.latLng(lat, lng)); + } + return true; + + } } -); -\ No newline at end of file +); diff --git a/src/lib/leaflet.control.printPages/form.html b/src/lib/leaflet.control.printPages/form.html @@ -78,7 +78,7 @@ <div class="download-button-row"> <div class="button-minimize" data-bind="click: setMinimized"></div> <a class="text-button button-save" data-bind=" - click: savePdf, + click: onSavePdfClicked, visible: !makingPdf()">Save PDF</a> <div data-bind=" component: { diff --git a/src/lib/leaflet.control.printPages/map-render.js b/src/lib/leaflet.control.printPages/map-render.js @@ -1,46 +1,51 @@ +import L from 'leaflet'; +import 'lib/leaflet.layer.rasterize'; +import {XHRQueue} from 'lib/xhr-promise'; -function getZIndex(el) { - return parseInt(window.getComputedStyle(el).zIndex, 10) || 0; -} -function compare(i1, i2) { - if (i1 < i2) { - return -1; - } else if (i1 > i2) { - return 1; +function getLayersForPrint(map, xhrQueue) { + function getZIndex(el) { + return parseInt(window.getComputedStyle(el).zIndex, 10) || 0; } - return 0; -} -function compareArrays(ar1, ar2) { - const len = Math.min(ar1.length, ar2.length); - for (let i = 0; i < len; i++) { - let c = compare(ar1[i], ar2[i]); - if (c) { - return c; + function compare(i1, i2) { + if (i1 < i2) { + return -1; + } else if (i1 > i2) { + return 1; } + return 0; } - return compare(ar1.length, ar2.length); -} -function getLayerZOrder(layer) { - let el = layer._container || layer._path; - if (!el) { - throw TypeError('Unsupported layer type'); + function compareLayersOrder(layer1, layer2) { + return compareArrays(getLayerZOrder(layer1), getLayerZOrder(layer2)); } - const order = []; - while (el !== layer._map._container) { - order.push(getZIndex(el)); - el = el.parentElement; + + function getLayerZOrder(layer) { + let el = layer._container || layer._path; + if (!el) { + throw TypeError('Unsupported layer type'); + } + const order = []; + while (el !== layer._map._container) { + order.push(getZIndex(el)); + el = el.parentElement; + } + return order.reverse() } - return order.reverse() -} -function compareLayersOrder(layer1, layer2) { - return compareArrays(getLayerZOrder(layer1), getLayerZOrder(layer2)); -} -function getLayersForPrint(map) { + function compareArrays(ar1, ar2) { + const len = Math.min(ar1.length, ar2.length); + for (let i = 0; i < len; i++) { + let c = compare(ar1[i], ar2[i]); + if (c) { + return c; + } + } + return compare(ar1.length, ar2.length); + } + let layers = []; map.eachLayer((layer) => { if (layer.options.print) { @@ -49,20 +54,202 @@ function getLayersForPrint(map) { } ); layers.sort(compareLayersOrder); - layers = layers.map((l) => {l.clone()}); + layers = layers.map((l) => l.cloneForPrint({xhrQueue})); return layers; } -function renderPage(layers, bounds, zoom, resolution) { - // const canvas = +class PageComposer { + constructor(destSize, pixelBoundsAtZoom24) { + this.destSize = destSize; + this.projectedBounds = pixelBoundsAtZoom24; + this.currentCanvas = null; + this.currentZoom = null; + this.targetCanvas = this.createCanvas(destSize); + } + + createCanvas(size) { + const canvas = L.DomUtil.create('canvas'); + canvas.width = size.x; + canvas.height = size.y; + return canvas; + } + + putTile({image, tilePos, tileSize, zoom}) { + if (image === null) { + return; + } + if (zoom !== this.currentZoom) { + this.mergeCurrentCanvas(); + this.setupCurrentCanvas(zoom); + } + this.currentCanvas.getContext('2d').drawImage(image, tilePos.x, tilePos.y, tileSize.x, tileSize.y); + } + + setupCurrentCanvas(zoom) { + const q = 1 << (24 - zoom); + const + topLeft = this.projectedBounds.min.divideBy(q).round(), + bottomRight = this.projectedBounds.max.divideBy(q).round(), + size = bottomRight.subtract(topLeft); + this.currentCanvas = this.createCanvas(size); + this.currentZoom = zoom; + // this.currentOffset = topLeft; + } + + mergeCurrentCanvas() { + if (!this.currentCanvas) { + return; + } + this.targetCanvas.getContext('2d').drawImage(this.currentCanvas, 0, 0, + this.destSize.x, this.destSize.y + ); + this.currentCanvas = null; + } + + getDataUrl() { + this.mergeCurrentCanvas(); + const dataUrl = this.targetCanvas.toDataURL("image/jpeg"); + this.targetCanvas = null; + return dataUrl; + } +} + +function getTempMap(zoom) { + const container = L.DomUtil.create('div', '', document.body); + Object.assign(container.style, { + width: '100px', + height: '100px', + position: 'absolute', + left: '0', + top: '0', + // visibility: 'hidden' + } + ); + const map = L.map(container, {fadeAnimation: false, zoomAnimation: false, inertia: false}); + map.setView(L.latLng(0, 0), zoom); + return map; +} + +function disposeMap(map) { + const container = map._container; + map.remove(); + L.DomUtil.remove(container); +} + +async function* iterateLayersTiles(layers, latLngBounds, zooms) { + const defaultXHROptions = { + responseType: 'blob', + timeout: 10000, + isResponseSuccess: (xhr) => xhr.status === 200 || xhr.status === 404 + }; + let doStop; + for (let layer of layers) { + let zoom; + if (layer.options.scaleDependent) { + zoom = zooms.mapZoom; + } else { + zoom = zooms.satZoom; + } + let map = getTempMap(zoom); + let pixelBounds = L.bounds( + map.project(latLngBounds.getNorthWest()).round(), + map.project(latLngBounds.getSouthEast()).round() + ); + map.addLayer(layer); + let {iterateTilePromises, count} = await layer.getTilesInfo({xhrOptions: defaultXHROptions, pixelBounds}); + for (let tilePromise of iterateTilePromises()) { + tilePromise.tilePromise = + tilePromise.tilePromise.then((tileInfo) => Object.assign(tileInfo, {zoom, progressInc: 1 / count})); + doStop = yield tilePromise; + if (doStop) { + tilePromise.abortLoading(); + console.log('DO STOP3'); + break; + } + } + disposeMap(map); + if (doStop) { + break; + } + } } -function savePageJpg(map, bounds, zoom, resolution) { - const layers = getLayersForPrint(map); +async function* promiseQueueBuffer(source, maxActive) { + const queue = []; + while (queue.length < maxActive) { + let {value, done} = await source.next(); + if (done) { + break; + } + queue.push(value); + } + + while (queue.length) { + let doStop = yield queue.shift(); + if (doStop) { + console.log('DO STOP2'); + let {value, done} = await source.next(true); + if (!done) { + queue.push(value); + } + for (let {abortLoading} of queue) { + abortLoading(); + } + + return; + } + let {value, done} = await source.next(); + if (!done) { + queue.push(value); + } + } } -function savePagesPdf(map, boundsList, zoom, resolution) { - const layers = getLayersForPrint(map); + +async function renderPages({map, pages, zooms, resolution, progressCallback}) { + const xhrQueue = new XHRQueue(); + const layers = getLayersForPrint(map, xhrQueue); + const progressRange = pages.length * layers.length; + const pageImagesInfo = []; + for (let page of pages) { + let destPixelSize = page.printSize.multiplyBy(resolution / 25.4).round(); + let pixelBounds = L.bounds( + map.project(page.latLngBounds.getNorthWest(), 24).round(), + map.project(page.latLngBounds.getSouthEast(), 24).round() + ); + + const composer = new PageComposer(destPixelSize, pixelBounds); + let tilesIterator = await iterateLayersTiles(layers, page.latLngBounds, zooms); + let queuedTilesIterator = promiseQueueBuffer(tilesIterator, 20); + while (true) { + let {value: tilePromise, done} = await queuedTilesIterator.next(); + iterateLayersTiles(layers, page.latLngBounds, zooms); + if (done) { + break; + } + let tileInfo; + try { + tileInfo = await tilePromise.tilePromise; + } catch (e) { + console.log('DO STOP1'); + queuedTilesIterator.next(true); + throw e; + } + progressCallback(tileInfo.progressInc, progressRange); + composer.putTile(tileInfo); + } + const dataUrl = composer.getDataUrl(); + let data = dataUrl.substring(dataUrl.indexOf(',') + 1); + data = atob(data); + pageImagesInfo.push({ + data, + width: destPixelSize.x, + height: destPixelSize.y + } + ); + } + return pageImagesInfo; } -export {savePageJpg, savePagesPdf}; -\ No newline at end of file + +export {renderPages}; +\ No newline at end of file diff --git a/src/lib/leaflet.control.printPages/pageFeature.js b/src/lib/leaflet.control.printPages/pageFeature.js @@ -75,10 +75,13 @@ const PageFeature = L.Marker.extend({ setSize: function(paperSize, scale) { this.paperSize = paperSize; this.scale = scale; - console.log(paperSize, scale); this.updateView(); }, + getPrintSize: function() { + return L.point(...this.paperSize); + }, + rotate: function(e) { this.paperSize = [this.paperSize[1], this.paperSize[0]]; this.updateView(); diff --git a/src/lib/leaflet.control.printPages/pdf.js b/src/lib/leaflet.control.printPages/pdf.js @@ -0,0 +1,155 @@ +function header() { + return '%PDF-1.3'; +} + + +function eof() { + return '%%EOF\n'; +} + + +function recCatalog() { + return ( +`1 0 obj +<< /Type /Catalog +/Pages 2 0 R +>> +endobj`); +} + + +function recPages(count) { + let kidsIds = []; + for (let i = 0; i < count; i++) { + kidsIds.push(`${i * 3 + 3} 0 R`); + } + kidsIds = kidsIds.join(' '); + return ( +`2 0 obj +<< /Type /Pages +/Kids [ ${kidsIds} ] +/Count ${count} +>> +endobj`); +} + + +function recPage(serialNum, width, height) { + const pageRecId = serialNum * 3 + 3; + const contentRecId = pageRecId + 1; + const imageRecId = pageRecId + 2; + return ( +`${pageRecId} 0 obj +<< /Type /Page +/Parent 2 0 R +/MediaBox [0 0 ${width} ${height}] +/Contents ${contentRecId} 0 R +/Resources << + /XObject << /Im${serialNum} ${imageRecId} 0 R >> + /ProcSet [ /PDF /Text /ImageC ] + >> +>> +endobj`); +} + + +function recContent(serialNum, width, height) { + const pageRecId = serialNum * 3 + 3; + const contentRecId = pageRecId + 1; + const contents = ( +`q +${width} 0 0 ${height} 0 0 cm +/Im${serialNum} Do +Q `); + return ( +`${contentRecId} 0 obj +<< +/Length ${contents.length + 1} +>> +stream +${contents} +endstream +endobj`); +} + + +function recImage(serialNum, widthPixels, heightPixels, data) { + const pageRecId = serialNum * 3 + 3; + const imageRecId = pageRecId + 2; + + return ( +`${imageRecId} 0 obj +<< /Type /XObject +/Subtype /Image +/Name /Im${serialNum} +/Filter [ /DCTDecode ] +/Width ${widthPixels} +/Height ${heightPixels} +/ColorSpace /DeviceRGB +/BitsPerComponent 8 +/Length ${data.length} +>> +stream +${data} +endstream +endobj`); +} + + +function recTrailer(recOffsets, currentOffset) { + const trailer = []; + trailer.push( +`xref +0 ${recOffsets.length + 1} +0000000000 65535 f `); + const zeroes = '0000000000'; + for (let offset of recOffsets) { + offset = offset.toString(); + let padding = zeroes.substr(offset.length); + trailer.push(`${padding}${offset} 00000 n `); + } + + trailer.push( +`trailer +<< /Root 1 0 R +/Size ${recOffsets.length + 1} +>> +startxref +${currentOffset}`); + return trailer.join('\n'); +} + + +function makePdf(imagesInfo, resolution) { + let offset = 0; + const offsets = []; + let pdf = []; + + function addRec(s, skipOffset) { + pdf.push(s); + offset += s.length + 1; + if (!skipOffset) { + offsets.push(offset); + } + } + + addRec(header(), true); + addRec(recCatalog()); + addRec(recPages(imagesInfo.length)); + for (let [i, {data, width, height}] of imagesInfo.entries()) { + let widthPoints = width / resolution * 72, + heightPoints = height / resolution * 72; + addRec(recPage(i, widthPoints, heightPoints)); + addRec(recContent(i, widthPoints, heightPoints)); + addRec(recImage(i, width, height, data)); + } + addRec(recTrailer(offsets, offset), true); + addRec(eof(), true); + + pdf = pdf.join('\n'); + return pdf; +} + + +export {makePdf}; + diff --git a/src/lib/leaflet.layer.rasterize/Bing.js b/src/lib/leaflet.layer.rasterize/Bing.js @@ -0,0 +1,30 @@ +import L from 'leaflet'; +import 'lib/leaflet.layer.bing'; + +L.BingLayer.include({ + whenReady: function() { + if (this._url) { + return Promise.resolve(); + } else { + return new Promise((resolve) => { + let i = setInterval(() => { + if (this._url) { + clearInterval(i); + resolve(); + } + }, 50 + ) + } + ); + } + }, + + cloneForPrint: function(options) { + return new L.BingLayer(this._key, L.Util.extend({}, this.options, options)); + }, + + getTilesInfo: function(printOptions) { + return this.whenReady().then(() => L.TileLayer.prototype.getTilesInfo.call(this, printOptions)); + } + } +); diff --git a/src/lib/leaflet.layer.rasterize/TileLayer.js b/src/lib/leaflet.layer.rasterize/TileLayer.js @@ -0,0 +1,52 @@ +import L from 'leaflet'; +import urlViaCorsProxy from 'lib/CORSProxy'; +import {imgFromDataString} from './imgFromDataString'; + +L.TileLayer.include({ + cloneForPrint: function(options) { + return L.tileLayer(this._url, L.Util.extend({}, this.options, options)); + }, + + getTilesInfo: function(printOptions) { + const {pixelBounds, xhrOptions} = printOptions; + const tileRange = this._pxBoundsToTileRange(pixelBounds); + const topLeft = pixelBounds.min; + const tilePromiseIterator = (function*() { + for (let j = tileRange.min.y; j <= tileRange.max.y; j++) { + for (let i = tileRange.min.x; i <= tileRange.max.x; i++) { + let coords = new L.Point(i, j); + coords.z = this._tileZoom; + + if (!this._isValidTile(coords)) { + continue; + } + + let url = this.getTileUrl(coords); + if (this.options.noCors) { + url = urlViaCorsProxy(url); + } + let tilePos = this._getTilePos(coords); + const coordsPlusOne = coords.add(L.point(1, 1)); + coordsPlusOne.z = coords.z; + const tileSize = this._getTilePos(coordsPlusOne).subtract(tilePos); + tilePos = tilePos.add(this._level.origin).subtract(topLeft); + let promise = this.options.xhrQueue.put(url, xhrOptions); + yield { + tilePromise: promise.then(imgFromDataString).then((image) => { + return {image, tilePos, tileSize}; + } + ), + abortLoading: () => promise.abort() + }; + } + } + }).bind(this); + return Promise.resolve({ + iterateTilePromises: tilePromiseIterator, + count: (tileRange.max.x - tileRange.min.x + 1) * (tileRange.max.y - tileRange.min.y + 1) + } + ); + } + } +); + diff --git a/src/lib/leaflet.layer.rasterize/Yandex.js b/src/lib/leaflet.layer.rasterize/Yandex.js @@ -0,0 +1,8 @@ +import L from 'leaflet'; +import 'lib/leaflet.layer.yandex'; + +L.Layer.Yandex.include({ + cloneForPrint: function(options) { + return new L.Layer.Yandex(this._mapType, L.Util.extend({}, this.options, options)); + }, +}); +\ No newline at end of file diff --git a/src/lib/leaflet.layer.rasterize/imgFromDataString.js b/src/lib/leaflet.layer.rasterize/imgFromDataString.js @@ -0,0 +1,19 @@ +function imgFromDataString(xhr) { + if (xhr.status === 200 && xhr.response.size) { + const image = new Image(); + let blobUrl = window.URL.createObjectURL(xhr.response); + const promise = new Promise((resolve) => { + image.onload = () => { + resolve(image); + window.URL.revokeObjectURL(blobUrl); + }; + } + ); + image.src = blobUrl; + return promise; + } else { + return null; + } +} + +export {imgFromDataString} diff --git a/src/lib/leaflet.layer.rasterize/index.js b/src/lib/leaflet.layer.rasterize/index.js @@ -0,0 +1,3 @@ +import './TileLayer' +import './Bing' +import './Yandex'