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

warwound

Expert
Licensed User
Longtime User
I have now updated some of the early examples in this thread.

The updates were to simply add the drawable-nodpi folder that is included with the library download to the example code attachments.

Early versions of the OSMDroid library had these drawables built in but as the library has been updated it's been better to keep them external - leaving the user to copy the drawable-nodpi and/or balloon_overlay.xml resources to their project's res folder.

All examples now seem ok and work fine.

Martin.
 

BarrySumpter

Active Member
Licensed User
Longtime User
Thanks heaps Martin!:sign0188:

Do we still need these comments in each post?


....
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


....
 
Last edited:

BarrySumpter

Active Member
Licensed User
Longtime User
In project 9.
If I change the lat n lng the map is blank.

B4X:
    'Marker2.Initialize("Elsewhere", "Downham Market", 52.60801, 0.39047, Null)
    Marker2.Initialize("Elsewhere", "Downham Market", -37.85091702146988, 145.05743605518342, Null)

Update:
I think I fixed this with:
B4X:
    Marker2.Initialize("Elsewhere", "Downham Market", -37.85091, 145.05743, Null)
 
Last edited:

BarrySumpter

Active Member
Licensed User
Longtime User
08 - OverlayMapMyLocation

On first install and run the GPS wouldn't turn on.
After rebooting the phone GPS worked properly.
Probably due to the state of my phone and not the app.

It was so nice to watch
the map move to next tile,
the location indicator move on the map,
and the location indicator to reflect the heading.

Super freakin nice!

I've sorted out the CloudMadeTiles password and API Key.
The caching works like a charm.
No huge blank screens waiting for the data over mobile connection.

Possible enhancements:
I'd like the Compass, MyLocation, and Follow Location to be on when run.
And to stay on when phone sleeps or orientation changes, or when I move the map.

Usually when I move the map I'm looking around for 5 to 10 seconds and would like the map to recenter on my location automaticly.

Or perhaps when I touch the compass to recenter on my location.
This feature may already be there?

Love your work, man.

I've attached my project that works for me.
When the map is moved and the FollowLocation is disabled,
I set a timer for 5 seconds to recenter map.
Just for testing.

Could be extended to 10 seconds.
Would prefer to trap a compass_click instead of the timer.
B4X:
Sub MyLocationOverlay1_Compass_Click
    MyLocationOverlay1.CompassEnabled=True
    MyLocationOverlay1.MyLocationEnabled=True
    MyLocationOverlay1.FollowLocationEnabled=True
End Sub
I'm working on the functionality at this point so the code isn't perfect.
Just a quick and dirty attempt at hacking.
I can't believe how little code is neccessary to get this all working.
Really enjoyng myself now.
 

Attachments

  • Map 09 - OverlayMap - Follow Me - Recentre Timer.zip
    39.7 KB · Views: 726
Last edited:

BarrySumpter

Active Member
Licensed User
Longtime User
I'm working with my enhanced Map 8 Overlay with recentre timer and melbourne zipped cached maps

Level 19 maps of a severely reduced area is 700 megs.
So I don't use level 19 maps at all.


In the attached pic,
the map is level 18

zoomed

(i.e. this is not a level 19 map but a zoomed level 18 map)

the flag to follow me: FollowMyLocation is toggled ON
when I drive off the current map tile
the next map tile will not load while map level 18 is zoomed.

B4X:
...
            MyXYTileSource.initialize(NewTileSourceName, 0, 19, 256, ".png", "http://localhost/")
...

I'd like the next map tile to load maintaining the zoom of a level 18 map.

I use this level when getting close to my destination.
i.e. I'm two houses away. And its on the right hand side of the street.


Any suggestions would be greatly appreciated.
 

Attachments

  • FollowMe Map Level 18 Zoomed.png
    FollowMe Map Level 18 Zoomed.png
    18.7 KB · Views: 564
Last edited:

warwound

Expert
Licensed User
Longtime User
I think that when you zoom from level 18 to 19 that the currently displayed tiles are stretched and displayed pending the level 19 tiles loading.
If DataConnection is enabled then the Mapview will request the level 19 tiles from localhost and all requests will fail.
Look in the unfiltered log and you'll see a related exception being raised for each failed tile request.
So if you pan the map at zoom level 19 i wouldn't expect it to display stretched level 18 tiles (for the panned to area) as those tiles were not loaded before the zoom level changed to 19.

So if you are sure that FollowMyLocation is still enabled, the question is does the fact that the level 19 tile requests raise exceptions prevent the map from automatically panning to the new location?

And if the map at zoom level 19 did automatically pan to the new location i'd expect it to display no tiles at all for the new area.
You probably don't want that but was hoping level 18 tiles would be loaded stretched.

Load your map at level 18 and wait til the tiles are displayed, zoom to level 19 and manually pan the map.
Do you see stretched level 18 tiles loaded for the panned to area?
If not then it's as i said - only already loaded tiles will display stretched at level 19 pending the loading of level 19 tiles.

So even if FollowMyLocation did still work at level 19 you'd not see anything as it panned to the new location except the plain map background.
Logically you might as well initialize the new XYTileSource with a maximum zoom level of 18 and forget about level 19 being created from stretched level 18 tiles.

That doesn't answer your original question - why doesn't FollowMyLocation work at zoom levels where no tiles exist.

Perhaps the failed tile requests cause FollowMyLocation to auto-disable and your timer has not yet run to re-enable it?

Perhaps FollowMyLocation is still enabled and working BUT, because the new panned to area is not covered by level 19 tiles and not covered by the stretched level 18 tiles, the map is panning to the new location but the display simply has nothing to display so displays the last view.

Try disabling the DataConnection as i suggested in previous posts and see if that causes FollowMyLocation to work at level 19.

Martin.
 

BarrySumpter

Active Member
Licensed User
Longtime User
Yes, its zoomed map 18 in anticipation of loading map 19.

Map 19 isn't available so NO map is loaded.

I'm happy to load the next map 18 then zoom to level 19.

I just need to know how to trap the situation / error.
 

warwound

Expert
Licensed User
Longtime User
You'll have to give me a bit more detail then...

Your map is on zoom level 18 and FollowMyLocation is enabled.
A timer will re-enable FollowMyLocation if it is disabled.

You are driving and the map fails to move to your current location...

Could be a problem with GPS reception in your car?

Or do you mean that your activity is automatically selecting zoom level 19 and once that zoom level has been selected - and things don't work as expected - then you want to trap when zoom level 19 gets automatically selected?
If you code automatically selects a zoom level you could trap it there or add a Sub which listens for the MapView ZoomChanged event.

An example of listening for the ZoomChanged event can be found here: http://www.b4x.com/forum/basic4andr...-osmdroid-mapview-b4a-tutorial.html#post92650

Martin.
 

BarrySumpter

Active Member
Licensed User
Longtime User
Appologies.
Got called away for a family errand.

I'm owrking with cached maps in zip file level 0 to 18 only.
GPS is working.
Track my location works without fail.
MapSample app is working as exactly as expected with zoom level 18.
i.e. Map View IS moving to centre my location on the screen.
Next map tile is loading properly.
And my location is still tracking correctly.
No problem at all.

I then + to map level 19
Map level 18 is zoomed in anticipation of loading map level 19,
Map level 19 does not exist.
So map level 18 stays zoomed
And MapSample app still follows my location on the zoomed map level 18.


When my location changes enough
to show no map but just the grid behind,

I - to move to level 18
which loads the the missing map tile at level 18 map correctly.

Then I + again to move to level 19
which zooms level 18 map in anticipation of loading level 19 map.

Leve 19 map does not exist
But the zoomed level 18 map stays
and coninutes to track my location correctly.


When my location changes enough
to where the next map tile at level 19 is needed.
The next map tile at level 19 does not exist
so is unable to load or display.
But still tracks my location as if there is a loaded map.
As in the pic where I've driven too far down the map.

I hope that is clearer.

----

I think I need to trap the error of map level 19 not being able to load.
i.e. I only have level 18 maps and specificly want to trap for this situation.

B4X:
If TryingToLoadNextTileAtLevel19 AND ErrorLoadingTile AND MaxMapsLevelAvailable is 18 then
         ZoomBackToLevel = 18
         LoadNextTileForLevel18
         ZoomInToLevel = 19
end if
B4X:
If TryingToLoadNextTileAtLevel19 AND ErrorLoadingTile AND MaxMapsLevelAvailable is 18 then
         ZoomBackToLevel = 18
         LoadNextTileForLevel18
         ' and DON'T ZoomInToLevel = 19 automaticly - i.e. make me zoom manually
end if
 
Last edited:

warwound

Expert
Licensed User
Longtime User
Ah right.

You wanted or expected the map to load and stretch zoom level 18 tiles when you were zoomed to level 19 and you wanted the map to do that as you panned.
But as you found only the tiles displayed when zoom level 19 was selected are stretched - panning the map at level 19 doesn't load stretched zoom level 18 tiles.

You could listen for the MyLocationOverlay LocationChanged(NewLocation As Location, IsFirstFix As Boolean) event.

When that event occurs get the MapView Zoom property and see if you are zoomed to level 19.
If you are then zoom back to a level where tiles exist.

That way you can zoom to level 19 and see the stretched level 18 tiles and as soon as you change your location the map will zoom out to a level where tiles exist.

The MapView doesn't expose errors such as failing to load tiles so you can't listen for an event there.

Martin.
 

BarrySumpter

Active Member
Licensed User
Longtime User
Excellent. Thank you Martin.

I'm wondering if there are any more interesting events we can trap?


----
Is there an event for Compass_Click ?

If not could you add one?
----

Is there a crosshair overlay where I can retrieve centre of screen/map lan n lng??
I need to move the map to a location
say a street corner
or bike path kick off point
under the crosshair.
Then get the centre lat n lng under the corsshair.
Then get the geoLocation of street address.

----
 
Last edited:

BarrySumpter

Active Member
Licensed User
Longtime User
Tried this code and will always snap back to display a proper map.
So will never have the background grid displayed.

but immediatly snaps back to Zoom level 18.
Will only hold stretched level 18 a second or two.
Even when stopped.


So no way to trap map change?


B4X:
Sub MyLocationOverlay1_LocationChanged(NewLocation As Location, IsFirstFix As Boolean)
    Log("MyLocationOverlay1_LocationChanged, Latitude="&NewLocation.Latitude&", Longitude="&NewLocation.Longitude&", IsFirstFix="&IsFirstFix)
    '    ToastMessageShow("ZoomLevel = " & MapView1.Zoom, False) ' true=3 sec , false=2 sec
    If MapView1.Zoom = 19 Then
    MapView1.Zoom = 18
    End If
End Sub

Is there a way I can place a button on the map?
 
Last edited:

warwound

Expert
Licensed User
Longtime User
You could change the sensitivity of the GPS detection.
I've not yet documented these methods and not yet used them myself.

Look at the MyLocationOverlay properties:

LocationUpdateMinDistance (Meters As Float)

LocationUpdateMinTime (Milliseconds As Long)

Both default to zero to enable maximum GPS sensitivity.

Experiment with various values for each and see if the Sub MyLocationOverlay1_LocationChanged is not executed so frequently.

Another option is to instead listen for the MapView CenterChanged event:

B4X:
Sub MapView1_CenterChanged
    If MapView1.Zoom>18 AND MyLocationOverlay1.FollowLocationEnabled Then
        MapView1.Zoom = 18
    End If
End Sub

That will change the zoom level back to 18 when the map center is changed , it might work better than changing the zoom level back to 18 when a new GPS fix is found.

Or try updating the code you posted to:

B4X:
Sub MyLocationOverlay1_LocationChanged(NewLocation As Location, IsFirstFix As Boolean)
    Log("MyLocationOverlay1_LocationChanged, Latitude="&NewLocation.Latitude&", Longitude="&NewLocation.Longitude&", IsFirstFix="&IsFirstFix)
    '    ToastMessageShow("ZoomLevel = " & MapView1.Zoom, False) ' true=3 sec , false=2 sec
    If MapView1.Zoom = 19 AND MyLocationOverlay1.FollowLocationEnabled Then
    MapView1.Zoom = 18
    End If
End Sub

Only change the zoom level back to 18 if FollowLocationEnabled is True.

The MyLocationOverlay doesn't detect any click events on the compass.
You could add a transparent Panel to your Activity sized and positioned the same as the compass - you could then listen for click and long click events on that Panel.

And just as you can add a Panel to your Activity or the Panel (or other container) that contains your MapView you can also add a Button or any other View.
Just be sure to add the Panel and Button etc after adding the MapView so they remain on top of the MapView.

Martin.
 

BarrySumpter

Active Member
Licensed User
Longtime User
...
The MyLocationOverlay doesn't detect any click events on the compass.
You could add a transparent Panel to your Activity sized and positioned the same as the compass - you could then listen for click and long click events on that Panel.
...


Now thats just genious.

Thats why I tried adding a button.


And just as you can add a Panel to your Activity or the Panel (or other container) that contains your MapView you can also add a Button or any other View.
Just be sure to add the Panel and Button etc after adding the MapView so they remain on top of the MapView.

Martin.

Tried that but was getting error on execution.

Will have another look.
 
Last edited:

warwound

Expert
Licensed User
Longtime User
Are you adding your Mapview directly to the Activity or to a Panel or other container?

Does your app just force close - can you post the unfiltered log showing the exception?

Martin.
 

BarrySumpter

Active Member
Licensed User
Longtime User
A sample script of adding a button ti an overlay would be better than me trying to guess how to do it again.

Testing now...

Using the AddViewsByCode.b4a app as a guide.

OK working now.
Don't know what I was doing to cause so many errors.
I think it was the sequence of initializing and adding etc.
Or a fresh boot. LOL.

U B so freakin fast man! I love it!

Got lucky.
Trying the AddView if different places.
This one worked.

Yeah, ok like you posted below.
Needs to be the last line in the sub.



B4X:
Sub Activity_Create(FirstTime As Boolean)
....
    
    Activity.AddView(MapView1, 0, 48dip, 100%x, 100%y-48dip)
    Activity.AddView(btnRecentre, 50dip, 50dip, 100dip, 60dip)
        
End Sub


Nice! Thanks for the sample below.
 
Last edited:

warwound

Expert
Licensed User
Longtime User
Here you go - tested on a Froyo emulator:

B4X:
Sub Process_Globals
End Sub

Sub Globals
   Dim Button1 As Button
   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)
   
   Button1.Initialize("Button1")
   Button1.Text="Click me"
   Activity.AddView(Button1, 5dip, 5dip, 120dip, 60dip)
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Sub Button1_Click
   Msgbox("Was clicked", "Button1")
End Sub

Martin.
 

Attachments

  • 01 - SimpleMap - AddButton.zip
    6 KB · Views: 666

BarrySumpter

Active Member
Licensed User
Longtime User
Thanks heaps for the sample app.


While we're discussing the emulator,
How do I get my 500megs of my_map_cache.zip
onto the emulator?

I don't have direct access to the virtualBOX sdCard.

I spent the day trying to get FTP to work.
First with b4a then on my IIS5.1 server.
grrrr

LOL. Looking for alpha = 255 on a panel view?

B4X:
pnlCompass.Color = Colors.Transparent    ' ????
Still testing.
 
Last edited:

warwound

Expert
Licensed User
Longtime User
You need to use the command line and the adb tool.

Have a look on Google for various techniques - not all are guaranteed to work so you'll have to experiment until you find one that does: https://www.google.co.uk/search?q=android+emulator+adb+push+to+sd+card&ie=UTF-8&oe=UTF-8.

Watch that if your OSMDroid uses the MyLocationOverlay it will possibly crash on the emulator when the app tries to enables sensors.

I gave up using the emulator when testing MyLocationOverlay apps but i see a possible workaround.

Open AVD Manager and select your virtual device.
Click Edit.
Now under the Hardware section you can click New and a dialog opens.
In the dialog is a Property drop down list where you can set or enable various hardware settings in the virtual device.

If you add GPS support and/or Magnetic field support then the virtual device might be configured with software emulation for the sensors and no longer crash.
This is on my list of things to do so may or may not work.

Note that i have found that if you change any settings in the Hardware dialog then the next time you start the virtual device all installed apps will be gone - it looks like changing a Hardware setting causes a device reset.

Have a look through the other available options - you can set device RAM size and density and some other values too which might be useful.

Martin.
 

warwound

Expert
Licensed User
Longtime User
I've just uploaded a new library ImageButtonsView.

You (Barry that is or anyone) might find it useful to create map controls.

Here's a screenshot of how i've used it in an app i'm developing:

image_buttons_view.jpg


Got a map type button, center the map button and a button to show a list of places currently on the map.

Along with the AHQuickAction3D library it adds a bit more function to the map.

Martin.
 
Top