- var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
-
- if (!noUpdate || tileZoomChanged) {
-
- this._tileZoom = tileZoom;
-
- if (this._abortLoading) {
- this._abortLoading();
- }
-
- this._updateLevels();
- this._resetGrid();
-
- if (tileZoom !== undefined) {
- this._update(center);
- }
-
- if (!noPrune) {
- this._pruneTiles();
- }
-
- // Flag to prevent _updateOpacity from pruning tiles during
- // a zoom anim or a pinch gesture
- this._noPrune = !!noPrune;
- }
-
- this._setZoomTransforms(center, zoom);
- },
-
- _setZoomTransforms: function (center, zoom) {
- for (var i in this._levels) {
- this._setZoomTransform(this._levels[i], center, zoom);
- }
- },
-
- _setZoomTransform: function (level, center, zoom) {
- var scale = this._map.getZoomScale(zoom, level.zoom),
- translate = level.origin.multiplyBy(scale)
- .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
-
- if (L.Browser.any3d) {
- L.DomUtil.setTransform(level.el, translate, scale);
- } else {
- L.DomUtil.setPosition(level.el, translate);
- }
- },
-
- _resetGrid: function () {
- var map = this._map,
- crs = map.options.crs,
- tileSize = this._tileSize = this.getTileSize(),
- tileZoom = this._tileZoom;
-
- var bounds = this._map.getPixelWorldBounds(this._tileZoom);
- if (bounds) {
- this._globalTileRange = this._pxBoundsToTileRange(bounds);
- }
-
- this._wrapX = crs.wrapLng && !this.options.noWrap && [
- Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
- Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
- ];
- this._wrapY = crs.wrapLat && !this.options.noWrap && [
- Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
- Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
- ];
- },
-
- _onMoveEnd: function () {
- if (!this._map || this._map._animatingZoom) { return; }
-
- this._update();
- },
-
- _getTiledPixelBounds: function (center) {
- var map = this._map,
- mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
- scale = map.getZoomScale(mapZoom, this._tileZoom),
- pixelCenter = map.project(center, this._tileZoom).floor(),
- halfSize = map.getSize().divideBy(scale * 2);
-
- return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
- },
-
- // Private method to load tiles in the grid's active zoom level according to map bounds
- _update: function (center) {
- var map = this._map;
- if (!map) { return; }
- var zoom = map.getZoom();
-
- if (center === undefined) { center = map.getCenter(); }
- if (this._tileZoom === undefined) { return; } // if out of minzoom/maxzoom
-
- var pixelBounds = this._getTiledPixelBounds(center),
- tileRange = this._pxBoundsToTileRange(pixelBounds),
- tileCenter = tileRange.getCenter(),
- queue = [],
- margin = this.options.keepBuffer,
- noPruneRange = new L.Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
- tileRange.getTopRight().add([margin, -margin]));
-
- for (var key in this._tiles) {
- var c = this._tiles[key].coords;
- if (c.z !== this._tileZoom || !noPruneRange.contains(L.point(c.x, c.y))) {
- this._tiles[key].current = false;
- }
- }
-
- // _update just loads more tiles. If the tile zoom level differs too much
- // from the map's, let _setView reset levels and prune old tiles.
- if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
-
- // create a queue of coordinates to load tiles from
- for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
- for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
- var coords = new L.Point(i, j);
- coords.z = this._tileZoom;
-
- if (!this._isValidTile(coords)) { continue; }
-
- var tile = this._tiles[this._tileCoordsToKey(coords)];
- if (tile) {
- tile.current = true;
- } else {
- queue.push(coords);
- }
- }
- }
-
- // sort tile queue to load tiles in order of their distance to center
- queue.sort(function (a, b) {
- return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
- });
-
- if (queue.length !== 0) {
- // if its the first batch of tiles to load
- if (!this._loading) {
- this._loading = true;
- // @event loading: Event
- // Fired when the grid layer starts loading tiles.
- this.fire('loading');
- }
-
- // create DOM fragment to append tiles in one batch
- var fragment = document.createDocumentFragment();
-
- for (i = 0; i < queue.length; i++) {
- this._addTile(queue[i], fragment);
- }
-
- this._level.el.appendChild(fragment);
- }
- },
-
- _isValidTile: function (coords) {
- var crs = this._map.options.crs;
-
- if (!crs.infinite) {
- // don't load tile if it's out of bounds and not wrapped
- var bounds = this._globalTileRange;
- if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
- (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
- }
-
- if (!this.options.bounds) { return true; }
-
- // don't load tile if it doesn't intersect the bounds in options
- var tileBounds = this._tileCoordsToBounds(coords);
- return L.latLngBounds(this.options.bounds).overlaps(tileBounds);
- },
-
- _keyToBounds: function (key) {
- return this._tileCoordsToBounds(this._keyToTileCoords(key));
- },
-
- // converts tile coordinates to its geographical bounds
- _tileCoordsToBounds: function (coords) {
-
- var map = this._map,
- tileSize = this.getTileSize(),
-
- nwPoint = coords.scaleBy(tileSize),
- sePoint = nwPoint.add(tileSize),
-
- nw = map.unproject(nwPoint, coords.z),
- se = map.unproject(sePoint, coords.z);
-
- if (!this.options.noWrap) {
- nw = map.wrapLatLng(nw);
- se = map.wrapLatLng(se);
- }
-
- return new L.LatLngBounds(nw, se);
- },
-
- // converts tile coordinates to key for the tile cache
- _tileCoordsToKey: function (coords) {
- return coords.x + ':' + coords.y + ':' + coords.z;
- },
-
- // converts tile cache key to coordinates
- _keyToTileCoords: function (key) {
- var k = key.split(':'),
- coords = new L.Point(+k[0], +k[1]);
- coords.z = +k[2];
- return coords;
- },
-
- _removeTile: function (key) {
- var tile = this._tiles[key];
- if (!tile) { return; }
-
- L.DomUtil.remove(tile.el);
-
- delete this._tiles[key];
-
- // @event tileunload: TileEvent
- // Fired when a tile is removed (e.g. when a tile goes off the screen).
- this.fire('tileunload', {
- tile: tile.el,
- coords: this._keyToTileCoords(key)
- });
- },
-
- _initTile: function (tile) {
- L.DomUtil.addClass(tile, 'leaflet-tile');
-
- var tileSize = this.getTileSize();
- tile.style.width = tileSize.x + 'px';
- tile.style.height = tileSize.y + 'px';
-
- tile.onselectstart = L.Util.falseFn;
- tile.onmousemove = L.Util.falseFn;
-
- // update opacity on tiles in IE7-8 because of filter inheritance problems
- if (L.Browser.ielt9 && this.options.opacity < 1) {
- L.DomUtil.setOpacity(tile, this.options.opacity);
- }
-
- // without this hack, tiles disappear after zoom on Chrome for Android
- // https://github.com/Leaflet/Leaflet/issues/2078
- if (L.Browser.android && !L.Browser.android23) {
- tile.style.WebkitBackfaceVisibility = 'hidden';
- }
- },
-
- _addTile: function (coords, container) {
- var tilePos = this._getTilePos(coords),
- key = this._tileCoordsToKey(coords);
-
- var tile = this.createTile(this._wrapCoords(coords), L.bind(this._tileReady, this, coords));
-
- this._initTile(tile);
-
- // if createTile is defined with a second argument ("done" callback),
- // we know that tile is async and will be ready later; otherwise
- if (this.createTile.length < 2) {
- // mark tile as ready, but delay one frame for opacity animation to happen
- L.Util.requestAnimFrame(L.bind(this._tileReady, this, coords, null, tile));
- }
-
- L.DomUtil.setPosition(tile, tilePos);
-
- // save tile in cache
- this._tiles[key] = {
- el: tile,
- coords: coords,
- current: true
- };
-
- container.appendChild(tile);
- // @event tileloadstart: TileEvent
- // Fired when a tile is requested and starts loading.
- this.fire('tileloadstart', {
- tile: tile,
- coords: coords
- });
- },
-
- _tileReady: function (coords, err, tile) {
- if (!this._map) { return; }
-
- if (err) {
- // @event tileerror: TileErrorEvent
- // Fired when there is an error loading a tile.
- this.fire('tileerror', {
- error: err,
- tile: tile,
- coords: coords
- });
- }
-
- var key = this._tileCoordsToKey(coords);
-
- tile = this._tiles[key];
- if (!tile) { return; }
-
- tile.loaded = +new Date();
- if (this._map._fadeAnimated) {
- L.DomUtil.setOpacity(tile.el, 0);
- L.Util.cancelAnimFrame(this._fadeFrame);
- this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
- } else {
- tile.active = true;
- this._pruneTiles();
- }
-
- L.DomUtil.addClass(tile.el, 'leaflet-tile-loaded');
-
- // @event tileload: TileEvent
- // Fired when a tile loads.
- this.fire('tileload', {
- tile: tile.el,
- coords: coords
- });
-
- if (this._noTilesToLoad()) {
- this._loading = false;
- // @event load: Event
- // Fired when the grid layer loaded all visible tiles.
- this.fire('load');
-
- if (L.Browser.ielt9 || !this._map._fadeAnimated) {
- L.Util.requestAnimFrame(this._pruneTiles, this);
- } else {
- // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
- // to trigger a pruning.
- setTimeout(L.bind(this._pruneTiles, this), 250);
- }
- }
- },
-
- _getTilePos: function (coords) {
- return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
- },
-
- _wrapCoords: function (coords) {
- var newCoords = new L.Point(
- this._wrapX ? L.Util.wrapNum(coords.x, this._wrapX) : coords.x,
- this._wrapY ? L.Util.wrapNum(coords.y, this._wrapY) : coords.y);
- newCoords.z = coords.z;
- return newCoords;
- },
-
- _pxBoundsToTileRange: function (bounds) {
- var tileSize = this.getTileSize();
- return new L.Bounds(
- bounds.min.unscaleBy(tileSize).floor(),
- bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
- },
-
- _noTilesToLoad: function () {
- for (var key in this._tiles) {
- if (!this._tiles[key].loaded) { return false; }
- }
- return true;
- }
-});
-
-// @factory L.gridLayer(options?: GridLayer options)
-// Creates a new instance of GridLayer with the supplied options.
-L.gridLayer = function (options) {
- return new L.GridLayer(options);
-};