mistersquirrle Posted June 15, 2018 Posted June 15, 2018 (edited) 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]"): expandcollapse popup#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 June 15, 2018 by mistersquirrle Added unrelated question We ought not to misbehave, but we should look as though we could.
AutoBert Posted June 15, 2018 Posted June 15, 2018 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: expandcollapse popup23: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
mistersquirrle Posted June 15, 2018 Author Posted June 15, 2018 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.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now