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

Spectre

Active Member
Licensed User
Longtime User
How about this...

  • OSMDroid_CircleOverlay
    Fields:
    • RADIUS_UNITS_TYPE_METERS As RadiusUnitsType
    • RADIUS_UNITS_TYPE_PIXELS As RadiusUnitsType
    Methods:
    • AddCircleOverlayItem (Center As GeoPoint, Radius As Float, RadiusUnitsType1 As RadiusUnitsType, StrokeWidth As Float, StrokeColor As Int) As CircleOverlayItem
      Creates, adds and return a CircleOverlayItem object.
    • AddCircleOverlayItem2 (Latitude As Double, Longitude As Double, Radius As Float, RadiusUnitsType1 As RadiusUnitsType, StrokeWidth As Float, StrokeColor As Int) As CircleOverlayItem
    • ClearCircleOverlayItems
      Clear the CircleOverlay of all CircleOverlayItems.
    • GetBoundingBox As BoundingBox
      Returns a BoundingBox that represents the area covered by the centers of all CircleOverlayItems.
    • Initialize
    • IsInitialized As Boolean
    • RemoveCircleOverlayItem (CircleOverlayItem1 As CircleOverlayItem) As Boolean
      Remove CircleOverlayItem1 from the CircleOverlay.
      Return True on success otherwise False.
  • OSMDroid_CircleOverlayItem
    Fields:
    • FILL As Style
      Geometry and text drawn with this style will be filled, ignoring all stroke-related settings in the paint.
    • FILL_AND_STROKE As Style
      Geometry and text drawn with this style will be both filled and stroked at the same time, respecting the stroke-related fields on the paint.
    • STROKE As Style
      Geometry and text drawn with this style will be stroked, respecting the stroke-related fields on the paint.
      This is the CircleOverlayItem default.
    Properties:
    • Center As GeoPoint
    • Radius As Float
    • RadiusUnitsType As RadiusUnitsType
    • StrokeColor As Int
    • StrokeStyle As Style
    • StrokeWidth As Float
    • Tag As Object
The CircleOverlay displays CircleOverlayItems - a CircleOverlayItem has a center, radius, stroke width, stroke color, stroke type and Tag property.
The CircleOverlay methods AddCircleOverlayItem and AddCircleOverlayItem2 create, add and return a CircleOverlayItem.
That reference to the CircleOverlayItem can be used to remove it from the overlay using the method RemoveCircleOverlayItem.

All CircleOverlayItem properties can be modified at runtime though you'll need to call the MapView Invalidate method to see any modifications applied.

You can set a CircleOverlayItem radius in units of pixels (fixed size circle) or meters (circle size depends on zoom level).
You can choose the stroke style (same as PathOverlay) of FILL, FILL_AND_STROKE or STROKE.
And the CircleOverlay now has a GetBoundingBox method.

Updated library files and demo b4a project can be downloaded from here: http://b4a.martinpearman.co.uk/osmdroid/CircleOverlay-20140505.zip.

Martin.

fantastic. I try right away...
 

Spectre

Active Member
Licensed User
Longtime User
Hi Martin, great job...
All works very well ... I have some problems on the distances in meters of the circle ...

CircleOverlayItem1=CircleOverlay1.AddCircleOverlayItem(CurrentPosition, 300, CircleOverlay1.RADIUS_UNITS_TYPE_METERS, 2dip, Colors.red)

300=200 meter +/-
850=800 meter +/-
 

warwound

Expert
Licensed User
Longtime User
@Spectre

Look here: http://code.google.com/p/osmdroid/s...src/main/java/org/osmdroid/views/MapView.java.
Lines around 1477:

B4X:
public float metersToEquatorPixels(final float meters) {
                        return meters / (float) TileSystem.GroundResolution(0, mZoomLevelProjection);
                }

This is the method used to convert pixels to meters for each zoom level.
So a circle with a radius of 300 meters will have the expected pixel size if it is centered on the equator.
Further north or south will introduce inaccuracies due to the nature of a spherical projection.

If you center your circles on the equator do you see a 'more correct' size?

Martin.
 

Spectre

Active Member
Licensed User
Longtime User
I have read ... I try to make sure, thanks for the suggestion and the link.

Riccardo.
 

eronquillo

Member
Licensed User
Longtime User
Hi, is there an example on how to utilize gps coordinates from a MySQL DB to be used to display the markers within the bounding box?
 

warwound

Expert
Licensed User
Longtime User
Hi, is there an example on how to utilize gps coordinates from a MySQL DB to be used to display the markers within the bounding box?

Can you give a bit more detail?

Are you wanting to only select rows from your table that are within the map bounds and then create a marker for each row?

Or are you wanting to select all rows from your table but only create a Marker if the row is within the map bounds?

When the map bounds changes do you then need to query the database again?

Martin.
 

eronquillo

Member
Licensed User
Longtime User
Can you give a bit more detail?

Are you wanting to only select rows from your table that are within the map bounds and then create a marker for each row?

Or are you wanting to select all rows from your table but only create a Marker if the row is within the map bounds?

When the map bounds changes do you then need to query the database again?

Martin.

Hi Martin! You're doing an awesome job with OSM Droid. I have incorporated your tutorials into a project where I can check in/out and then store the gps coordinates within an MySQL table. What I would like to do now is select the rows that have unique coordinates (lat and long) that reside within the bounding box and then display them. When the map moves, a re-query on the DB takes place and displays markers.


Edward
 

warwound

Expert
Licensed User
Longtime User
@eronquillo

I don't think you'll find a specific example that'll do as you require.

You say an MySQL database so presumably you're storing the coordinates online and not locally on the device?
Take a look at this Google search https://www.google.co.uk/search?q=mysql+spatial+extensions&ie=UTF-8&oe=UTF-8.
You'd make things easier to do if you use MySQL spatial extensions to store the coordinate data.
It'll take a bit more time to set up table columns using a spatial data type instead of a float or double data type but once set up you'll be able to make much more efficient queries.

Next, if each time the map is panned or zoomed you query your database for coordinates within the map bounds then you'll probably find you're making too many queries - if the user slowly pans the map bit by bit for example you don't want to query the database repeatedly.
I'm not sure what best to suggest here, ideally you want to have the database locally on the device so you don't have to wait for network requests to complete.

Have you seen the Spatialite library?
Spatialite is basically SQLite with spatial extensions.
A local (device based) spatialite database would enable you to make queries much faster and more efficiently than a remote MySQL database.

I think what i'd try to do is:
  • Fetch all coordinates from the database in a single query.
  • Create a b4a Type with properties to contain the coordinate data, use a GeoPoint type for the coordinate property.
  • Iterate through the database query result creating a List of these b4a Type objects - an object for each row in the results.
So you now have a List of objects, each object has a GeoPoint type property.
When you want to update the Markers, get the map bounds as a BoundingBox and iterate through the List of objects:
  • If the object's GeoPoint is within the BoundingBox and the object hasn't yet been added to the map as a Marker then create and add a Marker to the map.
    If a Marker has already been created for the object and added to the map then do nothing.
  • If the object's GeoPoint is not within the BoundingBox and the object has a corresponding Marker on the map then remove the Marker.
    If the object has no corresponding Marker then do nothing.

Martin.
 

IslamQabel

Active Member
Licensed User
Longtime User
Dear Warwound......
I would like to save the current location in map view as image (take screen shot ), is there a way to do that???
Thanks
 

warwound

Expert
Licensed User
Longtime User
Dear Warwound......
I would like to save the current location in map view as image (take screen shot ), is there a way to do that???
Thanks

This has been asked before and there is still no option in OSMDroid to create a Bitmap from the MapView.
There are various generic techniques to convert a View to a Bitmap but i'm not sure if these technqiues would work with the OSMDroid MapView.

I can only suggest that you search the forum, here's a starting point: http://www.b4x.com/android/forum/threads/get-bitmap-of-a-view.28236/

Martin.
 

Spectre

Active Member
Licensed User
Longtime User
Hi Martin!
to the problem of being able to always see the name of the point on the map next to the symbol, you can do something?

Riccardo...
 

warwound

Expert
Licensed User
Longtime User
to the problem of being able to always see the name of the point on the map next to the symbol, you can do something?

Didn't you manage to use the new ViewHostOverlay and ViewHost objects to add Labels to the map next to each point?

Martin.
 

Spectre

Active Member
Licensed User
Longtime User
Didn't you manage to use the new ViewHostOverlay and ViewHost objects to add Labels to the map next to each point?

Martin.

Hi!
you gave me an example, but the labels were visible only when you did click!, I was waiting for developments.
 

warwound

Expert
Licensed User
Longtime User
I've attached a project that creates Markers and adds a Label for each Marker - the Labels are permanently visible.

Martin.
 

Attachments

  • MultiViewHost-20140512.zip
    278.6 KB · Views: 617

Spectre

Active Member
Licensed User
Longtime User
I've attached a project that creates Markers and adds a Label for each Marker - the Labels are permanently visible.

Martin.

now it works but I have labels that are not anchored to the icon and move the map ...
 

warwound

Expert
Licensed User
Longtime User
On a zoom change as the map redraws, the Labels will be redrawn after the map, so there'll be a brief period where the Labels appear to not be anchored to the map.
Is that what you're seeing?

If you're not zooming the map then you should see the Labels anchored to the map and they should smoothly pan as you pan the map.

Does my example in post #375 work as you require but the code you've added to your project not work as required?
 

Spectre

Active Member
Licensed User
Longtime User
It happens when I move the map ... See image when I move the icons moving to DX and SX labels ...
In your example work ...
 

Attachments

  • Screenshot_2014-05-12-14-29-22.png
    Screenshot_2014-05-12-14-29-22.png
    391.9 KB · Views: 617

warwound

Expert
Licensed User
Longtime User
Hmmm...
That's odd.

I would ask you to post your project so i can take a look but bet it's not something you want to post and also it's probably a big project too?
How can i help you debug the problem?

Can you post just the code that creates the ViewHost and ViewHostOverlay?

Martin.
 

Spectre

Active Member
Licensed User
Longtime User
Hmmm...
That's odd.

I would ask you to post your project so i can take a look but bet it's not something you want to post and also it's probably a big project too?
How can i help you debug the problem?

Can you post just the code that creates the ViewHost and ViewHostOverlay?

Martin.
Hi Martin.

I work on for quite some time, even though I was working in VB.net I would like to avoid if possible to publish for now. I have also tried to copy the code of your project, but the result is the same.
B4X:
Sub load_WP ' load wp on map e fit to box+******************************************************************************************
Dim i,kkk As Int
Dim May_LatLng As LatLon
Dim MarkersList As List

MarkersList.Initialize
    Try

    If  Global.ListWPtoNavigate.Get(0) <> "" Then   
        May_LatLng.Initialize       
        Dim WPLoadOnMap As List
        WPLoadOnMap.Initialize
        spinWpSel.Clear
        For i=0 To Global.ListWPtoNavigate.Size -1       
            spinWpSel.Add(Global.ListWPtoNavigate.Get(i))'ad wp on spin to track   
            Dim Marker1 As OSMDroid_Marker
            ModWpAfterMArk(Global.ListWPtoNavigate.Get(i))           
            Dim g As Geodesic' conevert UTM To Lat lon
            g.Initialize
            Dim u As UTM
              u.UtmXZone = CurrentWPFuse
              u.X =CurrentWP_X
              u.Y = CurrentWP_Y
          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
            If Global.Glob_FUSE_N=True Then
                u.NorthHemisphere=True
            Else
                u.NorthHemisphere=False
            End If
            'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
              Dim ll As LatLon
              ll = g.WGS84UTMTOLatLon(u) 
              May_LatLng.Lat=ll.Lat 'coordinate lat lon on active WP
              May_LatLng.Lon=ll.Lon   
           
            Marker1.Initialize(NomeCurrentWPLong,CurrentWPZone &" " & CurrentWPFuse &" " & CurrentWP_X  &"-" & CurrentWP_Y ,May_LatLng.Lat, May_LatLng.Lon)
            Dim MarkerIcon As BitmapDrawable
           
            Dim AA1,AA2 As Int
            Dim DummyIcon As String
            Dim IconSrch As String
               
            For kkk=0 To Global.ListIconWPMap.Size -1
                IconSrch=Global.ListIconWPMap.Get(kkk)
                AA1=IconSrch.IndexOf("|")
                IconSrch=IconSrch.SubString2(AA1+1,IconSrch.Length)           
                If NomeCurrentWPLong.Contains(IconSrch) = True Then           
                    DummyIcon=Global.ListIconWPMap.Get(kkk)
                    DummyIcon=DummyIcon.SubString2(0,AA1)
                End If
            Next       
           
            Try       
                Dim Bitmap2 As Bitmap=LoadBitmap(Global.GPSDir & "/NatoIcon",DummyIcon)
            Catch
                Dim Bitmap2 As Bitmap=LoadBitmap(Global.GPSDir & "/NatoIcon", "Wp.png")       
            End Try
           
            MarkerIcon.Initialize(Bitmap2)
            Marker1.SetMarkerIcon(MarkerIcon)
            MarkersList.Add(Marker1)
           
            Dim MarkerLabel As Label
            MarkerLabel.Initialize("")
            MarkerLabel.Color=Colors.Yellow
            MarkerLabel.TextColor=Colors.Black
            MarkerLabel.Gravity=Gravity.CENTER           
            MarkerLabel.TextSize=10
            MarkerLabel.Text=Marker1.GetTitle.ToUpperCase
           
            Dim ViewHost1 As OSMDroid_ViewHost
            ViewHost1.Initialize(MarkerLabel, Marker1.GetPoint, 0, 0, Constants1.LayoutParams.ALIGN_CENTER, True)       
            ViewHostOverlay1.AddItem(ViewHost1)               
        Next       
       
        If LastMarker.IsInitialized = False Then
            LastMarker.Initialize(May_LatLng.Lat,May_LatLng.Lon)   
        Else
            LastMarker.Initialize(May_LatLng.Lat,May_LatLng.Lon)
        End If
       
        If MarkerOverlay1.IsInitialized Then
            MapView1.GetOverlays.Remove(MarkerOverlay1)
        End If
       
        MarkerOverlay1.Initialize2("MarkerOverlay1", MapView1, Null)
        MarkerOverlay1.AddItems(MarkersList)
        MapView1.GetOverlays.Add(MarkerOverlay1)
       
    End If
Catch
    Return
End Try
End Sub
 

Spectre

Active Member
Licensed User
Longtime User
Hmmm...
That's odd.

I would ask you to post your project so i can take a look but bet it's not something you want to post and also it's probably a big project too?
How can i help you debug the problem?

Can you post just the code that creates the ViewHost and ViewHostOverlay?

Martin.

Hi!!
I saw something that the labels seem to turn bad compared to the map. I set the orientation map with the magnetic sensor, it seems that overlay the label is in a different orientation or different overlay...
I confirm that depends on the orientation map ... If you orient the map with the compass labels moving ... if I disable orientation map it all works ...

Best Regards, Riccardo
 
Top