flutter_map icon indicating copy to clipboard operation
flutter_map copied to clipboard

[FEATURE] Fallback tile URLs in-case of HTTP error

Open juanlabrador opened this issue 2 years ago • 9 comments

Describe The Problem I'm try to catch an error callback when the tile failed, so, the idea is when the first tile url map failed, called another, but this method errorTileCallback doesn't call, so, don't find in examples an example, maybe someone know somehow? Thanks

Additional Information TileLayerOptions getMapBoxStyle(Function refresh, {Key key}) => TileLayerOptions( key: key, urlTemplate: globalTileMap, errorTileCallback: (tile, error) { globalTileMap = 'https://tiles.picap.app/styles/osm-bright/{z}/{x}/{y}.png'; refresh.call(); }, backgroundColor: PiColors.backgroundTileMap);

juanlabrador avatar Mar 31 '22 14:03 juanlabrador

I haven't tested the errorTileCallback code....but maybe it depends how it failed ? For example, if there just isn't a response from a server, would it know ? It may taking a bit of digging and debugging the tile_layer.dart in the layers folder to try and get an idea of what's happening.

ibrierley avatar Apr 01 '22 18:04 ibrierley

Hi there @juanlabrador, Did @ibrierley's suggestion help? Happy to help more if required :)

JaffaKetchup avatar Apr 02 '22 21:04 JaffaKetchup

Hi guys, thanks for answer, well, the simple example its put a bad url, the tile when faile its return 403 error, I mean, the tile does't show map, so, in that cases, the idea is use another tile url like support, but I don't know where catch this 403 error

juanlabrador avatar Apr 04 '22 13:04 juanlabrador

At the moment, fallback URLs aren't supported - if that's what you mean. But it sounds like an interesting and useful idea! Converting to feature request...

JaffaKetchup avatar Apr 04 '22 17:04 JaffaKetchup

Note that for the time being, feature requests have an indefinite wait time, as we have quite a backlog of issues and bugs to get to. Many thanks for your suggestion!

JaffaKetchup avatar Apr 04 '22 17:04 JaffaKetchup

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar May 05 '22 02:05 github-actions[bot]

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Jun 08 '22 02:06 github-actions[bot]

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Jul 10 '22 02:07 github-actions[bot]

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Aug 10 '22 02:08 github-actions[bot]

Hi @JaffaKetchup, I think I've been able to add a fallbackUrl to the FMNetworkImageProvider, it would require to specify NetworkTileProvider() as the tile provider of TileLayer but the result is as @juanlabrador would've wanted. If an error occured it will retry to fetch tiles with the fallback url instead.

Code Sample

TileLayer(
  urlTemplate: 'https://fake-tile-provider.org/{z}/{x}/{y}.png',
  fallbackUrl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
  subdomains: const ['a', 'b', 'c'],
  userAgentPackageName: 'dev.fleaflet.flutter_map.example',
  tileProvider: NetworkTileProvider(),
),
Implementation
class FMNetworkImageProvider extends ImageProvider<FMNetworkImageProvider> {
  // ...

  /// The fallback URL from which the image will be fetched.
  final String? fallbackUrl;

  // ...

  FMNetworkImageProvider(
    this.url, {
    required this.fallbackUrl,
    RetryClient? retryClient,
    this.headers = const {},
  }) : retryClient = retryClient ?? RetryClient(Client());

  // ...

  Future<ImageInfo> _loadWithRetry(
    FMNetworkImageProvider key,
    DecoderCallback decode, [
    bool useFallback = false,
  ]) async {
    assert(key == this);
    assert(useFallback == false || fallbackUrl != null);

    try {
      final uri = Uri.parse(useFallback ? fallbackUrl! : url);
      final response = await retryClient.get(uri, headers: headers);

      if (response.statusCode != 200) {
        throw NetworkImageLoadException(
            statusCode: response.statusCode, uri: uri);
      }

      final codec = await decode(response.bodyBytes);
      final image = (await codec.getNextFrame()).image;

      return ImageInfo(image: image);
    } catch (e) {
      if (!useFallback && fallbackUrl != null) {
        return _loadWithRetry(key, decode, true);
      }
      rethrow;
    }
  }
}

Would you be interested in a PR ? (I've already created a full sample added to the example app)

TesteurManiak avatar Aug 28 '22 12:08 TesteurManiak

Hi @TesteurManiak, This looks great, and we would appreciate any PR you can make :) Just a question, why does it require NetworkTileProvider()? I think it should work for all providers if possible. Many thanks.

JaffaKetchup avatar Aug 28 '22 12:08 JaffaKetchup

Thanks for the quick response, I think I can make it work for all providers, it was just easier for NetworkTileProvider as it was already relying on a RetryClient. I'll make a PR as soon as I've finished the remaining improvements.

TesteurManiak avatar Aug 28 '22 12:08 TesteurManiak