2012-07-16 11:28:13 +08:00
|
|
|
/*
|
|
|
|
* L.Map is the central class of the API - it is used to create a map.
|
|
|
|
*/
|
|
|
|
|
|
|
|
L.Map = L.Class.extend({
|
|
|
|
|
|
|
|
includes: L.Mixin.Events,
|
|
|
|
|
|
|
|
options: {
|
|
|
|
crs: L.CRS.EPSG3857,
|
|
|
|
|
|
|
|
/*
|
|
|
|
center: LatLng,
|
|
|
|
zoom: Number,
|
|
|
|
layers: Array,
|
|
|
|
*/
|
|
|
|
|
|
|
|
fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android23,
|
|
|
|
trackResize: true,
|
|
|
|
markerZoomAnimation: L.DomUtil.TRANSITION && L.Browser.any3d
|
|
|
|
},
|
|
|
|
|
|
|
|
initialize: function (id, options) { // (HTMLElement or String, Object)
|
2012-11-08 02:30:56 +08:00
|
|
|
options = L.setOptions(this, options);
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
this._initContainer(id);
|
|
|
|
this._initLayout();
|
|
|
|
this._initEvents();
|
|
|
|
|
|
|
|
if (options.maxBounds) {
|
|
|
|
this.setMaxBounds(options.maxBounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.center && options.zoom !== undefined) {
|
2013-04-24 21:24:56 +08:00
|
|
|
this.setView(L.latLng(options.center), options.zoom, {reset: true});
|
2012-07-16 11:28:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
this._initLayers(options.layers);
|
2013-04-30 21:51:45 +08:00
|
|
|
|
|
|
|
this._handlers = [];
|
|
|
|
|
|
|
|
this.callInitHooks();
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// public methods that modify map state
|
|
|
|
|
|
|
|
// replaced by animation-powered implementation in Map.PanAnimation.js
|
|
|
|
setView: function (center, zoom) {
|
|
|
|
this._resetView(L.latLng(center), this._limitZoom(zoom));
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2013-04-24 21:24:56 +08:00
|
|
|
setZoom: function (zoom, options) {
|
2013-04-24 23:13:36 +08:00
|
|
|
return this.setView(this.getCenter(), zoom, {zoom: options});
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
2013-04-24 21:24:56 +08:00
|
|
|
zoomIn: function (delta, options) {
|
|
|
|
return this.setZoom(this._zoom + (delta || 1), options);
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
2013-04-24 21:24:56 +08:00
|
|
|
zoomOut: function (delta, options) {
|
|
|
|
return this.setZoom(this._zoom - (delta || 1), options);
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
2013-04-24 21:24:56 +08:00
|
|
|
setZoomAround: function (latlng, zoom, options) {
|
2013-04-11 17:07:07 +08:00
|
|
|
var scale = this.getZoomScale(zoom),
|
|
|
|
viewHalf = this.getSize().divideBy(2),
|
|
|
|
containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng),
|
|
|
|
|
|
|
|
centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
|
|
|
|
newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
|
|
|
|
|
2013-04-24 23:13:36 +08:00
|
|
|
return this.setView(newCenter, zoom, {zoom: options});
|
2013-04-11 17:07:07 +08:00
|
|
|
},
|
|
|
|
|
2013-04-24 21:24:56 +08:00
|
|
|
fitBounds: function (bounds, options) {
|
2013-04-22 20:21:30 +08:00
|
|
|
|
2013-04-24 21:24:56 +08:00
|
|
|
options = options || {};
|
2013-04-22 20:21:30 +08:00
|
|
|
bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds);
|
|
|
|
|
2013-04-24 21:24:56 +08:00
|
|
|
var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
|
|
|
|
paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
|
|
|
|
|
|
|
|
zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)),
|
|
|
|
paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
|
2013-04-19 20:43:39 +08:00
|
|
|
|
2013-04-22 20:21:30 +08:00
|
|
|
swPoint = this.project(bounds.getSouthWest(), zoom),
|
|
|
|
nePoint = this.project(bounds.getNorthEast(), zoom),
|
|
|
|
center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
|
2013-04-19 20:43:39 +08:00
|
|
|
|
2013-04-24 21:24:56 +08:00
|
|
|
return this.setView(center, zoom, options);
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
2013-04-24 21:24:56 +08:00
|
|
|
fitWorld: function (options) {
|
|
|
|
return this.fitBounds([[-90, -180], [90, 180]], options);
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
2013-04-24 21:24:56 +08:00
|
|
|
panTo: function (center, options) { // (LatLng)
|
2013-04-24 23:13:36 +08:00
|
|
|
return this.setView(center, this._zoom, {pan: options});
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
panBy: function (offset) { // (Point)
|
|
|
|
// replaced with animated panBy in Map.Animation.js
|
|
|
|
this.fire('movestart');
|
|
|
|
|
|
|
|
this._rawPanBy(L.point(offset));
|
|
|
|
|
|
|
|
this.fire('move');
|
|
|
|
return this.fire('moveend');
|
|
|
|
},
|
|
|
|
|
|
|
|
setMaxBounds: function (bounds) {
|
|
|
|
bounds = L.latLngBounds(bounds);
|
|
|
|
|
|
|
|
this.options.maxBounds = bounds;
|
|
|
|
|
|
|
|
if (!bounds) {
|
|
|
|
this._boundsMinZoom = null;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
var minZoom = this.getBoundsZoom(bounds, true);
|
|
|
|
|
|
|
|
this._boundsMinZoom = minZoom;
|
|
|
|
|
|
|
|
if (this._loaded) {
|
|
|
|
if (this._zoom < minZoom) {
|
|
|
|
this.setView(bounds.getCenter(), minZoom);
|
|
|
|
} else {
|
|
|
|
this.panInsideBounds(bounds);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-18 22:05:47 +08:00
|
|
|
this.on('moveend', this._panInsideMaxBounds, this);
|
|
|
|
|
2012-07-16 11:28:13 +08:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
panInsideBounds: function (bounds) {
|
|
|
|
bounds = L.latLngBounds(bounds);
|
|
|
|
|
2013-04-18 22:05:47 +08:00
|
|
|
var viewBounds = this.getPixelBounds(),
|
|
|
|
viewSw = viewBounds.getBottomLeft(),
|
|
|
|
viewNe = viewBounds.getTopRight(),
|
2012-07-16 11:28:13 +08:00
|
|
|
sw = this.project(bounds.getSouthWest()),
|
|
|
|
ne = this.project(bounds.getNorthEast()),
|
|
|
|
dx = 0,
|
|
|
|
dy = 0;
|
|
|
|
|
|
|
|
if (viewNe.y < ne.y) { // north
|
2013-04-18 22:05:47 +08:00
|
|
|
dy = Math.ceil(ne.y - viewNe.y);
|
2012-07-16 11:28:13 +08:00
|
|
|
}
|
|
|
|
if (viewNe.x > ne.x) { // east
|
2013-04-18 22:05:47 +08:00
|
|
|
dx = Math.floor(ne.x - viewNe.x);
|
2012-07-16 11:28:13 +08:00
|
|
|
}
|
|
|
|
if (viewSw.y > sw.y) { // south
|
2013-04-18 22:05:47 +08:00
|
|
|
dy = Math.floor(sw.y - viewSw.y);
|
2012-07-16 11:28:13 +08:00
|
|
|
}
|
|
|
|
if (viewSw.x < sw.x) { // west
|
2013-04-18 22:05:47 +08:00
|
|
|
dx = Math.ceil(sw.x - viewSw.x);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dx || dy) {
|
2013-04-22 22:20:34 +08:00
|
|
|
return this.panBy([dx, dy]);
|
2012-07-16 11:28:13 +08:00
|
|
|
}
|
|
|
|
|
2013-04-18 22:05:47 +08:00
|
|
|
return this;
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
2012-07-27 17:32:24 +08:00
|
|
|
addLayer: function (layer) {
|
2012-07-16 11:28:13 +08:00
|
|
|
// TODO method is too big, refactor
|
|
|
|
|
2012-11-08 02:30:56 +08:00
|
|
|
var id = L.stamp(layer);
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
if (this._layers[id]) { return this; }
|
|
|
|
|
|
|
|
this._layers[id] = layer;
|
|
|
|
|
|
|
|
// TODO getMaxZoom, getMinZoom in ILayer (instead of options)
|
2012-11-30 04:43:40 +08:00
|
|
|
if (layer.options && (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom))) {
|
2012-11-19 05:07:27 +08:00
|
|
|
this._zoomBoundLayers[id] = layer;
|
|
|
|
this._updateZoomLevels();
|
2012-07-16 11:28:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO looks ugly, refactor!!!
|
|
|
|
if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
|
|
|
|
this._tileLayersNum++;
|
2013-06-02 12:49:45 +08:00
|
|
|
this._tileLayersToLoad++;
|
|
|
|
layer.on('load', this._onTileLayerLoad, this);
|
2012-07-16 11:28:13 +08:00
|
|
|
}
|
|
|
|
|
2013-06-02 13:11:42 +08:00
|
|
|
if (this._loaded) {
|
|
|
|
this._layerAdd(layer);
|
|
|
|
}
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
removeLayer: function (layer) {
|
2012-11-08 02:30:56 +08:00
|
|
|
var id = L.stamp(layer);
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
if (!this._layers[id]) { return; }
|
|
|
|
|
2013-06-02 12:48:41 +08:00
|
|
|
if (this._loaded) {
|
|
|
|
layer.onRemove(this);
|
|
|
|
}
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
delete this._layers[id];
|
2012-11-30 04:43:40 +08:00
|
|
|
if (this._zoomBoundLayers[id]) {
|
2012-11-19 05:07:27 +08:00
|
|
|
delete this._zoomBoundLayers[id];
|
|
|
|
this._updateZoomLevels();
|
|
|
|
}
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
// TODO looks ugly, refactor
|
|
|
|
if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
|
|
|
|
this._tileLayersNum--;
|
2013-06-02 12:49:45 +08:00
|
|
|
this._tileLayersToLoad--;
|
|
|
|
layer.off('load', this._onTileLayerLoad, this);
|
2012-07-16 11:28:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return this.fire('layerremove', {layer: layer});
|
|
|
|
},
|
|
|
|
|
|
|
|
hasLayer: function (layer) {
|
2013-01-22 06:22:01 +08:00
|
|
|
if (!layer) { return false; }
|
|
|
|
|
2013-04-23 00:20:19 +08:00
|
|
|
return (L.stamp(layer) in this._layers);
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
2013-02-27 03:39:00 +08:00
|
|
|
eachLayer: function (method, context) {
|
|
|
|
for (var i in this._layers) {
|
2013-04-20 16:32:15 +08:00
|
|
|
method.call(context, this._layers[i]);
|
2013-02-27 03:39:00 +08:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2012-07-31 07:59:06 +08:00
|
|
|
invalidateSize: function (animate) {
|
2012-07-16 11:28:13 +08:00
|
|
|
var oldSize = this.getSize();
|
|
|
|
|
|
|
|
this._sizeChanged = true;
|
|
|
|
|
|
|
|
if (this.options.maxBounds) {
|
|
|
|
this.setMaxBounds(this.options.maxBounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this._loaded) { return this; }
|
|
|
|
|
2013-04-18 20:26:45 +08:00
|
|
|
var newSize = this.getSize(),
|
|
|
|
offset = oldSize.subtract(newSize).divideBy(2).round();
|
2012-08-06 18:36:52 +08:00
|
|
|
|
2013-02-14 04:38:33 +08:00
|
|
|
if ((offset.x !== 0) || (offset.y !== 0)) {
|
|
|
|
if (animate === true) {
|
|
|
|
this.panBy(offset);
|
|
|
|
} else {
|
|
|
|
this._rawPanBy(offset);
|
2012-07-16 11:28:13 +08:00
|
|
|
|
2013-02-14 04:38:33 +08:00
|
|
|
this.fire('move');
|
2012-07-16 11:28:13 +08:00
|
|
|
|
2013-02-14 04:38:33 +08:00
|
|
|
clearTimeout(this._sizeTimer);
|
|
|
|
this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
|
|
|
|
}
|
2013-04-18 20:26:45 +08:00
|
|
|
this.fire('resize', {
|
|
|
|
oldSize: oldSize,
|
|
|
|
newSize: newSize
|
|
|
|
});
|
2012-07-31 07:59:06 +08:00
|
|
|
}
|
2012-07-16 11:28:13 +08:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
// TODO handler.addTo
|
|
|
|
addHandler: function (name, HandlerClass) {
|
|
|
|
if (!HandlerClass) { return; }
|
|
|
|
|
2013-04-30 21:51:45 +08:00
|
|
|
var handler = this[name] = new HandlerClass(this);
|
|
|
|
|
|
|
|
this._handlers.push(handler);
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
if (this.options[name]) {
|
2013-04-30 21:51:45 +08:00
|
|
|
handler.enable();
|
2012-07-16 11:28:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2013-02-20 09:35:19 +08:00
|
|
|
remove: function () {
|
2013-02-22 09:29:41 +08:00
|
|
|
if (this._loaded) {
|
|
|
|
this.fire('unload');
|
|
|
|
}
|
2013-04-30 21:51:45 +08:00
|
|
|
|
2013-02-20 09:35:19 +08:00
|
|
|
this._initEvents('off');
|
2013-04-30 21:51:45 +08:00
|
|
|
|
2013-02-20 09:35:19 +08:00
|
|
|
delete this._container._leaflet;
|
2013-04-30 21:51:45 +08:00
|
|
|
|
|
|
|
this._clearPanes();
|
|
|
|
if (this._clearControlPos) {
|
|
|
|
this._clearControlPos();
|
|
|
|
}
|
|
|
|
|
|
|
|
this._clearHandlers();
|
|
|
|
|
2013-02-20 09:35:19 +08:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
// public methods for getting map state
|
|
|
|
|
|
|
|
getCenter: function () { // (Boolean) -> LatLng
|
2013-02-21 02:11:47 +08:00
|
|
|
this._checkIfLoaded();
|
|
|
|
|
|
|
|
if (!this._moved()) {
|
2013-02-21 02:54:12 +08:00
|
|
|
return this._initialCenter;
|
2013-02-21 02:11:47 +08:00
|
|
|
}
|
2012-07-16 11:28:13 +08:00
|
|
|
return this.layerPointToLatLng(this._getCenterLayerPoint());
|
|
|
|
},
|
|
|
|
|
|
|
|
getZoom: function () {
|
|
|
|
return this._zoom;
|
|
|
|
},
|
|
|
|
|
|
|
|
getBounds: function () {
|
|
|
|
var bounds = this.getPixelBounds(),
|
|
|
|
sw = this.unproject(bounds.getBottomLeft()),
|
|
|
|
ne = this.unproject(bounds.getTopRight());
|
|
|
|
|
|
|
|
return new L.LatLngBounds(sw, ne);
|
|
|
|
},
|
|
|
|
|
|
|
|
getMinZoom: function () {
|
|
|
|
var z1 = this.options.minZoom || 0,
|
|
|
|
z2 = this._layersMinZoom || 0,
|
|
|
|
z3 = this._boundsMinZoom || 0;
|
|
|
|
|
|
|
|
return Math.max(z1, z2, z3);
|
|
|
|
},
|
|
|
|
|
|
|
|
getMaxZoom: function () {
|
|
|
|
var z1 = this.options.maxZoom === undefined ? Infinity : this.options.maxZoom,
|
|
|
|
z2 = this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom;
|
|
|
|
|
|
|
|
return Math.min(z1, z2);
|
|
|
|
},
|
|
|
|
|
2013-04-22 20:21:30 +08:00
|
|
|
getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
|
2012-07-16 11:28:13 +08:00
|
|
|
bounds = L.latLngBounds(bounds);
|
|
|
|
|
2013-04-22 22:06:42 +08:00
|
|
|
var zoom = this.getMinZoom() - (inside ? 1 : 0),
|
2012-07-16 11:28:13 +08:00
|
|
|
maxZoom = this.getMaxZoom(),
|
2013-04-22 22:06:42 +08:00
|
|
|
size = this.getSize(),
|
|
|
|
|
|
|
|
nw = bounds.getNorthWest(),
|
|
|
|
se = bounds.getSouthEast(),
|
|
|
|
|
2013-04-22 20:21:30 +08:00
|
|
|
zoomNotFound = true,
|
2013-04-22 22:06:42 +08:00
|
|
|
boundsSize;
|
2013-04-22 20:21:30 +08:00
|
|
|
|
|
|
|
padding = L.point(padding || [0, 0]);
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
do {
|
|
|
|
zoom++;
|
2013-04-22 22:06:42 +08:00
|
|
|
boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)).add(padding);
|
|
|
|
zoomNotFound = !inside ? size.contains(boundsSize) : boundsSize.x < size.x || boundsSize.y < size.y;
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
} while (zoomNotFound && zoom <= maxZoom);
|
|
|
|
|
|
|
|
if (zoomNotFound && inside) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return inside ? zoom : zoom - 1;
|
|
|
|
},
|
|
|
|
|
|
|
|
getSize: function () {
|
|
|
|
if (!this._size || this._sizeChanged) {
|
|
|
|
this._size = new L.Point(
|
|
|
|
this._container.clientWidth,
|
|
|
|
this._container.clientHeight);
|
|
|
|
|
|
|
|
this._sizeChanged = false;
|
|
|
|
}
|
2012-08-11 03:40:55 +08:00
|
|
|
return this._size.clone();
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
getPixelBounds: function () {
|
|
|
|
var topLeftPoint = this._getTopLeftPoint();
|
|
|
|
return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
|
|
|
|
},
|
|
|
|
|
|
|
|
getPixelOrigin: function () {
|
2013-02-21 02:11:47 +08:00
|
|
|
this._checkIfLoaded();
|
2012-07-16 11:28:13 +08:00
|
|
|
return this._initialTopLeftPoint;
|
|
|
|
},
|
|
|
|
|
|
|
|
getPanes: function () {
|
|
|
|
return this._panes;
|
|
|
|
},
|
|
|
|
|
|
|
|
getContainer: function () {
|
|
|
|
return this._container;
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// TODO replace with universal implementation after refactoring projections
|
|
|
|
|
|
|
|
getZoomScale: function (toZoom) {
|
|
|
|
var crs = this.options.crs;
|
|
|
|
return crs.scale(toZoom) / crs.scale(this._zoom);
|
|
|
|
},
|
|
|
|
|
|
|
|
getScaleZoom: function (scale) {
|
|
|
|
return this._zoom + (Math.log(scale) / Math.LN2);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// conversion methods
|
|
|
|
|
|
|
|
project: function (latlng, zoom) { // (LatLng[, Number]) -> Point
|
|
|
|
zoom = zoom === undefined ? this._zoom : zoom;
|
|
|
|
return this.options.crs.latLngToPoint(L.latLng(latlng), zoom);
|
|
|
|
},
|
|
|
|
|
|
|
|
unproject: function (point, zoom) { // (Point[, Number]) -> LatLng
|
|
|
|
zoom = zoom === undefined ? this._zoom : zoom;
|
|
|
|
return this.options.crs.pointToLatLng(L.point(point), zoom);
|
|
|
|
},
|
|
|
|
|
|
|
|
layerPointToLatLng: function (point) { // (Point)
|
2013-02-19 22:54:29 +08:00
|
|
|
var projectedPoint = L.point(point).add(this.getPixelOrigin());
|
2012-07-16 11:28:13 +08:00
|
|
|
return this.unproject(projectedPoint);
|
|
|
|
},
|
|
|
|
|
|
|
|
latLngToLayerPoint: function (latlng) { // (LatLng)
|
|
|
|
var projectedPoint = this.project(L.latLng(latlng))._round();
|
2013-02-19 22:54:29 +08:00
|
|
|
return projectedPoint._subtract(this.getPixelOrigin());
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
containerPointToLayerPoint: function (point) { // (Point)
|
|
|
|
return L.point(point).subtract(this._getMapPanePos());
|
|
|
|
},
|
|
|
|
|
|
|
|
layerPointToContainerPoint: function (point) { // (Point)
|
|
|
|
return L.point(point).add(this._getMapPanePos());
|
|
|
|
},
|
|
|
|
|
|
|
|
containerPointToLatLng: function (point) {
|
|
|
|
var layerPoint = this.containerPointToLayerPoint(L.point(point));
|
|
|
|
return this.layerPointToLatLng(layerPoint);
|
|
|
|
},
|
|
|
|
|
|
|
|
latLngToContainerPoint: function (latlng) {
|
|
|
|
return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng)));
|
|
|
|
},
|
|
|
|
|
|
|
|
mouseEventToContainerPoint: function (e) { // (MouseEvent)
|
|
|
|
return L.DomEvent.getMousePosition(e, this._container);
|
|
|
|
},
|
|
|
|
|
|
|
|
mouseEventToLayerPoint: function (e) { // (MouseEvent)
|
|
|
|
return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
|
|
|
|
},
|
|
|
|
|
|
|
|
mouseEventToLatLng: function (e) { // (MouseEvent)
|
|
|
|
return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// map initialization methods
|
|
|
|
|
|
|
|
_initContainer: function (id) {
|
|
|
|
var container = this._container = L.DomUtil.get(id);
|
|
|
|
|
2013-04-09 00:28:27 +08:00
|
|
|
if (!container) {
|
2013-04-11 17:45:52 +08:00
|
|
|
throw new Error('Map container not found.');
|
2013-04-09 00:28:27 +08:00
|
|
|
} else if (container._leaflet) {
|
2013-04-11 17:45:52 +08:00
|
|
|
throw new Error('Map container is already initialized.');
|
2012-07-16 11:28:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
container._leaflet = true;
|
|
|
|
},
|
|
|
|
|
|
|
|
_initLayout: function () {
|
|
|
|
var container = this._container;
|
|
|
|
|
|
|
|
L.DomUtil.addClass(container, 'leaflet-container');
|
|
|
|
|
|
|
|
if (L.Browser.touch) {
|
|
|
|
L.DomUtil.addClass(container, 'leaflet-touch');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.options.fadeAnimation) {
|
|
|
|
L.DomUtil.addClass(container, 'leaflet-fade-anim');
|
|
|
|
}
|
|
|
|
|
|
|
|
var position = L.DomUtil.getStyle(container, 'position');
|
|
|
|
|
|
|
|
if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
|
|
|
|
container.style.position = 'relative';
|
|
|
|
}
|
|
|
|
|
|
|
|
this._initPanes();
|
|
|
|
|
|
|
|
if (this._initControlPos) {
|
|
|
|
this._initControlPos();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_initPanes: function () {
|
|
|
|
var panes = this._panes = {};
|
|
|
|
|
|
|
|
this._mapPane = panes.mapPane = this._createPane('leaflet-map-pane', this._container);
|
|
|
|
|
|
|
|
this._tilePane = panes.tilePane = this._createPane('leaflet-tile-pane', this._mapPane);
|
2012-11-07 20:13:06 +08:00
|
|
|
panes.objectsPane = this._createPane('leaflet-objects-pane', this._mapPane);
|
2012-07-16 11:28:13 +08:00
|
|
|
panes.shadowPane = this._createPane('leaflet-shadow-pane');
|
|
|
|
panes.overlayPane = this._createPane('leaflet-overlay-pane');
|
|
|
|
panes.markerPane = this._createPane('leaflet-marker-pane');
|
|
|
|
panes.popupPane = this._createPane('leaflet-popup-pane');
|
|
|
|
|
|
|
|
var zoomHide = ' leaflet-zoom-hide';
|
|
|
|
|
|
|
|
if (!this.options.markerZoomAnimation) {
|
|
|
|
L.DomUtil.addClass(panes.markerPane, zoomHide);
|
|
|
|
L.DomUtil.addClass(panes.shadowPane, zoomHide);
|
|
|
|
L.DomUtil.addClass(panes.popupPane, zoomHide);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_createPane: function (className, container) {
|
2012-11-07 20:13:06 +08:00
|
|
|
return L.DomUtil.create('div', className, container || this._panes.objectsPane);
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
2013-04-30 21:51:45 +08:00
|
|
|
_clearPanes: function () {
|
|
|
|
this._container.removeChild(this._mapPane);
|
|
|
|
},
|
|
|
|
|
2012-07-16 11:28:13 +08:00
|
|
|
_initLayers: function (layers) {
|
2013-01-22 06:22:01 +08:00
|
|
|
layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : [];
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
this._layers = {};
|
2012-11-19 05:07:27 +08:00
|
|
|
this._zoomBoundLayers = {};
|
2012-07-16 11:28:13 +08:00
|
|
|
this._tileLayersNum = 0;
|
|
|
|
|
|
|
|
var i, len;
|
|
|
|
|
|
|
|
for (i = 0, len = layers.length; i < len; i++) {
|
|
|
|
this.addLayer(layers[i]);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// private methods that modify map state
|
|
|
|
|
|
|
|
_resetView: function (center, zoom, preserveMapOffset, afterZoomAnim) {
|
|
|
|
|
|
|
|
var zoomChanged = (this._zoom !== zoom);
|
|
|
|
|
|
|
|
if (!afterZoomAnim) {
|
|
|
|
this.fire('movestart');
|
|
|
|
|
|
|
|
if (zoomChanged) {
|
|
|
|
this.fire('zoomstart');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this._zoom = zoom;
|
2013-02-21 02:11:47 +08:00
|
|
|
this._initialCenter = center;
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
this._initialTopLeftPoint = this._getNewTopLeftPoint(center);
|
|
|
|
|
|
|
|
if (!preserveMapOffset) {
|
|
|
|
L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
|
|
|
|
} else {
|
|
|
|
this._initialTopLeftPoint._add(this._getMapPanePos());
|
|
|
|
}
|
|
|
|
|
|
|
|
this._tileLayersToLoad = this._tileLayersNum;
|
|
|
|
|
2012-10-05 05:02:46 +08:00
|
|
|
var loading = !this._loaded;
|
|
|
|
this._loaded = true;
|
|
|
|
|
2013-04-20 17:07:00 +08:00
|
|
|
if (loading) {
|
|
|
|
this.fire('load');
|
2013-06-02 13:11:42 +08:00
|
|
|
this.eachLayer(this._layerAdd, this);
|
2013-04-20 17:07:00 +08:00
|
|
|
}
|
|
|
|
|
2012-07-16 11:28:13 +08:00
|
|
|
this.fire('viewreset', {hard: !preserveMapOffset});
|
|
|
|
|
|
|
|
this.fire('move');
|
|
|
|
|
|
|
|
if (zoomChanged || afterZoomAnim) {
|
|
|
|
this.fire('zoomend');
|
|
|
|
}
|
|
|
|
|
2012-07-24 22:55:48 +08:00
|
|
|
this.fire('moveend', {hard: !preserveMapOffset});
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
_rawPanBy: function (offset) {
|
|
|
|
L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
|
|
|
|
},
|
|
|
|
|
2013-02-14 19:00:08 +08:00
|
|
|
_getZoomSpan: function () {
|
|
|
|
return this.getMaxZoom() - this.getMinZoom();
|
|
|
|
},
|
|
|
|
|
2012-11-19 05:07:27 +08:00
|
|
|
_updateZoomLevels: function () {
|
2012-11-19 11:36:13 +08:00
|
|
|
var i,
|
|
|
|
minZoom = Infinity,
|
2013-02-14 10:04:10 +08:00
|
|
|
maxZoom = -Infinity,
|
2013-02-14 19:00:08 +08:00
|
|
|
oldZoomSpan = this._getZoomSpan();
|
2012-11-19 11:36:13 +08:00
|
|
|
|
2012-11-19 05:07:27 +08:00
|
|
|
for (i in this._zoomBoundLayers) {
|
2013-04-20 16:32:15 +08:00
|
|
|
var layer = this._zoomBoundLayers[i];
|
|
|
|
if (!isNaN(layer.options.minZoom)) {
|
|
|
|
minZoom = Math.min(minZoom, layer.options.minZoom);
|
|
|
|
}
|
|
|
|
if (!isNaN(layer.options.maxZoom)) {
|
|
|
|
maxZoom = Math.max(maxZoom, layer.options.maxZoom);
|
2012-11-19 11:36:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-30 04:43:40 +08:00
|
|
|
if (i === undefined) { // we have no tilelayers
|
2012-11-19 11:36:13 +08:00
|
|
|
this._layersMaxZoom = this._layersMinZoom = undefined;
|
|
|
|
} else {
|
|
|
|
this._layersMaxZoom = maxZoom;
|
|
|
|
this._layersMinZoom = minZoom;
|
2012-11-19 05:07:27 +08:00
|
|
|
}
|
2013-02-14 10:04:10 +08:00
|
|
|
|
2013-02-14 19:00:08 +08:00
|
|
|
if (oldZoomSpan !== this._getZoomSpan()) {
|
2013-04-11 17:45:52 +08:00
|
|
|
this.fire('zoomlevelschange');
|
2013-02-14 10:04:10 +08:00
|
|
|
}
|
2012-11-19 05:07:27 +08:00
|
|
|
},
|
2012-07-16 11:28:13 +08:00
|
|
|
|
2013-04-18 22:05:47 +08:00
|
|
|
_panInsideMaxBounds: function () {
|
|
|
|
this.panInsideBounds(this.options.maxBounds);
|
|
|
|
},
|
|
|
|
|
2013-02-21 02:11:47 +08:00
|
|
|
_checkIfLoaded: function () {
|
|
|
|
if (!this._loaded) {
|
|
|
|
throw new Error('Set map center and zoom first.');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-07-16 11:28:13 +08:00
|
|
|
// map events
|
|
|
|
|
2013-02-20 09:35:19 +08:00
|
|
|
_initEvents: function (onOff) {
|
2012-07-16 11:28:13 +08:00
|
|
|
if (!L.DomEvent) { return; }
|
|
|
|
|
2013-02-20 09:35:19 +08:00
|
|
|
onOff = onOff || 'on';
|
|
|
|
|
|
|
|
L.DomEvent[onOff](this._container, 'click', this._onMouseClick, this);
|
2012-07-16 11:28:13 +08:00
|
|
|
|
2012-11-07 20:13:06 +08:00
|
|
|
var events = ['dblclick', 'mousedown', 'mouseup', 'mouseenter',
|
|
|
|
'mouseleave', 'mousemove', 'contextmenu'],
|
|
|
|
i, len;
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
for (i = 0, len = events.length; i < len; i++) {
|
2013-02-20 09:35:19 +08:00
|
|
|
L.DomEvent[onOff](this._container, events[i], this._fireMouseEvent, this);
|
2012-07-16 11:28:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this.options.trackResize) {
|
2013-02-20 09:35:19 +08:00
|
|
|
L.DomEvent[onOff](window, 'resize', this._onResize, this);
|
2012-07-16 11:28:13 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_onResize: function () {
|
|
|
|
L.Util.cancelAnimFrame(this._resizeRequest);
|
2012-11-07 20:13:06 +08:00
|
|
|
this._resizeRequest = L.Util.requestAnimFrame(
|
|
|
|
this.invalidateSize, this, false, this._container);
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
_onMouseClick: function (e) {
|
|
|
|
if (!this._loaded || (this.dragging && this.dragging.moved())) { return; }
|
|
|
|
|
|
|
|
this.fire('preclick');
|
|
|
|
this._fireMouseEvent(e);
|
|
|
|
},
|
|
|
|
|
|
|
|
_fireMouseEvent: function (e) {
|
|
|
|
if (!this._loaded) { return; }
|
|
|
|
|
|
|
|
var type = e.type;
|
|
|
|
|
|
|
|
type = (type === 'mouseenter' ? 'mouseover' : (type === 'mouseleave' ? 'mouseout' : type));
|
|
|
|
|
|
|
|
if (!this.hasEventListeners(type)) { return; }
|
|
|
|
|
|
|
|
if (type === 'contextmenu') {
|
|
|
|
L.DomEvent.preventDefault(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
var containerPoint = this.mouseEventToContainerPoint(e),
|
2012-11-07 20:13:06 +08:00
|
|
|
layerPoint = this.containerPointToLayerPoint(containerPoint),
|
|
|
|
latlng = this.layerPointToLatLng(layerPoint);
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
this.fire(type, {
|
|
|
|
latlng: latlng,
|
|
|
|
layerPoint: layerPoint,
|
|
|
|
containerPoint: containerPoint,
|
|
|
|
originalEvent: e
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
_onTileLayerLoad: function () {
|
|
|
|
this._tileLayersToLoad--;
|
2013-02-21 00:40:00 +08:00
|
|
|
if (this._tileLayersNum && !this._tileLayersToLoad) {
|
|
|
|
this.fire('tilelayersload');
|
2012-07-16 11:28:13 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-04-30 21:51:45 +08:00
|
|
|
_clearHandlers: function () {
|
|
|
|
for (var i = 0, len = this._handlers.length; i < len; i++) {
|
|
|
|
this._handlers[i].disable();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-10-13 03:49:12 +08:00
|
|
|
whenReady: function (callback, context) {
|
|
|
|
if (this._loaded) {
|
|
|
|
callback.call(context || this, this);
|
|
|
|
} else {
|
|
|
|
this.on('load', callback, context);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2013-06-02 13:11:42 +08:00
|
|
|
_layerAdd: function(layer) {
|
|
|
|
layer.onAdd(this);
|
|
|
|
this.fire('layeradd', {layer: layer});
|
|
|
|
},
|
|
|
|
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
// private methods for getting map state
|
|
|
|
|
|
|
|
_getMapPanePos: function () {
|
|
|
|
return L.DomUtil.getPosition(this._mapPane);
|
|
|
|
},
|
|
|
|
|
2013-02-21 02:11:47 +08:00
|
|
|
_moved: function () {
|
|
|
|
var pos = this._getMapPanePos();
|
2013-04-22 22:20:34 +08:00
|
|
|
return pos && !pos.equals([0, 0]);
|
2013-02-21 02:11:47 +08:00
|
|
|
},
|
|
|
|
|
2012-07-16 11:28:13 +08:00
|
|
|
_getTopLeftPoint: function () {
|
2013-02-19 22:54:29 +08:00
|
|
|
return this.getPixelOrigin().subtract(this._getMapPanePos());
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
_getNewTopLeftPoint: function (center, zoom) {
|
2012-08-11 03:40:55 +08:00
|
|
|
var viewHalf = this.getSize()._divideBy(2);
|
2012-07-16 11:28:13 +08:00
|
|
|
// TODO round on display, not calculation to increase precision?
|
|
|
|
return this.project(center, zoom)._subtract(viewHalf)._round();
|
|
|
|
},
|
|
|
|
|
|
|
|
_latLngToNewLayerPoint: function (latlng, newZoom, newCenter) {
|
|
|
|
var topLeft = this._getNewTopLeftPoint(newCenter, newZoom).add(this._getMapPanePos());
|
2012-07-25 16:14:21 +08:00
|
|
|
return this.project(latlng, newZoom)._subtract(topLeft);
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
2013-02-22 23:29:52 +08:00
|
|
|
// layer point of the current center
|
2012-07-16 11:28:13 +08:00
|
|
|
_getCenterLayerPoint: function () {
|
2012-08-11 03:40:55 +08:00
|
|
|
return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
2013-02-22 23:29:52 +08:00
|
|
|
// offset of the specified place to the current center in pixels
|
|
|
|
_getCenterOffset: function (latlng) {
|
|
|
|
return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
|
2012-07-16 11:28:13 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
_limitZoom: function (zoom) {
|
|
|
|
var min = this.getMinZoom(),
|
2012-11-07 20:13:06 +08:00
|
|
|
max = this.getMaxZoom();
|
2012-07-16 11:28:13 +08:00
|
|
|
|
|
|
|
return Math.max(min, Math.min(max, zoom));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
L.map = function (id, options) {
|
|
|
|
return new L.Map(id, options);
|
|
|
|
};
|