diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d878762..62c3995f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,9 +14,9 @@ Leaflet Changelog #### Usability improvements - * Map now preserves its center after resize. + * Map now preserves its center after resize. * Limited maximum zoom change on a single mouse wheel movement (so you won't zoom across the whole zoom range in one scroll). [#149](https://github.com/CloudMade/Leaflet/issues/149) - * Improved circles performance by not drawing them if they're off the clip region. + * Improved circles performance by not drawing them if they're off the clip region. #### API improvements @@ -25,11 +25,14 @@ Leaflet Changelog * Added `hasLayer` method to `Map`. * Added `TileLayer` `continuousWorld` option to disable tile coordinates checking/wrapping. * Added `Polyline` `closestLayerPoint` method that's can be useful for interaction features (by [@anru](https://github.com/anru)). [#186](https://github.com/CloudMade/Leaflet/pull/186) - * Added `setLatLngs` method to `MultiPolyline` and `MultiPolygon` (by [@anru](https://github.com/anru)). [#194](https://github.com/CloudMade/Leaflet/pull/194) + * Added `setLatLngs` method to `MultiPolyline` and `MultiPolygon` (by [@anru](https://github.com/anru)). [#194](https://github.com/CloudMade/Leaflet/pull/194) * Added `DomUtil.removeClass` method (by [@anru](https://github.com/anru)). * Improved browser-specific code to rely more on feature detection rather than user agent string. * Improved superclass access mechanism to work with inheritance chains of 3 or more classes; now you should use `Klass.superclass` instead of `this.superclass` (by [@anru](https://github.com/anru)). [#179](https://github.com/CloudMade/Leaflet/pull/179) * Added `minWidth` option to `Popup` (by [@marphi](https://github.com/marphi)). [#214](https://github.com/CloudMade/Leaflet/pull/214) + * Added `TileLayer` `tileunload` event fired when tile gets removed after panning (by [@CodeJosch](https://github.com/CodeJosch)). [#256](https://github.com/CloudMade/Leaflet/pull/256) + * Added `LatLngBounds` `toBBoxString` method for convenience (by [@JasonSanford](https://github.com/JasonSanford)). [#263](https://github.com/CloudMade/Leaflet/pull/263) + * Added `getBounds` method to `Polyline` and `Polygon` (by [@JasonSanford](https://github.com/JasonSanford)). [#253](https://github.com/CloudMade/Leaflet/pull/253) ### Bugfixes @@ -53,7 +56,7 @@ Leaflet Changelog * Fixed occasional crashes on Mac Safari (thanks to [@lapinos03](https://github.com/lapinos03)). [#191](https://github.com/CloudMade/Leaflet/issues/191) * Fixed a bug that raised error in IE6-8 when clicking on popup close button. [#235](https://github.com/CloudMade/Leaflet/issues/235) * Fixed a bug that caused map overlays to appear blurry in some cases under WebKit browsers. - * Fixed a bug that was causing errors in some Webkit builds (requestAnimationFrame-related), thanks to Chris Martens. + * Fixed a bug that was causing errors in some Webkit/Linux builds (requestAnimationFrame-related), thanks to Chris Martens. #### Mobile browser bugfixes @@ -71,26 +74,26 @@ Leaflet Changelog * Added **WMS** support (`TileLayer.WMS` layer). * Added different **projections** support, having `EPSG:3857`, `EPSG:4326` and `EPSG:3395` out of the box (through `crs` option in `Map`). Thanks to [@Miroff](https://github.com/Miroff) & [@Komzpa](https://github.com/Komzpa) for great advice and explanation regarding this. * Added **GeoJSON** layer support. - + ### Improvements - + #### Usability improvements - + * Improved panning performance in Chrome and FF considerably with the help of `requestAnimationFrame`. [#130](https://github.com/CloudMade/Leaflet/issues/130) * Improved click responsiveness in mobile WebKit (now it happens without delay). [#26](https://github.com/CloudMade/Leaflet/issues/26) * Added tap tolerance (so click happens even if you moved your finger slighly when tapping). * Improved geolocation error handling: better error messages, explicit timeout, set world view on locateAndSetView failure. [#61](https://github.com/CloudMade/Leaflet/issues/61) - + #### API improvements - * Added **MultiPolyline** and **MultiPolygon** layers. [#77](https://github.com/CloudMade/Leaflet/issues/77) + * Added **MultiPolyline** and **MultiPolygon** layers. [#77](https://github.com/CloudMade/Leaflet/issues/77) * Added **LayerGroup** and **FeatureGroup** layers for grouping other layers. * Added **TileLayer.Canvas** for easy creation of canvas-based tile layers. * Changed `Circle` to be zoom-dependent (with radius in meters); circle of a permanent size is now called `CircleMarker`. * Added `mouseover` and `mouseout` events to map, markers and paths; added map `mousemove` event. * Added `setLatLngs`, `spliceLatLngs`, `addLatLng`, `getLatLngs` methods to polylines and polygons. * Added `setLatLng` and `setRadius` methods to `Circle` and `CircleMarker`. - * Improved `LatLngBounds contains` method to accept `LatLng` in addition to `LatLngBounds`, the same for `Bounds contains` and `Point` + * Improved `LatLngBounds contains` method to accept `LatLng` in addition to `LatLngBounds`, the same for `Bounds contains` and `Point` * Improved `LatLngBounds` & `Bounds` to allow their instantiation without arguments (by [@snc](https://github.com/snc)). * Added TMS tile numbering support through `TileLayer` `scheme: 'tms'` option (by [@tmcw](https://github.com/tmcw)). * Added `TileLayer` `noWrap` option to disable wrapping `x` tile coordinate (by [@jasondavies](https://github.com/jasondavies)). @@ -100,15 +103,15 @@ Leaflet Changelog * Added `maxZoom` argument to `map.locateAndSetView` method. * Added ability to pass Geolocation options to map `locate` and `locateAndSetView` methods (by [@JasonSanford](https://github.com/JasonSanford)). * Improved `Popup` to accept HTML elements in addition to strings as its content. - + #### Development workflow improvements - + * Added `Makefile` for building `leaflet.js` on non-Windows machines (by [@tmcw](https://github.com/tmcw)). * Improved `debug/leaflet-include.js` script to allow using it outside of `debug` folder (by [@antonj](https://github.com/antonj)). * Improved `L` definition to be compatible with CommonJS. [#122](https://github.com/CloudMade/Leaflet/issues/122) - + ### Bug fixes - + #### General bugfixes * Fixed a bug where zooming is broken if the map contains a polygon and you zoom to an area where it's not visible. [#47](https://github.com/CloudMade/Leaflet/issues/47) @@ -116,16 +119,16 @@ Leaflet Changelog * Fixed a bug where marker that was added, removed and then added again would not appear on the map. [#66](https://github.com/CloudMade/Leaflet/issues/66) * Fixed a bug where tile layer that was added, removed and then added again would not appear on the map. * Fixed a bug where some tiles would not load when panning across the date line. [#97](https://github.com/CloudMade/Leaflet/issues/97) - * Fixed a bug where map div with `position: absolute` is reset to `relative`. [#100](https://github.com/CloudMade/Leaflet/issues/100) + * Fixed a bug where map div with `position: absolute` is reset to `relative`. [#100](https://github.com/CloudMade/Leaflet/issues/100) * Fixed a bug that caused an error when trying to add a marker without shadow in its icon. * Fixed a bug where popup content would not update on `setContent` call. [#94](https://github.com/CloudMade/Leaflet/issues/94) * Fixed a bug where double click zoom wouldn't work if popup is opened on map click * Fixed a bug with click propagation on popup close button. [#99](https://github.com/CloudMade/Leaflet/issues/99) * Fixed inability to remove ImageOverlay layer. - + #### Browser bugfixes - - * Fixed a bug where paths would not appear in IE8. + + * Fixed a bug where paths would not appear in IE8. * Fixed a bug where there were occasional slowdowns before zoom animation in WebKit. [#123](https://github.com/CloudMade/Leaflet/issues/123) * Fixed incorrect zoom animation & popup styling in Opera 11.11. * Fixed popup fade animation in Firefox and Opera. diff --git a/src/layer/tile/TileLayer.js b/src/layer/tile/TileLayer.js index 5dc122f2..63220397 100644 --- a/src/layer/tile/TileLayer.js +++ b/src/layer/tile/TileLayer.js @@ -4,7 +4,7 @@ L.TileLayer = L.Class.extend({ includes: L.Mixin.Events, - + options: { minZoom: 0, maxZoom: 18, @@ -16,71 +16,71 @@ L.TileLayer = L.Class.extend({ scheme: 'xyz', continuousWorld: false, noWrap: false, - + unloadInvisibleTiles: L.Browser.mobile, updateWhenIdle: L.Browser.mobile }, - + initialize: function(url, options) { L.Util.setOptions(this, options); - + this._url = url; - + if (typeof this.options.subdomains == 'string') { this.options.subdomains = this.options.subdomains.split(''); } }, - + onAdd: function(map, insertAtTheBottom) { this._map = map; this._insertAtTheBottom = insertAtTheBottom; - + // create a container div for tiles this._initContainer(); - + // create an image to clone for tiles this._createTileProto(); - + // set up events - map.on('viewreset', + map.on('viewreset', function(e) { this._reset(e.hard); }, this); - + if (this.options.updateWhenIdle) { map.on('moveend', this._update, this); } else { this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this); map.on('move', this._limitedUpdate, this); } - + this._reset(); this._update(); }, - + onRemove: function(map) { this._map.getPanes().tilePane.removeChild(this._container); this._container = null; - + this._map.off('viewreset', this._reset, this); - + if (this.options.updateWhenIdle) { this._map.off('moveend', this._update, this); } else { this._map.off('move', this._limitedUpdate, this); } }, - + getAttribution: function() { return this.options.attribution; }, - + setOpacity: function(opacity) { this.options.opacity = opacity; - + this._setOpacity(opacity); - + // stupid webkit hack to force redrawing of tiles if (L.Browser.webkit) { for (i in this._tiles) { @@ -88,42 +88,42 @@ L.TileLayer = L.Class.extend({ } } }, - + _setOpacity: function(opacity) { if (opacity < 1) { L.DomUtil.setOpacity(this._container, opacity); } }, - + _initContainer: function() { var tilePane = this._map.getPanes().tilePane, first = tilePane.firstChild; - + if (!this._container || tilePane.empty) { this._container = L.DomUtil.create('div', 'leaflet-layer'); - + if (this._insertAtTheBottom && first) { tilePane.insertBefore(this._container, first); } else { tilePane.appendChild(this._container); } - + this._setOpacity(this.options.opacity); } }, - + _reset: function(clearOldContainer) { this._tiles = {}; if (clearOldContainer && this._container) - this._container.innerHTML = ""; + this._container.innerHTML = ""; this._initContainer(); this._container.innerHTML = ''; }, - + _update: function() { var bounds = this._map.getPixelBounds(), tileSize = this.options.tileSize; - + var nwTilePoint = new L.Point( Math.floor(bounds.min.x / tileSize), Math.floor(bounds.min.y / tileSize)), @@ -131,74 +131,73 @@ L.TileLayer = L.Class.extend({ Math.floor(bounds.max.x / tileSize), Math.floor(bounds.max.y / tileSize)), tileBounds = new L.Bounds(nwTilePoint, seTilePoint); - + this._addTilesFromCenterOut(tileBounds); - + if (this.options.unloadInvisibleTiles) { this._removeOtherTiles(tileBounds); } }, - + _addTilesFromCenterOut: function(bounds) { var queue = [], center = bounds.getCenter(); - + for (var j = bounds.min.y; j <= bounds.max.y; j++) { - for (var i = bounds.min.x; i <= bounds.max.x; i++) { + for (var i = bounds.min.x; i <= bounds.max.x; i++) { if ((i + ':' + j) in this._tiles) { continue; } queue.push(new L.Point(i, j)); } } - + // load tiles in order of their distance to center queue.sort(function(a, b) { return a.distanceTo(center) - b.distanceTo(center); }); - + var fragment = document.createDocumentFragment(); - + this._tilesToLoad = queue.length; for (var k = 0, len = this._tilesToLoad; k < len; k++) { this._addTile(queue[k], fragment); } - + this._container.appendChild(fragment); }, - + _removeOtherTiles: function(bounds) { - var kArr, x, y, key; - + var kArr, x, y, key, tile; + for (key in this._tiles) { if (this._tiles.hasOwnProperty(key)) { kArr = key.split(':'); x = parseInt(kArr[0], 10); y = parseInt(kArr[1], 10); - + // remove tile if it's out of bounds if (x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) { - - var tile = this._tiles[key]; + + tile = this._tiles[key]; this.fire("tileunload", {tile: tile, url: tile.src}); - + // evil, don't do this! crashes Android 3, produces load errors, doesn't solve memory leaks - // this._tiles[key].src = ''; - + // this._tiles[key].src = ''; + if (tile.parentNode == this._container) { this._container.removeChild(tile); } - // could be tile...? delete this._tiles[key]; } } - } + } }, - + _addTile: function(tilePoint, container) { var tilePos = this._getTilePos(tilePoint), zoom = this._map.getZoom(), key = tilePoint.x + ':' + tilePoint.y, tileLimit = (1 << zoom); - + // wrap tile coordinates if (!this.options.continuousWorld) { if (!this.options.noWrap) { @@ -207,37 +206,37 @@ L.TileLayer = L.Class.extend({ this._tilesToLoad--; return; } - + if (tilePoint.y < 0 || tilePoint.y >= tileLimit) { this._tilesToLoad--; return; } } - + // create tile var tile = this._createTile(); L.DomUtil.setPosition(tile, tilePos); - + this._tiles[key] = tile; - + if (this.options.scheme == 'tms') { tilePoint.y = tileLimit - tilePoint.y - 1; } this._loadTile(tile, tilePoint, zoom); - + container.appendChild(tile); }, - + _getTilePos: function(tilePoint) { var origin = this._map.getPixelOrigin(), tileSize = this.options.tileSize; - + return tilePoint.multiplyBy(tileSize).subtract(origin); }, - + // image-specific code (override to implement e.g. Canvas or SVG tile layer) - + getTileUrl: function(tilePoint, zoom) { var subdomains = this.options.subdomains, s = this.options.subdomains[(tilePoint.x + tilePoint.y) % subdomains.length]; @@ -248,47 +247,47 @@ L.TileLayer = L.Class.extend({ .replace('{x}', tilePoint.x) .replace('{y}', tilePoint.y); }, - + _createTileProto: function() { this._tileImg = L.DomUtil.create('img', 'leaflet-tile'); this._tileImg.galleryimg = 'no'; - + var tileSize = this.options.tileSize; this._tileImg.style.width = tileSize + 'px'; this._tileImg.style.height = tileSize + 'px'; }, - + _createTile: function() { var tile = this._tileImg.cloneNode(false); tile.onselectstart = tile.onmousemove = L.Util.falseFn; return tile; }, - + _loadTile: function(tile, tilePoint, zoom) { tile._layer = this; tile.onload = this._tileOnLoad; tile.onerror = this._tileOnError; tile.src = this.getTileUrl(tilePoint, zoom); }, - + _tileOnLoad: function(e) { var layer = this._layer; - - this.className += ' leaflet-tile-loaded'; + + this.className += ' leaflet-tile-loaded'; layer.fire('tileload', {tile: this, url: this.src}); - + layer._tilesToLoad--; if (!layer._tilesToLoad) { layer.fire('load'); } }, - + _tileOnError: function(e) { var layer = this._layer; - + layer.fire('tileerror', {tile: this, url: this.src}); - + var newUrl = layer.options.errorTileUrl; if (newUrl) { this.src = newUrl;