TileLayer refactoring, add loading event, update changelog, closes #177

This commit is contained in:
Vladimir Agafonkin 2012-07-25 17:10:11 +03:00
parent 55c35828a8
commit c50aaabd6d
2 changed files with 74 additions and 42 deletions

View File

@ -60,6 +60,7 @@ Icon API was improved to be more flexible, but one of the changes is backwards-i
#### Other breaking API changes #### Other breaking API changes
* Improved `TileLayer` constructor to interpolate URL template values from options (removed third `urlParams` argument). * Improved `TileLayer` constructor to interpolate URL template values from options (removed third `urlParams` argument).
* Changed `TileLayer` `scheme: 'tms'` option to `tms: true`.
* Replaced ugly control position constants (e.g. `L.Control.Position.TOP_LEFT`) with light strings (`'topleft'`, `'bottomright'`, etc.) * Replaced ugly control position constants (e.g. `L.Control.Position.TOP_LEFT`) with light strings (`'topleft'`, `'bottomright'`, etc.)
* Removed `Map` `locateAndSetView` method (use `locate` with `setView: true` option) * Removed `Map` `locateAndSetView` method (use `locate` with `setView: true` option)
* Changed popup `minWidth` and `maxWidth` options to be applied to content element, not the whole popup. * Changed popup `minWidth` and `maxWidth` options to be applied to content element, not the whole popup.
@ -73,6 +74,7 @@ Icon API was improved to be more flexible, but one of the changes is backwards-i
* Added `TileLayer` `redraw` method for re-requesting tiles (by [@greeninfo](https://github.com/greeninfo)). [#719](https://github.com/CloudMade/Leaflet/issues/719) * Added `TileLayer` `redraw` method for re-requesting tiles (by [@greeninfo](https://github.com/greeninfo)). [#719](https://github.com/CloudMade/Leaflet/issues/719)
* Added `TileLayer` `setUrl` method for dynamically changing the tile URL template. * Added `TileLayer` `setUrl` method for dynamically changing the tile URL template.
* Added `bringToFront` and `bringToBack` methods to `TileLayer` and vector layers. [#185](https://github.com/CloudMade/Leaflet/issues/185) [#505](https://github.com/CloudMade/Leaflet/issues/505) * Added `bringToFront` and `bringToBack` methods to `TileLayer` and vector layers. [#185](https://github.com/CloudMade/Leaflet/issues/185) [#505](https://github.com/CloudMade/Leaflet/issues/505)
* Added `TileLayer` `loading` event that fires when its tiles start to load (thanks to [@lapinos03](https://github.com/lapinos03)). [#177](https://github.com/CloudMade/Leaflet/issues/177)
* Added `TileLayer.WMS` `setParams` method for setting WMS parameters at runtime (by [@greeninfo](https://github.com/greeninfo)). [#719](https://github.com/CloudMade/Leaflet/issues/719) * Added `TileLayer.WMS` `setParams` method for setting WMS parameters at runtime (by [@greeninfo](https://github.com/greeninfo)). [#719](https://github.com/CloudMade/Leaflet/issues/719)
* Added `TileLayer.WMS` subdomain support (`{s}` in the url) (by [@greeninfo](https://github.com/greeninfo)). [#735](https://github.com/CloudMade/Leaflet/issues/735) * Added `TileLayer.WMS` subdomain support (`{s}` in the url) (by [@greeninfo](https://github.com/greeninfo)). [#735](https://github.com/CloudMade/Leaflet/issues/735)
* Added `originalEvent` property to `MouseEvent` (by [@k4](https://github.com/k4)). [#521](https://github.com/CloudMade/Leaflet/pull/521) * Added `originalEvent` property to `MouseEvent` (by [@k4](https://github.com/k4)). [#521](https://github.com/CloudMade/Leaflet/pull/521)
@ -118,7 +120,7 @@ Icon API was improved to be more flexible, but one of the changes is backwards-i
* Fixed a bug where `TileLayer` `setOpacity` wouldn't work when setting it back to 1. * Fixed a bug where `TileLayer` `setOpacity` wouldn't work when setting it back to 1.
* Fixed a bug where vector layer `setStyle({stroke: false})` wouldn't remove stroke and the same for fill. [#441](https://github.com/CloudMade/Leaflet/issues/441) * Fixed a bug where vector layer `setStyle({stroke: false})` wouldn't remove stroke and the same for fill. [#441](https://github.com/CloudMade/Leaflet/issues/441)
* Fixed a bug where `Marker` `bindPopup` method wouldn't take `offset` option into account. * Fixed a bug where `Marker` `bindPopup` method wouldn't take `offset` option into account.
* Fixed a bug where `TileLayer` `load` event wasn't fired if some tile didn't load (by [@cfis](https://github.com/cfis)) [#682](https://github.com/CloudMade/Leaflet/pull/682) * Fixed a bug where `TileLayer` `load` event wasn't fired if some tile didn't load (by [@lapinos03](https://github.com/lapinos03) and [@cfis](https://github.com/cfis)) [#682](https://github.com/CloudMade/Leaflet/pull/682)
* Fixed error when removing `GeoJSON` layer. [#685](https://github.com/CloudMade/Leaflet/issues/685) * Fixed error when removing `GeoJSON` layer. [#685](https://github.com/CloudMade/Leaflet/issues/685)
* Fixed error when calling `GeoJSON` `clearLayer` (by [@runderwood](https://github.com/runderwood)). [#617](https://github.com/CloudMade/Leaflet/pull/617) * Fixed error when calling `GeoJSON` `clearLayer` (by [@runderwood](https://github.com/runderwood)). [#617](https://github.com/CloudMade/Leaflet/pull/617)
* Fixed a bug where polygons/polylines sometimes throwed an error when making them editable manually (by [@cfis](https://github.com/cfis)). [#669](https://github.com/CloudMade/Leaflet/pull/669) * Fixed a bug where polygons/polylines sometimes throwed an error when making them editable manually (by [@cfis](https://github.com/cfis)). [#669](https://github.com/CloudMade/Leaflet/pull/669)

View File

@ -13,7 +13,7 @@ L.TileLayer = L.Class.extend({
errorTileUrl: '', errorTileUrl: '',
attribution: '', attribution: '',
opacity: 1, opacity: 1,
scheme: 'xyz', tms: false,
continuousWorld: false, continuousWorld: false,
noWrap: false, noWrap: false,
zoomOffset: 0, zoomOffset: 0,
@ -192,6 +192,7 @@ L.TileLayer = L.Class.extend({
} }
this._tiles = {}; this._tiles = {};
this._tilesToLoad = 0;
if (this.options.reuseTiles) { if (this.options.reuseTiles) {
this._unusedTiles = []; this._unusedTiles = [];
@ -234,16 +235,21 @@ L.TileLayer = L.Class.extend({
var queue = [], var queue = [],
center = bounds.getCenter(); center = bounds.getCenter();
var j, i; var j, i, point;
for (j = bounds.min.y; j <= bounds.max.y; j++) { for (j = bounds.min.y; j <= bounds.max.y; j++) {
for (i = bounds.min.x; i <= bounds.max.x; i++) { for (i = bounds.min.x; i <= bounds.max.x; i++) {
if (!((i + ':' + j) in this._tiles)) { point = new L.Point(i, j);
queue.push(new L.Point(i, j));
if (this._tileShouldBeLoaded(point)) {
queue.push(point);
} }
} }
} }
if (queue.length === 0) { return; } var tilesToLoad = queue.length;
if (tilesToLoad === 0) { return; }
// load tiles in order of their distance to center // load tiles in order of their distance to center
queue.sort(function (a, b) { queue.sort(function (a, b) {
@ -252,16 +258,37 @@ L.TileLayer = L.Class.extend({
var fragment = document.createDocumentFragment(); var fragment = document.createDocumentFragment();
this._tilesToLoad = queue.length; // if its the first batch of tiles to load
if (!this._tilesToLoad) {
this.fire('loading');
}
var k, len; this._tilesToLoad += tilesToLoad;
for (k = 0, len = this._tilesToLoad; k < len; k++) {
this._addTile(queue[k], fragment); for (i = 0; i < tilesToLoad; i++) {
this._addTile(queue[i], fragment);
} }
this._container.appendChild(fragment); this._container.appendChild(fragment);
}, },
_tileShouldBeLoaded: function (tilePoint) {
if ((tilePoint.x + ':' + tilePoint.y) in this._tiles) {
return false; // already loaded
}
if (!this.options.continuousWorld) {
var limit = this._getWrapTileNum();
if (this.options.noWrap && (tilePoint.x < 0 || tilePoint.x >= limit) ||
tilePoint.y < 0 || tilePoint.y >= limit) {
return false; // exceeds world bounds
}
}
return true;
},
_removeOtherTiles: function (bounds) { _removeOtherTiles: function (bounds) {
var kArr, x, y, key; var kArr, x, y, key;
@ -299,25 +326,7 @@ L.TileLayer = L.Class.extend({
}, },
_addTile: function (tilePoint, container) { _addTile: function (tilePoint, container) {
var tilePos = this._getTilePos(tilePoint), var tilePos = this._getTilePos(tilePoint);
zoom = this._map.getZoom(),
key = tilePoint.x + ':' + tilePoint.y,
limit = Math.pow(2, this._getOffsetZoom(zoom));
// wrap tile coordinates
if (!this.options.continuousWorld) {
if (!this.options.noWrap) {
tilePoint.x = ((tilePoint.x % limit) + limit) % limit;
} else if (tilePoint.x < 0 || tilePoint.x >= limit) {
this._tilesToLoad--;
return;
}
if (tilePoint.y < 0 || tilePoint.y >= limit) {
this._tilesToLoad--;
return;
}
}
// get unused tile - or create a new tile // get unused tile - or create a new tile
var tile = this._getTile(); var tile = this._getTile();
@ -327,22 +336,24 @@ L.TileLayer = L.Class.extend({
// (other browsers don't currently care) - see debug/hacks/jitter.html for an example // (other browsers don't currently care) - see debug/hacks/jitter.html for an example
L.DomUtil.setPosition(tile, tilePos, L.Browser.chrome); L.DomUtil.setPosition(tile, tilePos, L.Browser.chrome);
this._tiles[key] = tile; this._tiles[tilePoint.x + ':' + tilePoint.y] = tile;
if (this.options.scheme === 'tms') { this._loadTile(tile, tilePoint);
tilePoint.y = limit - tilePoint.y - 1;
}
this._loadTile(tile, tilePoint, zoom);
if (tile.parentNode !== this._container) { if (tile.parentNode !== this._container) {
container.appendChild(tile); container.appendChild(tile);
} }
}, },
_getOffsetZoom: function (zoom) { _getZoomForUrl: function () {
var options = this.options;
zoom = options.zoomReverse ? options.maxZoom - zoom : zoom; var options = this.options,
zoom = this._map.getZoom();
if (options.zoomReverse) {
zoom = options.maxZoom - zoom;
}
return zoom + options.zoomOffset; return zoom + options.zoomOffset;
}, },
@ -355,15 +366,34 @@ L.TileLayer = L.Class.extend({
// image-specific code (override to implement e.g. Canvas or SVG tile layer) // image-specific code (override to implement e.g. Canvas or SVG tile layer)
getTileUrl: function (tilePoint, zoom) { getTileUrl: function (tilePoint) {
this._adjustTilePoint(tilePoint);
return L.Util.template(this._url, L.Util.extend({ return L.Util.template(this._url, L.Util.extend({
s: this._getSubdomain(tilePoint), s: this._getSubdomain(tilePoint),
z: this._getOffsetZoom(zoom), z: this._getZoomForUrl(),
x: tilePoint.x, x: tilePoint.x,
y: tilePoint.y y: tilePoint.y
}, this.options)); }, this.options));
}, },
_getWrapTileNum: function () {
// TODO refactor, limit is not valid for non-standard projections
return Math.pow(2, this._getZoomForUrl());
},
_adjustTilePoint: function (tilePoint) {
// wrap tile coordinates
if (!this.options.continuousWorld && !this.options.noWrap) {
var limit = this._getWrapTileNum();
tilePoint.x = ((tilePoint.x % limit) + limit) % limit;
}
if (this.options.tms) {
tilePoint.y = limit - tilePoint.y - 1;
}
},
_getSubdomain: function (tilePoint) { _getSubdomain: function (tilePoint) {
var index = (tilePoint.x + tilePoint.y) % this.options.subdomains.length; var index = (tilePoint.x + tilePoint.y) % this.options.subdomains.length;
return this.options.subdomains[index]; return this.options.subdomains[index];
@ -397,12 +427,12 @@ L.TileLayer = L.Class.extend({
return tile; return tile;
}, },
_loadTile: function (tile, tilePoint, zoom) { _loadTile: function (tile, tilePoint) {
tile._layer = this; tile._layer = this;
tile.onload = this._tileOnLoad; tile.onload = this._tileOnLoad;
tile.onerror = this._tileOnError; tile.onerror = this._tileOnError;
tile.src = this.getTileUrl(tilePoint, zoom); tile.src = this.getTileUrl(tilePoint);
}, },
_tileLoaded: function () { _tileLoaded: function () {