/* Stanislav Sumbera, August , 2015 - scaled SVG draw prototype on top of Leaflet 1.0 beta - note it uses L.map patch to get it working right - SVG data are not modified, only scaled and optionaly radius/stroke width etc. can be specified on onScaleChange callback - very experimental */ //-- Patch to get leaflet properly zoomed L.Map.prototype.__latLngToLayerPoint = function (latlng) { // (LatLng) var projectedPoint = this.project(L.latLng(latlng));//._round(); return projectedPoint._subtract(this.getPixelOrigin()); }; L.Map.prototype.___getNewPixelOrigin = function (center, zoom) { var viewHalf = this.getSize()._divideBy(2); return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos());//._round(); }; // -- from leaflet 1.0 to get this working right on v 0.7 too L.Map.prototype.__getZoomScale = function (toZoom, fromZoom) { var crs = this.options.crs; fromZoom = fromZoom === undefined ? this._zoom : fromZoom; return crs.scale(toZoom) / crs.scale(fromZoom); }; L.SVGScaleOverlay = L.Class.extend({ options: { pane: 'overlayPane', nonBubblingEvents: [], // Array of events that should not be bubbled to DOM parents (like the map) // how much to extend the clip area around the map view (relative to its size) // e.g. 0.1 would be 10% of map view in each direction; defaults to clip with the map view padding: 0 }, isLeafletVersion1: function () { return L.Layer ? true : false; }, initialize: function (options) { L.setOptions(this, options); L.stamp(this); }, // ---------------------------------------------------------------------------------- getScaleDiff:function(zoom) { var zoomDiff = this._groundZoom - zoom; var scale = (zoomDiff < 0 ? Math.pow(2, Math.abs(zoomDiff)) : 1 / (Math.pow(2, zoomDiff))); return scale; }, initSvgContainer: function () { var xmlns = "http://www.w3.org/2000/svg"; this._svg = document.createElementNS(xmlns, "svg"); this._g = document.createElementNS(xmlns, "g"); if (!this.isLeafletVersion1()) { L.DomUtil.addClass(this._g, 'leaflet-zoom-hide'); } var size = this._map.getSize(); this._svgSize = size; this._svg.setAttribute('width', size.x); this._svg.setAttribute('height', size.y); this._svg.appendChild(this._g); this._groundZoom = this._map.getZoom(); this._shift = new L.Point(0, 0); this._lastZoom = this._map.getZoom(); var bounds = this._map.getBounds(); this._lastTopLeftlatLng = new L.LatLng(bounds.getNorth(), bounds.getWest()); ////this._initialTopLeft = this._map.layerPointToLatLng(this._lastLeftLayerPoint); }, resize: function (e) { var size = this._map.getSize(); this._svgSize = size; this._svg.setAttribute('width', size.x); this._svg.setAttribute('height', size.y); }, moveEnd: function (e) { var bounds = this._map.getBounds(); var topLeftLatLng = new L.LatLng(bounds.getNorth(), bounds.getWest()); var topLeftLayerPoint = this._map.latLngToLayerPoint(topLeftLatLng); var lastLeftLayerPoint = this._map.latLngToLayerPoint(this._lastTopLeftlatLng); var zoom = this._map.getZoom(); var scaleDelta = this._map.getZoomScale(zoom, this._lastZoom); var scaleDiff = this.getScaleDiff(zoom); if (this._lastZoom != zoom) { if (typeof (this.onScaleChange) == 'function') { this.onScaleChange(scaleDiff); } } this._lastZoom = zoom; var delta = lastLeftLayerPoint.subtract(topLeftLayerPoint); this._lastTopLeftlatLng = topLeftLatLng; L.DomUtil.setPosition(this._svg, topLeftLayerPoint); this._shift._multiplyBy(scaleDelta)._add(delta); this._g.setAttribute("transform", "translate(" + this._shift.x + "," + this._shift.y + ") scale(" + scaleDiff + ")"); // --we use viewBox instead }, animateSvgZoom: function (e) { var scale = this._map.getZoomScale(e.zoom, this._lastZoom), offset = this._map._latLngToNewLayerPoint(this._lastTopLeftlatLng, e.zoom, e.center); L.DomUtil.setTransform(this._svg, offset, scale); }, getEvents: function () { var events = { resize: this.resize, moveend: this.moveEnd }; if (this._zoomAnimated && this.isLeafletVersion1()) { // events.zoomanim = this.animateSvgZoom; } return events; }, /* from Layer , extension to get it worked on lf 1.0, this is not called on ,1. versions */ _layerAdd: function (e) { this.onAdd(e.target); }, /*end Layer */ onAdd: function (map) { // -- from _layerAdd // check in case layer gets added and then removed before the map is ready if (!map.hasLayer(this)) { return; } this._map = map; this._zoomAnimated = map._zoomAnimated; // --onAdd leaflet 1.0 if (!this._svg) { this.initSvgContainer(); if (this._zoomAnimated) { //L.DomUtil.addClass(this._svg, 'leaflet-zoom-animated'); L.DomUtil.addClass(this._svg, 'leaflet-zoom-hide'); } } var pane = this._map.getPanes().overlayPane; pane.appendChild(this._svg); if (typeof (this.onInitData) == 'function') { this.onInitData(); } //---------- from _layerAdd if (this.getAttribution && this._map.attributionControl) { this._map.attributionControl.addAttribution(this.getAttribution()); } if (this.getEvents) { map.on(this.getEvents(), this); } map.fire('layeradd', { layer: this }); }, onRemove: function () { L.DomUtil.remove(this._svg); } }); L.SvgScaleOverlay = function (options) { return new L.SVGScaleOverlay(options); };