mapbox-maps-flutter icon indicating copy to clipboard operation
mapbox-maps-flutter copied to clipboard

Offline map question

Open Miguelstucom opened this issue 1 year ago • 9 comments

Hello, I'm new to this and I've been using the example code for a couple of days. I wanted to ask if there is any way for me to create a button in the main menu to download the map information I need. After that, disconnect the phone from the internet, enter the map offline, and see everything that has been downloaded in the main menu? Just to avoid forcing the users of my project's app to enter the map to download all the information.

I'm sorry if this is a basic question, but I've been programming for a short time and I can't find a way to do it.

Miguelstucom avatar Jul 06 '24 11:07 Miguelstucom

Hi @Miguelstucom , we do have an example showcasing how Offline map works. To disconnect Mapbox stack to the network you can use

OfflineSwitch.shared.isMapboxStackConnected(false);

maios avatar Jul 09 '24 09:07 maios

Thanks for answering @maios, the thing is that im following that example, in my project I have a button which download the mapstyle and some needed tiles, when the download is complete, I enter the map without internet connection, i should see the mapstyle and those tiles, but the map is still empty, even if I hardcode lat,lon geometry, I cant see that area after download when I enter the map offline.

Is your code needed to show information downloaded? And Im not able to find documentation about your code in flutter package.

Thanks for your help.

Miguelstucom avatar Jul 09 '24 10:07 Miguelstucom

Could you provide a some sample code, better yet a small application, that would be a great help for us to debug this issue :-D

maios avatar Jul 11 '24 08:07 maios

Thanks for the help @maios, here is the code, most of it is just the example code, just to note, it is not downloading the stylepack or the tiles, any of both

LoadStylePack and downloadMap are the await functions called when I press my button at the main menu, after this stop downloading, Im able to enter the map, but before entering, I remove my connection to see if everything downloaded correctly and I do not force the user to enter the map to cache it.

DownloadItineraryMap is supposed to be called more than once to download more than one tile if needed.

  Future<void> _downloadMap(Travel travel) async {
    await _initializeMapbox();
    LatLngBounds getBounds(Itinerary itinerary) {
      return LatLngBounds(
        northeast: Point(
            coordinates: Position(
              itinerary.destination!.bboxNe!.coordinates![1],
              itinerary.destination!.bboxNe!.coordinates![0],
            )),
        southwest: Point(
            coordinates: Position(
              itinerary.destination!.bboxSw!.coordinates![1],
              itinerary.destination!.bboxSw!.coordinates![0],
            )),
      );
    }

    final boundsProcessed = <LatLngBounds>[];

    for (final itinerary in travel.itineraries!) {
      final bounds = getBounds(itinerary);
      if (!boundsProcessed.contains(bounds)) {
        final minZoom = itinerary.destination!.minZoom!.toInt();
        final maxZoom = itinerary.destination!.maxZoom!.toInt();
        await _downloadItineraryMap(bounds, minZoom, maxZoom);
        boundsProcessed.add(bounds);
      }
    }
  }
  Future<void> _initializeMapbox() async {
    MapboxOptions.setAccessToken("key");
  }
  Future<void> _downloadStylePack() async {
    await _initializeMapbox();
    final offlineManager = await OfflineManager.create();
    final stylePackLoadOptions = StylePackLoadOptions(
        glyphsRasterizationMode: GlyphsRasterizationMode.IDEOGRAPHS_RASTERIZED_LOCALLY,
        metadata: {"tag": "test"},
        acceptExpired: false);

    await offlineManager.loadStylePack(
        'mapbox://styles/mapbox/streets-v11', stylePackLoadOptions, (progress) {
      final percentage =
          progress.completedResourceCount / progress.requiredResourceCount;
      if (!_stylePackProgress.isClosed) {
        _stylePackProgress.sink.add(percentage);
      }
    }).then((value) {
      _stylePackProgress.sink.add(1);
      _stylePackProgress.sink.close();
    }).catchError((error) {
      print('Error loading style pack: $error');
    });
  }
  Future<void> _downloadItineraryMap(
      LatLngBounds bounds,
      int minZoom,
      int maxZoom,
      ) async {
    var downloading = true;

    double centerLatitude =
        (bounds.southwest.coordinates[0]! + bounds.northeast.coordinates[0]!) / 2;
    double centerLongitude =
        (bounds.southwest.coordinates[1]! + bounds.northeast.coordinates[1]!) / 2;
    num southWestLatitude = bounds.southwest.coordinates[0]!;
    num southWestLongitude = bounds.southwest.coordinates[1]!;
    num northEastLatitude = bounds.northeast.coordinates[0]!;
    num northEastLongitude = bounds.northeast.coordinates[1]!;

    final bbox = BBox(
        southWestLongitude,
        southWestLatitude,
        northEastLongitude,
        northEastLatitude
    );

    final tmpDir = await getTemporaryDirectory();
    final tileStore = await TileStore.createAt(tmpDir.uri);
    if (tileStore != null) {
      print('TileStore created at ${tmpDir.uri}');
    } else {
      print('Failed to create TileStore');
    }

    final tileRegionLoadOptions = TileRegionLoadOptions(
        geometry: Point(
            coordinates: Position(centerLongitude, centerLatitude),
            bbox: bbox
        ).toJson(),
        descriptorsOptions: [
          TilesetDescriptorOptions(
            styleURI: 'mapbox://styles/mapbox/streets-v11',
            minZoom: minZoom,
            maxZoom: maxZoom,
          )
        ],
        acceptExpired: true,
        networkRestriction: NetworkRestriction.NONE);

    await tileStore.loadTileRegion(
        '$centerLatitude,$centerLongitude', tileRegionLoadOptions, (progress) {
      final percentage =
          progress.completedResourceCount / progress.requiredResourceCount;
      if (!_tileRegionLoadProgress.isClosed) {
        _tileRegionLoadProgress.sink.add(percentage);
      }
    }).then((value) {
      _tileRegionLoadProgress.sink.add(1);
    }).catchError((error) {
      print('Error loading tile region: $error');
    });
  }

This is my map widget at a different app page

      builder: (context, state) {
        _itinerary = state.selectedItinerary;
        _userLocation = state.userLocation;
        initLocation();
        MapboxOptions.setAccessToken("Key");
        return MapWidget(
          key: ValueKey("Key"),
          textureView: true,
          onMapCreated: (MapCreatedCallback) {
            _controller = MapCreatedCallback;
            _controller?.loadStyleURI('mapbox://styles/mapbox/streets-v11');
            _controller?.scaleBar.updateSettings(ScaleBarSettings(enabled: false));
            _controller?.location.updateSettings(LocationComponentSettings(
                enabled: true, locationPuck: LocationPuck(locationPuck2D: DefaultLocationPuck2D(),),),);
            _initMapComponents(_itinerary!);
          },
          onTapListener: (contextGesture) {
            _resetSelectedSymbol();
          },
        );
      },

Just to advice, if I just enter the map without downloading and with internet connection, its shows perfectly, the problem is trying to download the map information and entering it without internet connection.

Miguelstucom avatar Jul 11 '24 09:07 Miguelstucom

Hi @Miguelstucom sorry for late reply, could you provide a minimal sample app to help investigating this?

maios avatar Jul 24 '24 11:07 maios

Hi @maios I was able to reproduce it using the example app you mentioned here: https://github.com/mapbox/mapbox-maps-flutter/issues/609#issuecomment-2217207528

  1. Open the map and download a tile for geometry: Point(coordinates: Position(-80.1263, 25.7845)).toJson(), (Miami, Florida)
  2. Turn on the airplane mode on the phone.
  3. Zoom in on Miami Beach in Florida - no detailed map is available. I can see only a low-poly contour from the cache (see attached image).

1000000696

wnowak-vialytics avatar Jul 26 '24 12:07 wnowak-vialytics

@maios @wnowak-vialytics any update on this? im also facing same

vijinhps avatar Aug 10 '24 16:08 vijinhps

Has anyone managed to solve it? I have the same problem in version 2.5.0

kelvinbremm avatar Jan 16 '25 13:01 kelvinbremm

@maios I also have the same issue on both. Any news?

David-Prelinger avatar Jan 26 '25 19:01 David-Prelinger