Android Tutorial OSMDroid - MapView for B4A tutorial

Discussion in 'Tutorials & Examples' started by warwound, Mar 26, 2012.

  1. warwound

    warwound Expert Licensed User

    You can find the OSMDroid library thread here: http://www.basic4ppc.com/forum/addi...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.

    Code:
    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, 00100%x100%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.751920.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.
     

    Attached Files:

    Last edited: Mar 26, 2012
    DKHDKH, Jannis1985, koaunglay and 3 others like this.
  2. warwound

    warwound Expert Licensed User

    AIM: Save and restore the MapView center and zoom level on device orientation change.

    Code:
    Sub Process_Globals
       
    '   create a process global for each map state to be saved and restored on orientation change
       
       
    '   use a GeoPoint object to save the map center
       Dim MapCenter As GeoPoint
       
       
    '   an Int is all that's required to save the map zoom level
       Dim ZoomLevel As Int
    End Sub

    Sub Globals
       
    Dim MapView1 As MapView
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
       MapView1.Initialize(
    "")
       
    Activity.AddView(MapView1, 00100%x100%y)
       MapView1.SetZoomEnabled(
    True)
       MapView1.SetMultiTouchEnabled(
    True)
       
       
    If FirstTime Then
          
    '   set the default initial map center and zoom level
          MapCenter.Initialize(52.751920.40505)
          ZoomLevel=
    14
       
    End If
       
       MapView1.Zoom=ZoomLevel
       MapView1.SetCenter3(MapCenter)
    End Sub

    Sub Activity_Resume
    End Sub

    Sub Activity_Pause (UserClosed As Boolean)
       
    '   save the current map zoom level and center
       MapCenter=MapView1.GetCenter
       ZoomLevel=MapView1.Zoom
    End Sub
    Again the code is pretty self-explanatory.

    Two process global variables are used to save the map center and zoom level in Activity_Pause.

    In Activity_Create we check whether this is the first time that the activity has been created and if so set a default zoom level and map center.
    The MapView zoom level and center are then set - setting the initial default state or restoring the last saved state.

    I have used a GeoPoint object to save the map center as this is the type of object returned by the MapView GetCenter method.

    Pan and zoom the map and rotate your device and you'll see the map retains it's previous zoom level and center.

    Martin.
     

    Attached Files:

    DKHDKH, toby, deyvitm and 1 other person like this.
  3. warwound

    warwound Expert Licensed User

    AIM: Change the MapView TileSource, save and restore the selected TileSource on device orientation change.

    By default the MapView displays a tile set provided by Mapnik:

    [​IMG]

    The MapView has other built in TileSource providers (and you can even add your own), you can get a List of all available TileSources by calling the MapView GetTileSources method.
    GetTileSources returns a List of Strings, each String item can be used in the MapView SetTileSource method to select that TileSource.

    Passing an invalid String to SetTileSource will result in an IllegalArgumentException.

    So what Strings are valid TileSources?
    This is what GetTileSources returns for the current version of OSMDroid: "Mapnik", "CycleMap", "OSMPublicTransport", "Base", "Topo", "Hills", "CloudMadeStandardTiles", "CloudMadeSmallTiles", "MapquestOSM", "MapquestAerial" and "MapsForFree".

    Only those Strings are valid to be used with SetTileSource.
    BUT not all of those TileSources will actually display tiles.
    Some require a license and/or an API key from the TileSource provider.

    What TileSources are will actually display tiles?

    Mapnik i mentioned is the default TileSource and obviously displays tiles.

    Currently the other TileSources that i've found to display tiles are: "CycleMap", "OSMPublicTransport", "MapquestOSM", "MapquestAerial" and "MapsForFree".

    So you have a choice of 6 built in TileSources to choose from.
    Note that different TileSources have different maximum zoom levels, "MapquestAerial" and "MapsForFree" have particularly low maximum zoom levels.
    That is unless you are looking at the USA, "MapquestAerial" has much higher maximum zoom level available for USA only.

    Here's some screenshots of the useable TileSources (not including Mapnik):

    CycleMap
    [​IMG]

    OSMPublicTransport
    [​IMG]

    MapquestOSM
    [​IMG]

    MapquestAerial
    [​IMG]

    MapsForFree
    [​IMG]

    So we now know what TileSources are available and how to set the current TileSource, let's write some code...

    Code:
    Sub Process_Globals
       
    Dim MapCenter As GeoPoint
       
    Dim ZoomLevel As Int
       
       
    '   create a new String to save the currently selected TileSource
       Dim TileSource As String
    End Sub

    Sub Globals
       
    Dim MapView1 As MapView
       
       
    '   we'll use a spinner to enable selection of TileSources
       Dim TileSourceSpinner As Spinner
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
       MapView1.Initialize(
    "")
       
       
    '   adjust the MapView height to make space for the Spinner
       Activity.AddView(MapView1, 048dip100%x100%y-48dip)
       
       MapView1.SetZoomEnabled(
    True)
       MapView1.SetMultiTouchEnabled(
    True)
       
       
    If FirstTime Then
          MapCenter.Initialize(
    52.751920.40505)
          ZoomLevel=
    14
          
          
    '   set the default initial TileSource
          TileSource="Mapnik"
       
    End If
       
       MapView1.Zoom=ZoomLevel
       MapView1.SetCenter3(MapCenter)
       
       TileSourceSpinner.Initialize(
    "TileSourceSelect")
       
    Activity.AddView(TileSourceSpinner, 00100%x48dip)
       
       TileSourceSpinner.AddAll(MapView1.GetTileSources)
       TileSourceSpinner.Prompt=
    "Select a TileSource"
       TileSourceSpinner.SelectedIndex=TileSourceSpinner.IndexOf(TileSource)
       
    '   manually call the Spinner ItemClick Sub to sync the MapView TileSource with the spinner SelectedIndex
       TileSourceSelect_ItemClick(TileSourceSpinner.SelectedIndex, TileSourceSpinner.SelectedItem)
    End Sub

    Sub Activity_Resume
    End Sub

    Sub Activity_Pause (UserClosed As Boolean)
       MapCenter=MapView1.GetCenter
       ZoomLevel=MapView1.Zoom
       
       
    '   save the currently selected MapView TileSource
       TileSource=MapView1.GetTileSource
    End Sub

    Sub TileSourceSelect_ItemClick (Position As Int, Value As Object)
       
    '   set the MapView TileSource
       MapView1.SetTileSource(Value)
    End Sub
    I've used a Spinner to enable the user to select the TileSource.
    A new String process global variable is used to save and restore the TileSource on device orientation change.

    You'll see that i have added all available TileSources to the Spinner - you can work your way through them to see which ones display tiles and which ones don't.
    It may be that some TileSources that have displayed no tiles for me will display tiles when a different part of the world is being viewed - i'm not sure.

    So part three of my tutorial is complete and you can add a MapView to your application and save and restore it's state.
    You can now also select different TileSources to display.

    What's next?

    I'll cover the various Overlay layers next...

    Martin.
     

    Attached Files:

    DKHDKH and Johan Schoeman like this.
  4. warwound

    warwound Expert Licensed User

    AIM: Add a ScaleBarOverlay layer to the MapView.

    The MapView has various Overlay layers available:

    • MarkersFocusOverlay
    • MarkersOverlay
    • MinimapOverlay
    • MyLocationOverlay
    • PathOverlay
    • ScaleBarOverlay
    • SimpleLocationOverlay

    I shall start with the simple layers and progress to the more advanced layers, first example then is the ScaleBarOverlay.

    ScaleBarOverlay displays a scalebar on the map, the scale adjusts as you zoom in and out.

    Some example code:

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

    Sub Globals
       
    Dim MapView1 As MapView
       
    Dim TileSourceSpinner As Spinner
       
       
    '   create a ScaleBarOverlay
       Dim ScaleBarOverlay1 As ScaleBarOverlay
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
       MapView1.Initialize(
    "")
       
    Activity.AddView(MapView1, 048dip100%x100%y-48dip)
       
       MapView1.SetMultiTouchEnabled(
    True)
       MapView1.SetZoomEnabled(
    True)
       
       
    If FirstTime Then
          MapCenter.Initialize(
    52.751920.40505)
          TileSource=
    "Mapnik"
          ZoomLevel=
    14
       
    End If
       
       MapView1.Zoom=ZoomLevel
       MapView1.SetCenter3(MapCenter)
       
       TileSourceSpinner.Initialize(
    "TileSourceSelect")
       
    Activity.AddView(TileSourceSpinner, 00100%x48dip)
       
       TileSourceSpinner.AddAll(MapView1.GetTileSources)
       TileSourceSpinner.Prompt=
    "Select a TileSource"
       TileSourceSpinner.SelectedIndex=TileSourceSpinner.IndexOf(TileSource)
       TileSourceSelect_ItemClick(TileSourceSpinner.SelectedIndex, TileSourceSpinner.SelectedItem)
       
       
    '   i'll add an activity menuitem so we can change various properties at run-time
       Activity.AddMenuItem("Do something""DoSomething")
       
       
    '   initialize the ScaleBarOverlay and add it to the MapView
       ScaleBarOverlay1.Initialize(MapView1)
       MapView1.AddOverlay(ScaleBarOverlay1)
       
    End Sub

    Sub Activity_Resume
    End Sub

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

    Sub DoSomething_Click
       
    '   change some ScaleBarOverlay defaults
       ScaleBarOverlay1.SetLineColor(Colors.Red)
       ScaleBarOverlay1.SetLineWidth(
    4)
       ScaleBarOverlay1.SetOffset(
    10128)
       ScaleBarOverlay1.SetScaleUnit(ScaleBarOverlay1.SCALE_UNIT_IMPERIAL)
       ScaleBarOverlay1.SetTextColor(
    Colors.Green)
       ScaleBarOverlay1.SetTextSize(
    24)
       
    '   it is required that the MapView Invalidate method is called to force the MapView to update it's display
       MapView1.Invalidate
    End Sub

    Sub TileSourceSelect_ItemClick (Position As Int, Value As Object)
       MapView1.SetTileSource(Value)
    End Sub
    The ScaleBarOverlay is initialized and added to the MapView using the AddOverlay method.
    All ScaleBarOverlay properties are left at their default values, the ScaleBarOverlay displays metric units and is positioned at (10, 10) within the MapView.

    I've added a MenuItem and, on click, that calls a Sub DoSomething_Click where i change some ScaleBarOverlay default values and then call the MapView Invalidate method to force the MapView to update it's display.

    A click on the MenuItem changes the scale bar line width and color, offset position, scale unit, text color and text size.

    The next Overlay that i'll cover is the MinimapOverlay...

    Martin.
     

    Attached Files:

    DKHDKH likes this.
  5. warwound

    warwound Expert Licensed User

    AIM: Add a MinimapOverlay layer to the MapView.


    The MinimapOverlay displays a small map in the bottom right hand corner of the MapView.
    It is an overview map - zoomed out more than the main MapView map to give an overview of the area being viewed.

    You can drag the MinimapOverlay and the main map will also be panned.
    The MinimapOverlay does not support double tap to zoom.

    Here's the example code:

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

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

    Sub Activity_Create(FirstTime As Boolean)
       
    Activity.AddMenuItem("Do something""DoSomething")
       
       MapView1.Initialize(
    "")
       
    Activity.AddView(MapView1, 048dip100%x100%y-48dip)
       
       MapView1.SetMultiTouchEnabled(
    True)
       MapView1.SetZoomEnabled(
    True)
       
       
    If FirstTime Then
          MapCenter.Initialize(
    52.751920.40505)
          TileSource=
    "Mapnik"
          ZoomLevel=
    14
       
    End If
       
       MapView1.Zoom=ZoomLevel
       MapView1.SetCenter3(MapCenter)
       
       ScaleBarOverlay1.Initialize(MapView1)
       MapView1.AddOverlay(ScaleBarOverlay1)
       
       
    '   initialize the MinimapOverlay and add it to the MapView
       '   ** IMPORTANT a MinimapOverlay must be the LAST overlay layer to be added to a MapView otherwise other overlay layers may not display correctly **
       '   This may be fixed with future versions of the native Android OSMDroid library.
       MinimapOverlay1.Initialize(MapView1)
       MapView1.AddOverlay(MinimapOverlay1)
       
       
       
    '   moved the spinner initialization to here so that when TileSourceSelect_ItemClick is called the MinimapOverlay has been initialized and it's TileSource will be set (as well as the MapView TileSource being set)
       TileSourceSpinner.Initialize("TileSourceSelect")
       
    Activity.AddView(TileSourceSpinner, 00100%x48dip)
       
       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

    Sub DoSomething_Click
       
    '   change some MinimapOverlay defaults
       MinimapOverlay1.Height=150
       MinimapOverlay1.Padding=
    50
       MinimapOverlay1.Width=
    75
       
    '   it is required that the MapView Invalidate method is called to force the MapView to update it's display
       MapView1.Invalidate
    End Sub

    Sub TileSourceSelect_ItemClick (Position As Int, Value As Object)
       MapView1.SetTileSource(Value)
       
       
    '   set the MinimapOverlay TileSource to match the main MapView map TileSource
       MinimapOverlay1.SetTileSource(Value)
    End Sub
    The MinimapOverlay is initialized and added to the MapView using the AddOverlay method.
    By default the MinimapOverlay is 100 pixels square and padded by 10 pixels from the bottom right hand corner of the Mapview.

    IMPORTANT a MinimapOverlay must be the LAST overlay layer to be added to a MapView otherwise other overlay layers may not display correctly.
    This may be fixed with future versions of the native Android OSMDroid library.


    I've updated the Sub TileSourceSelect_ItemClick so that it sets the MinimapOverlay TileSource as well as the main MapView TileSource.
    That required that the Spinner and the call to TileSourceSelect_ItemClick(TileSourceSpinner.SelectedIndex, TileSourceSpinner.SelectedItem) be moved in the code to after the MinimapOverlay has been initialized.

    A click on the MenuItem now changes the width, height and padding of the MinimapOverlay.

    Pan and zoom the map, change the TileSource and change the device orientation.
    The MinimapOverlay TileSource is now restored along with the main MapView TileSource.

    I shall cover the SimpleLocationOverlay layer next.

    Martin.
     

    Attached Files:

    DKHDKH and Johan Schoeman like this.
  6. warwound

    warwound Expert Licensed User

    AIM: Add a SimpleLocationOverlay layer to the MapView.

    The SimpleLocationOverlay displays a single placemark(marker) on the map with a default person icon.

    It allows you to to change the location of the placemark at runtime.
    But you cannot change the default icon.

    The SimpleLocationOverlay 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


    Here's the example code:

    Code:
    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 SimpleLocationOverlay
       Dim SimpleLocationOverlay1 As SimpleLocationOverlay
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
       
    Activity.AddMenuItem("Do something""DoSomething")
       
       
    '   pass an EventName when initializing the MapView
       '   MapView has two events: CenterChanged and ZoomChanged
       MapView1.Initialize("MapView1")
       
    Activity.AddView(MapView1, 048dip100%x100%y-48dip)
       
       MapView1.SetMultiTouchEnabled(
    True)
       MapView1.SetZoomEnabled(
    True)
       
       
    If FirstTime Then
          MapCenter.Initialize(
    52.751920.40505)
          TileSource=
    "Mapnik"
          ZoomLevel=
    14
       
    End If
       
       MapView1.Zoom=ZoomLevel
       MapView1.SetCenter3(MapCenter)
       
       ScaleBarOverlay1.Initialize(MapView1)
       MapView1.AddOverlay(ScaleBarOverlay1)
       
       
       
    '   initialize the SimpleLocationOverlay and add it to the MapView
       SimpleLocationOverlay1.Initialize(MapView1)
       MapView1.AddOverlay(SimpleLocationOverlay1)
       
       
    '   set the SimpleLocationOverlay to display it's icon at the map center
       SimpleLocationOverlay1.SetMyLocation3(MapView1.GetCenter)
       
    '   note that there is no need to call the MapView Invalidate method here as that gets called (internally) when the MinimapOverlay is added to the MapView next
       
       
    '   ensure that the MinimapOverlay is the LAST overlay added to the MapView
       MinimapOverlay1.Initialize(MapView1)
       MapView1.AddOverlay(MinimapOverlay1)
       
       TileSourceSpinner.Initialize(
    "TileSourceSelect")
       
    Activity.AddView(TileSourceSpinner, 00100%x48dip)
       
       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

    Sub DoSomething_Click
       
    '   set the map center to it's default location
       MapView1.SetCenter(52.751920.40505)
       
    '   it is NOT required that the MapView Invalidate method is called
       '   changes to an Overlay require Invalidate but not changes to the MapView
       '   MapView1.Invalidate
    End Sub

    '   listen for the MapView CenterChanged event
    Sub MapView1_CenterChanged
       
    Log("MapView1_CenterChanged")
       
    '   keep the SimpleLocationOverlay centered on the map center
       '   MapView.Invalidate is not called here as the MapView will (internally) invalidate itself when it's center changes
       SimpleLocationOverlay1.SetMyLocation3(MapView1.GetCenter)
    End Sub

    '   listen for the MapView ZoomChanged event
    Sub MapView1_ZoomChanged
       
    Log("MapView1_ZoomChanged ZoomLevel="&MapView1.Zoom)
    End Sub

    Sub TileSourceSelect_ItemClick (Position As Int, Value As Object)
       MapView1.SetTileSource(Value)
       MinimapOverlay1.SetTileSource(Value)
    End Sub
    I'm now initializing the MapView with an EventName.
    A MapView can generate two events: 'CenterChanged' and 'ZoomChanged'.
    So i've created Subs to listen for those events.

    The SimpleLocationOverlay is initialized and added to the MapView, it's icon set to display at the map center.

    When the MapView CenterChanged event is triggered, the SimpleLocationOverlay is set to display at the new map center.
    That keeps the SimpleLocationOverlay centered on the map center at all times.

    A click on the MenuItem sets the map center to it's default location - which causes a CenterChanged event and the SimpleLocationOverlay is again centered on the map center.

    The Sub MapView1_ZoomChanged does nothing except log that it has been called.

    I shall cover the PathOverlay next.

    Martin.
     

    Attached Files:

    Last edited: May 7, 2012
    DKHDKH and Johan Schoeman like this.
  7. warwound

    warwound Expert Licensed User

    AIM: Add a PathOverlay layer to the MapView.

    A PathOverlay enables you to display polylines and polygons in a MapView.

    Here's the example code, instead of starting with the code from the end of the SimpleLocationOverlay example i am starting with the code from the end of the MinimapOverlay example.
    (I'll do this with the examples for all of the more complex Overlays layers so that the example code is easier to follow).

    The code:

    Code:
    Sub Process_Globals
       
    Dim MapCenter As GeoPoint
       
    Dim TileSource As String
       
    Dim ZoomLevel As Int
       
       
    '   create a List to store the path points in Activity_Pause
       Dim PathPoints As List
    End Sub

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

    Sub Activity_Create(FirstTime As Boolean)
       
    Activity.AddMenuItem("Do something""DoSomething")
       
       MapView1.Initialize(
    "MapView1")
       
    Activity.AddView(MapView1, 048dip100%x100%y-48dip)
       
       MapView1.SetMultiTouchEnabled(
    True)
       MapView1.SetZoomEnabled(
    True)
       
       
    If FirstTime Then
          MapCenter.Initialize(
    52.751920.40505)
          TileSource=
    "Mapnik"
          ZoomLevel=
    12
          
          
    '   initialize the PathPoints List
          PathPoints.Initialize
       
    End If
       
       MapView1.Zoom=ZoomLevel
       MapView1.SetCenter3(MapCenter)
       
       ScaleBarOverlay1.Initialize(MapView1)
       MapView1.AddOverlay(ScaleBarOverlay1)
       
       
    '   initialize the PathOverlay and add it to the MapView, the path will be colored red
       PathOverlay1.Initialize(MapView1, Colors.Red)
       MapView1.AddOverlay(PathOverlay1)
       
       
    '   change the path StrokeWidth to 4 pixels (by default it is 2 pixels)
       PathOverlay1.StrokeWidth=4
       
       
    '   make the path 50% transparent (by default it is fully opaque)
       PathOverlay1.Alpha=128
       
       
    If FirstTime Then
          
    '   make the map center the first point on the path
          PathOverlay1.AddPoint3(MapCenter)
          
    '   no need to call MapView Invalidate - it will be called (internally) when the next Overlay (MinimapOverlay) is added to the MapView
       Else
          
    '   restore the path points
          PathOverlay1.AddPoints(PathPoints)
          
          
    '   clear the PathPoints List as it's no longer required
          PathPoints.Clear
       
    End If
       
       
       
    '   ensure that the MinimapOverlay is the LAST overlay added to the MapView
       MinimapOverlay1.Initialize(MapView1)
       MapView1.AddOverlay(MinimapOverlay1)
       
       TileSourceSpinner.Initialize(
    "TileSourceSelect")
       
    Activity.AddView(TileSourceSpinner, 00100%x48dip)
       
       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
       
       
    '   save the PathOverlay1 points
       PathPoints=PathOverlay1.GetAllPoints
    End Sub

    Sub DoSomething_Click
       
    Log("PathOverlay1 contains "&PathOverlay1.GetNumberOfPoints&" points")
       
    '   set the map center and zoom level so that it contains the entire path
       MapView1.FitMapToBoundingBox(PathOverlay1.GetBoundingBox)
       
    '   no need to call MapView Invalidate - the FitMapToBoundingBox methods calls this internally
       
       
    '   note that by recentering the map the path will extend from it's last point to the new map center
       
       
    '   MapView1.Invalidate
    End Sub

    Sub MapView1_CenterChanged
       
    Log("MapView1_CenterChanged")
       
    '   add the new map center to the path
       PathOverlay1.AddPoint3(MapView1.GetCenter)
    End Sub

    Sub MapView1_ZoomChanged
       
    Log("MapView1_ZoomChanged")
    End Sub

    Sub TileSourceSelect_ItemClick (Position As Int, Value As Object)
       MapView1.SetTileSource(Value)
       MinimapOverlay1.SetTileSource(Value)
    End Sub
    First i've created a List PathPoints which will be used to save and restore the path on orientation change.

    Then i create, initialize and add the PathOverlay to the MapView.
    The PathOverlay is initialized to display the path with color red.
    I override some PathOverlay defaults - change the stroke width to 4 pixels and the alpha transparency to 50% transparent.

    If this is the first time that the activity has been created then the map center is added to the path as it's first point, otherwise the previously saved point (PathPoints List) is used to set the points of the path.
    PathPoints List is cleared to reclaim any used memory - it's not going to be read again so there's no need to keep it populated.

    In Activity_Pause all the PathOverlay points are saved to Pathpoints List.

    Finally in the MapView CenterChanged event listener i add the new map center to the path.

    Start the example and drag the map, the path starts as a single point at the map center and follows the map center as you drag it.

    A click on the MenuItem demonstrates the use of two new methods.
    First the PathOverlay GetBoundingBox method is used.
    This returns a BoundingBox object that represents the area covered by the path.
    Then the MapView FitMapToBoundingBox is called passing the PathOverlay BoundingBox.
    The map will adjust it's center and zoom level tofully contain the path.

    Change the orientation of you device and you'll see the already drawn path is saved and restored.

    I'll cover the MyLocationOverlay next.

    Martin.
     

    Attached Files:

    DKHDKH and Johan Schoeman like this.
  8. warwound

    warwound Expert Licensed User

    AIM: Add a MyLocationOverlay layer to the MapView.

    MyLocationOverlay has quite a lot of built in functionality and should prove useful to many developers.

    It provides GPS related features that require little or no additional code to implement.

    The MyLocationOverlay 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


    Here's the example code:

    Code:
    Sub Process_Globals
       
    Dim MapCenter As GeoPoint
       
    Dim TileSource As String
       
    Dim ZoomLevel As Int
       
       
    '   create some variables to save the MyLocationOverlay state
       Dim CompassEnabled, FollowLocationEnabled, MyLocationEnabled As Boolean
    End Sub

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

    Sub Activity_Create(FirstTime As Boolean)
       
    '   update the MenuItems
       Activity.AddMenuItem("Toggle Compass""MenuItemSelect")
       
    Activity.AddMenuItem("Toggle MyLocation""MenuItemSelect")
       
    Activity.AddMenuItem("Toggle FollowLocation""MenuItemSelect")
       
       
    '   MapView initialized with no EventName as we'll not be listening for MapView events
       MapView1.Initialize("")
       
    Activity.AddView(MapView1, 048dip100%x100%y-48dip)
       
       MapView1.SetMultiTouchEnabled(
    True)
       MapView1.SetZoomEnabled(
    True)
       
       
    '   initialize and add the MyLocationOverlay to the MapView, an EventName is used as we'll be listening for all three events that this overlay generates
       MyLocationOverlay1.Initialize(MapView1, "MyLocationOverlay1")
       MapView1.AddOverlay(MyLocationOverlay1)
       
       
    If FirstTime Then
          MapCenter.Initialize(
    52.751920.40505)
          TileSource=
    "Mapnik"
          ZoomLevel=
    14
          
          
    '   set the default values for the new variables
          CompassEnabled=MyLocationOverlay1.CompassEnabled
          FollowLocationEnabled=MyLocationOverlay1.FollowLocationEnabled
          MyLocationEnabled=MyLocationOverlay1.MyLocationEnabled
       
    Else
          
    '   restore the MyLocationOverlay state
          MyLocationOverlay1.CompassEnabled=CompassEnabled
          MyLocationOverlay1.FollowLocationEnabled=FollowLocationEnabled
          MyLocationOverlay1.MyLocationEnabled=MyLocationEnabled
       
    End If
       
       MapView1.Zoom=ZoomLevel
       MapView1.SetCenter3(MapCenter)
       
       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, 00100%x48dip)
       
       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
       
       
    '   save the MyLocationOverlay state
       CompassEnabled=MyLocationOverlay1.CompassEnabled
       FollowLocationEnabled=MyLocationOverlay1.FollowLocationEnabled
       MyLocationEnabled=MyLocationOverlay1.MyLocationEnabled
       
       
    '   disable MyLocationOverlay compass and GPS listening
       MyLocationOverlay1.CompassEnabled=False
       MyLocationOverlay1.MyLocationEnabled=
    False
    End Sub

    Sub MenuItemSelect_Click
       
    Dim MenuItem As String
       
    MenuItem=Sender
       
    Select MenuItem
          
    Case "Toggle Compass"
             MyLocationOverlay1.CompassEnabled=
    Not(MyLocationOverlay1.CompassEnabled)
          
    Case "Toggle MyLocation"
             MyLocationOverlay1.MyLocationEnabled=
    Not(MyLocationOverlay1.MyLocationEnabled)
          
    Case "Toggle FollowLocation"
             MyLocationOverlay1.FollowLocationEnabled=
    Not(MyLocationOverlay1.FollowLocationEnabled)
             
    If MyLocationOverlay1.MyLocationEnabled=False Then
                
    '   FollowLocation will only function if MyLocation is ALSO enabled
                ToastMessageShow("FollowLocation will only function if MyLocation is ALSO enabled"True)
             
    End If
       
    End Select
    End Sub

    '   event listeners for the three events that MyLocationOverlay generates

    Sub MyLocationOverlay1_FollowLocationAutoDisabled
       
    Log("MyLocationOverlay1_FollowLocationAutoDisabled")
       
    '   if FollowLocation is enabled and the map is dragged then FollowLocation is automatically disabled
       ToastMessageShow("FollowLocation is now DISABLED due to the map being dragged"False)
    End Sub

    Sub MyLocationOverlay1_LocationChanged(NewLocation As Location, IsFirstFix As Boolean)
       
    Log("MyLocationOverlay1_LocationChanged, Latitude="&NewLocation.Latitude&", Longitude="&NewLocation.Longitude&", IsFirstFix="&IsFirstFix)
    End Sub

    Sub MyLocationOverlay1_ProviderChanged(Provider As String, Enabled As Boolean)
       
    '   note that i have yet to see this event generated
       Log("MyLocationOverlay1_ProviderChanged, Provider="&Provider&", Enabled="&Enabled)
    End Sub

    Sub TileSourceSelect_ItemClick (Position As Int, Value As Object)
       MapView1.SetTileSource(Value)
       MinimapOverlay1.SetTileSource(Value)
    End Sub
    First i've created three Boolean variables to save the state of the MyLocationOverlay.
    The MenuItems have been updated so they make a bit of sense!
    Then the MyLocationOverlay is initialized and added to the MapView.

    MyLocationOverlay generates three events:

    • 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.
    • LocationChanged is generated each time the MyLocationOverlay gets a new GPS fix.
      The new Location is passed to the event listener as a Location object.
      You must include the B4A GPS library in your project to enable you to work with Location objects.
      A Boolean is also passed to the event listener to indicate whether or not this is the first GPS fix that MyLocationOverlay has obtained.
    • ProviderChanged is generated whenever a location provider is enabled or disabled.
      The event listener is passed a String to identify the location provider and a Boolean to indicate whether or not the provider was enabled or disabled.

    These three event listeners do no more than create a log entry, i have included them just for example purposes.
    (The FollowLocationAutoDisabled does in fact display a toast notification).

    In Activity_Pause the state of the MyLocationOverlay is saved, and the compass and MyLocation listener disabled to prevent unnecessary battery usage of the device sensors.

    The Sub MenuItem_Click toggles the state of the MyLocationOverlay compass, MyLocation listener and the FollowLocation property.

    Run the example and toggle the compass - you can see the built in compass appear.
    Note that i've found this will crash the emulator as soon as the activity tries to enable the non-existent emulator sensors.

    Enable MyLocation and you'll probably see nothing on the map - it's centered on Norfolk, UK and you're probably elsewhere!
    Enable FollowLocation and the map should pan to show you your location, a circle (the DrawAccuracy circle) is drawn and it's radius indicates the accuracy of the last GPS fix.

    If you disable MyLocation after it has found at least one GPS fix then the person icon will still remain drawn on the map.

    Rotate your device after MyLocation has found at least one GPS fix and the person icon is being displayed - when the activity is recreated MyLocation will be restored to enabled but the person icon will not be drawn until a new fresh first GPS fix is found.

    Disabling MyLocation does not mean that the last fix will no longer be displayed- you would have to use the Mapview RemoveOverlay method to do that:

    Code:
    MapView1.RemoveOverlay(MyLocationOverlay1)
    So that's the MyLocationOverlay covered.
    There are two Overlay layers left to cover: MarkersOverlay and MarkersFocusOverlay.
    I shall cover these in the next two examples, starting with MarkersOverlay.

    Martin.
     

    Attached Files:

    Last edited: May 7, 2012
    DKHDKH and Johan Schoeman like this.
  9. warwound

    warwound Expert Licensed User

    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:

    Code:
    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:

    Code:
    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, 048dip100%x100%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.756100.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.608010.39047Null)
       
       
    '   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, 00100%x48dip)
       
       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 StringPoint As GeoPoint)
       
    Log("MarkersOverlay1_Click")
       
    ToastMessageShow(Title&": "&Description, True)
    End Sub

    Sub MarkersOverlay1_LongClick(Title As String, Description As StringPoint 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.
     

    Attached Files:

    Last edited: May 7, 2012
  10. warwound

    warwound Expert Licensed User

    AIM: Add a MarkersFocusOverlay layer to the MapView.

    The MarkersFocusOverlay is very similar to the MarkersOverlay with these differences:

    • MarkersFocusOverlay display a Marker's Title and Desciption in a balloon on the map when the Marker is Clicked..
    • MarkersFocusOverlay generates no events.

    MarkersFocusOverlay will be completely re-written as i learn more about the OSMDroid API.
    It is basic and functional but not customizable - the balloon background and text color are hard-coded as is the text font size.
    The balloon's width is also hard-coded to 200 pixels.
    The balloon seems to position itself with a different offset from the Marker depending on whether the Marker uses the default icon or a custom icon.

    So i'll show some example code but don't consider this overlay layer to be very useable in an application.

    If you use the default Marker icon, then MarkersFocusOverlay 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


    The code:

    Code:
    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 MarkersFocusOverlay1 As MarkersFocusOverlay
    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, 048dip100%x100%y-48dip)
       
       MapView1.SetMultiTouchEnabled(
    True)
       MapView1.SetZoomEnabled(
    True)
       
       
    '   initialize the MarkersFocusOverlay and add it to the MapView
       MarkersFocusOverlay1.Initialize(MapView1)
       MapView1.AddOverlay(MarkersFocusOverlay1)
       
       
    '   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.756100.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.608010.39047Null)
       
       
    '   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 MarkersFocusOverlay
       MarkersFocusOverlay1.AddMarkers(Markers)
       
       
    If FirstTime Then
          TileSource=
    "Mapnik"
          
          
    '   fit the MapView to the MarkersFocusOverlay
          MapView1.FitMapToBoundingBox(MarkersFocusOverlay1.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, 00100%x48dip)
       
       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

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

    Sub TileSourceSelect_ItemClick (Position As Int, Value As Object)
       MapView1.SetTileSource(Value)
       MinimapOverlay1.SetTileSource(Value)
    End Sub
    The code is much the same as the MarkersOverlay example code.
    MarkersFocusOverlay generates no events so no EventName is required in it's initialization and no event listeners need to be created.

    There is only one balloon that displays a Marker's Title and Description, Click a Marker and the balloon will display the Marker's Title and Description, Click another Marker and the balloon will that Marker's Title and Description.
    If the balloon is displaying a Marker's Title and Description and you Click that Marker the balloon will toggle to hidden.

    So i shall take some time to learn more about the native Android OSMDroid library and hopefully release an update which includes much better MarkersOverlay and MarkersFocusOverlay objects.

    Til then hope you all enjoy playing with OSMDroid for B4A!!

    Martin.
     

    Attached Files:

    Last edited: May 7, 2012
    DKHDKH and Johan Schoeman like this.
  11. rildomoraes

    rildomoraes Member Licensed User

    Brilliant! Thanks!
     
  12. warwound

    warwound Expert Licensed User

    OSMDroid has built in support for offline maps.

    You can download the tiles for any area, transfer them to the cache folder used by OSMDroid on a device's external memory and view that area without using a data connection.

    If a data connection is available and enabled and the user pans the map to an area where there are no offline tiles, then OSMDroid will try and download the required tiles.

    If a data connection is NOT available or has been disabled then the map will display a plain background for areas where no offline tiles are available.

    Downloading tiles has been made very easy!

    All you need is Mobile Atlas Creator.

    That link contains the instructions needed, Mobile Atlas Creator will download the tiles you require and package them into a ZIP file ready to be transferred to your device's external memory.
    It looks like you can even create a SQLite database to store your offline tiles - i have not tried this yet.

    I've used Mobile Atlas Creator to download the tiles which cover my hometown of King's Lynn, Norfolk, UK.
    It gave me the option to select an area and then select which zoom levels i wanted to download tiles for.
    To keep the offline tile archive file small so i could upload it to the forum i selected just zoom levels 0 to 12.
    I've also selected OSM Mapnik as the map tile source.

    Downloading tiles for all zoom levels for a large area will create a (very) large offline tile archive file.

    In Mobile Atlas Creator, click the Settings button (near Create atlas button) and select the Tile update tab.

    Tiles have an expiry date, if your offline tiles pass their expiry date then they should not be used and OSMDroid should download fresh tiles.
    I've not tested this yet so it may be that if no data connection is available then expired tiles will be displayed.
    I shall experiment and post my findings.

    Anyway lets leave the Tile update Settings tab as it is by default.

    The Directories tab let you select whereabouts your offline tile archive will be saved.

    So having selected a map tile source, an area of the map and finally the zoom levels for which you want to download tiles you just click Create atlas and wait for it to produce your offline tile archive.

    My offline tile archive is just 316KBs in size.

    If the created offline tile cache has a filename that uses one or more uppercase letters then rename it - we will include this file in the project assets and only all lowercase filenames are allowed.

    Transfer your offline file cache to your project's Files folder, remember to add it to your project using the Files tab Add files button so that it is included in the APK.

    Now before i go any further consider what tiles are already cached on your device's external storage...
    I've got many Mapnik tiles cached on my SD card - for this example i want to be sure that my offline tiles are used and not previously cached tiles.

    OSMDroid caches tiles to folder on your device's external storage named osmdroid.
    It's shared by all (installed) applications that use an OSMDroid based map - whether using this library or the native Android library.

    Inside the cache folder is a folder named tiles and inside that are folders named after the tilesource (Mapnik for example).
    Each tilesource folder contains all cached tiles for that tilesource named and structured according to the Slippy map tile naming convention.

    So i've used a file manager to rename my Mapnik tilecache from Mapnik to Mapnikbak.

    Now i can test my offline files:

    Code:
    Sub Process_Globals
       
    Dim MapCenter As GeoPoint
       
    Dim ZoomLevel As Int
    End Sub

    Sub Globals
       
    Dim MapView1 As MapView
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
       
    If FirstTime Then
          
    Dim OfflineTileCacheFilename, TileCacheDir As String
          OfflineTileCacheFilename=
    "offline_tile_cache_mapnik.zip"
          TileCacheDir=
    File.DirRootExternal&"/osmdroid"
          
    '   check if the offline tile cache has already been copied to the OSMDroid cache folder
          If File.Exists(TileCacheDir, OfflineTileCacheFilename)=False Then
             
    '   create the 'osmdroid' cache folder if it does not exist
             If File.Exists(TileCacheDir&"/""")=False Then
                
    File.MakeDir(File.DirRootExternal, "osmdroid")
             
    End If
             
    '   copy the offline tile cache to the OSMDroid cache folder
             File.Copy(File.DirAssets, OfflineTileCacheFilename, TileCacheDir, OfflineTileCacheFilename)
          
    End If
       
    End If
       
       MapView1.Initialize(
    "MapView1")
       
    Activity.AddView(MapView1, 00100%x100%y)
       
       MapView1.SetMultiTouchEnabled(
    True)
       MapView1.SetZoomEnabled(
    True)
       
       
    '   ensure that the map displays the Mapnik tiles
       MapView1.SetTileSource("Mapnik")
       
    '   disable the data connection
       MapView1.SetDataConnectionEnabled(False)
       
       
    If FirstTime Then
          
    '   center the map within the area covered by the offline tiles
          MapCenter.Initialize(52.751920.40505)
          
    '   choose a zoom level for which offline tiles exist
          ZoomLevel=10
       
    End If
       
       MapView1.Zoom=ZoomLevel
       MapView1.SetCenter3(MapCenter)
    End Sub

    Sub Activity_Resume
    End Sub

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

    Sub MapView1_ZoomChanged
       
    If MapView1.Zoom>12 Then
          
    '   i could have set the map zoom to 12 if it was more than 12 but this way you can see the level 12 tiles stretched to display where zoom level 13+ does not exist
          ToastMessageShow("Tiles are cached for zoom levels 0 to 12 only, current zoom level is: "&MapView1.Zoom, False)
       
    End If
    End Sub
    Everything works as desired.
    The offline tile cache file is copied to the osmdroid cache folder if it has not already been copied.
    And the MapView displays just my offline tiles - where there are no offline tiles the MapView displays just it's background grid.
    At zoom levels of 13 and higher the MapView stretches the tiles from zoom level 12 to fit the higher zoom levels.
    The MapView1_ZoomChanged event listener could just have easily set the MapView zoom to 12 if it was higher.

    There's a few things to watch for with offline tiles:
    • Mobile Atlas Creator does not support downloading of all the TileSources that the MapView supports.
      I see no option to download CycleMap tiles for example.
    • In the MapView the Mapnik tiles cover at least zoom level 18.
      Mobile Atlas Creator offers to download only up to zoom level 16!
    • If offline tiles expire and the MapView will no longer use them then watch that you don't have a massive offline tile archive wasting SD card space.

    As well as offline map tiles, we can use a MapView to display any other image tiles.
    That makes a MapView an ideal panorama viewer - cut your hi-res panorama image into tiles and view them in glorious hi-res in a MapView.
    Circuit diagrams and floorplans are other large images that could be displayed in a MapView without having to worry about the memory overhead that dealing with large bitmaps causes.

    Martin.

    [edit]Example code updated it now checks if the osmdroid cache folder exists, and creates it if necessary.[/edit]
     

    Attached Files:

    Last edited: Mar 30, 2012
  13. susu

    susu Well-Known Member Licensed User

    The best solo-thread ever! :D
     
    DonManfred likes this.
  14. Tom Christman

    Tom Christman Active Member Licensed User

    Amen to that. Whew!
     
  15. timwil

    timwil Active Member Licensed User

  16. bluedude

    bluedude Well-Known Member Licensed User

    Hi,

    I'm testing your mapview and it is great. One question comes up, how can I use the Cloudmade stuff? I have an API key for Cloudmade. Can I add the API key somewhere?
     
  17. warwound

    warwound Expert Licensed User

    I have updated the library to add support for using a Cloudmade API key.

    You must add your Cloudmade API key to your manifest.xml file, use Project menu > Manifest Editor and add it like this:

    Code:
    '   Add your Cloudmade API key here
    AddApplicationText(<meta-data android:name="CLOUDMADE_KEY" android:value="Your Cloudmade API key here" />)
    The meta-data element must be a child of the application element and NOT an activity element.

    The library update adds a new property to the MapView object: CloudmadeTileSourceStyle.

    You can set or get the Cloudmade TileSource Style as an Int using this new property.

    Some example code:

    Code:
    Sub Process_Globals
       
    Dim MapCenter As GeoPoint
       
    Dim ZoomLevel As Int
       
       
    ' use a process global to save and restore the current Cloudmade TileSource Style
       Dim CloudmadeTileSourceStyle As Int
    End Sub

    Sub Globals
       
    '   use a spinner to select the CloudMadeStandardTiles TileSource Style
       Dim CloudmadeTileSourceStyleSelect As Spinner
       
    Dim MapView1 As MapView
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
       MapView1.Initialize(
    "")
       
    Activity.AddView(MapView1, 048dip100%x100%y-48dip)
       
       MapView1.SetZoomEnabled(
    True)
       MapView1.SetMultiTouchEnabled(
    True)
       
       
    If FirstTime Then
          MapCenter.Initialize(
    52.751920.40505)
          ZoomLevel=
    14
          
          
    ' set the default Cloudmade TileSource Style
          CloudmadeTileSourceStyle=0
       
    End If
       
       
       
    '   select the CloudMadeStandardTiles TileSource
       '   an API key is required and should be set in the manifest file
       MapView1.SetTileSource("CloudMadeStandardTiles")
       
       
    '   set the CloudMadeStandardTiles TileSource Style
       MapView1.CloudmadeTileSourceStyle=CloudmadeTileSourceStyle
       
       MapView1.Zoom=ZoomLevel
       MapView1.SetCenter3(MapCenter)
       
       CloudmadeTileSourceStyleSelect.Initialize(
    "CloudmadeTileSourceStyleSelect")
       
    Activity.AddView(CloudmadeTileSourceStyleSelect, 00100%x48dip)
       
       CloudmadeTileSourceStyleSelect.AddAll(
    Array As String("0""1""2""3""4""5""6""7""8"))
       CloudmadeTileSourceStyleSelect.Prompt=
    "CloudmadeTileSourceStyle"
       CloudmadeTileSourceStyleSelect.SelectedIndex=CloudmadeTileSourceStyleSelect.IndexOf(CloudmadeTileSourceStyle)
       
       
    '   manually call the Spinner ItemClick Sub to sync the CloudMadeStandardTiles TileSource Style with the spinner SelectedIndex
       CloudmadeTileSourceStyleSelect_ItemClick(CloudmadeTileSourceStyleSelect.SelectedIndex, CloudmadeTileSourceStyleSelect.SelectedItem)
       
       
       
    Log("Activity_Create CloudmadeTileSourceStyle="&CloudmadeTileSourceStyle)
    End Sub

    Sub Activity_Resume
    End Sub

    Sub Activity_Pause (UserClosed As Boolean)
       MapCenter=MapView1.GetCenter
       ZoomLevel=MapView1.Zoom
       
       
    '   save the current Cloudmade TileSource Style
       CloudmadeTileSourceStyle=MapView1.CloudmadeTileSourceStyle
       
    Log("Activity_Pause CloudmadeTileSourceStyle="&CloudmadeTileSourceStyle)
    End Sub

    Sub CloudmadeTileSourceStyleSelect_ItemClick (Position As Int, Value As Object)
       MapView1.CloudmadeTileSourceStyle=Value
       
    '   changes to the Style are only applied when the CloudMade TileSource is (re)loaded
       MapView1.SetTileSource("CloudMadeStandardTiles")
    End Sub
    Note that changing the Style does not automatically update the MapView.
    Changes to the Style are applied when a Cloudmade TileSource is next loaded, so reloading the CloudMadeStandardTiles TileSource actually applies the change.

    I've played a bit with the CloudMadeSmallTiles TileSource but found it to behave unpredictably at times.
    The MapView panning to (0, 0) when i changed zoom levels sometimes.
    OSMDroid natively works with tiles that are 256 pixels square so is probably not properly handling the smaller tiles.

    CloudMadeStandardTiles however works fine.

    Example code attached - note that i have removed my Cloudmade API key from the example.

    Martin.
     

    Attached Files:

    DKHDKH, koaunglay and Johan Schoeman like this.
  18. Ohanian

    Ohanian Active Member Licensed User

    tanx for the lib

    i have a problem on compiling the sample, i got this error msg :

    Code:
    Compiling code.                         0.00
    Generating R 
    file.                      0.00
    Compiling generated Java code.          Error
    B4A line: 
    23
    Activity.AddView(MapView1, 00100%x100%y)
    javac 
    1.6.0_21
    src\uk\co\martinpearman\b4a\mapviewtutorial\main.java:
    230: cannot access org.osmdroid.views.MapView
    class 
    file for org.osmdroid.views.MapView not found
    mostCurrent._
    activity.AddView((android.view.View)(mostCurrent._mapview1.getObject()),(int)(0),(int)(0),anywheresoftware.b4a.keywords.Common.PerXToCurrent((float)(100),mostCurrent.activityBA),anywheresoftware.b4a.keywords.Common.PerYToCurrent((float)(100),mostCurrent.activityBA));
                                                                                     ^
    1 error
    did i miss something?
     
  19. warwound

    warwound Expert Licensed User

    I'd guess that you have not downloaded and copied the two native Android libraries to your B4A additional libraries folder.

    This library is OSMDroid.jar and OSMDroid.xml PLUS two additional (native Android) libraries NativeOSMDroid.jar and slf4j-android-1.5.8.jar.

    Those last two libraries are not in the B4A library download, you can get them from the main library thread, native_android_libraries.zip is the archive to download.

    That archive contains a readme.txt file which explains what the extra files are.

    Martin.
     
  20. Ohanian

    Ohanian Active Member Licensed User

    Already have these files in the lib folder!
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice