From e5f934e97f7a1ead4516a6b8b86bb976c163d378 Mon Sep 17 00:00:00 2001 From: mourner Date: Sun, 4 Mar 2012 00:42:50 +0200 Subject: [PATCH] more global refactoring --- src/control/Control.Attribution.js | 35 ++++- src/control/Control.Zoom.js | 13 +- src/core/Util.js | 6 +- src/geo/crs/CRS.EPSG3395.js | 1 + src/geo/crs/CRS.EPSG3857.js | 2 +- src/geo/crs/CRS.Simple.js | 5 + src/geo/crs/CRS.js | 16 +- src/geo/projection/Projection.Mercator.js | 4 +- .../Projection.SphericalMercator.js | 4 +- src/layer/Popup.js | 11 +- src/map/Map.js | 140 +++++++----------- src/map/anim/Map.PanAnimation.js | 1 + src/map/anim/Map.ZoomAnimation.js | 4 + src/map/ext/Map.Popup.js | 6 +- src/map/handler/Map.BoxZoom.js | 2 + src/map/handler/Map.DoubleClickZoom.js | 2 + src/map/handler/Map.Drag.js | 11 +- src/map/handler/Map.ScrollWheelZoom.js | 2 + src/map/handler/Map.TouchZoom.js | 2 + 19 files changed, 154 insertions(+), 113 deletions(-) create mode 100644 src/geo/crs/CRS.Simple.js diff --git a/src/control/Control.Attribution.js b/src/control/Control.Attribution.js index a99643f2..fb3a1b81 100644 --- a/src/control/Control.Attribution.js +++ b/src/control/Control.Attribution.js @@ -11,16 +11,25 @@ L.Control.Attribution = L.Control.extend({ }, onAdd: function (map) { - this._map = map; - this._container = L.DomUtil.create('div', 'leaflet-control-attribution'); L.DomEvent.disableClickPropagation(this._container); + map + .on('layeradd', this._onLayerAdd, this) + .on('layerremove', this._onLayerRemove, this); + this._update(); return this._container; }, + onRemove: function (map) { + map + .off('layeradd', this._onLayerAdd) + .off('layerremove', this._onLayerRemove); + + }, + setPrefix: function (prefix) { this.options.prefix = prefix; this._update(); @@ -65,5 +74,27 @@ L.Control.Attribution = L.Control.extend({ } this._container.innerHTML = prefixAndAttribs.join(' — '); + }, + + _onLayerAdd: function (e) { + if (e.layer.getAttribution) { + this.addAttribution(e.layer.getAttribution()); + } + }, + + _onLayerRemove: function (e) { + if (e.layer.getAttribution) { + this.removeAttribution(e.layer.getAttribution()); + } } }); + +L.Map.mergeOptions({ + attributionControl: true +}); + +L.Map.addInitHook(function () { + if (this.options.attributionControl) { + this.attributionControl = (new L.Control.Attribution()).addTo(this); + } +}); \ No newline at end of file diff --git a/src/control/Control.Zoom.js b/src/control/Control.Zoom.js index 52a2c185..06e63887 100644 --- a/src/control/Control.Zoom.js +++ b/src/control/Control.Zoom.js @@ -6,7 +6,7 @@ L.Control.Zoom = L.Control.extend({ onAdd: function (map) { var className = 'leaflet-control-zoom', container = L.DomUtil.create('div', className); - + this._createButton('Zoom in', className + '-in', container, map.zoomIn, map); this._createButton('Zoom out', className + '-out', container, map.zoomOut, map); @@ -26,3 +26,14 @@ L.Control.Zoom = L.Control.extend({ return link; } }); + +L.Map.mergeOptions({ + zoomControl: true +}); + +L.Map.addInitHook(function () { + if (this.options.zoomControl) { + this.zoomControl = new L.Control.Zoom(); + this.addControl(this.zoomControl); + } +}); \ No newline at end of file diff --git a/src/core/Util.js b/src/core/Util.js index 65557539..03fa4191 100644 --- a/src/core/Util.js +++ b/src/core/Util.js @@ -16,9 +16,10 @@ L.Util = { return dest; }, - bind: function (/*Function*/ fn, /*Object*/ obj) /*-> Object*/ { + bind: function (fn, obj) { // (Function, Object) -> Function + var args = Array.prototype.slice.call(arguments, 2); return function () { - return fn.apply(obj, arguments); + return fn.apply(obj, args || arguments); }; }, @@ -101,6 +102,7 @@ L.Util = { setOptions: function (obj, options) { obj.options = L.Util.extend({}, obj.options, options); + return obj.options; }, getParamString: function (obj) { diff --git a/src/geo/crs/CRS.EPSG3395.js b/src/geo/crs/CRS.EPSG3395.js index a0d40a9e..84d627e0 100644 --- a/src/geo/crs/CRS.EPSG3395.js +++ b/src/geo/crs/CRS.EPSG3395.js @@ -3,6 +3,7 @@ L.CRS.EPSG3395 = L.Util.extend({}, L.CRS, { code: 'EPSG:3395', projection: L.Projection.Mercator, + transformation: (function () { var m = L.Projection.Mercator, r = m.R_MAJOR, diff --git a/src/geo/crs/CRS.EPSG3857.js b/src/geo/crs/CRS.EPSG3857.js index d76722a3..158a00fe 100644 --- a/src/geo/crs/CRS.EPSG3857.js +++ b/src/geo/crs/CRS.EPSG3857.js @@ -5,7 +5,7 @@ L.CRS.EPSG3857 = L.Util.extend({}, L.CRS, { projection: L.Projection.SphericalMercator, transformation: new L.Transformation(0.5 / Math.PI, 0.5, -0.5 / Math.PI, 0.5), - project: function (/*LatLng*/ latlng)/*-> Point*/ { + project: function (latlng) { // (LatLng) -> Point var projectedPoint = this.projection.project(latlng), earthRadius = 6378137; return projectedPoint.multiplyBy(earthRadius); diff --git a/src/geo/crs/CRS.Simple.js b/src/geo/crs/CRS.Simple.js new file mode 100644 index 00000000..52def07a --- /dev/null +++ b/src/geo/crs/CRS.Simple.js @@ -0,0 +1,5 @@ + +L.CRS.Simple = L.Util.extend({}, L.CRS, { + projection: L.Projection.LonLat, + transformation: new L.Transformation(1, 0, 1, 0) +}); diff --git a/src/geo/crs/CRS.js b/src/geo/crs/CRS.js index eeb633a1..107f0788 100644 --- a/src/geo/crs/CRS.js +++ b/src/geo/crs/CRS.js @@ -1,17 +1,25 @@ L.CRS = { - latLngToPoint: function (/*LatLng*/ latlng, /*Number*/ scale)/*-> Point*/ { - var projectedPoint = this.projection.project(latlng); + latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point + var projectedPoint = this.projection.project(latlng), + scale = this.scale(zoom); + return this.transformation._transform(projectedPoint, scale); }, - pointToLatLng: function (/*Point*/ point, /*Number*/ scale, /*(optional) Boolean*/ unbounded)/*-> LatLng*/ { - var untransformedPoint = this.transformation.untransform(point, scale); + pointToLatLng: function (point, zoom, unbounded) { // (Point, Number[, Boolean]) -> LatLng + var scale = this.scale(zoom), + untransformedPoint = this.transformation.untransform(point, scale); + return this.projection.unproject(untransformedPoint, unbounded); //TODO get rid of 'unbounded' everywhere }, project: function (latlng) { return this.projection.project(latlng); + }, + + scale: function (zoom) { + return 256 * Math.pow(2, zoom); } }; diff --git a/src/geo/projection/Projection.Mercator.js b/src/geo/projection/Projection.Mercator.js index e89776ab..860bd5c6 100644 --- a/src/geo/projection/Projection.Mercator.js +++ b/src/geo/projection/Projection.Mercator.js @@ -5,7 +5,7 @@ L.Projection.Mercator = { R_MINOR: 6356752.3142, R_MAJOR: 6378137, - project: function (/*LatLng*/ latlng) /*-> Point*/ { + project: function (latlng) { // (LatLng) -> Point var d = L.LatLng.DEG_TO_RAD, max = this.MAX_LATITUDE, lat = Math.max(Math.min(max, latlng.lat), -max), @@ -25,7 +25,7 @@ L.Projection.Mercator = { return new L.Point(x, y); }, - unproject: function (/*Point*/ point, /*Boolean*/ unbounded) /*-> LatLng*/ { + unproject: function (point, unbounded) { // (Point, Boolean) -> LatLng var d = L.LatLng.RAD_TO_DEG, r = this.R_MAJOR, r2 = this.R_MINOR, diff --git a/src/geo/projection/Projection.SphericalMercator.js b/src/geo/projection/Projection.SphericalMercator.js index 275e7132..d6fb5ca9 100644 --- a/src/geo/projection/Projection.SphericalMercator.js +++ b/src/geo/projection/Projection.SphericalMercator.js @@ -2,7 +2,7 @@ L.Projection.SphericalMercator = { MAX_LATITUDE: 85.0511287798, - project: function (/*LatLng*/ latlng) /*-> Point*/ { + project: function (latlng) { // (LatLng) -> Point var d = L.LatLng.DEG_TO_RAD, max = this.MAX_LATITUDE, lat = Math.max(Math.min(max, latlng.lat), -max), @@ -13,7 +13,7 @@ L.Projection.SphericalMercator = { return new L.Point(x, y); }, - unproject: function (/*Point*/ point, /*Boolean*/ unbounded) /*-> LatLng*/ { + unproject: function (point, unbounded) { // (Point, Boolean) -> LatLng var d = L.LatLng.RAD_TO_DEG, lng = point.x * d, lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d; diff --git a/src/layer/Popup.js b/src/layer/Popup.js index ee7d5194..cadb30e2 100644 --- a/src/layer/Popup.js +++ b/src/layer/Popup.js @@ -71,9 +71,14 @@ L.Popup = L.Class.extend({ }, _close: function () { - if (this._map) { - this._map._popup = null; - this._map.removeLayer(this); + var map = this._map; + + if (map) { + map._popup = null; + + map + .removeLayer(this) + .fire('popupclose', {popup: this}); } }, diff --git a/src/map/Map.js b/src/map/Map.js index 20b192f8..b74d91a6 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -6,66 +6,32 @@ L.Map = L.Class.extend({ includes: L.Mixin.Events, options: { - // projection - crs: L.CRS.EPSG3857 || L.CRS.EPSG4326, - scale: function (zoom) { - return 256 * Math.pow(2, zoom); - }, + crs: L.CRS.EPSG3857, - // state - center: null, - zoom: null, - layers: [], + /* + center: LatLng, + zoom: Number, + layers: Array, + */ - // controls - zoomControl: true, - attributionControl: true, - - // animation fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android, - zoomAnimation: L.DomUtil.TRANSITION && !L.Browser.android && !L.Browser.mobileOpera, - - // misc - trackResize: true, - worldCopyJump: true + trackResize: true }, - initialize: function (id, options) { // (HTMLElement or String, Object) - L.Util.setOptions(this, options); - - // TODO method is too big, refactor - - var container = this._container = L.DomUtil.get(id); - - if (container._leaflet) { - throw new Error("Map container is already initialized."); - } - container._leaflet = true; + options = L.Util.setOptions(this, options); + this._initContainer(id); this._initLayout(); - - if (L.DomEvent) { - this._initEvents(); - if (L.Handler) { - this._initInteraction(); - } - if (L.Control) { - this._initControls(); - } - } - - options = this.options; + this._initHooks(); + this._initEvents(); if (options.maxBounds) { this.setMaxBounds(options.maxBounds); } - var center = options.center, - zoom = options.zoom; - - if (center && typeof zoom !== 'undefined') { - this.setView(center, zoom, true); + if (options.center && typeof options.zoom !== 'undefined') { + this.setView(options.center, options.zoom, true); } this._initLayers(options.layers); @@ -76,7 +42,6 @@ L.Map = L.Class.extend({ // replaced by animation-powered implementation in Map.PanAnimation.js setView: function (center, zoom) { - // reset the map view this._resetView(center, this._limitZoom(zoom)); return this; }, @@ -172,9 +137,7 @@ L.Map = L.Class.extend({ var id = L.Util.stamp(layer); - if (this._layers[id]) { - return this; - } + if (this._layers[id]) { return this; } this._layers[id] = layer; @@ -192,10 +155,6 @@ L.Map = L.Class.extend({ layer.on('load', this._onTileLayerLoad, this); } - if (this.attributionControl && layer.getAttribution) { - this.attributionControl.addAttribution(layer.getAttribution()); - } - var onMapLoad = function () { layer.onAdd(this, insertAtTheBottom); this.fire('layeradd', {layer: layer}); @@ -225,10 +184,6 @@ L.Map = L.Class.extend({ layer.off('load', this._onTileLayerLoad, this); } - if (this.attributionControl && layer.getAttribution) { - this.attributionControl.removeAttribution(layer.getAttribution()); - } - return this.fire('layerremove', {layer: layer}); }, @@ -253,16 +208,13 @@ L.Map = L.Class.extend({ this.fire('move'); - function fireMoveEnd() { - this.fire('moveend'); - } - clearTimeout(this._sizeTimer); - this._sizeTimer = setTimeout(L.Util.bind(fireMoveEnd, this), 200); + this._sizeTimer = setTimeout(L.Util.bind(this.fire, this, 'moveend'), 200); return this; }, + // TODO handler.addTo addHandler: function (name, HandlerClass) { if (!HandlerClass) { return; } @@ -278,7 +230,7 @@ L.Map = L.Class.extend({ // public methods for getting map state - getCenter: function (unbounded) { // (Boolean) + getCenter: function (unbounded) { // (Boolean) -> LatLng var viewHalf = this.getSize().divideBy(2), centerPoint = this._getTopLeftPoint().add(viewHalf); @@ -412,18 +364,28 @@ L.Map = L.Class.extend({ project: function (latlng, zoom) { // (LatLng[, Number]) -> Point zoom = typeof zoom === 'undefined' ? this._zoom : zoom; - return this.options.crs.latLngToPoint(latlng, this.options.scale(zoom)); + return this.options.crs.latLngToPoint(latlng, zoom); }, unproject: function (point, zoom, unbounded) { // (Point[, Number, Boolean]) -> LatLng // TODO remove unbounded, making it true all the time? zoom = typeof zoom === 'undefined' ? this._zoom : zoom; - return this.options.crs.pointToLatLng(point, this.options.scale(zoom), unbounded); + return this.options.crs.pointToLatLng(point, zoom, unbounded); }, // private methods that modify map state + _initContainer: function (id) { + var container = this._container = L.DomUtil.get(id); + + if (container._leaflet) { + throw new Error("Map container is already initialized."); + } + + container._leaflet = true; + }, + _initLayout: function () { var container = this._container; @@ -469,6 +431,15 @@ L.Map = L.Class.extend({ return L.DomUtil.create('div', className, container || this._objectsPane); }, + _initializers: [], + + _initHooks: function () { + var i, len; + for (i = 0, len = this._initializers.length; i < len; i++) { + this._initializers[i].call(this); + } + }, + _resetView: function (center, zoom, preserveMapOffset, afterZoomAnim) { var zoomChanged = (this._zoom !== zoom); @@ -510,7 +481,7 @@ L.Map = L.Class.extend({ }, _initLayers: function (layers) { - layers = layers instanceof Array ? layers : [layers]; + layers = layers ? (layers instanceof Array ? layers : [layers]) : []; this._layers = {}; this._tileLayersNum = 0; @@ -522,18 +493,6 @@ L.Map = L.Class.extend({ } }, - _initControls: function () { - // TODO refactor, this should happen automatically - if (this.options.zoomControl) { - this.zoomControl = new L.Control.Zoom(); - this.addControl(this.zoomControl); - } - if (this.options.attributionControl) { - this.attributionControl = new L.Control.Attribution(); - this.addControl(this.attributionControl); - } - }, - _rawPanBy: function (offset) { var newPos = L.DomUtil.getPosition(this._mapPane).subtract(offset); L.DomUtil.setPosition(this._mapPane, newPos); @@ -543,6 +502,8 @@ L.Map = L.Class.extend({ // map events _initEvents: function () { + if (!L.DomEvent) { return; } + L.DomEvent.addListener(this._container, 'click', this._onMouseClick, this); var events = ['dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'contextmenu']; @@ -594,15 +555,6 @@ L.Map = L.Class.extend({ }); }, - _initInteraction: function () { - this - .addHandler('dragging', L.Map.Drag) - .addHandler('touchZoom', L.Map.TouchZoom) - .addHandler('doubleClickZoom', L.Map.DoubleClickZoom) - .addHandler('scrollWheelZoom', L.Map.ScrollWheelZoom) - .addHandler('boxZoom', L.Map.BoxZoom); - }, - _onTileLayerLoad: function () { // TODO super-ugly, refactor!!! // clear scaled tiles after all new tiles are loaded (for performance) @@ -638,3 +590,13 @@ L.Map = L.Class.extend({ return Math.max(min, Math.min(max, zoom)); } }); + +L.Map.addInitHook = function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + + var init = typeof fn === 'function' ? fn : function () { + this[fn].apply(this, args); + }; + + this.prototype._initializers.push(init); +}; \ No newline at end of file diff --git a/src/map/anim/Map.PanAnimation.js b/src/map/anim/Map.PanAnimation.js index 88b1e241..44765de4 100644 --- a/src/map/anim/Map.PanAnimation.js +++ b/src/map/anim/Map.PanAnimation.js @@ -1,3 +1,4 @@ + L.Map.include(!(L.Transition && L.Transition.implemented()) ? {} : { setView: function (center, zoom, forceReset) { zoom = this._limitZoom(zoom); diff --git a/src/map/anim/Map.ZoomAnimation.js b/src/map/anim/Map.ZoomAnimation.js index f573a32d..0fb76eda 100644 --- a/src/map/anim/Map.ZoomAnimation.js +++ b/src/map/anim/Map.ZoomAnimation.js @@ -1,3 +1,7 @@ +L.Map.mergeOptions({ + zoomAnimation: L.DomUtil.TRANSITION && !L.Browser.android && !L.Browser.mobileOpera +}); + L.Map.include(!L.DomUtil.TRANSITION ? {} : { _zoomToIfCenterInView: function (center, zoom, centerOffset) { diff --git a/src/map/ext/Map.Popup.js b/src/map/ext/Map.Popup.js index 78335c25..c9e1752e 100644 --- a/src/map/ext/Map.Popup.js +++ b/src/map/ext/Map.Popup.js @@ -12,11 +12,7 @@ L.Map.include({ closePopup: function () { if (this._popup) { - this - .removeLayer(this._popup) - .fire('popupclose', {popup: this._popup}); - - this._popup = null; + this._popup._close(); } return this; } diff --git a/src/map/handler/Map.BoxZoom.js b/src/map/handler/Map.BoxZoom.js index 758b54d2..d796afc8 100644 --- a/src/map/handler/Map.BoxZoom.js +++ b/src/map/handler/Map.BoxZoom.js @@ -78,3 +78,5 @@ L.Map.BoxZoom = L.Handler.extend({ map.fitBounds(bounds); } }); + +L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom); \ No newline at end of file diff --git a/src/map/handler/Map.DoubleClickZoom.js b/src/map/handler/Map.DoubleClickZoom.js index db22f20c..7a7850dc 100644 --- a/src/map/handler/Map.DoubleClickZoom.js +++ b/src/map/handler/Map.DoubleClickZoom.js @@ -19,3 +19,5 @@ L.Map.DoubleClickZoom = L.Handler.extend({ this.setView(e.latlng, this._zoom + 1); } }); + +L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom); \ No newline at end of file diff --git a/src/map/handler/Map.Drag.js b/src/map/handler/Map.Drag.js index d973f598..76a7ef98 100644 --- a/src/map/handler/Map.Drag.js +++ b/src/map/handler/Map.Drag.js @@ -4,10 +4,15 @@ L.Map.mergeOptions({ dragging: true, + inertia: !L.Browser.android, inertiaDeceleration: L.Browser.touch ? 3000 : 2000, // px/s^2 inertiaMaxSpeed: L.Browser.touch ? 1500 : 1000, // px/s - inertiaThreshold: L.Browser.touch ? 32 : 16 // ms + inertiaThreshold: L.Browser.touch ? 32 : 16, // ms + + // TODO refactor, move to CRS + worldCopyJump: true, + continuousWorld: false }); L.Map.Drag = L.Handler.extend({ @@ -83,7 +88,7 @@ L.Map.Drag = L.Handler.extend({ _onPreDrag: function () { var map = this._map, - worldWidth = map.options.scale(map.getZoom()), + worldWidth = map.options.crs.scale(map.getZoom()), halfWidth = Math.round(worldWidth / 2), dx = this._initialWorldOffset.x, x = this._draggable._newPos.x, @@ -142,3 +147,5 @@ L.Map.Drag = L.Handler.extend({ this.panInsideBounds(this.options.maxBounds); } }); + +L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag); \ No newline at end of file diff --git a/src/map/handler/Map.ScrollWheelZoom.js b/src/map/handler/Map.ScrollWheelZoom.js index 05bd55bc..c0aaf90f 100644 --- a/src/map/handler/Map.ScrollWheelZoom.js +++ b/src/map/handler/Map.ScrollWheelZoom.js @@ -56,3 +56,5 @@ L.Map.ScrollWheelZoom = L.Handler.extend({ return map.unproject(newCenterPoint, map._zoom, true); } }); + +L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom); \ No newline at end of file diff --git a/src/map/handler/Map.TouchZoom.js b/src/map/handler/Map.TouchZoom.js index e896604c..b51d58e8 100644 --- a/src/map/handler/Map.TouchZoom.js +++ b/src/map/handler/Map.TouchZoom.js @@ -95,3 +95,5 @@ L.Map.TouchZoom = L.Handler.extend({ this._map._runAnimation(center, zoom, finalScale / this._scale, this._startCenter.add(centerOffset)); } }); + +L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom); \ No newline at end of file