|
/* |
|
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); |
|
}; |