B4A Library [B4X] PlusCodes Library



This is an implementation of the Open Location Code, OLC, library in B4X.
The class source is an adaption of the VBA source provided on GITBUB VBA Source

I stay these days in rural province area, common postal addresses are not a given, usually the nick name is sufficient for a village.
For example, in the time of Covid and travel restrictions, the number of online orders are grown up.
Delivery of online orders by services from outside not familiar with the village run into a time problem, houses in satellite areas are difficult to find.
The PlusCodes attached to the delivery addresses are often helpful in the last month, specially to deliver in satellite areas with minimum time delay.
I implement the PlusCodes in an B4A and B4J application for support. The library decode and encode PlusCodes addresses in longitude and latitude coordinates with given accuracy.
The coordinates are WGS84 compatible and can be used with other application. BTW Google Maps support PlusCodes Addresses directly.

I implemented OLC as standard class. So before use the class must be initialized.

For example running the test procedures

B4X:
            Private PC As PlusCode
            PC.Initialize
            PC.TestOLCLibrary

I modified the code where necessary (string operation in most cases) and changed the MSGBOX calls to LOG and LOGERROR in case of B4J specially in the included test routine
I did not test the library with B4I. There are no UI elements and the source code does not include platform relevant instructions.

The following ist part of the GITHUB Documentation....


Part of the library source is the protocol of the test run

Any improvements of library or comments are welcome
I have a weak internet connection here, so can happen I will answer delayed

Version 210821.01 is attached
 

Attachments

  • PlusCode.b4xlib
    17.2 KB · Views: 395

Theera

Well-Known Member
Licensed User
Longtime User
Is there example for test?
 

mading1309

Member
Licensed User
Longtime User
Is there example for test?
Hello Theera

It's been a while since I touched the source code. My internet connection here in Paraguay has not improved significantly (remote area). As far as I remember there is a test routine in the library.

I'll have a look tonight and give you a hint.
 

mading1309

Member
Licensed User
Longtime User
Hello Theera

The source code of the library (B4xlib is a ZIP file) contains a description of the library, the GITBUB source code and a test function. The test function uses the test data from the GITHUB source. I have simply taken this part of the source code from the source code I found on my notebook.

Test Function for PlusCode Library, included in the Library:
'
' This is a subroutine to test the functions of the library, using test data
' from the Github project. If you make any changes to the above code, run this
' subroutine to check that your changes have not introduced errors. If you
' identify tests that are faulty or would like to add tests, go to the
' Github project and raise an issue.
Public Sub TestOLCLibrary As String
    Private i As Int
    Private calcode As String
    Private OLCArea As OLCArea

    Private validity(17) As List
    ' Fields code,isValid,isShort,isFull
'     0 8fwc2345+G6            valid            notshort        full
'     1 8FWC2345+G6G            valid            notshort        full
'     2 8fwc2345+            valid            notshort        full
'     3 8FWCX400+            valid            notshort        full
'     4 WC2345+G6g            valid            Short            notfull
'     5 2345+G6                valid            short            notfull
'     6 45+G6                valid            short            notfull
'     7 +G6                    valid            short            notfull
'     8 G+                    notvalid        notshort        notfull
'     9 +                    notvalid        notshort        notfull
'    10 8FWC2345+G            notvalid        notshort        notfull
'    11 8FWC2_45+G6            notvalid        notshort        notfull
'    12 8FWC2η45+G6            notvalid        notshort        notfull
'    13 8FWC2345+G6+            notvalid        notshort        notfull
'    14 8FWC2300+G6            notvalid        notshort        notfull
'    15 WC2300+G6g            notvalid        notshort        notfull
'    16 WC2345+G                notvalid        notshort        notfull  
'  
    validity(0).Initialize2 (Array As String ("8fwc2345+G6", "true", "false", "true"))
    validity(1).Initialize2 (Array As String ("8FWC2345+G6G", "true", "false", "true"))
    validity(2).Initialize2 (Array As String ("8fwc2345+", "true", "false", "true"))  
    validity(3).Initialize2 (Array As String ("8FWCX400+", "true", "false", "true")) 'behind separator at least 1 sign
    validity(4).Initialize2 (Array As String ("WC2345+G6g", "true", "true", "false"))
    validity(5).Initialize2 (Array As String ("2345+G6", "true", "true", "false"))
    validity(6).Initialize2 (Array As String ("45+G6", "true", "true", "false"))
    validity(7).Initialize2 (Array As String ("+G6", "true", "true", "false"))
    validity(8).Initialize2 (Array As String ("G+", "false", "false", "false"))
    validity(9).Initialize2 (Array As String ("+", "false", "false", "false"))
    validity(10).Initialize2 (Array As String ("8FWC2345+G", "false", "false", "false"))
    validity(11).Initialize2 (Array As String ("8FWC2_45+G6", "false", "false", "false"))
    validity(12).Initialize2 (Array As String ("8FWC2η45+G6", "false", "false", "false"))
    validity(13).Initialize2 (Array As String ("8FWC2345+G6+", "false", "false", "false"))
    validity(14).Initialize2 (Array As String ("8FWC2300+G6", "false", "false", "false"))
    validity(15).Initialize2 (Array As String ("WC2300+G6g", "false", "false", "false"))
    validity(16).Initialize2 (Array As String ("WC2345+G", "false", "false", "false"))
    For i = 0 To validity.Length-1
        Log ("  ")
        Private v, s, f As Boolean
        Log ($"Code [${i}] : ${validity(i).Get(0)}"$)
       
        v = OLCIsValid(validity(i).Get(0))
        #if B4j
            Dim output As String = $"IsValid, ${IIf(validity(i).get(1)=v.As (String),"Okay","Failed")} => expected: ${validity(i).get(1)}, actual: ${v}"$
            If validity(i).get(1)=v.As (String) Then
                Log (output)      
            Else
                LogError (output)
            End If
        #else
            Log ($"IsValid, ${IIf(validity(i).get(1)=v.As (String),"Okay","Failed")} => expected: ${validity(i).get(1)}, actual: ${v}"$)
        #End If
        s = OLCIsShort(validity(i).Get(0))
        #if B4j
        Dim output As String = $"IsShort, ${IIf(validity(i).get(2)=s.As (String),"Okay","Failed")} => expected: ${validity(i).get(2)}, actual: ${s}"$
            If validity(i).get(2)=s.As (String) Then
                Log (output)      
            Else
                LogError (output)
            End If
        #else
            Log ($"IsShort, ${IIf(validity(i).get(2)=s.As (String),"Okay","Failed")} => expected: ${validity(i).get(2)}, actual: ${s}"$)
        #end if
        f = OLCIsFull(validity(i).Get(0))
        #if B4j
        Dim output As String = $"IsFull, ${IIf(validity(i).get(3)=f.As (String),"Okay","Failed")} => expected: ${validity(i).get(3)}, actual: ${f}"$
            If validity(i).get(3)=f.As (String) Then
                Log (output)      
            Else
                LogError (output)
            End If
        #else
            Log ($"IsFull, ${IIf(validity(i).get(3)=f.As (String),"Okay","Failed")} => expected: ${validity(i).get(3)}, actual: ${f}"$)
        #end if
'        If (v <> (validity(i).Get(1) = "true")) Or (s <> (validity(i).Get(2) = "true")) Or (f <> (validity(i).Get(3) = "true")) Then
'            Return False
'        End If
        Log ("  ")
    Next

    Private encodingCodes(15) As String
    ' Fields are lat,lng,latLo,lngLo,latHi,lngHi
    Private encodingCoordinates(15) As List
    encodingCodes(0) = "7fG49Q00+"
    encodingCoordinates(0).Initialize2 (Array As Double (20.375, 2.775, 20.35, 2.75, 20.4, 2.8))
    encodingCodes(1) = "7FG49QCJ+2v"
    encodingCoordinates(1).Initialize2 (Array As Double (20.3700625, 2.7821875, 20.37, 2.782125, 20.370125, 2.78225))
    encodingCodes(2) = "7FG49QCJ+2VX"
    encodingCoordinates(2).Initialize2 (Array As Double (20.3701125, 2.782234375, 20.3701, 2.78221875, 20.370125, 2.78225))
    encodingCodes(3) = "7FG49QCJ+2VXGJ"
    encodingCoordinates(3).Initialize2 (Array As Double (20.3701135, 2.78223535156, 20.370113, 2.782234375, 20.370114, 2.78223632813))
    encodingCodes(4) = "8FVC2222+22"
    encodingCoordinates(4).Initialize2 (Array As Double (47.0000625, 8.0000625, 47, 8, 47.000125, 8.000125))
    encodingCodes(5) = "4VCPPQGP+Q9"
    encodingCoordinates(5).Initialize2 (Array As Double (-41.2730625, 174.7859375, -41.273125, 174.785875, -41.273, 174.786))
    encodingCodes(6) = "62G20000+"
    encodingCoordinates(6).Initialize2 (Array As Double (0.5, -179.5, 0, -180, 1, -179))
    encodingCodes(7) = "22220000+"
    encodingCoordinates(7).Initialize2 (Array As Double (-89.5, -179.5, -90, -180, -89, -179))
    encodingCodes(8) = "7FG40000+"
    encodingCoordinates(8).Initialize2 (Array As Double (20.5, 2.5, 20, 2.0, 21.0, 3.0))
    encodingCodes(9) = "22222222+22"
    encodingCoordinates(9).Initialize2 (Array As Double (-89.9999375, -179.9999375, -90.0, -180.0, -89.999875, -179.999875))
    encodingCodes(10) = "6VGX0000+"
    encodingCoordinates(10).Initialize2 (Array As Double (0.5, 179.5, 0, 179, 1, 180))
    encodingCodes(11) = "CFX30000+"
    encodingCoordinates(11).Initialize2 (Array As Double (90, 1, 89, 1, 90, 2))
    encodingCodes(12) = "CFX30000+"
    encodingCoordinates(12).Initialize2 (Array As Double (92, 1, 89, 1, 90, 2))
    encodingCodes(13) = "62H20000+"
    encodingCoordinates(13).Initialize2 (Array As Double (1, 180, 1, -180, 2, -179))
    encodingCodes(14) = "62H30000+"
    encodingCoordinates(14).Initialize2 (Array As Double (1, 181, 1, -179, 2, -178))
    For i = 0 To encodingCodes.Length-1
        Log ("  ")
        Log ($"Encoding test [${i}] : ${encodingCodes(i)}"$)
        OLCArea = OLCDecode(encodingCodes(i))
       
        calcode = OLCEncode(encodingCoordinates(i).get(0), encodingCoordinates(i).get(1), OLCArea.CodeLength)
        Log ($"code generation expected: ${encodingCodes(i)}, actual: ${calcode};"$)
        Do Until encodingCodes(i).ToUpperCase=calcode
            calcode = OLCEncode(encodingCoordinates(i).get(0), encodingCoordinates(i).get(1), OLCArea.CodeLength)
            Log ($"code generation expected: ${encodingCodes(i)}, actual: ${calcode}"$)  
        Loop
       
        calcode = OLCEncode(OLCArea.LatCenter, OLCArea.LngCenter, OLCArea.CodeLength)
        Log ($"code recovery expected: ${encodingCodes(i)}, actual: ${calcode}"$)
        Log ($"coordinate check: "$)
        Log ($"     => given : ${OLCArea.latlo}, ${OLCArea.LngLo} ${OLCArea.LatHi}, ${OLCArea.LngHi}"$)
        Log ($"     => expected: " ${encodingCoordinates(i).get(2)}, ${encodingCoordinates(i).get(3)}, ${encodingCoordinates(i).get(4)}, ${encodingCoordinates(i).get(5)}"$)
    Next

    Private shortCodes(11) As List
    shortCodes(0).Initialize2 (Array As String ("9C3W9QCJ+2VX", "+2VX"))
    shortCodes(1).Initialize2 (Array As String ("9C3W9QCJ+2VX", "CJ+2VX"))
    shortCodes(2).Initialize2 (Array As String ("9C3W9QCJ+2VX", "CJ+2VX"))
    shortCodes(3).Initialize2 (Array As String ("9C3W9QCJ+2VX", "CJ+2VX"))
    shortCodes(4).Initialize2 (Array As String ("9C3W9QCJ+2VX", "CJ+2VX"))
    shortCodes(5).Initialize2 (Array As String ("9C3W9QCJ+2VX", "9QCJ+2VX"))
    shortCodes(6).Initialize2 (Array As String ("9C3W9QCJ+2VX", "9QCJ+2VX"))
    shortCodes(7).Initialize2 (Array As String ("9C3W9QCJ+2VX", "9QCJ+2VX"))
    shortCodes(8).Initialize2 (Array As String ("9C3W9QCJ+2VX", "9QCJ+2VX"))
    shortCodes(9).Initialize2 (Array As String ("8FJFW222+", "22+"))
    shortCodes(10).Initialize2 (Array As String ("796RXG22+", "22+"))
    Private shortCoordinates(11) As List
    shortCoordinates(0).Initialize2 (Array As Double (51.3701125, -1.217765625))
    shortCoordinates(1).Initialize2 (Array As Double (51.3708675, -1.217765625))
    shortCoordinates(2).Initialize2 (Array As Double (51.3693575, -1.217765625))
    shortCoordinates(3).Initialize2 (Array As Double (51.3701125, -1.218520625))
    shortCoordinates(4).Initialize2 (Array As Double (51.3701125, -1.217010625))
    shortCoordinates(5).Initialize2 (Array As Double (51.3852125, -1.217765625))
    shortCoordinates(6).Initialize2 (Array As Double (51.3550125, -1.217765625))
    shortCoordinates(7).Initialize2 (Array As Double (51.3701125, -1.232865625))
    shortCoordinates(8).Initialize2 (Array As Double (51.3701125, -1.202665625))
    shortCoordinates(9).Initialize2 (Array As Double (42.899, 9.012))
    shortCoordinates(10).Initialize2 (Array As Double (14.95125, -23.5001))
    For i = 0 To shortCodes.Length-1
        Log ("  ")
        calcode = OLCShorten(shortCodes(i).get(0), shortCoordinates(i).get(0), shortCoordinates(i).get(1))
        Log ($"Shorten test ${i}, expected: ${shortCodes(i).get(1)}, actual: ${calcode}"$)
        Dim str As String = shortCodes(i).Get(1)
        calcode = OLCRecoverNearest(shortCodes(i).get(1), shortCoordinates(i).get(0), shortCoordinates(i).get(1))
        Log ($"Recover test ${i}, expected: ${shortCodes(i).get(0)}, actual: ${calcode}"$)
        str = shortCodes(i).Get(0)
       
        Do Until str.Contains(calcode.SubString2(0,calcode.length-2))
            calcode = OLCRecoverNearest(shortCodes(i).get(1), shortCoordinates(i).get(0), shortCoordinates(i).get(1))
            Log ($"Recover test ${i}, expected: ${shortCodes(i).get(0)}, actual: ${calcode}"$)
        Loop
    Next

    ' North pole recovery test.
    calcode = OLCRecoverNearest("2222+22", 89.6, 0.0)
    Log ("  ")
    Log ($"North pole recovery test, expected: CFX22222+22, actual: ${calcode}"$)
    If calcode <> "CFX22222+22" Then Return False

    ' South pole recovery test.
    calcode = OLCRecoverNearest("XXXXXX+XX", -81.0, 0.0)
    Log ("  ")
    Log ($"South pole recovery test, expected: 2CXXXXXX+XX, actual: ${calcode}"$)
    If calcode <> "2CXXXXXX+XX" Then Return False

    Log ("All tests finished, check log output if passed")',"OLCLibrary Test")
    Return True
End Sub


or simply use my other test routine. The outputs are logs.


My additional Testcode:
Public Sub Initialize
'    B4XPages.GetManager.LogEvents = True
End Sub

'This event will be called once, before the page becomes visible.
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
    Private PC As PlusCode
    PC.Initialize
'    PC.TestOLCLibrary
Private lat As Double = 12.293918
Private lon As Double = 122.647114
    Log (PC.OLCEncode(lat,lon,PC.CODE_PRECISION_EXTRA_))  '7Q447JVW+HR9
    Log (PC.OLCEncode(lat,lon,PC.CODE_PRECISION_EXTRA_+4))  '7Q447JVW+HR9PP
    Log (PC.OLCEncode(lat,lon,PC.CODE_PRECISION_NORMAL_))  '7Q447JVW+HR
    Log (PC.OLCencode(11.577735039107786, 122.75344180996201,PC.CODE_PRECISION_EXTRA_))
    Log (PC.OLCDecode2array("7Q34QQ8V+6H7"))
    Log (PC.OLCDecode2array("7Q34HQH3+39"))
    Log ($"lat : ${lat} lon : ${lon}"$)
    Log (PC.OLCDecode2Array("7Q447JVW+HR9"))
    Log (PC.OLCDecode("7Q447JVW+HR9"))
    Log (PC.Distance(PC.OLCDecode("7Q447JVW+HR9PP"))*1000)
    Log (PC.DistanceLoCenter(PC.OLCDecode("7Q447JVW+HR9PP"))*1000)
    Log (PC.DistancehiCenter(PC.OLCDecode("7Q447JVW+HR9PP"))*1000)
    Log (PC.Distance(PC.OLCDecode("7Q447JVW+HR9"))*1000)
    Log (PC.DistanceLoCenter(PC.OLCDecode("7Q447JVW+HR9"))*1000)
    Log (PC.DistancehiCenter(PC.OLCDecode("7Q447JVW+HR9"))*1000)
    Log (PC.Distance(PC.OLCDecode("7Q447JVW+HR"))*1000)
    Log (PC.DistanceLoCenter(PC.OLCDecode("7Q447JVW+HR"))*1000)
    Log (PC.DistancehiCenter(PC.OLCDecode("7Q447JVW+HR"))*1000)
End Sub

I hope I have been able to help you a little.
 
Cookies are required to use this site. You must accept them to continue using the site. Learn more…