B4A Class Open Street Map viewer - GPS

Hi,

This b4Xlib contains a custom view (cvMap) which can display Open Street Map.


screenshot.png
View attachment 109081



The tiles are retrieved from the internet and cached in a database. You can add shapes and images on the map.

UI :
- Lat/lng Center of the map
- Zoom Level
- Compass Direction with rotation
- Scale
- Button Menu
- Grid
- Center
- GPS position and bearing

Event :
- ready
- Lat/lng changed
- Zoom Level Changed
- Compass Direction Changed
- Shape Clicked
- Map Clicked
- Center Lat/lng clicked
- Button Menu clicked
- Scale clicked
- Compass Clicked
- GPS Clicked

Tile Server
2 new properties :
This 2 properties are available in the designer or with setter/getter from the cvmap : userAgent, tileServer

Dependencies :
- Core
- SQL
- OKHttpUtils2
- XUI
- XUI views

Other files in the b4xlib :
- coMapUtilities : code module with Types, functions, helpers
- clTileManager : standard class module to load tile from database and/or internet and save them into the database
- clMapShapeCirgle : standard class to draw circle on the map
- clMapShapeLine : standard class to draw line on the map
- clMapShapePolygon : standard class to draw polygon on the map
- clMapShapeImage : standard class to draw image on the map
- layout cvmap.bal and cvmap.bjl
- images for the compass and gps



How to use it :
- Just add the cvMap custom view with the designer
- create sub to handle events if necessary
- set options (lat/lng, zoom.....)

B4A example and B4J example included



spsp
 

Attachments

  • Screenshot_2021-02-21-11-49-10-222_map.b4A.com.jpg
    Screenshot_2021-02-21-11-49-10-222_map.b4A.com.jpg
    258.3 KB · Views: 1,685
  • B4Xmap.zip
    23 KB · Views: 1,652
  • B4Xmap.b4xlib
    23.7 KB · Views: 567
Last edited:

Star-Dust

Expert
Licensed User
Longtime User
I turned the project into B4XPages. So you can also work only on one platform but all three platforms will be modified. It is not necessary to have b4i.

I have also attached the B4XLib version with some minor changes.

Thanks @spsp you have opened a very interesting path. I hope my contribution can be of help to you
 

Attachments

  • B4XOpenMap.b4xlib
    23.1 KB · Views: 486
  • B4XOpenMap.zip
    187 KB · Views: 520

AngeloR

New Member
Licensed User
Ho trasformato il progetto in B4XPages. Quindi puoi anche lavorare solo su una piattaforma, ma tutte e tre le piattaforme verranno modificate. Non è necessario avere b4i.

Ho anche collegato la versione B4XLib con alcune modifiche minori.

Grazie @spsp hai aperto un percorso molto interessante. Spero che il mio contributo possa essere di aiuto a voi
[/CITAZIONE]
would it be conceivable to design geometries on a map?
 

Star-Dust

Expert
Licensed User
Longtime User
I think so
 

spsp

Active Member
Licensed User
Longtime User

It should be a bit difficult but possible to draw shape on the map. The main problem is that a map is not picture but a set of tiles.

Which geometric shape do you need ? square, rectangle, circle, polygon...?
 

spsp

Active Member
Licensed User
Longtime User
I turned the project into B4XPages. So you can also work only on one platform but all three platforms will be modified. It is not necessary to have b4i.

I have also attached the B4XLib version with some minor changes.

Thanks @spsp you have opened a very interesting path. I hope my contribution can be of help to you

Thank you for your help.
 

Star-Dust

Expert
Licensed User
Longtime User
It should be a bit difficult but possible to draw shape on the map. The main problem is that a map is not picture but a set of tiles.

Which geometric shape do you need ? square, rectangle, circle, polygon...?
All, square, rectangle, circle, polygon :p
 

udg

Expert
Licensed User
Longtime User
Hi,
is it possible to draw personalized markers? I'd like to use different PNGs to mark different places.
Currently type TMapMarker seems to allow just a color and the coords.
 

spsp

Active Member
Licensed User
Longtime User
Hi,
is it possible to draw personalized markers? I'd like to use different PNGs to mark different places.
Currently type TMapMarker seems to allow just a color and the coords.

Hi, actually it's not possible

I'm now working on adding the ability to draw shape (circle, polygon, line) and image and the map.
Be patient.....

spsp
 

udg

Expert
Licensed User
Longtime User
Thank you. Take all the time you need. I just dowloaded the lib and run the B4J demo out of curiosity; BTW, great job!
 

spsp

Active Member
Licensed User
Longtime User

Hi,

You are now able to draw geometric shape like circle, line and polygon. adding image is also supported. See post #1 to download new version.
 

udg

Expert
Licensed User
Longtime User
Hi,
just downloaded the most recent version. I wish to signal a possible bug (or the need to better explain how to use the lib).
[B4J 8.90] Using your demo "as is" . Click on a point on the map not covered by shapes (eg. Venezia, Italy). Choose "Search" option. Enter "Milano, viale Certosa 1" and choose first one of the proposed alternatives.
The program crashes with the following log
Waiting for debugger to connect...
Program started.
Call B4XPages.GetManager.LogEvents = True to enable logging B4XPages events.
lat/Lng changed : [IsInitialized=false, fLat=0.0, fLng=0.0
]
zoom level changed : 0
lat/Lng changed : [IsInitialized=false, fLat=48.0, fLng=2.0
]
zoom level changed : 5
compass changed: 0
lat/Lng changed : [IsInitialized=false, fLat=48.0, fLng=2.0
]
Index time: 1 ms (2 Items)
lat/Lng changed : [IsInitialized=false, fLat=45.497823, fLng=9.1351504
]
compass changed: 0
lat/Lng changed : [IsInitialized=false, fLat=45.497823, fLng=9.1351504
]
Error occurred on line: 401 (cvMap)
java.lang.reflect.InvocationTargetException
at sun.reflect.GeneratedMethodAccessor9.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at anywheresoftware.b4a.keywords.Common.CallSubDebug(Common.java:455)
at b4j.example.cvmap._update_shapes(cvmap.java:622)
at b4j.example.cvmap._draw(cvmap.java:118)
at b4j.example.b4xmainpage$ResumableSub_parseGeocoding.resume(b4xmainpage.java:1068)
at anywheresoftware.b4a.shell.DebugResumableSub$DelegatableResumableSub.resumeAsUserSub(DebugResumableSub.java:47)
at sun.reflect.GeneratedMethodAccessor3.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:632)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:237)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
at sun.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:91)
at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:98)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:78)
at anywheresoftware.b4a.shell.DebugResumableSub$DelegatableResumableSub.resume(DebugResumableSub.java:42)
at anywheresoftware.b4a.BA.checkAndRunWaitForEvent(BA.java:136)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:85)
at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:98)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:78)
at anywheresoftware.b4a.keywords.Common$3.run(Common.java:1086)
at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at anywheresoftware.b4a.debug.Debug.CallSub4(Debug.java:134)
at anywheresoftware.b4a.debug.Debug.CallSubNew(Debug.java:78)
... 33 more
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at anywheresoftware.b4a.debug.Debug.CallSub4(Debug.java:115)
... 34 more
Caused by: java.lang.RuntimeException: Object should first be initialized (B4XView).
at anywheresoftware.b4a.AbsObjectWrapper.getObject(AbsObjectWrapper.java:32)
at anywheresoftware.b4a.objects.B4XViewWrapper.getNodeObject(B4XViewWrapper.java:120)
at anywheresoftware.b4a.objects.B4XViewWrapper.asViewWrapper(B4XViewWrapper.java:116)
at anywheresoftware.b4a.objects.B4XViewWrapper.getTag(B4XViewWrapper.java:697)
at b4j.example.cvmap._latlngtopoint(cvmap.java:2131)
at b4j.example.clmapshapecircle._draw(clmapshapecircle.java:139)
... 39 more
java.lang.RuntimeException: java.net.SocketException: Socket closed
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:120)
at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:98)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:78)
at anywheresoftware.b4j.objects.PaneWrapper$5.handle(PaneWrapper.java:153)
at anywheresoftware.b4j.objects.PaneWrapper$5.handle(PaneWrapper.java:1)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:394)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$353(GlassViewEventHandler.java:432)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:431)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:937)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.net.SocketException: Socket closed
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:118)
at java.net.SocketOutputStream.write(SocketOutputStream.java:155)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
at anywheresoftware.b4a.shell.ShellConnector.sendControlMessage(ShellConnector.java:55)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:189)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
at sun.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:91)
... 32 more

I guess the problem derives from a specific tile (the one good for the address searched for) missing from the current DB.
In fact, if we first move the map center to the place we'll be searching for next, the error doesn't happen.
 

agraham

Expert
Licensed User
Longtime User
Nice job 👍 I am going to use both the B4J and B4A versions to display Open Street Maps alongside my own mapping programs which use Ordnance Survey mapping. I could start taking the library apart but I thought that I would take the easy way out and ask if it possible to add mouse scroll-wheel scrolling in B4J and either pinch in or out or double tap (same place to zoom in, different places to zoom out) zooming?

A couple of niggles.
a) I think the fcvMap_MenuClicked dialog title ought to be "Settings"
b) The menu button on B4J is awkwardly stuck to the right hand edge of the window but is neatly positioned in B4A.
 

spsp

Active Member
Licensed User
Longtime User
Nice job 👍 I am going to use both the B4J and B4A versions to display Open Street Maps alongside my own mapping programs which use Ordnance Survey mapping. I could start taking the library apart but I thought that I would take the easy way out and ask if it possible to add mouse scroll-wheel scrolling in B4J and either pinch in or out or double tap (same place to zoom in, different places to zoom out) zooming?

A couple of niggles.
a) I think the fcvMap_MenuClicked dialog title ought to be "Settings"
b) The menu button on B4J is awkwardly stuck to the right hand edge of the window but is neatly positioned in B4A.

a) You're right. Title menu is now 'Settings'.
b) Corrected.

Attached files in Post #1 updated

For the mouse scroll wheel and double tap, i will take a look.
 

agraham

Expert
Licensed User
Longtime User
Because the Settings menu is too small and wraps hiding half of the text on a phone and because the dialog scrolls so slowly on the desktop that it's a pain I've changed the initialisation of fPrefDlg in B4XPage_Created
B4X:
#If b4a  
  fPrefDlg.Initialize(Root,"Map", 250dip , 350dip)
#else
  fPrefDlg.Initialize(Root,"Map",300, 600)  
#End If
 

udg

Expert
Licensed User
Longtime User
Hi,
I solved the crash illustrated at post #33 above simply inserting a "change zoom level" command before the drawing.
Your sub parseGeocoding (lines 240+) looks now:
B4X:
fcvMap.addShapeCircle(coMapUtilities.initShapeCircle(ll,15dip,d.Get("color"),True,d.Get("radius")*1dip,m.Get("display_name")))
fcvMap.CenterLatLng=ll
fcvMap.ZoomLevel = 15                'add this line
fcvMap.draw
 

Domrich_FR

New Member
Try to remove specific marker:
Public Sub removeMarker (Latitude As Double,Longitude As Double)
    Private point As TMapLatLng
    point = coMapUtilities.initLatLng(Latitude,Longitude)   
    Private image As TMapShapeImage   
    image.fLatLng = point
    image.fbitmap = fxui.LoadBitmapResize(File.DirAssets,"marker.png",20dip,25dip,True)
    image.fRotation = 0
    image.fData = "ball"         
    fcvMap.removeShape(image)   
    fcvMap.draw
End Sub
Hi
First congratulations for your very useful job.
I am writing an app to follow a stratospheric ballon (not professionnal) for students. Ballon send its position by radio
Working not to bad . I have added a pic (marker on the map with no problem but I did not succeed to remove a specific marker. I have tried fcvMap.removeShape. with exact coordinates given when adding.
Can you help? Thanks in advance.
Dominique
 

udg

Expert
Licensed User
Longtime User
Try with something like the following
B4X:
For i=0 To fMap.fShapes.Size-1
        Dim styp As String = CallSub(fMap.fShapes.Get(i),"getShape_Type")
        Dim sdat As String = CallSub(fMap.fShapes.Get(i),"getShape_Data")
        Log(styp)
        Log(sdat)
        If styp = "image" And sdat ="Marseille" Then
            fMap.fShapes.RemoveAt(i)
            Exit
        End If
    Next
and adapt it to your needs.

Update: if you need to remove based on coordinates, you may want to try somethin like the following:
B4X:
Private point As TMapLatLng
    point = coMapUtilities.initLatLng(Latitude,Longitude)
    For i=0 To fMap.fShapes.Size-1
      Dim styp As String = CallSub(fMap.fShapes.Get(i),"getShape_Type")
      If styp = "image" Then
        Dim test As TMapShapeImage = CallSub(fMap.fShapes.Get(i),"get_Shape")
        If ((test.fLatLng.fLat = point.fLat) And (test.fLatLng.fLng = point.fLng)) Then
            fMap.fShapes.RemoveAt(i)
            Exit
        End If
      End If
    Next
The above should remove the first image set at goven coord; remove the Exit and it should delete allt he images at those coords.
 
Last edited:
Top