commit 20eee8b4cf39b08c2089701ff0f7686e3dd00a79
parent ec7b6cfe2a32a1ee8639337c0ba7c6418508a171
Author: Sergej Orlov <wladimirych@gmail.com>
Date:   Wed, 15 Feb 2017 21:46:00 +0300
[print] support for google, westra passes, waypoints
Diffstat:
12 files changed, 235 insertions(+), 88 deletions(-)
diff --git a/src/layers.js b/src/layers.js
@@ -237,8 +237,6 @@ export default function getLayers() {
                 },
             ]
         },
-
-
         {
             group: 'OpenStreetMap alternatives',
             layers: [
diff --git a/src/lib/leaflet.control.printPages/map-render.js b/src/lib/leaflet.control.printPages/map-render.js
@@ -114,19 +114,33 @@ class PageComposer {
     }
 }
 
-function getTempMap(zoom) {
+function getTempMap(zoom, fullSize, pixelBounds) {
     const container = L.DomUtil.create('div', '', document.body);
+    let width, height, center;
+    if (fullSize) {
+        const size = pixelBounds.getSize();
+        width = size.x;
+        height = size.y;
+        center = pixelBounds.min.add(size.divideBy(2));
+        center = L.CRS.EPSG3857.pointToLatLng(center, zoom);
+    } else {
+        width = 100;
+        height = 100;
+        center = L.latLng(0, 0);
+    }
+
     Object.assign(container.style, {
-            width: '100px',
-            height: '100px',
+            width: `${width}px`,
+            height: `${height}px`,
             position: 'absolute',
             left: '0',
             top: '0',
-            // visibility: 'hidden'
+            visibility: 'hidden'
         }
     );
+
     const map = L.map(container, {fadeAnimation: false, zoomAnimation: false, inertia: false});
-    map.setView(L.latLng(0, 0), zoom);
+    map.setView(center, zoom);
     return map;
 }
 
@@ -150,11 +164,11 @@ async function* iterateLayersTiles(layers, latLngBounds, zooms) {
         } else {
             zoom = zooms.satZoom;
         }
-        let map = getTempMap(zoom);
         let pixelBounds = L.bounds(
-            map.project(latLngBounds.getNorthWest()).round(),
-            map.project(latLngBounds.getSouthEast()).round()
+            L.CRS.EPSG3857.latLngToPoint(latLngBounds.getNorthWest(), zoom).round(),
+            L.CRS.EPSG3857.latLngToPoint(latLngBounds.getSouthEast(), zoom).round()
         );
+        let map = getTempMap(zoom, layer._rasterizeNeedsFullSizeMap, pixelBounds);
         map.addLayer(layer);
         let {iterateTilePromises, count} = await layer.getTilesInfo({xhrOptions: defaultXHROptions, pixelBounds});
         for (let tilePromise of iterateTilePromises()) {
@@ -163,7 +177,6 @@ async function* iterateLayersTiles(layers, latLngBounds, zooms) {
             doStop = yield tilePromise;
             if (doStop) {
                 tilePromise.abortLoading();
-                console.log('DO STOP3');
                 break;
             }
         }
@@ -187,7 +200,6 @@ async function* promiseQueueBuffer(source, maxActive) {
     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);
@@ -231,7 +243,6 @@ async function renderPages({map, pages, zooms, resolution, progressCallback}) {
             try {
                 tileInfo = await tilePromise.tilePromise;
             } catch (e) {
-                console.log('DO STOP1');
                 queuedTilesIterator.next(true);
                 throw e;
             }
diff --git a/src/lib/leaflet.control.track-list/track-list.js b/src/lib/leaflet.control.track-list/track-list.js
@@ -21,32 +21,6 @@ import 'lib/leaflet.control.elevation-profile';
 var MeasuredEditableLine = L.MeasuredLine.extend({});
 MeasuredEditableLine.include(L.Polyline.EditMixin);
 
-var Waypoints = L.Layer.CanvasMarkers.extend({
-        options: {
-            scaleDependent: true
-        },
-
-        clone: function() {
-            var markers = this.rtree.all(),
-                markersCopy;
-
-            function cloneMarker(marker) {
-                return {
-                    latlng: {lat: marker.latlng.lat, lng: marker.latlng.lng},
-                    label: marker.label,
-                    icon: marker.icon
-                };
-            }
-
-            markersCopy = markers.map(cloneMarker);
-            var options = {};
-            L.extend(options, this.options, {iconScale: 1.5, labelFontSize: 14});
-            return new Waypoints(markersCopy, options);
-        }
-    }
-);
-
-
 L.Control.TrackList = L.Control.extend({
         options: {position: 'bottomright'},
 
@@ -126,7 +100,7 @@ L.Control.TrackList = L.Control.extend({
                     {text: 'Delete hidden tracks', callback: this.deleteHiddenTracks.bind(this)}
                 ]
             );
-            this._markerLayer = new Waypoints(null, {print: true}).addTo(map);
+            this._markerLayer = new L.Layer.CanvasMarkers(null, {print: true, scaleDependent: true}).addTo(map);
             this._markerLayer.on('markerclick markercontextmenu', this.onMarkerClick, this);
             map.on('resize', this._setAdaptiveHeight, this);
             setTimeout(() => this._setAdaptiveHeight(), 0);
@@ -491,6 +465,7 @@ L.Control.TrackList = L.Control.extend({
 
             var fileText = exporter(lines, name, points);
             var filename = name + extension;
+            // FIXME: make function stringToBlob: convert string to byteArray first
             saveAs(new Blob([fileText], {type: 'application/download'}), filename);
         },
 
diff --git a/src/lib/leaflet.layer.google/index.js b/src/lib/leaflet.layer.google/index.js
@@ -7,7 +7,6 @@ L.Layer.Google = L.GridLayer.extend({
         // Possible types: SATELLITE, ROADMAP, HYBRID, TERRAIN
         initialize: function(mapType, options) {
             L.GridLayer.prototype.initialize.call(this, options);
-            L.Layer.prototype.constructor(options);
             this.mapType = mapType;
         },
 
@@ -55,7 +54,7 @@ L.Layer.Google = L.GridLayer.extend({
             }
             this._googleMap = null;
             this._clearTiles();
-            map.getContainer().removeChild(this._googleMapContainer);
+            L.DomUtil.remove(this._googleMapContainer);
             map.off({
                     viewreset: this._clearTiles,
                     resize: this._onResize,
diff --git a/src/lib/leaflet.layer.rasterize/Bing.js b/src/lib/leaflet.layer.rasterize/Bing.js
@@ -2,7 +2,7 @@ import L from 'leaflet';
 import 'lib/leaflet.layer.bing';
 
 L.BingLayer.include({
-        whenReady: function() {
+        waitTilesReadyToGrab: function() {
             if (this._url) {
                 return Promise.resolve();
             } else {
@@ -22,9 +22,5 @@ L.BingLayer.include({
         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/CanvasMarkers.js b/src/lib/leaflet.layer.rasterize/CanvasMarkers.js
@@ -0,0 +1,28 @@
+import L from "leaflet";
+import 'lib/leaflet.layer.canvasMarkers'
+import {CanvasLayerGrabMixin} from './TileLayer';
+
+L.Layer.CanvasMarkers.include(CanvasLayerGrabMixin);
+L.Layer.CanvasMarkers.include({
+        cloneMarkers: function() {
+            const markers = this.rtree.all();
+
+            function cloneMarker(marker) {
+                return {
+                    latlng: {lat: marker.latlng.lat, lng: marker.latlng.lng},
+                    label: marker.label,
+                    icon: marker.icon
+                };
+            }
+
+            const markersCopy = markers.map(cloneMarker);
+            return markersCopy;
+        },
+
+        cloneForPrint: function(options) {
+            options = L.Util.extend({}, this.options, {iconScale: 1.5, labelFontSize: 14});
+            return new L.Layer.CanvasMarkers(this.cloneMarkers(), options);
+
+        }
+    }
+);
diff --git a/src/lib/leaflet.layer.rasterize/Google.js b/src/lib/leaflet.layer.rasterize/Google.js
@@ -0,0 +1,44 @@
+import L from 'leaflet';
+import 'lib/leaflet.layer.google';
+import {TileLayerGrabMixin} from './TileLayer';
+
+
+
+const GooglePrint = L.Layer.Google.extend({
+    includes: TileLayerGrabMixin,
+
+    _rasterizeNeedsFullSizeMap: true,
+
+    _dummyTile: L.DomUtil.create('div'),
+
+    onAdd: function(map) {
+        this._waitTilesReadyToGrab = new Promise((resolve) => {
+           this._tilesReadyToGrab = resolve;
+        });
+        L.Layer.Google.prototype.onAdd.call(this, map);
+    },
+
+    waitTilesReadyToGrab: function() {
+        return this._waitTilesReadyToGrab;
+    },
+
+    createTile: function() {
+        return this._dummyTile;
+    },
+
+    _fullfillPendingTiles: function() {
+        this._tilesReadyToGrab();
+    },
+
+    getTileUrl: function(coords) {
+        return this._readyTiles[this._tileCoordsToKey(coords)];
+    }
+});
+
+
+L.Layer.Google.include({
+    cloneForPrint: function(options) {
+        return new GooglePrint(this.mapType, L.Util.extend({}, this.options, options));
+    },
+});
+
diff --git a/src/lib/leaflet.layer.rasterize/TileLayer.js b/src/lib/leaflet.layer.rasterize/TileLayer.js
@@ -2,51 +2,100 @@ import L from 'leaflet';
 import urlViaCorsProxy from 'lib/CORSProxy';
 import {imgFromDataString} from './imgFromDataString';
 
-L.TileLayer.include({
+const GridLayerGrabMixin = {
+    tileImagePromiseFromCoords: function(coords) {
+        throw new Error('Method not implemented');
+    },
+
+    waitTilesReadyToGrab: function() {
+        return Promise.resolve(null);
+    },
+
+    getTilesInfo: async function(printOptions) {
+        await this.waitTilesReadyToGrab();
+        const {pixelBounds} = 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;
+                    coords = this._wrapCoords(coords);
+
+                    if (!this._isValidTile(coords)) {
+                        continue;
+                    }
+                    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 {tilePromise, abortLoading} = this.tileImagePromiseFromCoords(coords, printOptions);
+                    yield {
+                        tilePromise: tilePromise.then((image) => {
+                                return {image, tilePos, tileSize};
+                            }
+                        ),
+                        abortLoading
+                    };
+                }
+            }
+        }).bind(this);
+        return {
+            iterateTilePromises: tilePromiseIterator,
+            count: (tileRange.max.x - tileRange.min.x + 1) * (tileRange.max.y - tileRange.min.y + 1)
+        };
+    }
+};
+
+const TileLayerGrabMixin = L.Util.extend({}, GridLayerGrabMixin, {
         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)
-                }
-            );
+        tileImagePromiseFromCoords: function(coords, printOptions) {
+            let {xhrOptions} = printOptions;
+            let url = this.getTileUrl(coords);
+            if (this.options.noCors) {
+                url = urlViaCorsProxy(url);
+            }
+            let promise = this.options.xhrQueue.put(url, xhrOptions);
+            return {
+                tilePromise: promise.then(imgFromDataString),
+                abortLoading: () => promise.abort()
+            }
         }
     }
 );
 
+function noop() {
+
+}
+
+const CanvasLayerGrabMixin = L.Util.extend({}, GridLayerGrabMixin, {
+    getCanvasFromTile: function(tile) {
+        return tile;
+    },
+
+    tileImagePromiseFromCoords: function(coords) {
+        let tilePromise;
+        if (this.createTile.length < 2) {
+            let tile = this.createTile(coords);
+            tilePromise = Promise.resolve(tile);
+        } else {
+            tilePromise = new Promise((resolve) => {
+                return this.createTile(coords, resolve);
+            });
+        }
+
+        return {
+            tilePromise: tilePromise.then(this.getCanvasFromTile),
+            abortLoading: noop
+        }
+    }
+});
+
+L.TileLayer.include(TileLayerGrabMixin);
+
+export {TileLayerGrabMixin, GridLayerGrabMixin, CanvasLayerGrabMixin};
+\ No newline at end of file
diff --git a/src/lib/leaflet.layer.rasterize/WestraPasses.js b/src/lib/leaflet.layer.rasterize/WestraPasses.js
@@ -0,0 +1,41 @@
+import L from "leaflet";
+import 'lib/leaflet.layer.westraPasses';
+import {WestraPassesMarkers} from 'lib/leaflet.layer.westraPasses/westraPassesMarkers';
+import {CanvasLayerGrabMixin} from './TileLayer';
+import 'lib/leaflet.layer.canvasMarkers'
+
+const WestraPrint = L.Layer.CanvasMarkers.extend({
+    includes: CanvasLayerGrabMixin,
+
+    initialize: function(srcLayer, options) {
+        this.srcLayer = srcLayer;
+        L.Layer.CanvasMarkers.prototype.initialize.call(this, null, options);
+    },
+
+    waitTilesReadyToGrab: function() {
+        let promise;
+        if (this.srcLayer._dataLoaded) {
+            promise = Promise.resolve(null);
+        } else {
+            // FIXME: handle data load errors
+            promise = new Promise((resolve) => {
+                this.srcLayer.once('data-loaded', resolve);
+            })
+        }
+        return promise.then(() => {
+            this.addMarkers(this.srcLayer.rtree.all());
+        })
+    },
+});
+
+L.Layer.WestraPasses.addInitHook(function() {
+    this.markers.options.print = this.options.print;
+    this.options.print = false;
+});
+
+WestraPassesMarkers.include({
+    cloneForPrint: function (options) {
+        return new WestraPrint(this, L.Util.extend(
+            {}, this.options, {iconScale: 1.5, labelFontSize: 14}, options));
+    }
+});
diff --git a/src/lib/leaflet.layer.rasterize/index.js b/src/lib/leaflet.layer.rasterize/index.js
@@ -1,3 +1,6 @@
 import './TileLayer'
 import './Bing'
 import './Yandex'
+import './Google'
+import './WestraPasses'
+import './CanvasMarkers'
diff --git a/src/lib/leaflet.layer.westraPasses/index.js b/src/lib/leaflet.layer.westraPasses/index.js
@@ -24,6 +24,7 @@ L.Layer.WestraPasses = L.Layer.extend({
                 }
             );
         },
+
         _setRegionLabel: function(layerName, feature, layer) {
             var latlon = layer.getBounds().getCenter();
             var icon = L.divIcon({
@@ -96,4 +97,3 @@ L.Layer.WestraPasses = L.Layer.extend({
 );
 
 
-
diff --git a/src/lib/leaflet.layer.westraPasses/westraPassesMarkers.js b/src/lib/leaflet.layer.westraPasses/westraPassesMarkers.js
@@ -124,6 +124,8 @@ const WestraPassesMarkers = L.Layer.CanvasMarkers.extend({
                 markers.push(marker);
             }
             this.addMarkers(markers);
+            this._dataLoaded = true;
+            this.fire('data-loaded');
         },