rnmapbox / maps

A Mapbox react native module for creating custom maps
MIT License
2.22k stars 839 forks source link

[Bug]: Offline manager not downloading beyond zoom level 15? #3256

Closed jgrant41475 closed 9 months ago

jgrant41475 commented 9 months ago

Mapbox Implementation

Mapbox

Mapbox Version

default

Platform

iOS, Android

@rnmapbox/maps version

10.1.0-rc.4

Standalone component to reproduce

import geoViewport from '@mapbox/geo-viewport';
import Mapbox, {
    Camera,
    MapView,
    offlineManager,
    StyleURL,
} from '@rnmapbox/maps';
import React, { useState } from 'react';
import { Button, Dimensions, TextInput } from 'react-native';

const CENTER_COORD = [-73.970895, 40.723279];
const MAPBOX_VECTOR_TILE_SIZE = 512;
console.log('=> Mapbox[0]:', Mapbox);
console.log('=> Mapbox.StyleURL[1]:', Mapbox.StyleURL);
console.log('=> StyleURL[2]:', StyleURL);
const STYLE_URL = Mapbox.StyleURL.Satellite;

const OfflineExample = () => {
    const [packName, setPackName] = useState('pack-1');
    const [showEditTitle, setShowEditTitle] = useState(false);

    return (
        <>
            <Button
                title={`Pack name: ${packName}`}
                onPress={() => {
                    setShowEditTitle(!showEditTitle);
                }}
            />
            {showEditTitle && (
                <TextInput
                    value={packName}
                    autoFocus={true}
                    onChangeText={(text) => setPackName(text)}
                    onBlur={() => setShowEditTitle(false)}
                />
            )}
            <Button
                title="Get all packs"
                onPress={async () => {
                    const packs = await offlineManager.getPacks();
                    console.log('=> packs:', packs);
                    packs.forEach((pack) => {
                        console.log(
                            'pack:',
                            pack,
                            'name:',
                            pack.name,
                            'bounds:',
                            pack?.bounds,
                            'metadata',
                            pack?.metadata,
                        );
                    });
                }}
            />
            <Button
                title="Get pack"
                onPress={async () => {
                    const pack = await offlineManager.getPack(packName);
                    if (pack) {
                        console.log(
                            'pack:',
                            pack,
                            'name:',
                            pack.name,
                            'bounds:',
                            pack?.bounds,
                            'metadata',
                            pack?.metadata,
                        );

                        console.log('=> status', await pack?.status());
                    }
                }}
            />
            <Button
                title="Resume pack"
                onPress={async () => {
                    const pack = await offlineManager.getPack(packName);
                    if (pack) {
                        await pack.resume();
                    }
                }}
            />
            <Button
                title="Remove packs"
                onPress={async () => {
                    const result = await offlineManager.resetDatabase();
                    console.log('Reset DB done:', result);
                }}
            />
            <Button
                title="Create Pack"
                onPress={() => {
                    const { width, height } = Dimensions.get('window');
                    const bounds = geoViewport.bounds(
                        CENTER_COORD,
                        12,
                        [width, height],
                        MAPBOX_VECTOR_TILE_SIZE,
                    );

                    const options = {
                        name: packName,
                        styleURL: STYLE_URL,
                        bounds: [
                            [bounds[0], bounds[1]],
                            [bounds[2], bounds[3]],
                        ],
                        minZoom: 16,
                        maxZoom: 20,
                        metadata: {
                            whatIsThat: 'foo',
                        },
                    };
                    offlineManager.createPack(options, (region, status) =>
                        console.log(
                            '=> progress callback region:',
                            'status: ',
                            status,
                        ),
                    );
                }}
            />
            <MapView style={{ flex: 1 }} styleURL={STYLE_URL}>
                <Camera zoomLevel={10} centerCoordinate={CENTER_COORD} />
            </MapView>
        </>
    );
};

export default OfflineExample;

/* end-example-doc */

Observed behavior and steps to reproduce

Using the Offline Example I am able to successfully download an offline tilepack for zoom levels 10-20.

When the minZoom level is changed to be greater than or equal to 16, the download will fail, showing the progress percentage as 'NaN'

 LOG  => progress callback region: status:  {"completedResourceCount": 0, "completedResourceSize": 0, "erroredResourceCount": 0, "loadedResourceCount": 0, "loadedResourceSize": 0, "name": "pack-1", "percentage": NaN, "requiredResourceCount": 0, "state": "complete"}

Expected behavior

Offline manager should create a tilepack for the zoom levels request

Notes / preliminary analysis

Even when the minZoom is less than 16, the actual downloaded tiles don't contain any tiles for any zoom level past 15. This can be confirmed by creating a tilepack for zoom levels 10-20 and another for zoom levels 10-15, then comparing the resource size and noticing that they are exactly the same.

Additional links and references

https://github.com/rnmapbox/maps/blob/main/example/src/examples/Map/OfflineExample.tsx

mfazekas commented 9 months ago

See also: https://github.com/mapbox/mapbox-maps-ios/issues/1056

chris-gaona commented 6 months ago

See also: mapbox/mapbox-maps-ios#1056

@mfazekas in the link you posted about this upstream issue above, the person there recommended to continue using the legacy offline APIs for offline support of zooms higher than 16. Is there any way in version 10 of this repo to use the legacy offline APIs mentioned in the ticket? Thanks!

EDIT: Sorry disregard, I just noticed in the docs there is a legacy offline maps API still there. I'll try that out. Thanks!

sampathkumarch commented 4 months ago

@ZiZasaurus can you look this problem

https://github.com/rnmapbox/maps/issues/3076#issuecomment-2011396346

sampathkumarch commented 4 months ago

@jgrant41475 , am also facing the same if you solved the problem , please can you guide me.

jgrant41475 commented 3 months ago

@jgrant41475 , am also facing the same if you solved the problem , please can you guide me.

Hey @sampathkumarch, sorry for the late response I'm pretty bad at checking my notifications.

After discussing with the mapbox community team, we ultimately decided to accept the zoom limitations with the mapbox SDK.

The limitation stems from the fact that the default tiling scheme only goes up to zoom level 16, so if you are able to use a custom imagery source that does contain higher zoom levels, then the SDK should work as expected. Unfortunately, for our needs, this was not a feasible solution.

Below is a snippet of the communication that I hope provides useful context for others with this issue:

So I've learned from the team that the core restriction actually stems from the Tiling Scheme of the core SDK. The default (which was decided based on vector tiles, as you note) only goes up to level 16, which means that if a tiling scheme is not specified by the TileJSON for the tileset source, then GL native uses the default one (z16). To enable caching of higher zoom levels, the source (in this case, the core Mapbox satellite tileset) would need to include a custom tiling scheme that includes the higher zoom levels.