I write solutions to the problems I can't find much about elsewhere on the Web, also some code/script snippets that are absolutely awesome and make my life easier. Will be glad if someone finds these posts interesting and helpful!

Showing posts with label offline-maps. Show all posts
Showing posts with label offline-maps. Show all posts

Tuesday, June 7, 2011

Offline tiled layer with ArcGIS for Android

On the official ArcGIS mobile blog they have described how to create an offline tiled layer for iOS, but not a word about Android (probably because it's still in beta, thing might change and it doesn't gain any publicity anyway). The idea was taken from this blog: if not for that, I would've not even attempted to look into this direction. So thank God for a custom hack!
My first attempt for offline tiles was to create a local tile server and in the ArcGISTiledMapServiceLayer as a URL supply something with localhost in it. It totally worked, but there were two issues that forced me to look for another solution. Number one was technical: as soon as the connection was down (e.g. Airplane mode turned on) the tiles would just stop to download (even though their physical location was on the same SD card the application was installed on!); the second issue was mental: just for knowing that the files stored locally had to take a trip to the moon before being rendered (let alone maintaining a whole process of a local map server), caused an allergy and made me invest some time into research.
So here we go with offline tiled layer. Easier than easy, with a very little code, but totally impossible to find out how to make it! At least at the current version of ArcGIS for Android all the interesting stuff regarding custom tiled layers is undocumented.
Here's the implementation of the custom tiled layer:
Here's a usage example:

Now about the filesystem. In the usage example I have set some values to the OfflineTiledLayer constructor and these represent the directory structure. First off, I didn't use the cache created by ArcGIS. I mentioned in the beginning of the post that earlier I had implemented a local map server, that's why the file paths reflect the ones used by the online map servers. For instance, the absolute path of the tile at zoom level 3/row 44/column 65 looks like /mnt/sdcard/services/RoadMapsWebMercator101010/MapServer/tile/3/44/65. I guess it's not hard at all to modify my class to use the HEX paths of the cache created by ArcGIS.
Finally, a couple words about the map server specification. That paramter index.html of the OfflineTiledLayer constructor represents this. Again, I named it index.html to support the local map server implemented earlier, but in fact it contains what an online map server outputs by clicking on the link "REST" on the bottom of the MapServer description page of the ArcGIS server (the address looks something like /MapServer?f=json&pretty=true). 

And that's totally it. Hope it helps!

Thursday, May 5, 2011

Offline Google Maps for Android

This took me a while to figure out as I couldn't find any tutorial, but it's fairly easy after all.
The map container of my choice is OSMDroid, which is a great (and open source!) replacement for Google Maps container.
Resolve the MapView and set tile provider as follows

It basically defines a new OnlineTileSourceBase with:
  • name "Google Maps" (this is an important bit, as it will be used to lookup the directory with offline tiles)
  • resource id "unknown" (I also downloaded OSMDroid source code, added a value "google" to the ResourceProxy.string enum and used that instead)
  • minimum zoom level 1
  • maximum zoom level 20
  • tile size 256 pixels
  • tile file extension ".png"
  • tile base url
  • finally inside the overriden method getTileURLString it describes how to build a URL to get a tile for specific location and zoom level
OK, now the controller supports Google Maps and if setUseDataConnection was set to true it would already show everything and would work fine with Google Maps. But the mission is to make it work offline.
The best tool to export the tiles for an area is Mobile Atlas Creator (version up to 1.8, they removed Google Maps from 1.9). Export an area with map source set to Google Maps in desired zoom levels in Osmdroid ZIP format (it's going to take a while). Put the output ZIP file into the /sdcard/osmdroid/Google Maps directory (if it doesn't exist, create it; the name has to be the same as the first parameter in the OnlineTileSourceBase constructor). Again, as I downloaded the source code for OSMDroid, I changed some values in the org.osmdroid.tileprovider.constants.OpenStreetMapTileProviderConstants class (such as OSMDROID_PATH to put the tiles in my directory instead of /sdcard/osmdroid).
Start up the application, now it should show offline tiles. Notice that now, if the SD card is unmounted, the controller will appear empty.
But the story doesn't end here. As the Google Maps terms of use (10.1.3.b) state:
No Pre-Fetching, Caching, or Storage of Content. You must not pre-fetch, cache, or store any Content, except that you may store: (i) limited amounts of Content for the purpose of improving the performance of your Maps API Implementation if you do so temporarily, securely, and in a manner that does not permit use of the Content outside of the Service; and (ii) any content identifier or key that the Maps APIs Documentation specifically permits you to store. For example, you must not use the Content to create an independent database of “places.”
So it sounds like an application is quite limited to use offline Google Maps tiles. 
Nonetheless, it seems they don't disallow to temporarily cache the tiles and work in online mode (of course then one also needs to store the tiles securely). To achieve this, unzip the ZIP file with extracted map area into the location /sdcard/osmdroid/Google Maps/tiles (or whatever is the location specified in OpenStreetMapTileProviderConstants.OSMDROID_PATH), then set mapView.setUseDataConnection(true). The default cache expiry period is not very long, so I also altered it in the source code by setting the values of OpenStreetMapTileProviderConstants.TILE_EXPIRY_TIME_MILLISECONDS and OpenStreetMapTileProviderConstants.DEFAULT_MAXIMUM_CACHED_FILE_AGE to (1000 * 60 * 60 * 24 * 365 * 10) (that's 10 years) . This will make OSMDroid to use pre-fetched tiles for areas where available, but for the rest of the world it will download new tiles.
That's it. Hope it helps.
UPDATE As stated in the comments, you need the Mobile Atlas Creator version 1.8 (I see they removed all versions prior to 1.9 from sourceforge). The other tool capable of fetching tiles is "OsmAnd Map Creator" (I see they deprecated it too, still it's available for download), but I'm not sure what the output directories look like, so one would have to adjust it manually to the structure OSMDroid controller expects.