Skip to content

[FEATURE] Keep old tiles visible if tile cannot be loaded in place at higher zoom level #1968

@ChristopheLyonnaz

Description

@ChristopheLyonnaz

What is the bug?

TileLayer should be able to load tile of lower resolution if the tile provider cannot provide the tile at required zoom.

When TileLayer loads a tile at zoom Z, if the user zooms in, TileLayer gets the tile at zoom Z+1. if the tile provider cannot deliver this tile, or if the maxNativeZoom is defined as Z, TileLayer scales up the latest available tile (at zoom Z) and displays it correctly.
However, in this situation (where a tile is scaled up), if the user scrolls to adjacent tiles, TileLayer fails to recover the tile at zoom Z to scale it up and display it at the new position. It returns an error and displays a grey area, or the fallback strategy.

How can we reproduce it?

Create a map layer with a maxNativeZoom greater than the maximum zoom available in your tile provider.
Zoom on a area to display a tile with the maximum zoom. Continue zooming to have this tile scaled up.
Scroll to adjacent tile.

TileLayer fails to get the adjacent tile and returns an exception:

======== Exception caught by image resource service ================================================
The following ClientException was thrown resolving an image codec:
Request to https://tile.openstreetmap.org/20/521267/378404.png failed with status 400: Bad Request., uri=https://tile.openstreetmap.org/20/521267/378404.png

When the exception was thrown, this was the stack: 
#0      BaseClient._checkResponseSuccess (package:http/src/base_client.dart:103:5)
#1      BaseClient.readBytes (package:http/src/base_client.dart:59:5)
<asynchronous suspension>
#2      NetworkTileProvider.getImage.<anonymous closure> (package:flutter_map/src/layer/tile_layer/tile_provider/network_tile_provider.dart:68:31)
<asynchronous suspension>
#3      ImmutableBuffer.fromUint8List (dart:ui/painting.dart:6667:3)
<asynchronous suspension>
#4      PaintingBinding.instantiateImageCodecWithSize (package:flutter/src/painting/binding.dart:137:3)
<asynchronous suspension>
#5      MultiFrameImageStreamCompleter._handleCodecReady (package:flutter/src/painting/image_stream.dart:1005:3)
<asynchronous suspension>
URL: https://tile.openstreetmap.org/20/521267/378404.png
Fallback URL: null
Current provider: MapNetworkImageProvider()
====================================================================================================


Here is an example of code (inspired from 'Sliding Map' flutter_map example):

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_example/widgets/drawer/menu_drawer.dart';
import 'package:latlong2/latlong.dart';

class SlidingMapPage extends StatelessWidget {
  static const String route = '/sliding_map';
  static const northEast = LatLng(56.7378, 11.6644);
  static const southWest = LatLng(56.6877, 11.5089);

  const SlidingMapPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sliding Map')),
      drawer: const MenuDrawer(route),
      body: Column(
        children: [
          const Padding(
            padding: EdgeInsets.all(8),
            child: Text(
              'This is a map that can be panned smoothly when the '
              'boundaries are reached.',
            ),
          ),
          Flexible(
            child: FlutterMap(
              options: const MapOptions(
                initialCenter: LatLng(44.704173, -1.043808),
                minZoom: 1,
              ),
              children: [
                TileLayer(
                  urlTemplate: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
                  maxNativeZoom: 30,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

Do you have a potential solution?

Not yet a solution, but I suggest that _onTileUpdateEvent() function may be able to check for max available zoom from provider and updates tileZoom accordingly.

Currently, it only clamps zoom with maxNativeZoom, but this clam can be adjusted according to server capability.

Platforms

All Android and iOS plateforms

Severity

Erroneous: Prevents normal functioning and causes errors in the console

Metadata

Metadata

Assignees

No one assigned

    Labels

    P: ∞ (won't add/fix)This bug won't be fixed, or the feature won't be implemented

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions