B4A Library GoogleMapsExtras

GoogleMapsExtras is an ongoing project to implement more of the Google Maps Android v2 classes in Basic4Android.

Currently the library allows you to create these objects:

Tutorials for each object will be uploaded to the Google Maps Android v2 tutorial thread.

Martin.
 

Attachments

  • GoogleMapsExtras_v1_71.zip
    59.6 KB · Views: 2,853
  • MapsForgeTileProvider_v1.01.zip
    173.1 KB · Views: 2,733
  • GoogleMapsExtras_v2_0_library_files.zip
    82.5 KB · Views: 3,154
Last edited:

warwound

Expert
Licensed User
Longtime User
Here i'm releasing a library that enables you to add offline mapping to a GoogleMap.

MapsForgeTileProvider enables you to render a MapsForge map database file on a GoogleMap.

A MapsForge map database consists of (binary) vector map data and the MapsForgeTileProvider renders map tiles on the fly on the android device from the vector data in the database.
Instead of many MBs or GBs of pre-rendered image file map tiles to enable offline mapping, a map database enables you to provide offline mapping in a relatively compact format.

As an example i have a map database that contains data for Great Britain, this map database is 306MBs in size.
The MapsForgeTileProvider can render map tiles for Great Britain at zoom levels 0 to 21 from this database.
Covering a similar area using image files as tiles for zoom level 0 to 16 would require over 4.4 million individual image tiles.
The FAT32 file system used on most device's external memory uses a cluster size of at least 4KB, a single file stored on a FAT32 file system will always use at least 4KBs.
4.4 million multiplied by at least 4KBs is a lot of memory space!

MapsForgeTileProvider isn't perfect though...
  • It's a single threaded library that will not make use of multi-core CPUs.
  • The larger your map database, the slower tile rendering will be.
    This is more noticeable when zoomed out to low zoom levels.
  • The MapsForge library which i have created the MapsForgeTileProvider from has had a bug when rendering tiles which contain both land and sea for a long while.
    A few attempts have been made to fix the bug but it remains unfixed.
    If a tile contains both land and sea then the sea area will be rendered as white.
    UPDATE: Read this post for a possible solution.
  • If your map database is derived from Open Street Map data you may find it contains erroneous data.
    This is a problem with the Open Street Map project itself and it's contributors, not a MapsForge problem.
  • GoogleMaps allows variable zoom levels - you can for example pinch zoom to zoom level 6.45.
    The built in GoogleMaps MapTypes all look good at non-integer zoom levels.
    But the MapsForgeTileProvider is based on code that renders tiles for integer zoom levels only.
    If you pinch zoom the map to a non-integer zoom level then often you'll see visual artifacts (pixelation) where GoogleMaps has upscaled a MapsForgeTileProvider tile.

More info on the original android MapsForge project can be found here: http://code.google.com/p/mapsforge/.

MapsForgeTileProvider
Version:
1.0
  • MapDatabase
    Methods:
    • CloseFile
      Closes the map file and destroys all internal caches.
    • HasOpenFile As Boolean
      Returns True if a map file is currently opened, False otherwise.
    • IsInitialized As Boolean
    Properties:
    • MapFileInfo As MapFileInfo [read only]
  • MapFileInfo
    Methods:
    • IsInitialized As Boolean
    Properties:
    • Comment As String [read only]
      Get the comment field of the map database.
      The comment field can be set when the map database is created.
      It may not have been set, in which case this method will return an empty String "".
    • CreatedBy As String [read only]
      Get the created by field of the map database.
      The created by field can be set when the map database is created.
      It may not have been set, in which case this method will return an empty String "".
    • FileSize As Long [read only]
      Get the size of the map database, measured in bytes.
    • FileVersion As Int [read only]
      Get the file version of the map database.
    • LanguagePreference As String [read only]
      Get the preferred language for names as defined in ISO 3166-1.
      The preferred language for names can be set when the map database is created.
      It may not have been set, in which case this method will return an empty String "".
    • LatLngBounds As LatLngBounds [read only]
      Get the area covered by the map database file as a LatLngBounds object.
    • MapDate As Long [read only]
      Get the date of the map database in milliseconds since January 1, 1970.
    • PoiTags() As Tag [read only]
      Get the POI Tags from the map database.
    • StartLatLng As LatLngWrapper [read only]
      Get the map database start position.
      The start position can be set when the map database is created.
      It may not have been set, in which case this method will return a LatLng that is NOT initialized.
    • StartZoomLevel As Int [read only]
      Get the map start zoom level
      The start zoom can be set when the map database is created.
      It may not have been set, in which case this method will return a value of -1.
    • WayTags() As Tag [read only]
      Get the way Tags from the map database.
  • MapsForgeTileProvider
    Methods:
    • GetMapsForgeTileProviderOptions As MapsForgeTileProviderOptions
      Gets the MapsForgeTileProviderOptions that will be used when the MapsForgeTileProvider Initialize method is called.
    • Initialize
      Initialize the MapsForgeTileProvider.
      You must have set any MapsForgeTileProviderOptions required options before calling Initialize.
    • IsInitialized As Boolean
    • SetNoTileTile (NoTileTile As Tile)
      Sets a Tile to be used when there is no tile available from the MapsForge map database.
      Pass Null to restore the default 'no tile' tile.
    • SetTextScale (TextScale As Float)
      Sets the text scale.
      Default value is 1.
    Properties:
    • MapDatabase As MapDatabase [read only]
  • MapsForgeTileProviderOptions
    Fields:
    • BITMAP_CONFIG_ARGB_8888 As Config
      Each pixel is stored on 4 bytes.
      Each channel (RGB and alpha for translucency) is stored with 8 bits of precision.
    • BITMAP_CONFIG_RGB_565 As Config
      Each pixel is stored on 2 bytes and only the RGB channels are encoded.
    Methods:
    • SetBitmapConfig (BitmapConfig As Config) As MapsForgeTileProviderOptions
      Optionally set the Bitmap.Config for the rendered tiles.
      Default value is BITMAP_CONFIG_RGB_565.
    • SetDebugSettings (DrawTileCoordinates As Boolean, DrawTileFrames As Boolean, HighlightWaterTiles As Boolean) As MapsForgeTileProviderOptions
      Optionally set the DebugSettings.
      Default values are:
      DrawTileCoordinates - False.
      DrawTileFrames - False.
      HighlightWaterTiles - False.
    • SetMapDatabaseFile (Dir As String, FileName As String) As MapsForgeTileProviderOptions
      Set the path to the MapsForge map database file.
    • SetNoTileTile (NoTileTile As Tile) As MapsForgeTileProviderOptions
      Optionally set the Tile to be used when there is no tile available from the MapsForge map database.
    • SetRenderTheme (InputStream1 As InputStream) As MapsForgeTileProviderOptions
      Optionally set a custom RenderTheme to be used to render tiles.
      Default value is Null which means that the built in default RenderTheme will be used.
    • SetTextScale (TextScale As Float) As MapsForgeTileProviderOptions
      Optionally set the text scale.
      Default value is 1.
  • Tag
    Methods:
    • Equals (Tag1 As Tag) As Boolean
    • IsInitialized As Boolean
    Properties:
    • Key As String [read only]
    • Value As String [read only]

In order to use MapsForgeTileProvider you will need the latest version of GoogleMapsExtras.
I've not added MapsForgeTileProvider to GoogleMapsExtras.
MapsForgeTileProvider contains a folder of various images used in the rendering process and, if included in GoogleMapsExtras, these images would be included in your project whether you used MapsForgeTileProvider or not. We're talking about less than 100KBs here, not a lot but there's no point bloating a project with unused assets.

So this is MapsForgeTileProvider 1.0, and it is attached to the first post in this thread.

An example project can be found here: http://www.b4x.com/android/forum/threads/google-maps-android-v2-tutorial.24415/page-13#post-199802.

Martin.
 
Last edited:

warwound

Expert
Licensed User
Longtime User
GoogleMapsExtras updated to version 1.50

This updates adds support for the new features introduced with the October 2013 update to Google Play Services.
The Google Play Services changelog can be found here: https://developers.google.com/maps/documentation/android/releases.

You will need to update your Google Play Services library (google-play-services.jar) to the latest version using the android SDK Manager to be able to use the newest features.
If this causes problems take a look at my post here: http://www.b4x.com/android/forum/threads/google-play-services-resources-not-found.33091/#post-199976 for a possible solution.


These are the updates:
  • GoogleMapsExtras
    Added new method SetBuildingsEnabled which enables you to turn the 3D buildings layer on or off.
    Added new object OnMapLoadedCallback and new method SetOnMapLoadedCallback which enables you to be notified when the map has finished rendering.
  • GroundOverlay
    Added new properties Image and Image2 to allow you to change the image being used for the GroundOverlay.
    Added new method SetDimensions2 which allows both width and height to be set.
  • MarkerExtras
    Added new methods GetAlpha and SetAlpha to get or set the alpha transparency.
  • MarkerOptions
    Added new methods Alpha and GetAlpha to set or get the alpha transparency.

Version 1.50 of GoogleMapsExtras is attached to the first post in this thread.

Martin.
 

warwound

Expert
Licensed User
Longtime User
GoogleMapsExtras updated to version 1.51

This update contains no additional functionality but fixes a compilation problem that meant if you used the latest version of the Google Play Services library then you'd see in the unfiltered log many errors referring to resources not being found.

For anyone that wants to prevent these 'resource not found' messages in the log you need to:

  • Copy all of the resources from the android SDK android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/res folder to your project's Objects/res folder.
    Ensure that all resource files have their read only attribute set.
  • Compile using version 1.51+ of GoogleMapsExtras.

If you're using the latest version of Google Play Services it's important to note that it now seems to be a requirement to add a new manifest entry.
If you project crashes with an exception such as this:

** Activity (main) Create, isFirst = true **
main_activity_create (java line: 292)
java.lang.IllegalStateException: The meta-data tag in your app's AndroidManifest.xml does not have the right value. Expected 4030500 but found 0. You must have the following declaration within the <application> element: <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
at com.google.android.gms.common.GooglePlayServicesUtil.n(Unknown Source)
at com.google.android.gms.common.GooglePlayServicesUtil.isGooglePlayServicesAvailable(Unknown Source)
at anywheresoftware.b4a.objects.MapFragmentWrapper.IsGooglePlayServicesAvailable(MapFragmentWrapper.java:101)
at uk.co.martinpearman.b4a.googlemapdemo.main._activity_create(main.java:292)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:170)
at uk.co.martinpearman.b4a.googlemapdemo.main.afterFirstLayout(main.java:98)
at uk.co.martinpearman.b4a.googlemapdemo.main.access$100(main.java:16)
at uk.co.martinpearman.b4a.googlemapdemo.main$WaitForLayout.run(main.java:76)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4898)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
at dalvik.system.NativeStart.main(Native Method)
java.lang.IllegalStateException: The meta-data tag in your app's AndroidManifest.xml does not have the right value. Expected 4030500 but found 0. You must have the following declaration within the <application> element: <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
** Activity (main) Pause, UserClosed = false **
** Activity (main) Resume **

Then you need to:

  • Ensure that even if you have not copied all of the Google Play Services from the SDK folder to your project's Objects/res folder (as above), at least copy android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/res/values/version.xml to Objects/res/values/version.xml. Ensure that version.xml has it's read only attribute set.
  • Use the manifest editor to add this entry to your project:
    B4X:
    AddApplicationText(<meta-data
       android:name="com.google.android.gms.version"
       android:value="@integer/google_play_services_version" />)

Finally if you still have compilation or runtime problems with the latest version of the Google Play Services library then take a look here: https://developers.google.com/maps/documentation/android/start#specifying_permissions.
Looks like a new permission must be added to your project:

com.google.android.providers.gsf.permission.READ_GSERVICES
Allows the API to access Google web-based services.

The manifest entry required for this permission is:

B4X:
AddPermission(com.google.android.providers.gsf.permission.READ_GSERVICES)

Version 1.51 of GoogleMapsExtras is attached to the first post in this thread.

Martin.
 

scrat

Active Member
Licensed User
Longtime User
Hi martin
First Thanks for your MapsForgeTileProvider !
It is a great idea and the best option for offline maps.

I try to use the library, but I have a problem
After I have downloaded a map file from mapsforge I use this code

B4X:
Dim MforgeLayer As TileOverlay
Dim MforgeLayerOptions As TileOverlayOptions
       
Dim MforgeProvider As MapsForgeTileProvider
Dim MforgeProviderOptions As  MapsForgeTileProviderOptions
       
Dim Gmex As GoogleMapsExtras

MforgeProviderOptions.SetMapDatabaseFile(file.DirRootExternal,"maps.map")
MforgeProvider.GetMapsForgeTileProviderOptions
MforgeProvider.Initialize
               
MforgeLayerOptions.Initialize
MforgeLayerOptions.SetTileProvider(MforgeProvider)
MforgeLayer=Gmex.AddTileOverlay(Gmap, MforgeLayerOptions)

Log(MforgeProvider.MapDatabase.IsInitialized) ===> return true
Log(MforgeProvider.MapDatabase.HasOpenFile) ===> return false

but the map file is never opened.
How the link is created between MforgePovider and MforgeProviderOptions ?
Do you have an example?

Thanks
 

warwound

Expert
Licensed User
Longtime User
Each instance of a MapsForgeTileProvider has it's own instance of a MapsForgeTileProviderOptions.

So to fix your code you need to get that instance of MapsForgeTileProviderOptions from the MapsForgeTileProvider:

B4X:
Dim MforgeProviderOptions As MapsForgeTileProviderOptions=MforgeProvider.GetMapsForgeTileProviderOptions

MforgeProviderOptions is now the instance of MapsForgeTileProviderOptions that will be used when MforgeProvider.Initialize is executed.

Martin.
 

warwound

Expert
Licensed User
Longtime User
GoogleMapsExtras updated to version 1.52

This update is just a bug fix.
I noticed that i'd incorrectly named a MarkerOptions method.

The MarkerOptions method GetAnchorY should in fact be GetAnchorV:

GetAnchorV As Float
Vertical distance, normalized to [0, 1], of the anchor from the top edge.

So i've updated the library - anyone using this method will have to make a minor change to their code, but for consistency with the native android MarkerOptions method i think it's worth updating.

Version 1.52 of GoogleMapsExtras is attached to the first post in this thread.

Martin.
 

marcick

Well-Known Member
Licensed User
Longtime User
Hi Martin,
I'm playing with polyline with this code

B4X:
Sub Add_Track(la As String, lo As String)

    Dim l1 As LatLng
    l1.Initialize(la,lo)
    Points.Add(l1)
    If Pline.IsInitialized=False Then Pline=GoogleMap1.AddPolyline
    Pline.Points=Points
   
End Sub

But the line is visible only when I'm rendering Googlemap and disappear when I render Mapsforge tiles.
Any hints ?
 

westingenieria

Active Member
Licensed User
Longtime User
Again: it seems to me that if I place many markers with the property InfoWindowShown=true only the last one has the infowindows visible.
So it is not possible to have some or all infowindows visible sametime ?
Marco
Hi Marco, this is possible?

Regards
 

marcick

Well-Known Member
Licensed User
Longtime User
yes, seems to be like this. Also, if an infowindows is set to visible, when you click on the map it disappear.
 

synapse

Member
Licensed User
Longtime User
Other than creating an infowindow, is there a way of capturing a click on a marker to start another activity please?
 

warwound

Expert
Licensed User
Longtime User
I think you just add a Sub that listens for the MapFragment MarkerClick event and return True from that Sub.
Returning True means you have handled the event so the default action - open the infowindow - does not take place.

In your Sub you can do as you want - open an Activity etc.

B4X:
Sub MapFragment1_MarkerClick (SelectedMarker As Marker) As Boolean
    StartActivity(MyActivity)
    Return True
End Sub

Martin.
 

warwound

Expert
Licensed User
Longtime User
Can you give the attached update a test?

GoogleMapsExtras now has three new methods:

  • AnimateCamera (GoogleMap1 As GoogleMap, CameraUpdate1 As CameraUpdate)
    Animates the movement of the camera from the current position to the position defined in the CameraUpdate1.
  • AnimateCamera2 (GoogleMap1 As GoogleMap, CameraUpdate1 As CameraUpdate, CancelableCallback1 As CancelableCallback)
    Animates the movement of the camera from the current position to the position defined in the CameraUpdate1.
    Raising the CancelableCallback Cancel or Finish event on termination or completion.
    Pass Null as the CancelableCallback if not required.
  • AnimateCamera3 (GoogleMap1 As GoogleMap, CameraUpdate1 As CameraUpdate, DurationMs As Int, CancelableCallback1 As CancelableCallback)
    Animates the movement of the camera from the current position to the position defined in the CameraUpdate1 over the specified duration.
    Raising the CancelableCallback Cancel or Finish event on termination or completion.
    Pass Null as the CancelableCallback if not required.

I've implemented the CameraUpdateFactory:

CameraUpdateFactory
Methods:

  • NewCameraPosition (CameraPosition1 As CameraPosition) As CameraUpdate
  • NewLatLng (LatLng1 As LatLng) As CameraUpdate
  • NewLatLngBounds (LatLngBounds1 As LatLngBounds, Padding As Int) As CameraUpdate
  • NewLatLngBounds2 (LatLngBounds1 As LatLngBounds, Width As Int, Height As Int, Padding As Int) As CameraUpdate
  • NewLatLngZoom (LatLng1 As LatLng, Zoom As Float) As CameraUpdate
  • ScrollBy (XPixel As Float, YPixel As Float) As CameraUpdate
  • ZoomBy (Amount As Float, Point1 As Point) As CameraUpdate
  • ZoomBy2 (Amount As Float) As CameraUpdate
  • ZoomIn As CameraUpdate
  • ZoomOut As CameraUpdate
  • ZoomTo (ZoomLevel As Float) As CameraUpdate

CameraUpdate has been implemented too (this object has no methods or properties).

Finally there is the CancelableCallback.

CancelableCallback
Events:

  • Cancel
  • Finish
Methods:
  • Initialize (EventName As String)
  • IsInitialized As Boolean

I'm currently trying to repair my Windows computer - compiled these updates on Ubuntu - so i can't provide any sample b4a code.
But i'm sure that with the official documentation you should see how to use the new methods and objects.

So if you can test as much as you can and post back with your results, if it all works ok i'll upload the update as an 'official' update.

Thanks.

Martin.
 
Last edited:

rboeck

Well-Known Member
Licensed User
Longtime User
Thank you for your fast response!

I had no problems with existing code; but i have bigger problems to translate java code to b4a. (Never made anything in java)

Here my example:

B4X:
Dim CUF As CameraUpdateFactory

Dim Sy As CameraPosition
Sy.Initialize(-33.88, 151.21, 15)

GoogleMap1.MoveCamera(Sy)

CameraPosition1.Initialize2(-33.88, 151.21, 15, 90,30)
GoogleMap1.AnimateCamera(CameraPosition1)

Dim CP1 As CameraPosition

CP1.Initialize2(37.4,-122.1,10,90,30)          'Sydney
GoogleMapsExtras1.AnimateCamera3(GoogleMap1,CUF.ZoomIn,2000,Null)

Dim CP2 As CameraPosition
CP2.Initialize2(37.4,-122.1,17, 90,30)        'Mountain View
CP2.Bearing=90
CP2.Zoom=17
CP2.Tilt=30

CUF.NewCameraPosition(CP2)

GoogleMapsExtras1.AnimateCamera3(GoogleMap1,CUF.ZoomIn ,2000,Null)

I did check how to use CameraUpdateFactory exactly; this code transmits me to the destination point, but i couldnt set the parameters for timing etc. Please compare this sample with the java code in #78.

Many thanks,
Reinhard
 

warwound

Expert
Licensed User
Longtime User
First all instances of CameraUpdate are obtained from the CameraUpdateFactory.
You cannot Initialize a CameraUpdate nor a CameraPosition.
Instances of CameraPosition are obtained from the b4a GoogleMap library.

Here's some untested syntax examples that will hopefully

B4X:
Sub MySub
   Dim CameraUpdateFactory1 As CameraUpdateFactory
   Dim CameraUpdate1 As CameraUpdate
  
   Dim CancelableCallback1 As CancelableCallback
   CancelableCallback1.Initialize("CancelableCallback1")
  
   Dim LatLng1 As LatLng
   LatLng1.Initialize(12.34, 45.67)
  
   CameraUpdate1=CameraUpdateFactory1.NewLatLngZoom(LatLng1, 6)
  
   GoogleMapsExtras1.AnimateCamera(GoogleMap1, CameraUpdate1)
  
   GoogleMapsExtras1.AnimateCamera2(GoogleMap1, CameraUpdate1, CancelableCallback1)
  
   GoogleMapsExtras1.AnimateCamera3(GoogleMap1, CameraUpdate1, 500, CancelableCallback1)
  
   GoogleMapsExtras1.MoveCamera(GoogleMap1, CameraUpdateFactory1.NewLatLng(LatLng))
  
   GoogleMapsExtras1.AnimateCamera(GoogleMap1, CameraUpdateFactory1.ZoomIn)
  
End Sub

Sub CancelableCallback1_Cancel
  
End Sub

Sub CancelableCallback1_Finish
  
End Sub

I can see it'd be useful to implement the CameraPosition.Builder class - i'll see how the day goes.

Martin.
 
Last edited:

rboeck

Well-Known Member
Licensed User
Longtime User
Hi Martin,

thank you for helping with my syntax and understanding problems. I have now an animated flight to my first destination. What i could not solve is the new event system. I hoped, that sub CancelableCallback1_Finish is called, when travelling to the first destination is ready.
In the upper code i get two times the cancel event, but newer the finish. I want to program an anímated fly, which show's an visitor all the destinations, but not like a navi at earth level, but as short flight from point to point. At each stopping position i could update the map with maybe new markers etc.

You had only one typing error at line:
Instead of :GoogleMapsExtras1.MoveCamera(GoogleMap1, CameraUpdateFactory1.NewLatLng(LatLng)) i used LatLng1

Whats the state of your windows computer?

Reinhard
 
Top