$30 off During Our Annual Pro Sale. View Details »

Offline Maps

Offline Maps

Google Maps for Android has come a long way but we all make the kind assumption that our users have a fluid LTE connection. In reality there are 3 scenarios when trying to view google maps. Google maps has magically cached the area that you are interested in. The scary version, where you just see a vast sea of gray. Or we can take control and provide our users with an offline option that allows them to view their location even in the darkest subways.

Apoorv Kothari

May 16, 2015
Tweet

More Decks by Apoorv Kothari

Other Decks in Technology

Transcript

  1. Offline Maps
    Apoorv Kothari

    View Slide

  2. Current Options
    • Google map caching
    • Scary Version

    View Slide

  3. Goal
    • Generating an offline Tile source
    • Integration with Map layer
    • Efficiently download tiles

    View Slide

  4. Understanding Tile
    • Tile size (256x256 px)
    • Tile coordinates [x,y,z]
    • Exponential growth of memory 4^zoom
    • Caching Manhattan(15 zoom): 240MB
    • Caching the world(15 zoom): ~40086GB

    View Slide

  5. Storing Files
    Tiles
    0
    1
    2
    0_1.png
    1_0.png
    1_1.png
    1_2.png

    0_0.png
    0_0.png
    0_1.png
    0_2.png
    1_1.png

    View Slide

  6. Tile Source/Server
    • OpenStreetMaps is open data (ODbL)
    • http://wiki.openstreetmap.org/wiki/Tiles
    • MapQuest tile servers
    • http://developer.mapquest.com/web/products/open/map
    • http://otile1.mqcdn.com/tiles/1.0.0/map/[zoom]/[x]/[y].jpg

    View Slide

  7. Integrating with Map Layer

    View Slide

  8. Integrating with Map Layer
    • Google Map / TileOverlay / UrlTileProvider
    • getTileUrl() -> doesTileExists? -> saveToFile -> return file URL
    doesTileExists? saveTileToFile()
    getFileURL()
    getTileUrl()
    No
    Yes

    View Slide

  9. Integrating with Map Layer
    public class OfflineTileProvider extends UrlTileProvider

    {
    public static class TileCoordinate

    {

    public final int x;

    public final int y;

    public final int zoom;
    }

    public URL getTileUrl(int x, int y, int zoom)

    {

    MapQuestTiles.TileCoordinate coordinate = new MapQuestTiles.TileCoordinate(x, y, zoom);

    String url = "http://otile1.mqcdn.com/tiles/1.0.0/map/" + zoom + "/" + x + "/" + y + ".jpg";

    try

    {

    return new URL(url);

    }

    catch(MalformedURLException e){}

    }

    }

    View Slide

  10. Integrating with Map Layer
    public class OfflineTileProvider extends UrlTileProvider

    {

    ...


    public synchronized URL getTileUrl(int x, int y, int zoom)

    {

    MapQuestTiles.TileCoordinate coordinate = new MapQuestTiles.TileCoordinate(x, y, zoom);

    return getFileUrl(coordinate);

    }


    public static URL getFileUrl(MapQuestTiles.TileCoordinate coordinate)

    {

    if(coordinate.zoom >= 18)

    {

    return null;

    }


    URL url = null;

    if(MapQuestTiles.doesTileExist(coordinate))

    {

    try

    {

    File coordinateFile = MapQuestTiles.getCoordinateFile(coordinate);

    url = new URL("file:" + coordinateFile.getAbsolutePath());

    }

    catch(MalformedURLException e){}

    }


    return url;

    }

    }

    View Slide

  11. PreCaching Implementation

    View Slide

  12. View Slide

  13. PreCaching Imp.
    http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Java
    public static TileCoordinate latLongToTileCoordinate(final double lat, final double lon, final int zoom)

    {

    int xtile = (int) Math.floor((lon + 180) / 360 * (1 << zoom));

    int ytile = (int) Math.floor((1 - Math.log(Math.tan(Math.toRadians(lat)) + 1 / Math

    .cos(Math.toRadians(lat))) / Math.PI) / 2 * (1 << zoom));

    if(xtile < 0)

    {

    xtile = 0;

    }

    if(xtile >= (1 << zoom))

    {

    xtile = ((1 << zoom) - 1);

    }

    if(ytile < 0)

    {

    ytile = 0;

    }

    if(ytile >= (1 << zoom))

    {

    ytile = ((1 << zoom) - 1);

    }


    return new TileCoordinate(xtile, ytile, zoom);

    }

    View Slide

  14. PreCaching Imp. (avoiding pitfalls)
    • Restrict area size (10000 tiles)
    • Check all tiles have been downloaded (service)
    • Synchronized access to tile files!!
    • Multi thread with limited thread pool size!!

    View Slide

  15. Advance Usage: MD5/Lower Tile Creation
    public class OfflineTileProvider extends UrlTileProvider

    {
    public static final String BAD_TILE_MD5 = "bb0f77951f30229b14a4d3ed0b836390";

    ...


    public static URL getFileUrl(MapQuestTiles.TileCoordinate coordinate)

    {


    ...
    url = MD5.compareMD5(BAD_TILE_MD5, coordinateFile) ? null : url;

    if(url == null)

    {

    try

    {

    File file = generateHigherZoomTile(coordinate);

    }

    catch(IOException e) {}

    }
    return url;
    }

    View Slide

  16. Future
    • Detect when Google tiles are available
    • Figure out how to share single source of tiles with all apps
    • Invalidate file cache
    • Delete unused file cache

    View Slide

  17. Practical Applications
    • Travel apps
    • Hiking
    • Sports tracking apps
    • Offline use of glass (sports)
    • Virtual walking tours
    • Gamify (hide and seek, first person shooter)
    • Navigating the seas

    View Slide

  18. View Slide