nitaliano / react-native-mapbox-gl

A Mapbox GL react native module for creating custom maps
Other
2.16k stars 697 forks source link

Offline Manager Status Incorrect #1540

Closed sfratini closed 5 years ago

sfratini commented 5 years ago

I am using version 6.1.3. What is happening is that I can trigger a package to get download and I can see the progress get to 100% and to change to state = 2 so it gets marked as "complete". The problem is that as soon as I restart the app, I load the downloaded packs and it is shown as 99.6%.

Also, it seems like the status object is shared across all packages.

mapbox

kristfal commented 5 years ago

Can you specify the steps to reproduce? Which API calls are you using to download? Which platform?

sfratini commented 5 years ago

@kristfal My bad, I should have added more info. This is happening in Android. Let me add the relevant code

This is part of my splash screen. I am using redux and firebase config.

loadRemoteConfig = () => {

    if (__DEV__) {
        firebase.config().enableDeveloperMode();
    }

    let cache = __DEV__ ? 0 : 600;
    firebase.config().fetch(cache)
    .then(() => {
        return firebase.config().activateFetched();
    })
    .then(() => {
      console.log("Remote Config - Config")
      return firebase.config().getKeysByPrefix();
    })
    .then((arr) => firebase.config().getValues(arr))
    .then((objects) => {

        //Mapbox
        if (objects["config_api_mapbox"]){
          MapboxGL.setAccessToken(objects["config_api_mapbox"].val());
        }

        //Config
        let config = {};
        for (const key in objects) {
          if (objects.hasOwnProperty(key) && key.indexOf("config_") == 0) {
              config[key] = objects[key].val();
          }
        }
        this.props.setConfig(config)

    })
    .then(this.loadMaps)
    .then(packs => {
        let promises = packs.map(p => p.status())
        return Promise.all(promises)  //This promises object is the one that I am showing in the first post
    })
    .then(packStatus => {
      this.props.setTripCities([ //Esto luego deberia venir del backend
        {
            id: 'madrid',
            location: [lat,lng],
            radius: 2,
            places: 1500,
            imagesSize: 25000
        },
        {
            id: 'barcelona',
            location: [lat,lng],
            radius: 2,
            places: 360,
            imagesSize: 14657
        }
    ]);

      packStatus.forEach(status => {
        console.log("Splash Status", JSON.stringify(status))
        this.props.updateCityStatus(status.name, status);
      })

    })
    .then(() => {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          this.props.updateCurrentLocation({
            latitude: position.coords.latitude,
            longitude: position.coords.longitude
          })
          this.continueStartup();
        },
        (error) => {
          this.continueStartup();
        },
        null,
        );
    })
    .catch((error) => {
        this.continueStartup();
    })

  }

  loadMaps = () => {
    return new Promise((resolve, reject) => {
      MapboxGL.offlineManager.getPacks()
      .then(packs => {
        resolve(packs);
      })
      .catch(err => reject(err))
    });
  }

I am aware that the console might analyze the objects only when you "expand" them and that sometimes show data that is noy in sync but the weird part is that I download the package with the bounds and the progress says it downladed everything, 100% and every resource. 1284 resources. But I restart and it says it downloaded 1284 out of 1289 (4 resources added out of nowehere)

Then this is my container that downloads, resumes and pauses. I am trying to handle the scenario where the download was interrupted and I just delete the pack and start over since I dont have a reference to the offlinePack.

state = {
        offlineRegions: {}
    }

    componentDidMount(){
        MapboxGL.offlineManager.setProgressEventThrottle(1000);
    }

    calculateBoundaries = (location, radius = 15) => {

        if (location && location[0] && location[1]){
            let meters = parseInt(radius) * 1.609344 * 1000;
            let coef = meters * 0.0000089;

  return {
                neLng: parseInt(location[1] + coef / Math.cos(location[0] * 0.018)),
                swLng: parseInt(location[1] - coef / Math.cos(location[0] * 0.018)),
                neLat: parseInt(location[0] + coef),
                swLat: parseInt(location[0] - coef)
            }

        } else return null;

    }

    deleteCity = (id) => {
        this.props.updateCityStatus(id, {state: 99});
        MapboxGL.offlineManager.deletePack(id)
        .then(() => {
            this.props.updateCityStatus(id, {});
        })
        .catch(err => {
            this.props.updateCityStatus(id, {});
            this.props.error(err, true)
        });
    }

    resumeCity = (id, city, downloadImages = true) => {
        console.log(`Resuming ${id}`)
        if (this.state.offlineRegions[id])
            this.state.offlineRegions[id].resume();
        else {
            this.props.updateCityStatus(id, {state: 99});
            MapboxGL.offlineManager.deletePack(id)
            .then(() => {
                this.downloadCity(city)
            })
        }

    }

    pauseCity = (id, city, downloadImages = true) => {
        console.log(`Pausing ${id}`)
        this.state.offlineRegions[id].pause();
    }

    downloadCity = (city, downloadImages = true) => {

        console.log("Download City", city);

        const progressListener = (offlineRegion, status) => {
            //console.log("Download Progress", offlineRegion.name, status);
            let { offlineRegions } = this.state;
            offlineRegions[offlineRegion.name] = offlineRegion
            this.setState({offlineRegions});
            this.props.updateCityStatus(offlineRegion.name, status)
        }
        const errorListener = (offlineRegion, err) => {
            this.props.error(err, true)
            console.log("Download Error", offlineRegion, err)
        };

        const {id, location, radius} = city;
        const bounds = this.calculateBoundaries(location, radius)

        /*
        Object {requiredResourceCount: 1284, completedTileSize: 0, completedResourceSize: 73223366, completedResourceCount: 1278, percentage: 99.53271028037383}
        */

        if (bounds != null){

            MapboxGL.offlineManager.createPack({
                name: id,
                styleURL: MapboxGL.StyleURL.Street,
                minZoom: 12,
                maxZoom: 20,
                bounds: [[bounds.neLng, bounds.neLat], [bounds.swLng, bounds.swLat]]
                }, progressListener, errorListener)
                .then(() => {
                    console.log(`Downloading ${city.title}`)
                })
                .catch(err => {
                    console.log("catch", err);
                    this.props.error(err, true);
                })

        } else {
            this.props.error(new Error("Can't generate boundaries"), true);
        }

    }

Another weird part is that if I enable airplane mode, one time it worked fine and I could see the area and zoom in and out, but then it was not working in all zoom levels (I guess it was not the offline tiles but the cached ones by the map while I was browsing it). Like it is not downloading it correctly.

I am wondering if I should update to 7.0.0 and see what happens. Unless I am missing something it is not being reliable as I expected.

Bottom line, what I am doing is downloading an area, refreshing the code, and the status that comes back from the pack is not 100% even tho I got the progress to finish with status 2 and downloaded every resource.

Let me know if there s something else I could send or check.

sfratini commented 5 years ago

To add something more, I just tried again. I waited till the progress was 100%, like this:

madrid progress 100

Then I refreshed the app, got 2 packs (which is fine, I have both downloaded) and it shows this:

{"requiredResourceCount":1289,"completedTileSize":0,"completedResourceSize":73378402,"completedResourceCount":1284,"percentage":99.61210240496509,"state":0,"completedTileCount":0,"name":"madrid"}

Right now, I am doing Math.ceil and if it shows 100% I assume the city downloaded completely. But again, not very reliable and still can't say for sure that the tiles are being used.

sfratini commented 5 years ago

@kristfal Nevermind, I just found the issue. I guessed I just needed a good night sleep. I was getting a "location is not an int" or something like that before, when I defined the bounds so I left the parseInt() on each coordinate.

neLng: parseInt(location[1] + coef / Math.cos(location[0] * 0.018)),

madrid bounds

This was making the bounds lose the floating point so that is why it was downloading the wrong place. Removing the parseInt for each coordnate did the trick. Now, it also returns 100% when I reload the app. I am not sure why the difference in resource count before but I assume that it is a rounding error when it calculates the total amount of resources based on bounds.

Seems to work fine now. I'll just leave it open in case someone wants to review that logic, otherwise I am fine now.

kristfal commented 5 years ago

Ok, good that you resolved it. I’ll close this for now.