From cf343ffeece916732e16fa08ce7da8e984ee2240 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Tue, 7 Apr 2020 19:09:13 +0200 Subject: [PATCH 1/3] Evict error tiles --- lib/src/layer/tile_layer.dart | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/src/layer/tile_layer.dart b/lib/src/layer/tile_layer.dart index 0b0aaaed9..d16837eea 100644 --- a/lib/src/layer/tile_layer.dart +++ b/lib/src/layer/tile_layer.dart @@ -152,6 +152,11 @@ class TileLayerOptions extends LayerOptions { // it can 0 to avoid fade in final Duration tileFadeInDuration; + // If a Tile was loaded with error and if this flag is true, then TileProvider + // will be asked to evict Image during _pruneTiles / _abortLoading calls + // (see #576 - even Error Images are cached in flutter) + final bool evictErrorTiles; + TileLayerOptions( {this.urlTemplate, this.tileSize = 256.0, @@ -167,6 +172,7 @@ class TileLayerOptions extends LayerOptions { this.backgroundColor = const Color(0xFFE0E0E0), this.placeholderImage, this.errorImage, + this.evictErrorTiles = false, this.tileProvider = const CachedNetworkTileProvider(), this.tms = false, // ignore: avoid_init_to_null @@ -397,7 +403,7 @@ class _TileLayerState extends State with TickerProviderStateMixin { var tile = _tiles[key]; tile.tileReady = null; - tile.dispose(); + tile.dispose(tile.loadError && options.evictErrorTiles); _tiles.remove(key); } } @@ -799,7 +805,7 @@ class _TileLayerState extends State with TickerProviderStateMixin { return; } - tile.dispose(); + tile.dispose(tile.loadError && options.evictErrorTiles); _tiles.remove(key); } @@ -942,10 +948,13 @@ class Tile implements Comparable { // call this before GC! void dispose([bool evict = false]) { if (evict && imageProvider != null) { - imageProvider - .evict() - .then((bool succ) => print('evict tile: $coords -> $succ')) - .catchError((error) => print('evict tile: $coords -> $error')); + try { + imageProvider.evict().catchError(print); + } catch (e) { + // this may be never called because catchError will handle errors, however + // we want to avoid random crashes like in #444 / #536 + print(e); + } } animationController?.removeStatusListener(_onAnimateEnd); From eaf27a413738d28d670a82de56457079c59c1981 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Fri, 10 Apr 2020 01:58:46 +0200 Subject: [PATCH 2/3] add EvictErrorTileStrategy enum --- lib/src/layer/tile_layer.dart | 133 ++++++++++++++++++++++++---------- 1 file changed, 95 insertions(+), 38 deletions(-) diff --git a/lib/src/layer/tile_layer.dart b/lib/src/layer/tile_layer.dart index d16837eea..62b1f5685 100644 --- a/lib/src/layer/tile_layer.dart +++ b/lib/src/layer/tile_layer.dart @@ -13,6 +13,19 @@ import 'package:tuple/tuple.dart'; import 'layer.dart'; +enum EvictErrorTileStrategy { + // never evict error Tiles + none, + // evict error Tiles during _pruneTiles / _abortLoading calls + dispose, + // evict error Tiles which are not visible anymore but respect margin (see keepBuffer option) + // (Tile's zoom level not equals current _tileZoom or Tile is out of viewport) + notVisibleRespectMargin, + // evict error Tiles which are not visible anymore + // (Tile's zoom level not equals current _tileZoom or Tile is out of viewport) + notVisible, +} + /// Describes the needed properties to create a tile-based layer. /// A tile is an image binded to a specific geographical position. class TileLayerOptions extends LayerOptions { @@ -152,43 +165,43 @@ class TileLayerOptions extends LayerOptions { // it can 0 to avoid fade in final Duration tileFadeInDuration; - // If a Tile was loaded with error and if this flag is true, then TileProvider - // will be asked to evict Image during _pruneTiles / _abortLoading calls + // If a Tile was loaded with error and if strategy isn't `none` then TileProvider + // will be asked to evict Image based on current strategy // (see #576 - even Error Images are cached in flutter) - final bool evictErrorTiles; - - TileLayerOptions( - {this.urlTemplate, - this.tileSize = 256.0, - this.minZoom = 0.0, - this.maxZoom = 18.0, - this.minNativeZoom, - this.maxNativeZoom, - this.zoomReverse = false, - this.zoomOffset = 0.0, - this.additionalOptions = const {}, - this.subdomains = const [], - this.keepBuffer = 2, - this.backgroundColor = const Color(0xFFE0E0E0), - this.placeholderImage, - this.errorImage, - this.evictErrorTiles = false, - this.tileProvider = const CachedNetworkTileProvider(), - this.tms = false, - // ignore: avoid_init_to_null - this.wmsOptions = null, - this.opacity = 1.0, - // Tiles will not update more than once every `updateInterval` milliseconds - // (default 200) when panning. - // It can be 0 (but it will calculating for loading tiles every frame when panning / zooming, flutter is fast) - // This can save some fps and even bandwidth - // (ie. when fast panning / animating between long distances in short time) - int updateInterval = 200, - // Tiles fade in duration in milliseconds (default 100), - // it can 0 to avoid fade in - int tileFadeInDuration = 100, - rebuild}) - : updateInterval = + final EvictErrorTileStrategy evictErrorTileStrategy; + + TileLayerOptions({ + this.urlTemplate, + this.tileSize = 256.0, + this.minZoom = 0.0, + this.maxZoom = 18.0, + this.minNativeZoom, + this.maxNativeZoom, + this.zoomReverse = false, + this.zoomOffset = 0.0, + this.additionalOptions = const {}, + this.subdomains = const [], + this.keepBuffer = 2, + this.backgroundColor = const Color(0xFFE0E0E0), + this.placeholderImage, + this.errorImage, + this.evictErrorTileStrategy = EvictErrorTileStrategy.none, + this.tileProvider = const CachedNetworkTileProvider(), + this.tms = false, + // ignore: avoid_init_to_null + this.wmsOptions = null, + this.opacity = 1.0, + // Tiles will not update more than once every `updateInterval` milliseconds + // (default 200) when panning. + // It can be 0 (but it will calculating for loading tiles every frame when panning / zooming, flutter is fast) + // This can save some fps and even bandwidth + // (ie. when fast panning / animating between long distances in short time) + int updateInterval = 200, + // Tiles fade in duration in milliseconds (default 100), + // it can 0 to avoid fade in + int tileFadeInDuration = 100, + rebuild, + }) : updateInterval = updateInterval <= 0 ? null : Duration(milliseconds: updateInterval), tileFadeInDuration = tileFadeInDuration <= 0 ? null @@ -403,7 +416,8 @@ class _TileLayerState extends State with TickerProviderStateMixin { var tile = _tiles[key]; tile.tileReady = null; - tile.dispose(tile.loadError && options.evictErrorTiles); + tile.dispose(tile.loadError && + options.evictErrorTileStrategy != EvictErrorTileStrategy.none); _tiles.remove(key); } } @@ -760,6 +774,8 @@ class _TileLayerState extends State with TickerProviderStateMixin { } } + _evictErrorTilesBasedOnStrategy(tileRange); + // sort tile queue to load tiles in order of their distance to center queue.sort((a, b) => (a.distanceTo(tileCenter) - b.distanceTo(tileCenter)).toInt()); @@ -805,7 +821,8 @@ class _TileLayerState extends State with TickerProviderStateMixin { return; } - tile.dispose(tile.loadError && options.evictErrorTiles); + tile.dispose(tile.loadError && + options.evictErrorTileStrategy != EvictErrorTileStrategy.none); _tiles.remove(key); } @@ -823,6 +840,46 @@ class _TileLayerState extends State with TickerProviderStateMixin { ); } + void _evictErrorTilesBasedOnStrategy(Bounds tileRange) { + if (options.evictErrorTileStrategy == + EvictErrorTileStrategy.notVisibleRespectMargin) { + var toRemove = []; + for (var entry in _tiles.entries) { + var tile = entry.value; + + if (tile.loadError && !tile.current) { + toRemove.add(entry.key); + } + } + + for (var key in toRemove) { + var tile = _tiles[key]; + + tile.dispose(true); + _tiles.remove(key); + } + } else if (options.evictErrorTileStrategy == + EvictErrorTileStrategy.notVisible) { + var toRemove = []; + for (var entry in _tiles.entries) { + var tile = entry.value; + var c = tile.coords; + + if (tile.loadError && + (!tile.current || !tileRange.contains(CustomPoint(c.x, c.y)))) { + toRemove.add(entry.key); + } + } + + for (var key in toRemove) { + var tile = _tiles[key]; + + tile.dispose(true); + _tiles.remove(key); + } + } + } + void _tileReady(Coords coords, dynamic error, Tile tile) { if (null != error) { print(error); From 947997082cc3af114254cca5298827db380903d9 Mon Sep 17 00:00:00 2001 From: maRci002 Date: Wed, 26 Aug 2020 16:16:40 +0200 Subject: [PATCH 3/3] revert wms example --- example/lib/pages/wms_tile_layer.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/example/lib/pages/wms_tile_layer.dart b/example/lib/pages/wms_tile_layer.dart index 5a4c8a932..70bf91130 100644 --- a/example/lib/pages/wms_tile_layer.dart +++ b/example/lib/pages/wms_tile_layer.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong/latlong.dart'; + import '../widgets/drawer.dart'; class WMSLayerPage extends StatelessWidget { @@ -27,10 +28,12 @@ class WMSLayerPage extends StatelessWidget { ), layers: [ TileLayerOptions( - wmsOptions: WMSTileLayerOptions( - baseUrl: 'http://maps.heigit.org/osm-wms/service?', - layers: ['europe_wms:hs_srtm_europa'], - )) + wmsOptions: WMSTileLayerOptions( + baseUrl: 'https://{s}.s2maps-tiles.eu/wms/?', + layers: ['s2cloudless-2018_3857'], + ), + subdomains: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'], + ) ], ), ),