diff --git a/lib/src/layer/tile_layer/tile_image.dart b/lib/src/layer/tile_layer/tile_image.dart index cbb414d00..cb8502001 100644 --- a/lib/src/layer/tile_layer/tile_image.dart +++ b/lib/src/layer/tile_layer/tile_image.dart @@ -92,10 +92,6 @@ class TileImage extends ChangeNotifier { /// tile display is used with a maximum opacity less than 1. bool get readyToDisplay => _readyToDisplay; - // Used to sort TileImages by their distance from the current zoom. - double zIndex(double maxZoom, int currentZoom) => - maxZoom - (currentZoom - coordinates.z).abs(); - /// Change the tile display options. set tileDisplay(TileDisplay newTileDisplay) { final oldTileDisplay = _tileDisplay; diff --git a/lib/src/layer/tile_layer/tile_image_manager.dart b/lib/src/layer/tile_layer/tile_image_manager.dart index 1eeca51bb..305c87f32 100644 --- a/lib/src/layer/tile_layer/tile_image_manager.dart +++ b/lib/src/layer/tile_layer/tile_image_manager.dart @@ -24,11 +24,11 @@ class TileImageManager { bool get allLoaded => _tiles.values.none((tile) => tile.loadFinishedAt == null); - /// Filter tiles to only tiles that would visible on screen. Specifically: + /// Filter tiles to only tiles that would be visible on screen. Specifically: /// 1. Tiles in the visible range at the target zoom level. /// 2. Tiles at non-target zoom level that would cover up holes that would /// be left by tiles in #1, which are not ready yet. - Iterable renderTiles({ + Iterable getTilesToRender({ required DiscreteTileRange visibleRange, }) => TileImageView( @@ -37,33 +37,22 @@ class TileImageManager { // `keepRange` is irrelevant here since we're not using the output for // pruning storage but rather to decide on what to put on screen. keepRange: visibleRange, - ).renderTiles(); - - /// Creates missing tiles in the given range. Does not initiate loading of the - /// tiles. - void createMissingTiles( - DiscreteTileRange tileRange, - TileBoundsAtZoom tileBoundsAtZoom, { - required TileCreator createTileImage, - }) { - for (final coordinates in tileBoundsAtZoom.validCoordinatesIn(tileRange)) { - _tiles[coordinates] ??= createTileImage(coordinates); - } - } + ).renderTiles; bool allWithinZoom(double minZoom, double maxZoom) => _tiles.values .map((e) => e.coordinates) .every((coord) => coord.z > maxZoom || coord.z < minZoom); - /// Creates and returns [TileImage]s which do not already exist with the given - /// [tileCoordinates]. - List createMissingTilesIn( - Iterable tileCoordinates, { + /// Creates missing [TileImage]s within the provided tile range. Returns a + /// list of [TileImage]s which haven't started loading yet. + List createMissingTiles( + DiscreteTileRange tileRange, + TileBoundsAtZoom tileBoundsAtZoom, { required TileCreator createTile, }) { final notLoaded = []; - for (final coordinates in tileCoordinates) { + for (final coordinates in tileBoundsAtZoom.validCoordinatesIn(tileRange)) { final tile = _tiles[coordinates] ??= createTile(coordinates); if (tile.loadStarted == null) { notLoaded.add(tile); @@ -192,7 +181,7 @@ class TileImageManager { TileImageView tileRemovalState, EvictErrorTileStrategy evictStrategy, ) { - for (final tileImage in tileRemovalState.staleTiles()) { + for (final tileImage in tileRemovalState.staleTiles) { _removeWithEvictionStrategy(tileImage.coordinates, evictStrategy); } } diff --git a/lib/src/layer/tile_layer/tile_image_view.dart b/lib/src/layer/tile_layer/tile_image_view.dart index 2423b78ca..ccd3b14f9 100644 --- a/lib/src/layer/tile_layer/tile_image_view.dart +++ b/lib/src/layer/tile_layer/tile_image_view.dart @@ -4,7 +4,7 @@ import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_image.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; -class TileImageView { +final class TileImageView { final Map _tileImages; final DiscreteTileRange _visibleRange; final DiscreteTileRange _keepRange; @@ -27,42 +27,42 @@ class TileImageView { tileImage.loadError && !_visibleRange.contains(tileImage.coordinates)) .toList(); - Iterable staleTiles() { + Iterable get staleTiles { final stale = HashSet(); final retain = HashSet(); for (final tile in _tileImages.values) { final c = tile.coordinates; - if (_keepRange.contains(c)) { - if (!tile.readyToDisplay) { - final retainedAncestor = - _retainAncestor(retain, c.x, c.y, c.z, c.z - 5); - if (!retainedAncestor) { - _retainChildren(retain, c.x, c.y, c.z, c.z + 2); - } - } - } else { + if (!_keepRange.contains(c)) { stale.add(tile); + continue; + } + + final retainedAncestor = _retainAncestor(retain, c.x, c.y, c.z, c.z - 5); + if (!retainedAncestor) { + _retainChildren(retain, c.x, c.y, c.z, c.z + 2); } } return stale.where((tile) => !retain.contains(tile)); } - Iterable renderTiles() { + Iterable get renderTiles { final retain = HashSet(); for (final tile in _tileImages.values) { final c = tile.coordinates; - if (_visibleRange.contains(c)) { - retain.add(tile); + if (!_visibleRange.contains(c)) { + continue; + } + + retain.add(tile); - if (!tile.readyToDisplay) { - final retainedAncestor = - _retainAncestor(retain, c.x, c.y, c.z, c.z - 5); - if (!retainedAncestor) { - _retainChildren(retain, c.x, c.y, c.z, c.z + 2); - } + if (!tile.readyToDisplay) { + final retainedAncestor = + _retainAncestor(retain, c.x, c.y, c.z, c.z - 5); + if (!retainedAncestor) { + _retainChildren(retain, c.x, c.y, c.z, c.z + 2); } } } diff --git a/lib/src/layer/tile_layer/tile_layer.dart b/lib/src/layer/tile_layer/tile_layer.dart index 7f641d656..02c415958 100644 --- a/lib/src/layer/tile_layer/tile_layer.dart +++ b/lib/src/layer/tile_layer/tile_layer.dart @@ -524,7 +524,7 @@ class _TileLayerState extends State with TickerProviderStateMixin { _tileImageManager.createMissingTiles( visibleTileRange, tileBoundsAtZoom, - createTileImage: (coordinates) => _createTileImage( + createTile: (coordinates) => _createTileImage( coordinates: coordinates, tileBoundsAtZoom: tileBoundsAtZoom, pruneAfterLoad: false, @@ -539,7 +539,7 @@ class _TileLayerState extends State with TickerProviderStateMixin { // We're happy to do a bit of diligent work here, since tiles not rendered are // cycles saved later on in the render pipeline. final tiles = _tileImageManager - .renderTiles(visibleRange: visibleTileRange) + .getTilesToRender(visibleRange: visibleTileRange) .map((tileImage) => Tile( // Must be an ObjectKey, not a ValueKey using the coordinates, in // case we remove and replace the TileImage with a different one. @@ -554,15 +554,20 @@ class _TileLayerState extends State with TickerProviderStateMixin { )) .toList(); - // Sort in render order; + // Sort in render order. In reverse: // 1. Tiles at the current zoom. // 2. Tiles at the current zoom +/- 1. // 3. Tiles at the current zoom +/- 2. // 4. ...etc - final maxZoom = widget.maxZoom; - int renderOrder(Tile a, Tile b) => a.tileImage - .zIndex(maxZoom, tileZoom) - .compareTo(b.tileImage.zIndex(maxZoom, tileZoom)); + int renderOrder(Tile a, Tile b) { + final (za, zb) = (a.tileImage.coordinates.z, b.tileImage.coordinates.z); + final cmp = (zb - tileZoom).abs().compareTo((za - tileZoom).abs()); + if (cmp == 0) { + // When compare parent/child tiles of equal distance, prefer higher res images. + return za.compareTo(zb); + } + return cmp; + } return MobileLayerTransformer( // ignore: deprecated_member_use_from_same_package @@ -677,8 +682,9 @@ class _TileLayerState extends State with TickerProviderStateMixin { // Build the queue of tiles to load. Marks all tiles with valid coordinates // in the tileLoadRange as current. final tileBoundsAtZoom = _tileBounds.atZoom(tileZoom); - final tilesToLoad = _tileImageManager.createMissingTilesIn( - tileBoundsAtZoom.validCoordinatesIn(tileLoadRange), + final tilesToLoad = _tileImageManager.createMissingTiles( + tileLoadRange, + tileBoundsAtZoom, createTile: (coordinates) => _createTileImage( coordinates: coordinates, tileBoundsAtZoom: tileBoundsAtZoom,