-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
change tile loading logic to match native #5119
Changes from 8 commits
0b1a27e
6515276
49747ce
9b444fb
3a34eca
9eb352f
b4734db
bc58f97
f6d3d50
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -212,6 +212,8 @@ class SourceCache extends Evented { | |
if (err) { | ||
tile.state = 'errored'; | ||
if (err.status !== 404) this._source.fire('error', {tile: tile, error: err}); | ||
// continue to try loading parent/children tiles if a tile doesn't exist (404) | ||
else this.update(this.transform); | ||
return; | ||
} | ||
|
||
|
@@ -333,34 +335,19 @@ class SourceCache extends Evented { | |
this.transform = transform; | ||
if (!this._sourceLoaded || this._paused) { return; } | ||
|
||
let i; | ||
let coord; | ||
let tile; | ||
let parentTile; | ||
|
||
this.updateCacheSize(transform); | ||
|
||
// Determine the overzooming/underzooming amounts. | ||
const zoom = (this._source.roundZoom ? Math.round : Math.floor)(this.getZoom(transform)); | ||
const minCoveringZoom = Math.max(zoom - SourceCache.maxOverzooming, this._source.minzoom); | ||
const maxCoveringZoom = Math.max(zoom + SourceCache.maxUnderzooming, this._source.minzoom); | ||
|
||
// Retain is a list of tiles that we shouldn't delete, even if they are not | ||
// the most ideal tile for the current viewport. This may include tiles like | ||
// parent or child tiles that are *already* loaded. | ||
const retain = {}; | ||
|
||
// Covered is a list of retained tiles who's areas are full covered by other, | ||
// Covered is a list of retained tiles who's areas are fully covered by other, | ||
// better, retained tiles. They are not drawn separately. | ||
this._coveredTiles = {}; | ||
|
||
let visibleCoords; | ||
let idealTileCoords; | ||
if (!this.used) { | ||
visibleCoords = []; | ||
idealTileCoords = []; | ||
} else if (this._source.coord) { | ||
visibleCoords = transform.getVisibleWrappedCoordinates((this._source.coord: any)); | ||
idealTileCoords = transform.getVisibleWrappedCoordinates((this._source.coord: any)); | ||
} else { | ||
visibleCoords = transform.coveringTiles({ | ||
idealTileCoords = transform.coveringTiles({ | ||
tileSize: this._source.tileSize, | ||
minzoom: this._source.minzoom, | ||
maxzoom: this._source.maxzoom, | ||
|
@@ -369,37 +356,28 @@ class SourceCache extends Evented { | |
}); | ||
|
||
if (this._source.hasTile) { | ||
visibleCoords = visibleCoords.filter((coord) => (this._source.hasTile: any)(coord)); | ||
idealTileCoords = idealTileCoords.filter((coord) => (this._source.hasTile: any)(coord)); | ||
} | ||
} | ||
|
||
for (i = 0; i < visibleCoords.length; i++) { | ||
coord = visibleCoords[i]; | ||
tile = this._addTile(coord); | ||
|
||
retain[coord.id] = true; | ||
|
||
if (tile.hasData()) | ||
continue; | ||
// Determine the overzooming/underzooming amounts. | ||
const zoom = (this._source.roundZoom ? Math.round : Math.floor)(this.getZoom(transform)); | ||
const minCoveringZoom = Math.max(zoom - SourceCache.maxOverzooming, this._source.minzoom); | ||
const maxCoveringZoom = Math.max(zoom + SourceCache.maxUnderzooming, this._source.minzoom); | ||
|
||
// The tile we require is not yet loaded. | ||
// Retain child or parent tiles that cover the same area. | ||
if (!this._findLoadedChildren(coord, maxCoveringZoom, retain)) { | ||
parentTile = this.findLoadedParent(coord, minCoveringZoom, retain); | ||
if (parentTile) { | ||
this._addTile(parentTile.coord); | ||
} | ||
} | ||
} | ||
// Retain is a list of tiles that we shouldn't delete, even if they are not | ||
// the most ideal tile for the current viewport. This may include tiles like | ||
// parent or child tiles that are *already* loaded. | ||
const retain = this._updateRetainedTiles(idealTileCoords, zoom); | ||
|
||
const parentsForFading = {}; | ||
|
||
if (isRasterType(this._source.type)) { | ||
const ids = Object.keys(retain); | ||
for (let k = 0; k < ids.length; k++) { | ||
const id = ids[k]; | ||
coord = TileCoord.fromID(+id); | ||
tile = this._tiles[id]; | ||
const coord = TileCoord.fromID(+id); | ||
const tile = this._tiles[id]; | ||
if (!tile) continue; | ||
|
||
// If the drawRasterTile has never seen this tile, then | ||
|
@@ -410,7 +388,7 @@ class SourceCache extends Evented { | |
if (this._findLoadedChildren(coord, maxCoveringZoom, retain)) { | ||
retain[id] = true; | ||
} | ||
parentTile = this.findLoadedParent(coord, minCoveringZoom, parentsForFading); | ||
const parentTile = this.findLoadedParent(coord, minCoveringZoom, parentsForFading); | ||
if (parentTile) { | ||
this._addTile(parentTile.coord); | ||
} | ||
|
@@ -428,14 +406,99 @@ class SourceCache extends Evented { | |
for (fadedParent in parentsForFading) { | ||
retain[fadedParent] = true; | ||
} | ||
|
||
// Remove the tiles we don't need anymore. | ||
const remove = util.keysDifference(this._tiles, retain); | ||
for (i = 0; i < remove.length; i++) { | ||
for (let i = 0; i < remove.length; i++) { | ||
this._removeTile(remove[i]); | ||
} | ||
} | ||
|
||
_updateRetainedTiles(idealTileCoords: Array<TileCoord>, zoom: number) { | ||
let i, coord, tile, covered; | ||
|
||
const retain = {}; | ||
const checked = {}; | ||
const minCoveringZoom = Math.max(zoom - SourceCache.maxOverzooming, this._source.minzoom); | ||
|
||
|
||
for (i = 0; i < idealTileCoords.length; i++) { | ||
coord = idealTileCoords[i]; | ||
tile = this._addTile(coord); | ||
let parentWasRequested = false; | ||
if (tile.hasData()) { | ||
retain[coord.id] = true; | ||
} else { | ||
// The tile we require is not yet loaded or does not exist. | ||
// We are now attempting to load child and parent tiles. | ||
|
||
// As we descend up and down the tile pyramid of the ideal tile, we check whether the parent | ||
// tile has been previously requested (and errored in this case due to the previous conditional) | ||
// in order to determine if we need to request its parent. | ||
parentWasRequested = tile.wasRequested(); | ||
|
||
// The tile isn't loaded yet, but retain it anyway because it's an ideal tile. | ||
retain[coord.id] = true; | ||
covered = true; | ||
const overscaledZ = zoom + 1; | ||
if (overscaledZ > this._source.maxzoom) { | ||
// We're looking for an overzoomed child tile. | ||
const childCoord = coord.children(this._source.maxzoom)[0]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, I don't follow what's happening in this branch -- why do we only get the first child tile? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overzoomed children are different from regular children. While regular children increase the z by one, and enumerate all four quadrants, an "overzoomed child" is represented with the same z/x/y as its parent, except that the |
||
const childTile = this.getTile(childCoord); | ||
if (!!childTile && childTile.hasData()) { | ||
retain[childCoord.id] = true; | ||
} else { | ||
covered = false; | ||
} | ||
} else { | ||
// Check all four actual child tiles. | ||
const children = coord.children(this._source.maxzoom); | ||
for (let j = 0; j < children.length; j++) { | ||
const childCoord = children[j]; | ||
const childTile = childCoord ? this.getTile(childCoord) : null; | ||
if (!!childTile && childTile.hasData()) { | ||
retain[childCoord.id] = true; | ||
} else { | ||
covered = false; | ||
} | ||
} | ||
} | ||
|
||
if (!covered) { | ||
|
||
// We couldn't find child tiles that entirely cover the ideal tile. | ||
for (let overscaledZ = zoom - 1; overscaledZ >= minCoveringZoom; --overscaledZ) { | ||
|
||
const parentId = coord.scaledTo(overscaledZ); | ||
if (checked[parentId.id]) { | ||
// Break parent tile ascent, this route has been previously checked by another child. | ||
break; | ||
} else { | ||
checked[parentId.id] = true; | ||
} | ||
|
||
|
||
tile = this.getTile(parentId); | ||
if (!tile && parentWasRequested) { | ||
tile = this._addTile(parentId); | ||
} | ||
|
||
if (tile) { | ||
retain[parentId.id] = true; | ||
// Save the current values, since they're the parent of the next iteration | ||
// of the parent tile ascent loop. | ||
parentWasRequested = tile.wasRequested(); | ||
if (tile.hasData()) { | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
return retain; | ||
} | ||
|
||
/** | ||
* Add a tile, given its coordinate, to the pyramid. | ||
* @private | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add flow types for these?