Android Tutorial OSMDroid - MapView for B4A tutorial

You can find the OSMDroid library thread here: http://www.b4x.com/forum/additional...tes/16309-osmdroid-mapview-b4a.html#post92643.

AIM: Create and initialize a MapView, enable the map zoom controller and multitouch controller, set a zoom level then center the map on a location.

B4X:
Sub Process_Globals
End Sub

Sub Globals
   Dim MapView1 As MapView
End Sub

Sub Activity_Create(FirstTime As Boolean)
   If File.ExternalWritable=False Then
      '   OSMDroid requires the use of external storage to cache tiles
      '   if no external storage is available then the MapView will display no tiles
      Log("WARNING NO EXTERNAL STORAGE AVAILABLE")
   End If
   
   '   no EventName is required as we don't need to listen for MapView events
   MapView1.Initialize("")
   Activity.AddView(MapView1, 0, 0, 100%x, 100%y)
   
   '   by default the map will zoom in on a double tap and also be draggable - no other user interface features are enabled
   
   '   enable the built in zoom controller - the map can now be zoomed in and out
   MapView1.SetZoomEnabled(True)
   
   '   enable the built in multi touch controller - the map can now be 'pinch zoomed'
   MapView1.SetMultiTouchEnabled(True)
   
   '   set the zoom level BEFORE the center (otherwise unpredictable map center may be set)
   MapView1.Zoom=14
   MapView1.SetCenter(52.75192, 0.40505)
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

The code is pretty self-explanatory.

I've added the code to check if the device has available external storage as OSMDroid will not display any tiles if no external storage is available.
External storage is used to save/cache all downloaded tiles - no external storage means no map!
(I'll omit that check from future tutorials but it's something to bear in mind - not that i know of any Android devices that have no external storage).

Create and initialize a MapView, add it to the Activity 100% width and 100% height.
Enable the zoom and multi-touch controller.
Zoom in to level 14 then set the MapView center to a location (sunny Norfolk, UK!).

I've found that setting the map center and then immediately setting the zoom level does not work as expected.
I think that while the MapView is setting the map center it also zooms in so the end result is unpredictable.

Pan and zoom the map, now rotate your device and you'll see the map returns to it's initial state of zoom level 14, center (52.75192, 0.40505).

I shall show you how to save and restore the MapView state next...

Martin.
 

Attachments

  • 01 - SimpleMap.zip
    5.8 KB · Views: 4,624
Last edited:

jsk001

Member
Licensed User
Longtime User
...Is there a way to display the marker info immediately (without clicking) ???


AIM: Add a MarkersOverlay layer to the MapView.

With MarkersOverlay you can add Marker objects to your MapView.

If you use the default Marker icon, then MarkersOverlay requires the resources in the drawable-nodpi folder that is included with the library download.
Copy the drawable-nodpi to your project's Objects/res folder and ensure that all files in that folder are set to read-only


MarkersOverlay generates two events: Click and LongClick.

A Marker object is initialized:

B4X:
Dim Marker1 As Marker
Marker1.Initialize("A title", "A description", aLatitude, aLongitude, anIcon)

When a Marker is Clicked or LongClicked, the title, description and position of the Marker are pass as parameters to the event listener.
The position is passed as a GeoPoint object.

MarkersOverlay leaves the developer to take action when a Marker is Clicked or LongClicked - it does nothing more than generate the events.

Here's the example code:

B4X:
Sub Process_Globals
   Dim MapCenter As GeoPoint
   Dim TileSource As String
   Dim ZoomLevel As Int
End Sub

Sub Globals
   Dim MapView1 As MapView
   Dim MinimapOverlay1 As MinimapOverlay
   Dim ScaleBarOverlay1 As ScaleBarOverlay
   Dim TileSourceSpinner As Spinner
  
   '   create the MarkersOverlay
   Dim MarkersOverlay1 As MarkersOverlay
End Sub

Sub Activity_Create(FirstTime As Boolean)
   '   update the MenuItems
   Activity.AddMenuItem("Fit map to markers", "MenuItemSelect")
  
   '   MapView initialized with no EventName as we'll not be listening for MapView events
   MapView1.Initialize("")
   Activity.AddView(MapView1, 0, 48dip, 100%x, 100%y-48dip)
  
   MapView1.SetMultiTouchEnabled(True)
   MapView1.SetZoomEnabled(True)
  
   '   initialize the MarkersOverlay and add it to the MapView
   '   an EventName is required as we will listen for the two events that MarkersOverlay generates
   MarkersOverlay1.Initialize(MapView1, "MarkersOverlay1")
   MapView1.AddOverlay(MarkersOverlay1)
  
   '   create and initialize 2 Markers
  
   '   for Marker1 i'll use a custom icon
   Dim Icon As BitmapDrawable
   Icon.Initialize(LoadBitmap(File.DirAssets, "my_icon.png"))
  
   Dim Marker1 As Marker
   Marker1.Initialize("Home sweet home", "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi egestas suscipit convallis. Etiam pellentesque gravida est, quis luctus nunc commodo at. Nam et risus orci. Integer malesuada lorem dolor. Maecenas vestibulum cursus enim, tincidunt luctus libero placerat non. In vitae metus tellus, nec euismod nibh. Phasellus ut quam vitae justo sagittis auctor. Sed vel sapien dolor. Etiam ut sem id dolor iaculis ullamcorper. Aenean eget sem nibh, a tempor augue. Nulla interdum luctus molestie.", 52.75610, 0.39748, Icon)
  
   '   Marker2 will display the default OSMDroid icon
   '   the default icon is used if Null is passed as the Icon parameter
   Dim Marker2 As Marker
   Marker2.Initialize("Elsewhere", "Downham Market", 52.60801, 0.39047, Null)
  
   '   create a List and initialize it with the 2 Markers
   Dim Markers As List
  
   '   ** example code updated **
   '   Markers.Initialize2(Array As Marker(Marker1, Marker2))   '   this is the previous code
   Markers.Initialize2(Array As Object(Marker1, Marker2))      '   this is the new code
  
   '   add the List of Markers to the MarkersOverlay
   MarkersOverlay1.AddMarkers(Markers)
  
   If FirstTime Then
      TileSource="Mapnik"
     
      '   fit the MapView to the MarkersOverlay
      MapView1.FitMapToBoundingBox(MarkersOverlay1.GetBoundingBox)
   Else
      '   restore saved zoom level and map center
      MapView1.Zoom=ZoomLevel
      MapView1.SetCenter3(MapCenter)
   End If
  
  
   ScaleBarOverlay1.Initialize(MapView1)
   MapView1.AddOverlay(ScaleBarOverlay1)
  
   '   ensure that the MinimapOverlay is the LAST overlay added to the MapView
   MinimapOverlay1.Initialize(MapView1)
   MapView1.AddOverlay(MinimapOverlay1)
  
   TileSourceSpinner.Initialize("TileSourceSelect")
   Activity.AddView(TileSourceSpinner, 0, 0, 100%x, 48dip)
  
   TileSourceSpinner.AddAll(MapView1.GetTileSources)
   TileSourceSpinner.Prompt="Select a TileSource"
   TileSourceSpinner.SelectedIndex=TileSourceSpinner.IndexOf(TileSource)
   TileSourceSelect_ItemClick(TileSourceSpinner.SelectedIndex, TileSourceSpinner.SelectedItem)
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
   MapCenter=MapView1.GetCenter
   TileSource=MapView1.GetTileSource
   ZoomLevel=MapView1.Zoom
End Sub

'   two event listeners listen for the MarkersOverlay's two events

Sub MarkersOverlay1_Click(Title As String, Description As String, Point As GeoPoint)
   Log("MarkersOverlay1_Click")
   ToastMessageShow(Title&": "&Description, True)
End Sub

Sub MarkersOverlay1_LongClick(Title As String, Description As String, Point As GeoPoint)
   Log("MarkersOverlay1_LongClick")
   ToastMessageShow(Title&": Latitude="&Point.Latitude&", Longitude="&Point.Longitude, True)
  
   '   zoom the map in and center (with animation) on the LongClicked Marker
   MapView1.Zoom=MapView1.GetMaxZoomLevel-1
   MapView1.AnimateTo3(Point)
End Sub

Sub MenuItemSelect_Click
   Dim MenuItem As String
   MenuItem=Sender
   Select MenuItem
      Case "Fit map to markers"
         '   fit the MapView to the MarkersOverlay
         MapView1.FitMapToBoundingBox(MarkersOverlay1.GetBoundingBox)
   End Select
End Sub

Sub TileSourceSelect_ItemClick (Position As Int, Value As Object)
   MapView1.SetTileSource(Value)
   MinimapOverlay1.SetTileSource(Value)
End Sub

A MarkersOverlay is created, initialized and added to the MapView.

Two Markers are created and initialized.
The first Marker is initialized with a custom icon, the second Marker is initialized with Null passed as it's icon so the second Marker will display the default OSMDroid icon.

MarkerOverlay has a single method to add Markers - AddMarkers (Markers As List).
So you must pass this method a List of Marker objects.

If this is the first time that the activity has been created then the MapView FitMapToBoundingBox method is called passing the bounding box returned by the MarkersOverlay GetBoundingBox method.
The map center and zoom level are adjusted to fully display all Markers on the MarkersOverlay.

If this is not the first time that the activity has been created then the previously saved map center and zoom level are restored.
In Activity_Pause the map center and zoom level are saved.

A Click on a Marker will display a toast message with the Marker's Title and Description.

A LongClick on a Marker will display a toast message with the Marker's Title, Latitude and Longitude.
It will also zoom the map in to it's maximum zoom level minus one and center on the Marker's position using the MapView AnimateTo methid.
The animation will likely not be noticeable unless the tiles for the zoomed in area have already been cached.

MarkersOverlay and MarkersFocusOverlay (which i shall cover next) are both functional but lacking in features i think.
You cannot change a Marker's position for example, and if you add Markers and have previously added Markers then the previous Markers are replaced not added to.

Another point to note is how difficult it is to Click or LongClick a small icon.
The default OSMDroid icon seems to small to me, the custom icon i have used is much easier to Click or LongClick.

I shall look at creating a more feature packed MarkersOverlay and MarkersFocusOverlay over the next week or so.

Anyway, on to the last example - the MarkersFocusOverlay.

Martin.
 

shashkiranr

Active Member
Licensed User
Longtime User
Hi Warwound,

I want to create an offiline map. I am using MOBAC but i am not able to download for mapnik as they have removed the support to download. Is there any way i can download and create the files for osm?

Regards,
SK
 

warwound

Expert
Licensed User
Longtime User
Hi Warwound,

I want to create an offiline map. I am using MOBAC but i am not able to download for mapnik as they have removed the support to download. Is there any way i can download and create the files for osm?

Regards,
SK

Take a look here:
https://switch2osm.org/serving-tiles/

You could set up your own tile server and connect to your tile server with MOBAC.

If you don't have a linux box available I'd suggest using VirtualBox to create a virtual Ubuntu 12.04 box.
Install the packages: https://switch2osm.org/serving-tiles/building-a-tile-server-from-packages/
Then download the binary map data and you can then generate your own tiles.
 

Carlo2015

Member
Licensed User
Longtime User
hi! Im new to b4a and I am impressed with its capabilities. and to you warwound for this great lib.

now my question is can I use this http://download.geofabrik.de/asia.html with your library? and if yes then can you please show some example?

when i browsed the folder where the tiles are found the format of the files are in .png.tile but the file from the above link is in pbf and it says "suitable for Osmium, Osmosis, imposm, osm2pgsql, mkgmap, and others".

I am confused about this one.

Or can you please give me a link where I can download mapnik tiles for asia or just for the Phlippines?

I need to let my users have it preinstalled before they went on their way.

Thanks

Edit: Im now experimenting with Mobile Atlas Creator. Will ask more questions if I hit a wall again.
 
Last edited:

warwound

Expert
Licensed User
Longtime User
@Carlo2015

There links to .osm.pbf file on the Geofabrik page and you can download these and display them offline on either OSMDroid or GoogleMaps.
Search the forum for MapsforgeTileSource (for OSMDroid) and MapsForgeTileProvider (GoogleMaps).

These .osm.pbf files are binary databases containing Open Street Map data and are far smaller in size than an equivalent 'offline tile archive' created by MOBAC which would contains 1000s of tile images.
 

Carlo2015

Member
Licensed User
Longtime User
warwound thanks for the fast reply, I was able to download maps for OSMPublicTransport but the files inside the folders(zip) are just .png whereas the files created by your examples are in .png.tile

I am more confused now. :)

Anyway I'm no way near giving up.
 
Last edited:

warwound

Expert
Licensed User
Longtime User
.png and .png.tile are identical...

I think that naming a .png to .png.tile simply prevents the android media scanner finding the tile images and so prevents the tile images from appearing in the device's Gallery.
 

Carlo2015

Member
Licensed User
Longtime User
Great!

I am currently fetchng tiles for the whole Philippines. I will just use a windows application to rename all png in the folder.

I will update here for the results.


Thanks.
 

warwound

Expert
Licensed User
Longtime User
Great!

I am currently fetchng tiles for the whole Philippines. I will just use a windows application to rename all png in the folder.

I will update here for the results.


Thanks.

http://download.geofabrik.de/asia.html
See the .oms.pbf binary data for the entire Philippines is 'just' 111MBs?
Once converted from .osm.pbf format to the MapsForge .map format that's likely to be around 60MBs.

So your choice is to have either:
  • A (massive) tile archive containing many .png tile images.
    This will look best on the device - the tiles should be quick to load.
    Coverage of different areas and zoom levels will depend entirely on what tile images you have in your tile archive.
    The tile archive will likely be far larger than 60MBs.
  • A single MapsForge binary database of around 60MBs.
    Rendering map tiles on the fly on the device is a single threaded solution.
    So when your map requires tiles they get queued and rendered one at a time.
    That results in a not so good user experience - not as good as an offline tile archive.
    But the MapsForge .map file will give you coverage of the entire Philippines at all zoom levels.

I've downloaded and am converting the Philippines .osm.pbf file to MapsForge .map format now and i'll post again when the conversion is complete.
You can compare an offline tile archive to a MapsForge binary database and choose which one to use.

Martin.
 

warwound

Expert
Licensed User
Longtime User
The Philippines .osm.pbf file converted to a MapsForge .map database of 111Mbs in size.
Normally the conversion results in a much smaller file but i guess with all the coastline data for the Philippines, the file size didn't reduce.

You can download the Philippines MapsForge .map file here:
http://b4a.martinpearman.co.uk/osmdroid/philippines.map.zip

Can you let me know when you have downloaded it - i'll then delete it from my server.

Thanks.
 

rayofcumbria

Member
Licensed User
Longtime User
Hi Warwound,

I want to create an offiline map. I am using MOBAC but i am not able to download for mapnik as they have removed the support to download. Is there any way i can download and create the files for osm?

Regards,
SK
Hi SK, try here for offline maps
http://download.mapsforge.org/maps/

here's how I load them too:
' OSM 4.1 library used
Private osmMaps As OSMDroid_MapView
Private MFTSrc As MapsForgeTileSource

MFTSrc.GetMapsForgeTileSourceOptions.SetMapDatabaseFile(File.DirRootExternal, "mapname.map")
MFTSrc.GetMapsForgeTileSourceOptions.SetTileSourceName("MyOwnMap")
MFTSrc.Initialize

osmMaps.Initialize("osmMap")

osmMaps.SetUseDataConnection(True)
osmMaps.SetTileSource(MFTSrc)
 
Last edited:

warwound

Expert
Licensed User
Longtime User
thanks, done that but it moves too frequently based on gps movement

You'll have to implement some sort of 'smoothing' algorithm into your code to smooth/normalize the changes in orientation.
Not sure what technique is required though.
 

G-ShadoW

Active Member
Licensed User
Longtime User
how to change personicon as android.graphics.bitmap
MyLocationOverlay1.PersonIcon ?
 

Tom1s

Member
Licensed User
Longtime User
I couldn't find osmroid 4.1 mapsforgetilesource example ? Is the older one still working with 4.1?
 

Tom1s

Member
Licensed User
Longtime User
I got it working. It works with Activity.AddView(MapView1, 0,0, 100%x, 100%y)
Not with panel7.AddView(MapView1, 0,0, 100%x, 100%y)

Is it possible to use it somehow like this:
Activity.LoadLayout("Pagemain") 'containts actionbar and pcontent

pContent.LoadLayout("page1")'contains panels

pContent.AddView(MapView1, 0,0, 100%x, 100%y) ' this works one time. I have a menu and i like to switch between panels and this map?

Removeviews and then again?
 
Last edited:

Tom1s

Member
Licensed User
Longtime User
How this can be made?
upload_2015-6-25_20-58-22.png
 
Top