@@ -44,19 +44,20 @@ class SourceCache extends Evented {
44
44
_source : Source ;
45
45
_sourceLoaded : boolean ;
46
46
_sourceErrored : boolean ;
47
- _tiles : { [ any ] : Tile } ;
47
+ _tiles : { [ string ] : Tile } ;
48
48
_prevLng: number | void ;
49
49
_cache: TileCache ;
50
50
_timers: { [ any ] : TimeoutID } ;
51
51
_cacheTimers: { [ any ] : TimeoutID } ;
52
52
_maxTileCacheSize: ?number ;
53
53
_paused: boolean ;
54
54
_shouldReloadOnResume: boolean ;
55
- _coveredTiles: { [ any ] : boolean } ;
55
+ _coveredTiles: { [ string ] : boolean } ;
56
56
transform: Transform ;
57
- _isIdRenderable: ( id : number , symbolLayer ? : boolean ) => boolean ;
57
+ _isIdRenderable: ( id : string , symbolLayer ? : boolean ) => boolean ;
58
58
used: boolean ;
59
59
_state: SourceFeatureState ;
60
+ _loadedParentTiles: { [ string ] : ?Tile } ;
60
61
61
62
static maxUnderzooming: number ;
62
63
static maxOverzooming: number ;
@@ -93,6 +94,7 @@ class SourceCache extends Evented {
93
94
this . _timers = { } ;
94
95
this . _cacheTimers = { } ;
95
96
this . _maxTileCacheSize = null ;
97
+ this . _loadedParentTiles = { } ;
96
98
97
99
this . _coveredTiles = { } ;
98
100
this . _state = new SourceFeatureState ( ) ;
@@ -179,25 +181,25 @@ class SourceCache extends Evented {
179
181
/**
180
182
* Return all tile ids ordered with z-order, and cast to numbers
181
183
*/
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 ) ;
184
186
}
185
187
186
- getRenderableIds ( symbolLayer ? : boolean ) : Array < number > {
187
- const ids = [ ] ;
188
+ getRenderableIds ( symbolLayer ? : boolean ) : Array < string > {
189
+ const renderables : Array < Tile > = [ ] ;
188
190
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 ] ) ;
190
192
}
191
193
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 ;
195
197
const rotatedA = ( new Point ( a . canonical . x , a . canonical . y ) ) . _rotate ( this . transform . angle ) ;
196
198
const rotatedB = ( new Point ( b . canonical . x , b . canonical . y ) ) . _rotate ( this . transform . angle ) ;
197
199
return a . overscaledZ - b . overscaledZ || rotatedB . y - rotatedA . y || rotatedB . x - rotatedA . x ;
198
- } ) ;
200
+ } ) . map ( tile => tile . tileID . key ) ;
199
201
}
200
- return ids . sort ( compareKeyZoom ) ;
202
+ return renderables . map ( tile => tile . tileID ) . sort ( compareTileId ) . map ( id => id . key ) ;
201
203
}
202
204
203
205
hasRenderableParent ( tileID : OverscaledTileID ) {
@@ -208,7 +210,7 @@ class SourceCache extends Evented {
208
210
return false ;
209
211
}
210
212
211
- _isIdRenderable ( id : number , symbolLayer ? : boolean ) {
213
+ _isIdRenderable ( id : string , symbolLayer ? : boolean ) {
212
214
return this . _tiles [ id ] && this . _tiles [ id ] . hasData ( ) &&
213
215
! this . _coveredTiles [ id ] && ( symbolLayer || ! this . _tiles [ id ] . holdingForFade ( ) ) ;
214
216
}
@@ -226,7 +228,7 @@ class SourceCache extends Evented {
226
228
}
227
229
}
228
230
229
- _reloadTile ( id : string | number , state : TileState ) {
231
+ _reloadTile ( id : string , state : TileState ) {
230
232
const tile = this . _tiles [ id ] ;
231
233
232
234
// this potentially does not address all underlying
@@ -245,7 +247,7 @@ class SourceCache extends Evented {
245
247
this . _loadTile ( tile , this . _tileLoaded . bind ( this , tile , id , state ) ) ;
246
248
}
247
249
248
- _tileLoaded ( tile : Tile , id : string | number , previousState : TileState , err : ?Error ) {
250
+ _tileLoaded ( tile : Tile , id : string , previousState : TileState , err : ?Error ) {
249
251
if ( err ) {
250
252
tile . state = 'errored' ;
251
253
if ( ( err : any ) . status !== 404 ) this . _source . fire ( new ErrorEvent ( err , { tile} ) ) ;
@@ -313,7 +315,7 @@ class SourceCache extends Evented {
313
315
/**
314
316
* Get a specific tile by id
315
317
*/
316
- getTileByID ( id : string | number ) : Tile {
318
+ getTileByID ( id : string ) : Tile {
317
319
return this . _tiles [ id ] ;
318
320
}
319
321
@@ -367,19 +369,33 @@ class SourceCache extends Evented {
367
369
* Find a loaded parent of the given tile (up to minCoveringZoom)
368
370
*/
369
371
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
+ }
370
380
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 ) {
374
384
return tile ;
375
385
}
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 ;
380
386
}
381
387
}
382
388
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
+
383
399
/**
384
400
* Resizes the tile cache based on the current viewport's size
385
401
* or the maxTileCacheSize option passed during map creation
@@ -423,7 +439,7 @@ class SourceCache extends Evented {
423
439
this . _prevLng = lng ;
424
440
425
441
if ( wrapDelta ) {
426
- const tiles = { } ;
442
+ const tiles : { [ string ] : Tile } = { } ;
427
443
for ( const key in this . _tiles ) {
428
444
const tile = this . _tiles [ key ] ;
429
445
tile . tileID = tile . tileID . unwrapTo ( tile . tileID . wrap + wrapDelta ) ;
@@ -489,12 +505,12 @@ class SourceCache extends Evented {
489
505
const retain = this . _updateRetainedTiles ( idealTileIDs , zoom ) ;
490
506
491
507
if ( isRasterType ( this . _source . type ) ) {
492
- const parentsForFading = { } ;
508
+ const parentsForFading : { [ string ] : OverscaledTileID } = { } ;
493
509
const fadingTiles = { } ;
494
510
const ids = Object . keys ( retain ) ;
495
511
for ( const id of ids ) {
496
512
const tileID = retain [ id ] ;
497
- assert ( tileID . key === + id ) ;
513
+ assert ( tileID . key === id ) ;
498
514
499
515
const tile = this . _tiles [ id ] ;
500
516
if ( ! tile || tile . fadeEndTime && tile . fadeEndTime <= browser . now ( ) ) continue ;
@@ -537,6 +553,9 @@ class SourceCache extends Evented {
537
553
this . _removeTile ( tileID ) ;
538
554
}
539
555
}
556
+
557
+ // Construct a cache of loaded parents
558
+ this . _updateLoadedParentTileCache ( ) ;
540
559
}
541
560
542
561
releaseSymbolFadeTiles ( ) {
@@ -548,8 +567,8 @@ class SourceCache extends Evented {
548
567
}
549
568
550
569
_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 } = { } ;
553
572
const minCoveringZoom = Math . max ( zoom - SourceCache . maxOverzooming , this . _source . minzoom ) ;
554
573
const maxCoveringZoom = Math . max ( zoom + SourceCache . maxUnderzooming , this . _source . minzoom ) ;
555
574
@@ -628,6 +647,43 @@ class SourceCache extends Evented {
628
647
return retain ;
629
648
}
630
649
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
+
631
687
/**
632
688
* Add a tile, given its coordinate, to the pyramid.
633
689
* @private
@@ -666,7 +722,7 @@ class SourceCache extends Evented {
666
722
return tile ;
667
723
}
668
724
669
- _setTileReloadTimer ( id : string | number , tile : Tile ) {
725
+ _setTileReloadTimer ( id : string , tile : Tile ) {
670
726
if ( id in this . _timers ) {
671
727
clearTimeout ( this . _timers [ id ] ) ;
672
728
delete this . _timers [ id ] ;
@@ -685,7 +741,7 @@ class SourceCache extends Evented {
685
741
* Remove a tile, given its id, from the pyramid
686
742
* @private
687
743
*/
688
- _removeTile ( id : string | number ) {
744
+ _removeTile ( id : string ) {
689
745
const tile = this . _tiles [ id ] ;
690
746
if ( ! tile )
691
747
return ;
@@ -846,7 +902,7 @@ class SourceCache extends Evented {
846
902
* Sets the set of keys that the tile depends on. This allows tiles to
847
903
* be reloaded when their dependencies change.
848
904
*/
849
- setDependencies ( tileKey : string | number , namespace : string , dependencies : Array < string > ) {
905
+ setDependencies ( tileKey : string , namespace : string , dependencies : Array < string > ) {
850
906
const tile = this . _tiles [ tileKey ] ;
851
907
if ( tile ) {
852
908
tile . setDependencies ( namespace , dependencies ) ;
@@ -870,8 +926,13 @@ class SourceCache extends Evented {
870
926
SourceCache . maxOverzooming = 10 ;
871
927
SourceCache . maxUnderzooming = 3 ;
872
928
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 ;
875
936
}
876
937
877
938
function isRasterType ( type ) {
0 commit comments