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, September 23, 2011

Calculate distance between two points with MySQL

This function here is the right one. It returns pretty same results as the Google Maps distance, which leads me to believe that it's kind of correct.


I've seen a lot of ready solutions online for the same input/output/platform combination (i.e. WGS84 degrees input/output on MySQL), but all of them used some other formula (at the end multiplying by 1.1515) that was producing some results that never worked for me.

On the other hand the following formula returns results identical with what Google Maps returns.
My function output

Cross check
Apparently there's some issue with the bearing and that's why the result is still not precisely accurate, but it's much closer than the other methods' outputs I've tried before.


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!

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.

Friday, April 29, 2011

Android universal Intent to start a "navigation activity"

The mission is what the title says. It was quite hard to define the problem to search for a solution. I ended up asking for help at StackOverflow and as I was suggested, I implemented a way to list some known apps and let the user select a preferred nav app.

So, let's say, somewhere on Activity there's a button, which on click is supposed to call the Navigation application. The way I did it, on tap, the activity shows a custom dialog letting the user select one of the known applications or prompt Android to find suitable activities implicitly.

Here's the source code of the payload of the main method invoked from the OnClickListener - showNavigation():


Here are few utils needed only to follow up the last call in the showNavigation() method - showTableDialog():


These are the resource files:
* this goes to /res/values/themes.xml

* this goes to /res/values/styles.xml

* this goes to /res/layout/table_dialog.xml

* this goes to /res/drawable as menu_gps.png







And here is the result dialog you are supposed to see:

On click on each row it will start up the respective activity.
That's it, I hope it helps!