Jump to content

Strings not concatenating (combining) [Solved]


Recommended Posts

Hello!

 

I wrote myself a script to follow Google Maps Polyline encoding steps: https://developers.google.com/maps/documentation/utilities/polylinealgorithm, and that works (although I think that it's a bit janky), but now I'm having issues getting the output.

 

When I run the script, all the points come out correctly in the console, and even when they're the only things that I log, it displays them fine. However, I'm adding each point into a variable to return all of them at once at the end, fully formatted, and it's only taking the very first point. I can't figure out what I'm doing wrong, as it seems fine.

 

When run with the default value, it should output this at the end: Custom Polygon: _p~iF~ps|U_ulLnnqC_mqNvxq`@

But instead I'm just getting this: Custom Polygon: _p~iF

 

I know that it's reaching the string combination lines because it's logging the data before it (and even if a put log AFTER the $sPolygon &= $aPoints[0], it's logged fine).

 

Here's my full code (problem is lines ~209 - 234, search "$sPolygon &= $aPoints[1]"):

#include <Array.au3>
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>

_PolyGUI()

Func _PolyGUI()
    #Region ### START Koda GUI section ### Form=
    $hInputGUI = GUICreate("Lat Long encoder", 403, 301, 192, 124)
    GUISetFont(8, 400, 0, "Consolas")
    GUICtrlCreateLabel("Input polygon points here, format as:", 8, 8, 263, 19)
    GUICtrlSetFont(-1, 10, 800, 0, "Consolas")
    GUICtrlCreateLabel("Lat Long - Single point", 8, 24, 142, 17)
    GUICtrlCreateLabel("Lat Long, Lat Long, Lat Long - Multiple points", 8, 40, 280, 17)
    Local $sPoints = GUICtrlCreateEdit("", 8, 64, 385, 201, BitOR($ES_WANTRETURN, $WS_VSCROLL))
    GUICtrlSetData(-1, "38.5 -120.2, 40.7 -120.95, 43.252 -126.453")
    GUICtrlSetFont(-1, 10, 400, 0, "Consolas")
    $bOK = GUICtrlCreateButton("bOK", 16, 272, 123, 25)
    GUICtrlSetFont(-1, 12, 800, 0, "Consolas")
    $bCancel = GUICtrlCreateButton("bCancel", 304, 272, 75, 25)
    GUICtrlSetFont(-1, 12, 800, 0, "Consolas")
    GUISetState(@SW_SHOW, $hInputGUI)
    #EndRegion ### END Koda GUI section ###

    While 1
        $nMsg = GUIGetMsg()
        Switch $nMsg
            Case $GUI_EVENT_CLOSE
                Exit
            Case $bCancel
                Exit
            Case $bOK
                $sPoints = GUICtrlRead($sPoints)
                GUISetState(@SW_HIDE, $hInputGUI)
                _GetPoly($sPoints, True)
                ExitLoop
        EndSwitch
        Sleep(10)
    WEnd

EndFunc   ;==>_PolyGUI

;https://developers.google.com/maps/documentation/utilities/polylinealgorithm
;https://app.dsmobileidx.com/api/DescribeSearchForLinkId?linkId=469787
; Note that this will only really work inside the US (this side of the World), as it's assuming any negative is the Longitude
;https://gist.github.com/ismaels/6636986 - decoder

;Using:     41.83162    -87.64696
; Expected:     sfi~F       np}uO
; Actual:   sfi~f       np}uo
; If we remove 32 from the last ASCII code, since the last bit chunk is 0, we get the correct case/ characters
; We need to run this logic back through all the indexes though and do this to all that that <= 63

;LinkId=469787
; Expected: q{`aHpa_iVi[kp@}`Aa{@e[eCoqBbAyc@iRy{@g_@mz@|gA{eAh~@Vf~Etv@gB~p@gQ`^yg@~p@ekAldA{KfFxIrJ^pO~Mtl@dPrJnUz[nSpo@wf@fc@yw@n@ob@
; Actual:   s{`aHpa_iVg[kp@}`Aa{@g[gCmqBbA{c@iRy{@e_@kz@|gA{eAh~@Td~Evv@gB|p@gQb^wg@|p@ekAndA{KfFvIpJ`@rO~Mrl@dPrJnU|[lSpo@wf@dc@yw@n@mb@
; I assume that this is because of bad data, the points have repeating 9's and 0's, which looks fishy. The polygon is (very) close, but not quite the same.

Func _GetPoly($sPoints, $bLog = False)
    Local $timer = TimerInit(), $sConsole[11]
    Local $sPolygon = ""

    ; Step 1, take the initial signed value:
    Local $aCoords = StringRegExp($sPoints, "(-*?\d*\.\d*) (-*?\d*\.\d*)", 3), $aPoints[2]
;~ _ArrayDisplay($aCoords)
    If $bLog Then _Log(_ArrayToString($aCoords))

    For $c = 0 To (UBound($aCoords) - 1) Step 2
;~ If $bLog Then _Log($c)
        If $c = 0 Then
            $aPoints[0] = $aCoords[$c]
            $aPoints[1] = $aCoords[$c + 1]
        Else
            $aPoints[0] = $aCoords[$c] - $aCoords[$c - 2]
            $aPoints[1] = $aCoords[$c + 1] - $aCoords[$c - 1]
        EndIf
        If $bLog Then
            _Log("- Step 1, take the initial signed value:")
            _Log(" " & $aPoints[0])
            _Log(" " & $aPoints[1])
        EndIf

        ; Step 2, multiply each by 1e5, and round
        $aPoints[0] = Round($aPoints[0] * 1e5, 0)
        $aPoints[1] = Round($aPoints[1] * 1e5, 0)
        If $bLog Then
            _Log("- Step 2, multiply each by 1e5, and round")
            _Log(" " & $aPoints[0])
            _Log(" " & $aPoints[1])
        EndIf

        ; Step 3, convert Decimal to Binary, using two's complement for negatives. Padded to 32 bits
        $aPoints[0] = _NumberToBinary($aPoints[0])
        $aPoints[1] = _NumberToBinary($aPoints[1])
        If $bLog Then
            _Log("- Step 3, convert Decimal to Binary, using two's complement for negatives. Padded to 32 bits")
            _Log(" " & $aPoints[0])
            _Log(" " & $aPoints[1])
        EndIf

        ; Step 4, left-shifted 1 bit
        $aPoints[0] = StringTrimLeft($aPoints[0], 1) & "0"
        $aPoints[1] = StringTrimLeft($aPoints[1], 1) & "0"
        If $bLog Then
            _Log("- Step 4, left-shifted 1 bit")
            _Log(" " & $aPoints[0])
            _Log(" " & $aPoints[1])
        EndIf

        ; Step 5, if negative, invert binary
        If $c = 0 Then
            If $aCoords[$c] < 0 Then $aPoints[0] = _InvertBinary($aPoints[0])
            If $aCoords[$c + 1] < 0 Then $aPoints[1] = _InvertBinary($aPoints[1])
        Else
            If $aCoords[$c] - $aCoords[$c - 2] < 0 Then $aPoints[0] = _InvertBinary($aPoints[0])
            If $aCoords[$c + 1] - $aCoords[$c - 1] < 0 Then $aPoints[1] = _InvertBinary($aPoints[1])
        EndIf
        If $bLog Then
            _Log("- Step 5, if negative, invert binary")
            _Log(" " & $aPoints[0])
            _Log(" " & $aPoints[1])
        EndIf

        Local $aChunks[2][6], $0x20

        For $i = 0 To 1
            $0x20 = "1" ; This is out BitOR flag, 0x20 BitOR'd onto our 5-bit chunks is always 1*****, except the last chunk
            $sConsole[5] = "" ; Clearing console variables
            $sConsole[6] = ""
            $sConsole[7] = ""
            $sConsole[8] = ""
            $sConsole[9] = ""
            For $j = 0 To 5 ;There will always be 6 chunks
                ; Step 6 & 7, break into 5-bit chunks, and reverse order
                $aChunks[$i][$j] = StringTrimLeft($aPoints[$i], StringLen($aPoints[$i]) - 5)
                ; This splits into 5-bit chunks in reverse order, doing 6 & 7 in one operation
;~              If $bLog Then _Log(" " & $aPoints[$i])
;~              If $bLog Then _Log(" " & StringLen($aPoints[$i]))
;~              If $bLog Then _Log(" " & StringTrimLeft($aPoints[$i], StringLen($aPoints[$i]) - 5))
;~              If $bLog Then _Log(" " & $aChunks[$i][$j])

                ; Here we consume the original binary string, so the next loop gets the correct next 5-bit chunk
                $aPoints[$i] = StringTrimRight($aPoints[$i], 5)
                $sConsole[5] &= $aChunks[$i][$j] & " "

                ; Once consumed, if the remaining length isn't enough for another bit chunk, switch 0x20 to 0 (no following chunks)
                If StringLen($aPoints[$i]) <= 5 Then $0x20 = "0"
                ; Step 8, BitOR 100000 (0x20) to our 5-bit chunks (effectively)
                $aChunks[$i][$j] = $0x20 & $aChunks[$i][$j]
                $sConsole[7] &= $aChunks[$i][$j] & " "

                ; Step 9, converting the chunk from Binary back to Decimal
                $aChunks[$i][$j] = _BinaryToDec($aChunks[$i][$j])
                $sConsole[8] &= $aChunks[$i][$j] & " "

                ; Step 10, adding 63 to decimal values
                $aChunks[$i][$j] += 63
                $sConsole[9] &= $aChunks[$i][$j] & " "

                If StringLen($aPoints[$i]) < 5 Then ExitLoop
            Next
            If $bLog Then
                _Log("- Step 6 & 7 (part " & $i & "), break into 5-bit chunks, and reverse order")
                _Log(" " & $sConsole[5])
                _Log("- Step 8 (part " & $i & "), BitOR 100000 (0x20) to our 5-bit chunks (effectively)")
                _Log(" " & $sConsole[7])
                _Log("- Step 9 (part " & $i & "), converting the chunk from Binary back to Decimal")
                _Log(" " & $sConsole[8])
                _Log("- Step 10 (part " & $i & "), adding 63 to decimal values")
                _Log(" " & $sConsole[9])
            EndIf
        Next

        Local $aASCII[0]
        For $i = 0 To 1
            Dim $aASCII[0] ; Reset ASCII array
            For $j = 0 To (UBound($aChunks, 2) - 1) ; For both chunk sets
                ReDim $aASCII[UBound($aASCII) + 1] ; Add an index for the ASCII array
                If $aChunks[$i][$j] = "" Or $aChunks[$i][$j] <= 63 Then ; If the chunk is not useful
                    $l = $j
                    For $k = $l To 1 Step -1
                        If $aChunks[$i][$k] = "" Or $aChunks[$i][$k] <= 63 Or $aASCII[$k] <= 63 Then
                            $aASCII[$k - 1] -= 32
                            If $aASCII[$k - 1] <= 63 Then _ArrayDelete($aASCII, $k - 1)
                        Else
                            ExitLoop
                        EndIf
                    Next
                    ExitLoop
                EndIf
                $aASCII[$j] = Int($aChunks[$i][$j])
            Next
            ;Step 11, convert each value to ASCII equivalent
            For $k = UBound($aASCII) - 1 To 0
                If $aASCII[$k] <= 63 Or $aASCII[$k] = "" Then
                    ReDim $aASCII[UBound($aASCII) - 1]
                Else
                    ExitLoop
                EndIf
            Next
            $aPoints[$i] = StringFromASCIIArray($aASCII, 0, -1, 0)
        Next

        If $bLog Then _Log("- Step 11, convert each value to ASCII equivalent, finished")

        If $aCoords[$c] <= 0 Then
            ;@CRLF & " " &
            If $bLog Then
                _Log($aPoints[1])
                _Log($aPoints[0])
                _Log("Next set")
            EndIf
            $sPolygon &= $aPoints[1]
            $sPolygon &= $aPoints[0]
        Else
            If $bLog Then
                _Log($aPoints[0])
                _Log($aPoints[1])
                _Log("Next set")
            EndIf
            $sPolygon &= $aPoints[0]
            $sPolygon &= $aPoints[1]
        EndIf
    Next

    If $bLog Then
        _Log("Custom Polygon: " & $sPolygon)
        _Log(TimerDiff($timer) & @CRLF)
    EndIf
    Return $sPolygon
EndFunc   ;==>_GetPoly

Func _NumberToBinary($iNumber)
    Local $sBinString = ""
    ; Maximum 32-bit # range is -2147483648 to 2147483647
    If $iNumber < -2147483648 Or $iNumber > 2147483647 Then Return SetError(1, 0, "")

    ; Convert to a 32-bit unsigned integer. We can't work on signed #'s
    $iUnsignedNumber = BitAND($iNumber, 0x7FFFFFFF)

    ; Cycle through each bit, shifting to the right until 0
    Do
        $sBinString = BitAND($iUnsignedNumber, 1) & $sBinString
        $iUnsignedNumber = BitShift($iUnsignedNumber, 1)
    Until Not $iUnsignedNumber

    ; Was it a negative #? Put the sign bit on top, and pad the bits that aren't set
    If $iNumber < 0 Then Return '1' & StringRight("000000000000000000000000000000" & $sBinString, 31)

    ; Always return 32 bit binaries
    If StringLen($sBinString) < 32 Then Return StringRight("0000000000000000000000000000000" & $sBinString, 32)

    Return $sBinString
EndFunc   ;==>_NumberToBinary

Func _BinaryToDec($sBinary)
    Local Const $aPower[8] = [128, 64, 32, 16, 8, 4, 2, 1]
    Local $iDec

    If StringRegExp($sBinary, "[0-1]") Then
        If StringLen($sBinary) < 8 Then
            Do
                $sBinary = "0" & $sBinary
            Until StringLen($sBinary) = 8
        EndIf
        $aBinary = StringSplit($sBinary, "", 2)
        For $i = 0 To UBound($aBinary) - 1
;~          $aBinary[$i] = $aBinary[$i] * $aPower[$i]
            $iDec += $aBinary[$i] * $aPower[$i]
        Next
        Return $iDec
    Else
        Return SetError(0, 0, "Not a binary string")
    EndIf

EndFunc   ;==>_BinaryToDec

Func _InvertBinary($iNumber)
;~  ConsoleWrite(@CRLF & $iNumber)
    Local $sNumber
    $aNumber = StringSplit($iNumber, "")
    For $i = 1 To $aNumber[0]
        If $aNumber[$i] = 0 Then
            $aNumber[$i] = 1
        ElseIf $aNumber[$i] = 1 Then
            $aNumber[$i] = 0
        Else
            Return SetError(0, 0, "Not a binary number")
        EndIf
        $sNumber &= String($aNumber[$i])
    Next

    Return $sNumber

EndFunc   ;==>_InvertBinary

Func _Log($data)
;~  Local Static $LogEnable = True
    ConsoleWrite(@CRLF & @HOUR & ":" & @MIN & "." & @SEC & "    " & $data)
    LogData(@HOUR & ":" & @MIN & "." & @SEC & " " & $data, "logs/LOGFILE.txt")
EndFunc   ;==>_Log

Func LogData($text, $File = "logs/LOGFILE.txt")
    Global $LogFile = ""
    If $LogFile = "" Then
        $LogFile = FileOpen($File, 9)
        OnAutoItExitRegister(CloseLog)
    EndIf
    FileWriteLine($LogFile, $text)
EndFunc   ;==>LogData

Func CloseLog()
    If $LogFile <> "" Then
        _Log("Closing LoD script" & @CRLF)
        FileClose($LogFile)
    EndIf
EndFunc   ;==>CloseLog

 

I've tried:

$sPolygon &= $aPoints[0] & $aPoints[1]
;----
$sPolygon = $sPolygon & $aPoints[0] & $aPoints[1]
;----
$sPolygon = $sPolygon & String($aPoints[0] & $aPoints[1])
;----
$sPolygon = String($sPolygon) & String($aPoints[0]) & String($aPoints[1])
;----
$sPolygon &= $aPoints[1]
$sPolygon &= $aPoints[0]
;----

 

I'm sure it's something basic that I'm overlooking, but I don't understand why it's not combining the strings. 

Also, unrelated, why doesn't $LogFile = FileOpen($File, 9) create the directory/ file if they don't exist? 9 should be $FO_CREATEPATH (8) + $FO_APPEND (1)?

Thanks!

Edited by mistersquirrle
Added unrelated question

We ought not to misbehave, but we should look as though we could.

Link to comment
Share on other sites

I think your problem is the func StringFromASCIIArray, so i changed line #204 to

$aPoints[$i] = StringStripWS(StringFromASCIIArray($aASCII, 0, -1, 0),8)

,which just strips all whitespaces from resultstring. With this my console output is:

23:08.29    38.5|-120.2|40.7|-120.95|43.252|-126.453
23:08.29    - Step 1, take the initial signed value:
23:08.29     38.5
23:08.29     -120.2
23:08.29    - Step 2, multiply each by 1e5, and round
23:08.29     3850000
23:08.29     -12020000
23:08.29    - Step 3, convert Decimal to Binary, using two's complement for negatives. Padded to 32 bits
23:08.29     00000000001110101011111100010000
23:08.29     11111111010010001001011011100000
23:08.29    - Step 4, left-shifted 1 bit
23:08.29     00000000011101010111111000100000
23:08.29     11111110100100010010110111000000
23:08.29    - Step 5, if negative, invert binary
23:08.29     00000000011101010111111000100000
23:08.29     00000001011011101101001000111111
23:08.29    - Step 6 & 7 (part 0), break into 5-bit chunks, and reverse order
23:08.29     00000 10001 11111 01010 00111 00000 
23:08.29    - Step 8 (part 0), BitOR 100000 (0x20) to our 5-bit chunks (effectively)
23:08.29     100000 110001 111111 101010 100111 000000 
23:08.29    - Step 9 (part 0), converting the chunk from Binary back to Decimal
23:08.29     32 49 63 42 39 0 
23:08.29    - Step 10 (part 0), adding 63 to decimal values
23:08.29     95 112 126 105 102 63 
23:08.29    - Step 6 & 7 (part 1), break into 5-bit chunks, and reverse order
23:08.29     11111 10001 10100 11101 10110 00000 
23:08.29    - Step 8 (part 1), BitOR 100000 (0x20) to our 5-bit chunks (effectively)
23:08.29     111111 110001 110100 111101 110110 000000 
23:08.29    - Step 9 (part 1), converting the chunk from Binary back to Decimal
23:08.29     63 49 52 61 54 0 
23:08.29    - Step 10 (part 1), adding 63 to decimal values
23:08.29     126 112 115 124 117 63 
23:08.29    - Step 11, convert each value to ASCII equivalent, finished
23:08.29    _p~iF
23:08.29    ~ps|U
23:08.29    Next set
23:08.29    - Step 1, take the initial signed value:
23:08.29     2.2
23:08.29     -0.75
23:08.29    - Step 2, multiply each by 1e5, and round
23:08.29     220000
23:08.29     -75000
23:08.29    - Step 3, convert Decimal to Binary, using two's complement for negatives. Padded to 32 bits
23:08.29     00000000000000110101101101100000
23:08.29     11111111111111101101101100001000
23:08.29    - Step 4, left-shifted 1 bit
23:08.29     00000000000001101011011011000000
23:08.29     11111111111111011011011000010000
23:08.29    - Step 5, if negative, invert binary
23:08.29     00000000000001101011011011000000
23:08.29     00000000000000100100100111101111
23:08.29    - Step 6 & 7 (part 0), break into 5-bit chunks, and reverse order
23:08.29     00000 10110 01101 01101 00000 00000 
23:08.29    - Step 8 (part 0), BitOR 100000 (0x20) to our 5-bit chunks (effectively)
23:08.29     100000 110110 101101 101101 100000 000000 
23:08.29    - Step 9 (part 0), converting the chunk from Binary back to Decimal
23:08.29     32 54 45 45 32 0 
23:08.29    - Step 10 (part 0), adding 63 to decimal values
23:08.29     95 117 108 108 95 63 
23:08.29    - Step 6 & 7 (part 1), break into 5-bit chunks, and reverse order
23:08.29     01111 01111 10010 00100 00000 00000 
23:08.29    - Step 8 (part 1), BitOR 100000 (0x20) to our 5-bit chunks (effectively)
23:08.29     101111 101111 110010 100100 100000 000000 
23:08.29    - Step 9 (part 1), converting the chunk from Binary back to Decimal
23:08.29     47 47 50 36 32 0 
23:08.29    - Step 10 (part 1), adding 63 to decimal values
23:08.29     110 110 113 99 95 63 
23:08.29    - Step 11, convert each value to ASCII equivalent, finished
23:08.29    _ulL
23:08.29    nnqC
23:08.29    Next set
23:08.29    - Step 1, take the initial signed value:
23:08.29     2.552
23:08.29     -5.503
23:08.29    - Step 2, multiply each by 1e5, and round
23:08.29     255200
23:08.29     -550300
23:08.29    - Step 3, convert Decimal to Binary, using two's complement for negatives. Padded to 32 bits
23:08.29     00000000000000111110010011100000
23:08.29     11111111111101111001101001100100
23:08.29    - Step 4, left-shifted 1 bit
23:08.29     00000000000001111100100111000000
23:08.29     11111111111011110011010011001000
23:08.29    - Step 5, if negative, invert binary
23:08.29     00000000000001111100100111000000
23:08.29     00000000000100001100101100110111
23:08.29    - Step 6 & 7 (part 0), break into 5-bit chunks, and reverse order
23:08.29     00000 01110 10010 01111 00000 00000 
23:08.30    - Step 8 (part 0), BitOR 100000 (0x20) to our 5-bit chunks (effectively)
23:08.30     100000 101110 110010 101111 100000 000000 
23:08.30    - Step 9 (part 0), converting the chunk from Binary back to Decimal
23:08.30     32 46 50 47 32 0 
23:08.30    - Step 10 (part 0), adding 63 to decimal values
23:08.30     95 109 113 110 95 63 
23:08.30    - Step 6 & 7 (part 1), break into 5-bit chunks, and reverse order
23:08.30     10111 11001 10010 00001 00001 00000 
23:08.30    - Step 8 (part 1), BitOR 100000 (0x20) to our 5-bit chunks (effectively)
23:08.30     110111 111001 110010 100001 100001 000000 
23:08.30    - Step 9 (part 1), converting the chunk from Binary back to Decimal
23:08.30     55 57 50 33 33 0 
23:08.30    - Step 10 (part 1), adding 63 to decimal values
23:08.30     118 120 113 96 96 63 
23:08.30    - Step 11, convert each value to ASCII equivalent, finished
23:08.30    _mqN
23:08.30    vxq`@
23:08.30    Next set
23:08.30    Custom Polygon: _p~iF~ps|U_ulLnnqC_mqNvxq`@
23:08.30    116.391903472455

23:08.30    Closing LoD script
+>23:08:30 AutoIt3.exe ended.rc:0
+>23:08:30 AutoIt3Wrapper Finished.
>Exit code: 0    Time: 5.448

 

Link to comment
Share on other sites

38 minutes ago, AutoBert said:

I think your problem is the func StringFromASCIIArray, so i changed line #204 to

$aPoints[$i] = StringStripWS(StringFromASCIIArray($aASCII, 0, -1, 0),8)

,which just strips all whitespaces from resultstring. With this my console output is:

Hello AutoBert,

Thanks for the reply, I was also debugging this with a friend, and found the issue, similar to you. It is indeed an unprintable character that causes this issue, and it's because I was adding an index that was never removed, and the ASCIIArray was creating that unprintable character.

I DID have a fix in place for that, but forgot a -1:

For $k = UBound($aASCII) - 1 To 0 Step -1
                If $aASCII[$k] <= 63 Or $aASCII[$k] = "" Then
                    ReDim $aASCII[UBound($aASCII) - 1]
                Else
                    ExitLoop
                EndIf
            Next

I had just:

For $k = UBound($aASCII) - 1 To 0

Which returned immediately, and never cleared up the empty index. By adding that or by adding ReDim $aASCII[UBound($aASCII) - 1] right after the For loop that creates the array fixed the issue correctly.

Thanks for your time on this though, and that is another solution as there shouldn't ever be whitespace within the created polyline.

We ought not to misbehave, but we should look as though we could.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...