B4A Library OSMDroid - MapView for B4A

Here we have my latest library - OSMDroid provides a MapView for B4A.

More info on the original (native Android) OSMDroid project can be found here: osmdroid - OpenStreetMap-Tools for Android - Google Project Hosting.

Library reference is no longer included in this post due to limits on the number of characters allowed in a single post.

I have created some tutorials to show basic usage of the library and will update this thread with a link to them as soon as i have them all uploaded.

** Your attention is drawn to the included file Apache License Version 2.0.txt, which is a copy of the Apache License Version 2.0 under which the native Android OSMDroid library is released **

Martin.
 

Attachments

  • OSMDroid_3_0_8_v3.60.zip
    361.9 KB · Views: 4,065
Last edited:

androidvh

Member
Licensed User
Longtime User
Martin,

Thank you very much for your excellent support.
all is fine and it is very simple:

Dim MyXYTileSource AsXYTileSource
MyXYTileSource.Initialize("MapnikOffline", 10, 22, 256, ".png", http://localhost/)
Mapview1.AddXYTileSource(MyXYTileSource)
Mapview1.SetTileSource("MapnikOffline")

Volker...
 

canalrun

Well-Known Member
Licensed User
Longtime User
Hello,

The Mapnik tile source is the default tile source. I have been using this successfully for the past couple of years (it seems). I noticed this past Monday that I no longer receive tiles from Mapnik using an app that previously worked and hasn't changed in months. I do receive tiles if I change the map source to some of the other choices: CycleMap, MapQuestOSM, and a few others.

Are you aware of something that has changed with Mapnik?

Added a little later:

I did a test. I downloaded off-line tiles for the same area, same zoom level, from Mapnik. I put these into a zip file in the osmdroid/tiles directory. (I have used zip files of off-line tiles frequently.) Looking at the individual tiles with an image editor, they do contain correct looking data.

When I rerun the app, it picks up these tiles and displays them properly.

I also noticed something else strange. Within the osmdroid/tiles directory there is no Mapnik directory created when Mapnik is the chosen online tile source. When CycleMap is the chosen online tile source, as expected, I will find the directory osmdroid/tiles/CycleMap.

Barry.
 
Last edited:

warwound

Expert
Licensed User
Longtime User
Hello,

The Mapnik tile source is the default tile source. I have been using this successfully for the past couple of years (it seems). I noticed this past Monday that I no longer receive tiles from Mapnik using an app that previously worked and hasn't changed in months. I do receive tiles if I change the map source to some of those choices: CycleMap, MapQuestOSM, and a few others.

Are you aware of something that has changed with Mapnik?

Barry.

Take a look here: https://groups.google.com/forum/?fromgroups&hl=en#!topic/osmdroid/5SQEn-IG9D0

The Mapnik tileservers have been blocking access to tiles for the past few days.
That link says that adding a header to HTTP tile requests will solve the problem - the tileservers will recognise the request for a tile and not reject it.
The linked to thread also says that the Mapnik tileservers may simply too busy to recognise all requests for tiles - and that adding a header to the tile requests and that 'seeming' to work is just a coincidence (the tileservers are no longer too busy so would have handled the request anyway).

Whichever of those 2 is true i don't know and it'll be 'a few days or more' before anyone knows why OSMDroid requests for tiles are not being served.
When we know what's going on i'll be happy to update the library to add any headers required if that is the solution.

Let's keep an eye on that thread to see what develops.
Meanwhile you could recompile your app without any option to display Mapnik tiles...

Martin.
 

canalrun

Well-Known Member
Licensed User
Longtime User
Take a look here: https://groups.google.com/forum/?fromgroups&hl=en#!topic/osmdroid/5SQEn-IG9D0

The Mapnik tileservers have been blocking access to tiles for the past few days.
That link says that adding a header to HTTP tile requests will solve the problem - the tileservers will recognise the request for a tile and not reject it.
The linked to thread also says that the Mapnik tileservers may simply too busy to recognise all requests for tiles - and that adding a header to the tile requests and that 'seeming' to work is just a coincidence (the tileservers are no longer too busy so would have handled the request anyway).

Martin.

Thanks for the reply. I will check out the links you posted and also keep an eye on this.

I guess you were replying while I was making my additional comments. Your reply also explains the things I added to my original post.

Thanks,
Barry.
 

warwound

Expert
Licensed User
Longtime User
I guess you were replying while I was making my additional comments. Your reply also explains the things I added to my original post.

Your missing Mapnik directory in the external memory osmdroid/tiles/ directory sounds odd.
But if this device has not previously successfully download and cached tiles from the Mapnik tileserver then i guess it's logical.

I've been thinking and wondering if i could set up a tileserver, take a look at this and subsequent posts: http://www.b4x.com/android/forum/th...-basic4-remote-mysql-setup.36452/#post-216923.
If Mapnik are no longer going to recognise requests from applications using OSMDroid then i could create a private tile server on one of my mainly unused VPSs.
As long as it has enough disk space and we can wait while the entire planet openstreetmap data is imported then i'd have my own working tileserver.
(I've used the VPS previously to serve tiles for country sized areas but am not sure if it'd handle the entire planet - the entire planet is around 60GBs of data).
With a working private tile server i could allow access only to requests from users of the B4A OSMDroid library or even only allow access to forum users that have (previously) agreed access.

Leave me to investigate whether my VPS can be set up as a tileserver for the entire planet - i suspect that it's 60GB disk space will only be enough to download the OSM planet data, leaving no space for the creation of the database to import the planet data into.

Martin.
 

canalrun

Well-Known Member
Licensed User
Longtime User
Setting up your own server is an interesting idea.

I wonder why Mapnik suddenly made this change. I tested a few other OSMdroid data sources and they seem to work as before. I wonder if they will be next to change?

What actually happened in my case is I noticed tiles were not updating as I scrolled the map around different areas. At that point I had cached tiles for some areas but I was scrolling outside those areas. Today, as a test, I deleted all the zip files and directories from my osmdroid directory to remove all the cached tiles. I then ran the app. No Mapnik tiles showed up, but changing to other sources tiles did show up. I looked in the osmdroid directory. There were sub-directories for the other sources, but none for Mapnik.

Today I downloaded off-line tiles using the tile downloader I've mentioned in this thread previously. Browsing the downloaded tiles I see that the PNG images are correct. Putting the zip files in the osmdroid directory allows them to be used as cached tiles.

My tile downloader basically uses HTTPUtils2 and the Mapnik tile source address in the form:
http://[abc].tile.openstreetmap.org/zoom/x/y.png

The tiles are still on Mapnik. I tend to think the HTTP headers mentioned in the Google Groups link you sent earlier might be the solution. I don't know what headers HTTPUtils2 adds.

Barry.
 

warwound

Expert
Licensed User
Longtime User
I tend to think the HTTP headers mentioned in the Google Groups link you sent earlier might be the solution. I don't know what headers HTTPUtils2 adds.

I've have guessed that HttpUtils2 adds no header to it's requests - just as OSMDroid adds no header to it's tile requests.
In the b4a library source i have found the code that creates the tile requests and can see where i can simply add a line such as:

B4X:
head.setHeader("User-Agent", "OSMDroid");

Following the thread on Google groups it's been said not to use 'OSMDroid' as a user-agent as technically speaking your application is not the official OSMDroid application.
I thought it'd be better to instead use the application packagename - but there's no way to get this packagename in the part of the code that sets the header.
And then we might find that using the packagename as the user-agent does not force the Mapnik tileserver to return tiles.
Or it might even be that the user-agent setting has no effect whatsoever on whether the Mapnik tileserver will return a tile - the Mapnik tileserver may only return a tile if it is not overloaded.

Martin.
 

warwound

Expert
Licensed User
Longtime User
There's been some developments regarding the Mapnik tiles not being served issue:
The problem is:
The default Apache HttpClient user-agent was recently banned from accessing tile.openstreetmap.org (server returns 403). As a result, the Mapnik provider is no longer serving any tiles for osmdroid.

OpenStreetMap's tile usage policy ( http://wiki.openstreetmap.org/wiki/Tile_usage_policy ) says that a "Valid User-Agent identifying application" must be sent. The current user-agent osmdroid sends when it downloads a tile is "Apache-HttpClient/UNAVAILABLE (java 1.4)" which is pretty generic.

The official java solution has been to release version 4.1 of the (java) OSMDroid library which sets a tile request header and everything works again.

A solution for the b4a library will be for me to patch the code with a method to allow the developer to set the tile request header, and at some point in the future when i have plenty of time to spare i'll be able to update the b4a library to use the new version of the (java) OSMDroid library.

Hopefully i'll get the b4a library updated this weekend.

Martin.
 

warwound

Expert
Licensed User
Longtime User
OSMDroid updated to version 3.60

This update primarily fixes the recent issue where Mapnik tiles no longer download.
Read Post #263 onwards in this thread for info.

My solution is to use the B4A application packagename as the HTTP User-Agent request header value.
When you call the MapView Initialize method the HTTP User-Agent request header value is set to your application's packagename.
I've also created a new MapView method that enables you to set whatever value you like for the HTTP User-Agent request header:

SetHttpRequestUserAgent (UserAgent As String)
Sets the value of the UserAgent header when requesting tiles using HTTP.
Default value is your application's packagename.
Recommended value is a unique value that identifies your application.


The default value of your packagename should work fine - but the method is there if it's needed.
Other updates in version 3.60 are explained in these threads:
Version 3.60 is attached to the first post in this thread.

Martin.
 

warwound

Expert
Licensed User
Longtime User
Hi,
Is there any way to detect the water on the map (lakes, see...) ?
I mean if a point is chosen - is it water or not ?

Seems the system has key for describin the objects: http://wiki.openstreetmap.org/wiki/Key:water

That 'key' exists in the OpenStreetMap data that is used to create map tiles.
There's no way to get any of that OpenStreetMap data from the map tiles - the map tiles are after all just image files that know nothing about the data that was used to create them.

A possible solution is to use a Spatialite database.
Look at the Spatialite example in post #2 of that thread.

See how it detects whether a coordinate is within a country boundary?
And the country boundary data has been imported into a Spatialite database from a 'readily available open source' shapefile?

Imagine replacing that country boundary data with coastline data - you can now detect if a coordinate is on land or at sea.

So that is a possible solution BUT requires that you have the required coastline data.
You could start by looking at the data available here: http://www.naturalearthdata.com/downloads/.

I think detecting whether a coordinate is on land or at sea is pretty straightforward as the data is available.
You mention lakes - is data for lake boundaries available, or could you create it yourself?
If so then that too is possible.

Your only limit is whether you can obtain the data that defines sea/lake/ocean boundaries.

Martin.
 

warwound

Expert
Licensed User
Longtime User
@peacemaker

Just a thought...
Are you using an SQLite database in your project?
If you decide to use Spatialite to detect whether a coordinate is on land or over water then you might also want to move all data from SQLite to the Spatialite database - no point using two different databases when a single Spatialite database can be used for all data.

Martin.
 

peacemaker

Expert
Licensed User
Longtime User
Thanks for info.
Seems, simpler "dumb" solution is to GetPixel's Color of a loaded tile....
Need to get screen coordinates of a marker... possible ?
 

warwound

Expert
Licensed User
Longtime User
You'd need to get two things from the geographical coordinate:
  • The filename of the tile that contains the geographical coordinate, which would be formatted something like:
    <tilesource-name>/<zoom-level>/<tile-x>/<tile-y>.png
  • The cartesian coordinates of the geographical coordinate on the tile.
    The cartesian coordinates X and Y values would range from 0 to 255.
    Screen coordinates can be converted to tile coordinates as long as you know the tile filename tile-x and tile-y values.
You could then get the tile from the cache folder on external memory, and read the pixel value.

Look at the other example project in the Spatialite thread: http://www.b4x.com/android/forum/threads/spatialite.36296/#post-213031.
You'll see i included a small helper library named Mercator which can be found here: http://b4a.martinpearman.co.uk/spatialite/.
This small library (Mercator_library_files.zip) contains methods to convert between WGS84 and screen coordinates.

That other Spatialite example project contains code that should help you work out how to get the values you need, here's a summary of the Mercator library methods:

Mercator
  • Mercator
    Fields:
    • MAX_LAT As Double
    • MIN_LAT As Double
    Methods:
    • FalseEasting (ZoomLevel As Int) As Int
    • FalseNorthing (ZoomLevel As Int) As Int
    • GetMaxPixels (ZoomLevel As Int) As Int
    • LatToY (Latitude As Double, ZoomLevel As Int) As Int
    • LonToX (Longitude As Double, ZoomLevel As Int) As Int
    • Radius (ZoomLevel As Int) As Double
    • XToLon (X As Int, ZoomLevel As Int) As Double
    • YToLat (Y As Int, ZoomLevel As Int) As Double

Given the overhead of loading a cached tile each time you want to check if a point is on land or at sea, and the questionable reliability of using a tile pixel value to establish if a point is on land or at sea i really think the Spatialite solution is better.

Martin.
 

warwound

Expert
Licensed User
Longtime User
Very interesting web-site: http://maps.cloudmade.com/?styleId=22688#
How is made ? Seems, basing on the same OSM.

OpenStreetMap data is pure binary data that defines points, polylines and polygons.
You can render this data with whatever visual style you want.
Take a look here: http://tiledrawer.com/.
See the differently styled tiles available, in the right hand column?
This is all the same OpenStreetMap binary data, rendered with different visual styles.

More technical info can be found here: http://wiki.openstreetmap.org/wiki/Stylesheet.

Martin.
 

peacemaker

Expert
Licensed User
Longtime User
OpenStreetMap data is pure binary data that defines points, polylines and polygons.
You can render this data with whatever visual style you want.
Take a look here: http://tiledrawer.com/.
See the differently styled tiles available, in the right hand column?
This is all the same OpenStreetMap binary data, rendered with different visual styles.

More technical info can be found here: http://wiki.openstreetmap.org/wiki/Stylesheet.

Martin.

And no such render features in your B4A libs ?
warwound said:
  • LatToY (Latitude As Double, ZoomLevel As Int) As Int
  • LonToX (Longitude As Double, ZoomLevel As Int) As Int


No such methods in your main OSMDroid lib ?
 

warwound

Expert
Licensed User
Longtime User
If you are displaying map tiles from an online tile provider then your tiles have already been rendered - you can't change their appearance.
You can display tiles from any online tile provider that you want.
So, for example, if you like a particular Cloudmade tile style you can display those tiles on your map, see here for an example: http://www.b4x.com/android/forum/threads/osmdroid-mapview-for-b4a-tutorial.16310/#post-92864.
I think Cloudmade even let you design your own custom tile style - but am not 100% sure on this. Create a developer account at Cloudmade and you'll see if this option is available.

Another option is to use the MapsForgeTileProvider as a tile source.
The MapsForgeTileProvider comes with a default 'render theme', you can see how that render theme defines the visual style of the tiles that it renders here: http://code.google.com/p/mapsforge/.../src/main/resources/osmarender/osmarender.xml.
And there's some technical info here: http://code.google.com/p/mapsforge/wiki/RenderThemeAPI.
The MapsForgeTileProvider uses it's built in render theme by default but you can also create your own render theme and use that instead.

But MapsForgeTileProvider renders tiles on the fly on the device, so it requires a MapsForge map database to be available on the device.
A MapsForge map database contains OpenStreetMap data in a highly optimised and compressed format.
Read the MapsForgeTileProvider post linked to above - it details how the size of the area you wish to render tiles for is proportional to the size of the map database.

Yet another option is to render your own map tiles with a custom style - this is an alternative to rendering your own tiles on the device with the MapsForgeTileProvider.

The 'SWITCH2OSM' website is a great source of info, you'll find it a useful read: http://switch2osm.org/.

As for OSMDroid containing any coordinate conversion methods - currently it does not.
There is an OSMDroid class named TileSystem which could be wrapped into the library to enable such coordinate conversions.
But i'm too busy to do that at the moment so the Mercator library is a quick and readily available alternative.
You could of course write your own conversion methods in B4A.

Martin.
 

peacemaker

Expert
Licensed User
Longtime User
More or less clear, thanks Martin.
Is there an event at the end of loading all tiles ?
 
Top