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!

Friday, May 6, 2011

Cloudmade routing on OSMDroid

Another mystery without much relevant results on Google search that turned out to be quite an easy task.
Cloudmade seems to make a lot of effort to provide developers with a lot of mapping features, and as I haven't found anywhere on the site how to pay them, I take it that their service is free. It seems also that they use OpenStreetMaps as a back-end, therefore (here goes the disclaimer) ROUTING IS NOT VERY ACCURATE. At least not yet, but it's being improved a lot; I've added a Starbucks shop about a month ago, and now it's visible on the map!
Among other services Cloudmade offers Routing HTTP API, which seems to be a breeze for use within HTML code (they support JSONP-style callback for script injection to go around cross-origin resource sharing restrictions). This is the one to be used in the open map view with open map controller, because Google Maps doesn't allow routing anywhere outside their domain (Google even removed routing API from Android right after the very first release!).
My implementation of the BlueLine does everything from requesting directions, all the way to drawing them on the map.
Create a class extending PathOverlay with the following code:

The Cloudmade API key goes into the manifest file within the application element like here

And that's pretty much it. It's very easy to make it work now with the application:

That's really 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.