Skip to content

Commit 220ff94

Browse files
mpulkki-mapboxmike-unearth
authored andcommitted
Change the type of tile id key to string (mapbox#8979)
* Change tile id key type from number to string
1 parent 42fee37 commit 220ff94

9 files changed

+261
-110
lines changed

src/geo/transform.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ class Transform {
5050
_maxPitch: number;
5151
_center: LngLat;
5252
_constraining: boolean;
53-
_posMatrixCache: {[number]: Float32Array};
54-
_alignedPosMatrixCache: {[number]: Float32Array};
53+
_posMatrixCache: {[string]: Float32Array};
54+
_alignedPosMatrixCache: {[string]: Float32Array};
5555

5656
constructor(minZoom: ?number, maxZoom: ?number, minPitch: ?number, maxPitch: ?number, renderWorldCopies: boolean | void) {
5757
this.tileSize = 512; // constant

src/render/painter.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class Painter {
103103
viewportSegments: SegmentVector;
104104
quadTriangleIndexBuffer: IndexBuffer;
105105
tileBorderIndexBuffer: IndexBuffer;
106-
_tileClippingMaskIDs: { [number]: number };
106+
_tileClippingMaskIDs: { [string]: number };
107107
stencilClearMode: StencilMode;
108108
style: Style;
109109
options: PainterOptions;

src/source/source_cache.js

+95-34
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,20 @@ class SourceCache extends Evented {
4444
_source: Source;
4545
_sourceLoaded: boolean;
4646
_sourceErrored: boolean;
47-
_tiles: {[any]: Tile};
47+
_tiles: {[string]: Tile};
4848
_prevLng: number | void;
4949
_cache: TileCache;
5050
_timers: {[any]: TimeoutID};
5151
_cacheTimers: {[any]: TimeoutID};
5252
_maxTileCacheSize: ?number;
5353
_paused: boolean;
5454
_shouldReloadOnResume: boolean;
55-
_coveredTiles: {[any]: boolean};
55+
_coveredTiles: {[string]: boolean};
5656
transform: Transform;
57-
_isIdRenderable: (id: number, symbolLayer?: boolean) => boolean;
57+
_isIdRenderable: (id: string, symbolLayer?: boolean) => boolean;
5858
used: boolean;
5959
_state: SourceFeatureState;
60+
_loadedParentTiles: {[string]: ?Tile};
6061

6162
static maxUnderzooming: number;
6263
static maxOverzooming: number;
@@ -93,6 +94,7 @@ class SourceCache extends Evented {
9394
this._timers = {};
9495
this._cacheTimers = {};
9596
this._maxTileCacheSize = null;
97+
this._loadedParentTiles = {};
9698

9799
this._coveredTiles = {};
98100
this._state = new SourceFeatureState();
@@ -179,25 +181,25 @@ class SourceCache extends Evented {
179181
/**
180182
* Return all tile ids ordered with z-order, and cast to numbers
181183
*/
182-
getIds(): Array<number> {
183-
return Object.keys(this._tiles).map(Number).sort(compareKeyZoom);
184+
getIds(): Array<string> {
185+
return (Object.values(this._tiles): any).map((tile: Tile) => tile.tileID).sort(compareTileId).map(id => id.key);
184186
}
185187

186-
getRenderableIds(symbolLayer?: boolean): Array<number> {
187-
const ids = [];
188+
getRenderableIds(symbolLayer?: boolean): Array<string> {
189+
const renderables: Array<Tile> = [];
188190
for (const id in this._tiles) {
189-
if (this._isIdRenderable(+id, symbolLayer)) ids.push(+id);
191+
if (this._isIdRenderable(id, symbolLayer)) renderables.push(this._tiles[id]);
190192
}
191193
if (symbolLayer) {
192-
return ids.sort((a_, b_) => {
193-
const a = this._tiles[a_].tileID;
194-
const b = this._tiles[b_].tileID;
194+
return renderables.sort((a_: Tile, b_: Tile) => {
195+
const a = a_.tileID;
196+
const b = b_.tileID;
195197
const rotatedA = (new Point(a.canonical.x, a.canonical.y))._rotate(this.transform.angle);
196198
const rotatedB = (new Point(b.canonical.x, b.canonical.y))._rotate(this.transform.angle);
197199
return a.overscaledZ - b.overscaledZ || rotatedB.y - rotatedA.y || rotatedB.x - rotatedA.x;
198-
});
200+
}).map(tile => tile.tileID.key);
199201
}
200-
return ids.sort(compareKeyZoom);
202+
return renderables.map(tile => tile.tileID).sort(compareTileId).map(id => id.key);
201203
}
202204

203205
hasRenderableParent(tileID: OverscaledTileID) {
@@ -208,7 +210,7 @@ class SourceCache extends Evented {
208210
return false;
209211
}
210212

211-
_isIdRenderable(id: number, symbolLayer?: boolean) {
213+
_isIdRenderable(id: string, symbolLayer?: boolean) {
212214
return this._tiles[id] && this._tiles[id].hasData() &&
213215
!this._coveredTiles[id] && (symbolLayer || !this._tiles[id].holdingForFade());
214216
}
@@ -226,7 +228,7 @@ class SourceCache extends Evented {
226228
}
227229
}
228230

229-
_reloadTile(id: string | number, state: TileState) {
231+
_reloadTile(id: string, state: TileState) {
230232
const tile = this._tiles[id];
231233

232234
// this potentially does not address all underlying
@@ -245,7 +247,7 @@ class SourceCache extends Evented {
245247
this._loadTile(tile, this._tileLoaded.bind(this, tile, id, state));
246248
}
247249

248-
_tileLoaded(tile: Tile, id: string | number, previousState: TileState, err: ?Error) {
250+
_tileLoaded(tile: Tile, id: string, previousState: TileState, err: ?Error) {
249251
if (err) {
250252
tile.state = 'errored';
251253
if ((err: any).status !== 404) this._source.fire(new ErrorEvent(err, {tile}));
@@ -313,7 +315,7 @@ class SourceCache extends Evented {
313315
/**
314316
* Get a specific tile by id
315317
*/
316-
getTileByID(id: string | number): Tile {
318+
getTileByID(id: string): Tile {
317319
return this._tiles[id];
318320
}
319321

@@ -367,19 +369,33 @@ class SourceCache extends Evented {
367369
* Find a loaded parent of the given tile (up to minCoveringZoom)
368370
*/
369371
findLoadedParent(tileID: OverscaledTileID, minCoveringZoom: number): ?Tile {
372+
if (tileID.key in this._loadedParentTiles) {
373+
const parent = this._loadedParentTiles[tileID.key];
374+
if (parent && parent.tileID.overscaledZ >= minCoveringZoom) {
375+
return parent;
376+
} else {
377+
return null;
378+
}
379+
}
370380
for (let z = tileID.overscaledZ - 1; z >= minCoveringZoom; z--) {
371-
const parentKey = tileID.calculateScaledKey(z, true);
372-
const tile = this._tiles[parentKey];
373-
if (tile && tile.hasData()) {
381+
const parentTileID = tileID.scaledTo(z);
382+
const tile = this._getLoadedTile(parentTileID);
383+
if (tile) {
374384
return tile;
375385
}
376-
// TileCache ignores wrap in lookup.
377-
const parentWrappedKey = tileID.calculateScaledKey(z, false);
378-
const cachedTile = this._cache.getByKey(parentWrappedKey);
379-
if (cachedTile) return cachedTile;
380386
}
381387
}
382388

389+
_getLoadedTile(tileID: OverscaledTileID): ?Tile {
390+
const tile = this._tiles[tileID.key];
391+
if (tile && tile.hasData()) {
392+
return tile;
393+
}
394+
// TileCache ignores wrap in lookup.
395+
const cachedTile = this._cache.getByKey(tileID.wrapped().key);
396+
return cachedTile;
397+
}
398+
383399
/**
384400
* Resizes the tile cache based on the current viewport's size
385401
* or the maxTileCacheSize option passed during map creation
@@ -423,7 +439,7 @@ class SourceCache extends Evented {
423439
this._prevLng = lng;
424440

425441
if (wrapDelta) {
426-
const tiles = {};
442+
const tiles: {[string]: Tile} = {};
427443
for (const key in this._tiles) {
428444
const tile = this._tiles[key];
429445
tile.tileID = tile.tileID.unwrapTo(tile.tileID.wrap + wrapDelta);
@@ -489,12 +505,12 @@ class SourceCache extends Evented {
489505
const retain = this._updateRetainedTiles(idealTileIDs, zoom);
490506

491507
if (isRasterType(this._source.type)) {
492-
const parentsForFading = {};
508+
const parentsForFading: {[string]: OverscaledTileID} = {};
493509
const fadingTiles = {};
494510
const ids = Object.keys(retain);
495511
for (const id of ids) {
496512
const tileID = retain[id];
497-
assert(tileID.key === +id);
513+
assert(tileID.key === id);
498514

499515
const tile = this._tiles[id];
500516
if (!tile || tile.fadeEndTime && tile.fadeEndTime <= browser.now()) continue;
@@ -537,6 +553,9 @@ class SourceCache extends Evented {
537553
this._removeTile(tileID);
538554
}
539555
}
556+
557+
// Construct a cache of loaded parents
558+
this._updateLoadedParentTileCache();
540559
}
541560

542561
releaseSymbolFadeTiles() {
@@ -548,8 +567,8 @@ class SourceCache extends Evented {
548567
}
549568

550569
_updateRetainedTiles(idealTileIDs: Array<OverscaledTileID>, zoom: number): { [string]: OverscaledTileID} {
551-
const retain = {};
552-
const checked: {[number]: boolean } = {};
570+
const retain: {[string]: OverscaledTileID} = {};
571+
const checked: {[string]: boolean } = {};
553572
const minCoveringZoom = Math.max(zoom - SourceCache.maxOverzooming, this._source.minzoom);
554573
const maxCoveringZoom = Math.max(zoom + SourceCache.maxUnderzooming, this._source.minzoom);
555574

@@ -628,6 +647,43 @@ class SourceCache extends Evented {
628647
return retain;
629648
}
630649

650+
_updateLoadedParentTileCache() {
651+
this._loadedParentTiles = {};
652+
653+
for (const tileKey in this._tiles) {
654+
const path = [];
655+
let parentTile: ?Tile;
656+
let currentId = this._tiles[tileKey].tileID;
657+
658+
// Find the closest loaded ancestor by traversing the tile tree towards the root and
659+
// caching results along the way
660+
while (currentId.overscaledZ > 0) {
661+
662+
// Do we have a cached result from previous traversals?
663+
if (currentId.key in this._loadedParentTiles) {
664+
parentTile = this._loadedParentTiles[currentId.key];
665+
break;
666+
}
667+
668+
path.push(currentId.key);
669+
670+
// Is the parent loaded?
671+
const parentId = currentId.scaledTo(currentId.overscaledZ - 1);
672+
parentTile = this._getLoadedTile(parentId);
673+
if (parentTile) {
674+
break;
675+
}
676+
677+
currentId = parentId;
678+
}
679+
680+
// Cache the result of this traversal to all newly visited tiles
681+
for (const key of path) {
682+
this._loadedParentTiles[key] = parentTile;
683+
}
684+
}
685+
}
686+
631687
/**
632688
* Add a tile, given its coordinate, to the pyramid.
633689
* @private
@@ -666,7 +722,7 @@ class SourceCache extends Evented {
666722
return tile;
667723
}
668724

669-
_setTileReloadTimer(id: string | number, tile: Tile) {
725+
_setTileReloadTimer(id: string, tile: Tile) {
670726
if (id in this._timers) {
671727
clearTimeout(this._timers[id]);
672728
delete this._timers[id];
@@ -685,7 +741,7 @@ class SourceCache extends Evented {
685741
* Remove a tile, given its id, from the pyramid
686742
* @private
687743
*/
688-
_removeTile(id: string | number) {
744+
_removeTile(id: string) {
689745
const tile = this._tiles[id];
690746
if (!tile)
691747
return;
@@ -846,7 +902,7 @@ class SourceCache extends Evented {
846902
* Sets the set of keys that the tile depends on. This allows tiles to
847903
* be reloaded when their dependencies change.
848904
*/
849-
setDependencies(tileKey: string | number, namespace: string, dependencies: Array<string>) {
905+
setDependencies(tileKey: string, namespace: string, dependencies: Array<string>) {
850906
const tile = this._tiles[tileKey];
851907
if (tile) {
852908
tile.setDependencies(namespace, dependencies);
@@ -870,8 +926,13 @@ class SourceCache extends Evented {
870926
SourceCache.maxOverzooming = 10;
871927
SourceCache.maxUnderzooming = 3;
872928

873-
function compareKeyZoom(a, b) {
874-
return ((a % 32) - (b % 32)) || (b - a);
929+
function compareTileId(a: OverscaledTileID, b: OverscaledTileID): number {
930+
// Different copies of the world are sorted based on their distance to the center.
931+
// Wrap values are converted to unsigned distances by reserving odd number for copies
932+
// with negative wrap and even numbers for copies with positive wrap.
933+
const aWrap = Math.abs(a.wrap * 2) - +(a.wrap < 0);
934+
const bWrap = Math.abs(b.wrap * 2) - +(b.wrap < 0);
935+
return a.overscaledZ - b.overscaledZ || bWrap - aWrap || b.canonical.y - a.canonical.y || b.canonical.x - a.canonical.x;
875936
}
876937

877938
function isRasterType(type) {

src/source/tile_cache.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import type Tile from './tile';
1212
*/
1313
class TileCache {
1414
max: number;
15-
data: {[key: number | string]: Array<{ value: Tile, timeout: ?TimeoutID}>};
16-
order: Array<number>;
15+
data: {[key: string]: Array<{ value: Tile, timeout: ?TimeoutID}>};
16+
order: Array<string>;
1717
onRemove: (element: Tile) => void;
1818
/**
1919
* @param {number} max number of permitted values
@@ -110,7 +110,7 @@ class TileCache {
110110
/*
111111
* Get and remove the value with the specified key.
112112
*/
113-
_getAndRemoveByKey(key: number): ?Tile {
113+
_getAndRemoveByKey(key: string): ?Tile {
114114
const data = this.data[key].shift();
115115
if (data.timeout) clearTimeout(data.timeout);
116116

@@ -125,7 +125,7 @@ class TileCache {
125125
/*
126126
* Get the value with the specified (wrapped tile) key.
127127
*/
128-
getByKey(key: number): ?Tile {
128+
getByKey(key: string): ?Tile {
129129
const data = this.data[key];
130130
return data ? data[0].value : null;
131131
}

0 commit comments

Comments
 (0)