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,809
Last edited:

warwound

Expert
Licensed User
Longtime User
You need to keep a reference to all Marker objects that you create as you need that reference in order to remove the Marker from the map.

The various overlays that you add a Marker to all have a RemoveMarker method, and a RemoveMarkers method too.
So you can remove a single Marker as long as you have a reference to the Marker and pass the reference to the overlay's RemoveMarker method.
And you can remove all Markers from an overlay by calling RemoveMarkers - no reference to the Markers is required with this method.

Martin.
 

davelew1s

Active Member
Licensed User
Longtime User
Hi Martin!
Thanks or the quick reply..you said,,
"You need to keep a reference to all Marker objects that you create as you need that reference in order to remove the Marker from the map."
I'm not 100% sure what that means, here is the sub I use to put markers down:-
Sub Mark_Click
Dim b As Button
b = Sender
Dim value As String
value = b.Text

Select value
Case "Start Drift"
Dim Icon As BitmapDrawable
Icon.Initialize(LoadBitmap(File.DirAssets, "marker_start_icon.png"))
Case "End Drift"
Dim Icon As BitmapDrawable
Icon.Initialize(LoadBitmap(File.DirAssets, "marker_end_icon.png"))
Case "Fish"
Dim Icon As BitmapDrawable
Icon.Initialize(LoadBitmap(File.DirAssets, "marker_fish1_icon.png"))
End Select

markersactive = True
Dim name As String = Marker & markernumber

Marker0 .Initialize3 (name,"Text to gohere",MapView1.GetCenter,Icon)
markernumber = markernumber + 1
markers .Add (Marker0)
MarkersOverlay1.AddMarkers(markers)
MapView1 .Invalidate
lastmark = (MapView1.GetCenter) 'Remember last mark so you can get the distance later

End Sub

is 'Marker0' the reference you mean because all my markers have the same ref.
I tried to do (marker & marknumber) .Initialize3 (nam..... but it didn't work I searched the forum for any help on this but got nothing.
Can you point me in the right direction?
Thanks Dave.
 

warwound

Expert
Licensed User
Longtime User
is 'Marker0' the reference you mean

Yep!

Maybe you could do something like this:

B4X:
Sub Globals
   '   create a Map in your Globals
   Dim MyMarkers As Map
End Sub

Sub Activity_Create(FirstTime As Boolean)
   '   Initialize the Map in Create
   MyMarkers.Initialize
End Sub

Sub Mark_Click
   Dim b As Button
   b = Sender
   Dim value As String
   value = b.Text

   Select value
   Case "Start Drift"
   Dim Icon As BitmapDrawable
   Icon.Initialize(LoadBitmap(File.DirAssets, "marker_start_icon.png"))
   Case "End Drift"
   Dim Icon As BitmapDrawable
   Icon.Initialize(LoadBitmap(File.DirAssets, "marker_end_icon.png"))
   Case "Fish"
   Dim Icon As BitmapDrawable
   Icon.Initialize(LoadBitmap(File.DirAssets, "marker_fish1_icon.png"))
   End Select

   markersactive = True
   Dim name As String = Marker & markernumber
   
   '   create a new Marker, keep a reference to it using the Marker name as a key in the MyMarkers Map
   Dim NewMarker As Marker
   NewMarker.Initialize3 (name,"Text to gohere",MapView1.GetCenter,Icon)
   MyMarkers.Put(name, NewMarker)
   
   markernumber = markernumber + 1
   markers .Add (NewMarker)
   MarkersOverlay1.AddMarkers(markers)
   MapView1.Invalidate
   lastmark = (MapView1.GetCenter) 'Remember last mark so you can get the distance later
End Sub

Sub RemoveMarker(Name As String)
   Dim MarkerToRemove As Marker=MyMarkers.Get(Name)
   MarkersOverlay1.RemoveMarker(MarkerToRemove)
End Sub

You can use a Map where the map key is the Marker (unique) name and the Map value is the reference to the Marker that you will later require to remove it from the MarkersOverlay.
(Obviously that's not a complete example, it just shows were in 'Sub Globals' and 'Activity_Create' you'd create and initialize the new MyMarkers object).

Martin.
 

davelew1s

Active Member
Licensed User
Longtime User
Hi Martin!
That works great I've not used a map before so I will have to do some reading to fully understand it.
One final (probably not) question which may be linked with this ... is there a way to save to file and read back to the tablet and PC. I was hoping for a csv file,saving the pathoverlay should be easy but the markers list might not...any thoughts?
Thanks Dave.
 

warwound

Expert
Licensed User
Longtime User
You might be able to use the RandomAccessFile library and it's WriteObject and ReadObject methods to save and load a PathOverlay and a Map containing Markers.

I have successfully used RandomAccessFile to save and load single GeoPoint objects and think it will also work with a Map of Markers though am not sure about a PathOverlay.
PathOverlay has a GetAllPoints method that returns a List of GeoPoints - so if saving and loading a PathOverlay fails next try saving and loading this List of GeoPoints.

Within the OSMDroid library java these OSMDroid objects such as GeoPoint implement a java interface that enables them to be serialized and therefore saved and loaded to and from file.
But if the RandomAccessFile fails to work then parsing all your PathOverlay GeoPoints and Marker properties to a CSV file is an alternative solution - not a very elegant solution.
A more elegant solution would be to use the SQL library to save all of that data, it'd be a more suitable tool for the job.
SQL tutorial can be found here: http://www.b4x.com/forum/basic4android-getting-started-tutorials/6736-sql-tutorial.html.

Martin.
 

davelew1s

Active Member
Licensed User
Longtime User
OK Martin I'll play around with you suggestions and see if I can make it work.
Once again thanks for all your work and your help.
Dave.
 

davelew1s

Active Member
Licensed User
Longtime User
Hi!
I've just discovered the beauty of the MyLocationOverlay and wondered if the following can be changed from true to false?

'FollowLocationAutoDisabled is generated if FollowLocation is enabled and the user drags the map. The drag causes FollowLocation to be automatically disabled.
This is the default behaviour of the native Android OSMDroid library - i couldn't decide whether to change that default behaviour so have left it as it is for now.'
Thanks Dave.
 

warwound

Expert
Licensed User
Longtime User
Hi!
I've just discovered the beauty of the MyLocationOverlay and wondered if the following can be changed from true to false?

'FollowLocationAutoDisabled is generated if FollowLocation is enabled and the user drags the map. The drag causes FollowLocation to be automatically disabled.
This is the default behaviour of the native Android OSMDroid library - i couldn't decide whether to change that default behaviour so have left it as it is for now.'
Thanks Dave.

The best solution is to listen for the FollowLocationAutoDisabled event and re-enable FollowLocationEnabled if it is disabled when the user drags the map.

Martin.
 

ValDog

Active Member
Licensed User
Longtime User
Wow, this library effort is incredible. I've worked through all the tutorial programs and read through all the related forum discussion, and am still trying to get my head around all of it. I had been working with the WebView methodology, but found that limiting for a number of reasons. Your library seems ideal for my needs, but I need to tie together all the overlay elements to accomplish what I am trying to do. Here's a basic description of my program needs:

- load a map, say using the Mapnik source
- listen for changes in GPS location; display a marker for each new location and draw the connecting path between the markers
- display (balloon?) information for a marker when it it touched.

I sure would appreciate a basic roadmap to accomplish this...
 

javiers

Active Member
Licensed User
Longtime User
Version 3.21 of OSMDroid supports the TilesOverlay

TilesOverlay is similar to the XYTileSource, you use it to add a new TileSource to your map.
A TilesOverlay however displays on top of the currently selected TileSource.
You use it to add a transparent tile layer to your map.

Take a look at this tile from the OpenSeaMap project:

10962.png


It's a transparent tile created so that it can be displayed on top of another tile layer.
This is the type of tile you'll use a TilesOverlay to display.

Some example code:

B4X:
Sub Process_Globals
End Sub

Sub Globals
   Dim MapView1 As MapView
   Dim TilesOverlay1 As TilesOverlay
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
  
   MapView1.Initialize("")
   MapView1.SetMultiTouchEnabled(True)
   MapView1.SetZoomEnabled(True)
   MapView1.Zoom=9
   MapView1.SetCenter(53.024, 0.421)
   Activity.AddView(MapView1, 0, 0, 100%x, 100%y)
  
   '   open sea map tiles are available from zoom levels 11 to 19
   TilesOverlay1.Initialize("SeaMap", 11, 19, 256, ".png", "http://tiles.openseamap.org/seamark/")
  
   '   TilesOverlay1.LoadingBackgroundColor=Colors.LightGray
   '   TilesOverlay1.LoadingLineColor=Colors.DarkGray
  
   MapView1.AddOverlay(TilesOverlay1)
  
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Load the MapView, displaying the default Mapnik tiles and displays the OpenSeaMap tiles on top ofthe Mapnik tiles.

You'll need to pan to an area of sea to see the OpenSeaMap tiles.
(There is a windfarm that should be visible in the demo code).

And you can uncomment the LoadingBackgroundColor and LoadingLineColor lines to experiment with those properties and see how they work.

Note that OpenSeaMap tiles do not exist for many areas and at many zoom levels.
Where an OpenStreetMap tile is requested and it does not exist, the MapView will (internally) generate a 404 tile not found error.
If the LoadingBackgroundColor is set to Colors.TRANSPARENT then no background color is displayed while waiting for a tile to load and there is no problem.
If you set a LoadingBackgroundColor that color is displayed until the requested tile has been downloaded.
So where tiles do not exist and fail to download you will see the background color persist and block the underlying Mapnik tiles.

I have not yet tried offline tiles with a TilesOverlay but it should work - the TilesOverlay should fetch tiles from any tiles archive in the osmdroid folder and display them instead of downloading tiles from the internet.

Martin.


Hello, how can load layers of openseamaps weather?
 

warwound

Expert
Licensed User
Longtime User
Have you tried to modify the example that displays the openseamap 'seamarks' tiles?
Have you got a working URL to the weather tiles so you can replace the seamarks URL with the weather URL?

Martin.
 

davelew1s

Active Member
Licensed User
Longtime User
Hi!
While we are on a nautical theme, I've written a small app for use when I go out fishing...it seems to be working OK...so now's the time to mess it up.
Has anyone found and loaded nautical charts? I've used openseamaps whick is OK but I was thinking more like Garmin Bluechart..charts with all nautical info.
Dave.
 

javiers

Active Member
Licensed User
Longtime User
Hi, the URL is as follows. I do not know if it is correct;

TilesOverlay2.Initialize ("SeaMap", 4, 7, 256, ".Png", "http://www.openportguide.org/tiles/actual/wind_vector/5/7/67/41/")

More information is at: http://www.openportguide.org/wiki_/Help:Weather_tiles


Fixed. The correct URL is:

TilesOverlay2.Initialize ("SeaMap", 4, 7, 256, ".Png", "http://www.openportguide.org/tiles/actual/wind_vector/5/")

where 5 is the value corresponding to:

the forcast time is following with "t /". Actually "t" Can Have the Following values: 5, 7, 9, 11, 15, 19, 23 or 27. A more detailed description for "t":
 

davelew1s

Active Member
Licensed User
Longtime User
Hi javiers!
Not quite what I looking for but very interesting...got it working the same as you, will probabaly use it later in another project.
Thanks Dave.
 

javiers

Active Member
Licensed User
Longtime User
It's a way to get weather forecasts for interventions of emergency services (forest fires, floods ...).

This community and the developers are awesome. A greeting.
 

warwound

Expert
Licensed User
Longtime User
Hello, is possible move a marker on the map?

Nope!

A marker is in fact an OverlayItem (using the java terminology) and if you take a look at the documentation: http://code.google.com/p/osmdroid/s...a/org/osmdroid/views/overlay/OverlayItem.java you'll see it states:

Immutable class describing a GeoPoint with a Title and a Description.

The only solution is to remove a marker and then create a new one and add it to the map when you want to change a marker position.

Martin.
 
Top