Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android offline maps, load vector tiles/geoson from local storage #626

Closed
nomizodiac opened this issue Mar 15, 2016 · 10 comments
Closed

Android offline maps, load vector tiles/geoson from local storage #626

nomizodiac opened this issue Mar 15, 2016 · 10 comments

Comments

@nomizodiac
Copy link

If i have vector tiles in Android local storage. How can i load those Tiles/GeoJson as datasource? In this link it is discussed before but there is no further discussion about how it could be implemented. Is there any further development in it? Loading geojson string is mentioned here that

The way to add something like a "marker" right now is through the MapData class. You would add some geometry and properties, either as a complete string of GeoJSON or as a point geometry and a map of properties. That data will then be styled according to the scene file

But how to load vector tiles directly?

@hak-tpl
Copy link

hak-tpl commented Mar 26, 2016

@nomizodiac

I have achieved offline map tiles by storing simple .json map files into phone storage, instead of requesting htttpHandler, and reading and sending bytesArray data to onUrlSuccess(<bytesData> callbackPtr) to MapController.

Its an inefficient way but some how it works. Waiting for a proper solution!

@matteblair
Copy link
Member

Nice tip! I agree that we can make it simpler and more efficient to load tiled data from a file system.

@nomizodiac Do you have vector tiles in a specific directory structure on your device? If you can write the tile locations like a url template, then with a small change to the tangram-es code, we could redirect file:// urls to load from the local file system (fun fact: this already happens on OS X and iOS). Would that be a solution to your issue?

@nomizodiac
Copy link
Author

@blair1618 yes i got the point. I can load offline tiles now. I have deployed vectors tiles in same format z/x/y at phones local storage and in method startUrlRequest(String url, final long callbackPtr) i get tiles from local storage instead of sending web request and giving json bytes to onUrlSuccess(<bytesData> callbackPtr). It is working fine. Thanks

@TGISer
Copy link

TGISer commented May 23, 2016

@nomizodiac can you give me the sorce code to me i'm very need it thank you !!!

@hak-tpl
Copy link

hak-tpl commented May 23, 2016

Here is the hack we did for offline map data. In MapController class

 public boolean startUrlRequest(String url, final long callbackPtr) throws Exception {

        if (httpHandler == null) {
            return false;
        }
        if (!OFFLINE_MODE) {
            httpHandler.onRequest(url, new Callback() {
                @Override
                public void onFailure(Request request, IOException e) {
                    onUrlFailure(callbackPtr);
                    //e.printStackTrace();
                }

                @Override
                public void onResponse(Response response) throws IOException {
                    if (!response.isSuccessful()) {
                        onUrlFailure(callbackPtr);
                        throw new IOException("Unexpected response code: " + response);
                    }
                    BufferedSource source = response.body().source();
                    byte[] bytes = source.readByteArray();
                    onUrlSuccess(bytes, callbackPtr);
                }
            });
        } else {
            // Fetch data from local storage
            Boolean isSDPresent = android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
            String path;
            if (isSDPresent) {
                path = Environment.getExternalStorageDirectory().getPath() + File.separator + "TPL Maps"
                        + File.separator + url.substring(url.indexOf("composite"), url.length());
            } else {
                path = mContext.getFilesDir().getPath() + File.separator + "TPL Maps"
                        + File.separator + url.substring(url.indexOf("composite"), url.length());
            }

            Log.i(TAG, "Path: " + path);
            final byte[] bytesToSend = readfromStorage(path);
            if (bytesToSend == null)
                return false;
            onUrlSuccess(bytesToSend, callbackPtr);
            return true;

        }
        return true;
    }
    private byte[] readfromStorage(String path) {
        String ret = "";
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(path);

            if (inputStream != null) {
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
                String receiveString = "";
                StringBuilder stringBuilder = new StringBuilder();

                while ((receiveString = bufferedReader.readLine()) != null) {
                    stringBuilder.append(receiveString);
                }

                inputStream.close();
                ret = stringBuilder.toString();
                Log.i(TAG, "Response: " + ret);

                return ret.getBytes();
            }
        } catch (FileNotFoundException e) {
            Log.e("login activity", "File not found: " + e.toString());
        } catch (IOException e) {
            Log.e("login activity", "Can not read file: " + e.toString());
        }

        return null;
    }

The method readfromStorage(String path) do need some optimization here.

@mr-z-ro
Copy link

mr-z-ro commented Jul 21, 2016

@hak-tpl thank you very much for sharing! I'm working on a similar line, and am curious to learn a bit more about what you ended up doing.

You mentioned you're using a "simple .json map files into phone storage" which I've been trying to replicate prior to coming across this thread here. I'm curious, for your scene.yaml then, are you just putting something to the effect of:

sources:
    osm:
        type: GeoJSON
        url:  citymap.geojson
        max_zoom: 18

where citymap.geojson is a dump directly from Metro Extracts, for instance? I'm running into inexplicable crashes at the moment, perhaps due to memory overflows in trying to load the file in all at once, and am wondering if you've run into anything of the sort. Any more insights would be greatly appreciated!

@hak-tpl
Copy link

hak-tpl commented Jul 21, 2016

@msgrasser

I didn't make any changes in .yaml file. The source in yaml file was my online server link like https://<my server>/composite/{z}/{x}/{y}.json format. I compiled Tangram library in android studio by placing all Tangram classes (not using the dependency( and made changes in MapController.java class. I simply hacked the URL which was returned to me in startUrlRequest() method and instead of requesting my online server, I redirected it to my local storage. It's just a hack!

@mr-z-ro
Copy link

mr-z-ro commented Jul 21, 2016

@hak-tpl Thanks so much - after your comment here and reviewing your code above, that makes a lot more sense now.

How do you go about generating each local json file in the first place then? Are you basically just hitting your server for every z/x/y combo, saving each tile as its own json file, and putting them all on Android's disk?

And yes, I definitely understand this is a hack. I'm putting this together for a proof of concept, and am hoping by the time I need to scale this will be added as a feature to the Android SDK itself (like it is on iOS and OSX, for instance), which it sounds like Mapzen has in their roadmap [and really shouldn't take too much effort to redirect "file://" urls].

@hak-tpl
Copy link

hak-tpl commented Jul 22, 2016

@msgrasser

I had my vector tiles stored on my phone storage in z/x/y format. I had my server access, so I copied generated vector data from it and stored into phone storage manually. And yes it takes a lot of size on your storage.

@hallahan
Copy link
Contributor

hallahan commented Aug 6, 2016

Have you considered storing your offline data in SQLite? This would keep everything together in one file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants