Making sense of EXIF longitude and latitude - exif

If I right click the details on an image I get the following under GPS
When querying the EXIF (GpsLatitude and GpsLongitude) data I get
and
I can see that rows 0 and 8 are the degrees and minutes, but the rest makes no sense. What am I missing?
Thank you

Exif stores latitude and longitude as three rational numbers (fractions) where the numerator and denominator are four bytes long.
In your example, you are only showing the first 14 values. There are 24 bytes of values to consider.
Specifically, the latitude hours value is 0x00000027 / 0x00000001, or 39 / 1.
Note that the values are stored little endian, so the bytes must be reversed.
By only taking the bytes 0/8/16, you are potentially missing values that spill into the other bytes, and you're also assuming the denominator is always one.
It's likely much easier to use a library to extract this information from your images. I maintain such a library for .NET which will work with VB.NET just fine, and has full support for Exif and many other kinds of metadata from many file formats.
If you're interested, take a look at https://github.com/drewnoakes/metadata-extractor-dotnet

Can't get the last set to show the decimal place (ToDouble doesn't work), but this is very close
If Not vLong Is Nothing Then
vLong1 = BitConverter.ToUInt32(vLong, 0)
vLong2 = BitConverter.ToUInt32(vLong, 8)
vLong3 = BitConverter.ToUInt32(vLong, 16)
vLongDecode = CType(vLong1, String) & " " & CType(vLong2, String) & " " & CType(vLong3, String)
End If
If Not vLat Is Nothing Then
vLat2 = BitConverter.ToUInt32(vLat, 8)
vLat1 = BitConverter.ToUInt32(vLat, 0)
vLat3 = BitConverter.ToUInt32(vLat, 16)
vLatDecode = CType(vLat1, String) & " " & CType(vLat2, String) & " " & CType(vLat3, String)
End If

Related

Handing strings and copy paste variables

I have a code where I have an inputbox where I usually copy in some numbers.
I need vba to handle these and pass them to the cell as european format.
The format that comes in is US and comes as:
##,### ## ##
I use the following code to transform it to the format I want:
dim earned as variant
earned = Replace(Me.txtEarnedG.Value, ".", "")
earned = Replace(earned, ",", "")
earned = Replace(earned, " ", "")
earned = Left(earned, Len(earned) - 4) & "," & Right(earned, 4)
amount_earned = CDbl(earned)
This works perfectly and would transform
35,545 45 55 (US format with spaces, without a ".")
into
35545,4555 (EU format)
My issue comes when I try to transform a number that doesn't 2 digits in between one of the spaces like,
35,545 4 13 becomes 3554,5413 instead of 35545,0413
35,545 12 4 becomes 3554,5124 instead of 35545,1204
I was thinking of using Instr and right in some way. But I can't figure on how to introduce it in easily. (The number should always finish with 4 decimal places, but the rest may vary and can be bigger or smaller than thousands)
I think you could use the Split function as #Porcupine suggests and combine with Format to get the number format you wanted.
A simple example using your numbers below
Public Sub test()
Const TEST_NUM1 = "35,545 4 13"
Const TEST_NUM2 = "35,545 12 4"
Const TEST_NUM3 = "35,545 45 55"
FormatAsEU TEST_NUM1
FormatAsEU TEST_NUM2
FormatAsEU TEST_NUM3
End Sub
Public Function FormatAsEU(strNumber As String) As String
Dim varInput As Variant
varInput = Split(strNumber, " ")
FormatAsEU = Format(varInput(0), "#.#") & Format(varInput(1), "00") & Format(varInput(2), "00")
Debug.Print FormatAsEU
End Function
Debug Results:
> 35545.0413
> 35545.1204
> 35545.4555

Comparing time in ASP classic/VBscript

I am working on a function for an asp page that compares if a time entered is greater than a time with added leeway. I noticed certain times when checked would fail the test when the times are equal. Included is a snip of my function to illustrate. Not sure why equal dates would fail, and would like to know if this is a good way to go about comparing time.
<%
function TimeTest(testTime, checkTime, buffer, try)
checkingTime = FormatDateTime(cdate(DateAdd("n", buffer, cdate(checkTime))),4)
if try = 1 then
testTime = FormatDateTime(testTime, 4)
checktime = FormatDateTime(checkTime, 4)
end if
if cdate(testTime) > DateAdd("n", buffer, cdate(checkTime)) then
TimeTest = "<p class = 'redS'>Fails! testTime: "&testTime&" < checkTime:"&checkingTime&"</p>"
else
TimeTest = "<p class = 'greenS'>Works! testTime: "&testTime&" > checkTime:"&checkingTime&"</p>"
end if
end function
response.write("<br><br><h1>Test2</h1><br>")
for i=0 to 23
for j=0 to 59
response.write(TimeTest(i&":"&j&":00", i&":00:00", j, 1))
response.write("<BR>")
next
next
%>
This problem has earned my attention! I can reproduce the results and it's very unclear what's going on behind the scenes in these comparisons. However, I have a workaround for you
Here is a modified version of the code that I've been using to analyse the issue...
<%
Option Explicit
Function TimeTest(a, b, buffer)
Dim c : c = DateAdd("n", buffer, b)
Dim s : s = Join(Array("a=" & a, "b=" & b, "c=" & c, "buffer=" & buffer), ", ")
Dim passed : passed = a <= c
'Dim passed : passed = DateDiff("s", a, c) <= 0
If passed Then Exit Function
Dim color : color = "red" : If passed Then color = "green"
TimeTest = "<div style='color:" & color & "'>" & s & "</div>"
End Function
Dim i, j, a, b
For i = 0 To 23
For j = 0 To 59
a = CDate(i & ":" & j & ":00")
b = CDate(i & ":00:00")
'a = CDate(Date() & " " & i & ":" & j & ":00")
'b = CDate(Date() & " " & i & ":00:00")
Response.Write(TimeTest(a, b, j))
Next
Response.Write("<hr>")
Next
%>
Note that commenting out line 13 will reveal lines that pass. By default, I'm showing only failures.
The first thing to note is that I have some commented variants on lines 24-25 where I add today's date to the value before casting it. Interestingly, doing this changes the pattern of which times fail the test. There are still roughly the same number of failures but they occur at different buffer values.
This leads me to believe that behind the scenes in VBScript, these datetimes might be cast to floating-point numbers when you use the native < <= > >= comparison operators on them and that's resulting in some precision errors. If they were converted to long integers, then they should surely be correct.
I did a version of the code where instead of using a direct comparison on the VBDateTimes, I compared the integer representation of them (unix time) using this function:
Function date2epoch(myDate)
date2epoch = DateDiff("s", "01/01/1970 00:00:00", myDate)
End Function
When doing that, all tests passed. However, it is an unusual way to do things. I thought there should be a more 'normal' way.
I then went back and replaced the straightforward <= operator with a call to DateDiff instead (comment out line 10, uncomment line 11). Whether I used seconds or minutes, the tests passed. So, I think the takeaway lesson here might be to always use DateDiff when comparing VBDateTimes. As someone who's used VBS for a while and never encountered issues with native comparisons before, this is a revelation and I may need to offer this advice to my colleagues too.

Standardise telephone number format

I have a column with 32 different phone formats that I need to consolidate into one format type: (###)###-####. The goal is to upload this formatted data into an existing db. I did find some formulas that help but these require helper cell.
I think some of the cells that contain less than 10 digits or more than ten digits will be manually fixed but no decision has been made yet. So, for now, I'll have some cells that have less than or more than the normal phone number (10 characters)
Here is a table of some of the original data and the result I need to see.
Original Data Result
*6.5033E+14 (650)329-670061133
*5.07127E+12 (507)127-2004904
*4.0809E+11 (408)089-787487
*9258254882 (925)825-4882
*6547621 (654)762-1
*310921278 (310)921-278
*415 6995743 (415)699-5743
*209-986-334 (209)986-334
*661-331-2792 (661)331-2792
*(831)383-8650 (1103) (313)838-6501103
*(415)488-9437 (517) (415)488-9437517
*(831)383-9452 (32) (831)383-945232
*(408)927-9482 (408)927-9482
*(000)408-7089 (000)408-7089
*b
*Oakland
Is it possible to create a macro so I won't have to use helper cells with various formulas? Also, I do have cells without a number so I would need a condition to ignore these cells as well.
Use this UDF.
Function TelFormat(s As String)
Dim sRp As String, n As Integer
s = Replace(s, "(", "")
s = Replace(s, ")", "")
s = Replace(s, "-", "")
s = Replace(s, " ", "")
n = Len(s) - 6
sRp = WorksheetFunction.Rept("#", n)
TelFormat = Format(s, "(000)-###-" & sRp)
End Function

String sub not working correctly

I've got yet another question about lua. I've created a method to calculate the total amount of some prices. The prices are in this format: £500. So to convert them to numbers I'm using string:sub() and tonumber(), but I'm getting some weird results. Here is my code:`
function functions.calculateTotalAmount()
print("calculating total amount")
saveData.totalAmount = 0
print("There are " .. #saveData.amounts .. " in the amount file")
for i=1, #saveData.names do
print("SaveData.amounts[" .. i .. "] original = " .. saveData.amounts[i])
print("SaveData.amounts[" .. i .. "] after sub= " .. saveData.amounts[i]:sub(2))
print("totalAmount: " .. saveData.totalAmount)
if saveData.income[i] then
saveData.totalAmount = saveData.totalAmount + tonumber(saveData.amounts[i]:sub(2))
else
saveData.totalAmount = saveData.totalAmount - tonumber(saveData.amounts[i]:sub(2))
end
end
totalAmountStr.text = saveData.totalAmount .. " " .. currencyFull
loadsave.saveTable(saveData, "payMeBackTable.json")
end
I printed out some info in the for loop to determine the problem and this is what is being printed for the first 2 print statements in the for loop:
16:03:51.452 SaveData.amounts1 original = ¥201
16:03:51.452 SaveData.amounts1 after sub= 201
It looks fine here in stackoverflow but for the the ¥ is actually not gone in my log, instead it is replaced with a weird rectangle symbol. There will be a picture of the printed text attached to this post.
Does anyone see what is going on here?
Don't use sub in this case as the ¥ sign is likely a multi-byte sequence (depending on the encoding), so using sub(2) you are cutting it in the middle instead of removing it.
Use gsub("[^%d%.]+","") instead to remove all non-numeric parts.
string.sub() works on the bytes of a string, not on its chars. There is a difference when the string contains Unicode text.
If the number is at the end of the string, extract it with
amount = tonumber(saveData.amounts[i]:match("%d+$"))
Lua strings are strings of bytes, not strings of characters. ASCII characters are 1 byte long, but most other characters consume multiple bytes, so using string.sub() isn't going to work.
There are several standards for converting between bytes and characters (or code points), but by far the most common on the web is UTF-8. If you are using Lua 5.3 or greater, you can use new built-in functions for performing UTF-8 manipulation. For example, to take a substring of a UTF-8 string, you can do:
-- Simple version without bounds-checking.
function utf8_sub1(s, start_char_idx, end_char_idx)
start_byte_idx = utf8.offset(s, start_char_idx)
end_byte_idx = utf8.offset(s, end_char_idx + 1) - 1
return string.sub(s, start_byte_idx, end_byte_idx)
end
-- More robust version with bounds-checking.
function utf8_sub2(s, start_char_idx, end_char_idx)
start_byte_idx = utf8.offset(s, start_char_idx)
end_byte_idx = utf8.offset(s, end_char_idx + 1)
if start_byte_idx == nil then
start_byte_idx = 1
end
if end_byte_idx == nil then
end_byte_idx = -1
else
end_byte_idx = end_byte_idx - 1
end
return string.sub(s, start_byte_idx, end_byte_idx)
end
s = "¥201"
print(string.sub(s, 2, 4)) -- an invalid byte sequence
print(utf8_sub1(s, 2, 4)) -- "201"
print(utf8_sub2(s, 2, 4)) -- "201"
print(utf8_sub1(s, 2, 5)) -- throws an error
If you don't have Lua 5.3, you can use a UTF-8 library like this one instead to achieve the same functionality.

How can I divide a number string by 1000 without converting to number?

I am trying to make a certain string with Digits change form, so that it looks like below.
Modified to show with Digits instead of X.
So let´s set, i get a number, like this.
234.123123123
I need to change how it appears, for 3 digits before a Dot, it would need to become.
0.234123123123
And let´s say it´s 2.23123123123, that would become, 0.0023123123123
Now i will try to explain the Formula, bare with my English.
The formula needs to change the position of the Dot ".".
You can see that it changes place, But you can also see that i am adding 0s.
That is important, the 0s needs to be added IF the dot get´s to the far left (the beginning of the string).
So how much must the Dot move?
The Dot must Always move 3 steps to the left.
And if it hit´s the wall (the start of the string) you need to add 0s, and then one 0s before the dot.
So if the number is, 2.222222
I will first have to move the dot to the left, let´s show step by step.
Step 1:
.2222222
Step 2:
.02222222
Step 3:
0.02222222
It must Always be a 0 Before the Dot, it it ever hit the Start.
It sounds more complicated then it is, it´s just that i don't know how to explain it.
EDIT:
My attempt:
TextPos = InStr(1, TextBox4.Value, ".") - 2
If (TextPos = 4) Then
sLeft = Left(TextBox4.Value, Len(TextBox4.Value) - Len(TextBox4.Value) + 2)
sRight = Right(TextBox4.Value, Len(TextBox4.Value) - 2)
sFinal = (sLeft & "." & Replace(sRight, ".", ""))
TextBox4.Value = Replace(sFinal, "-", "")
End If
If (TextPos = 3) Then
TextBox4.Value = "0." + Replace(TextBox4.Value, ".", "")
End If
If (TextPos = 2) Then
TextBox4.Value = "0.0" + Replace(TextBox4.Value, ".", "")
End If
If (TextPos = 1) Then
TextBox4.Value = "0.00" + Replace(TextBox4.Value, ".", "")
End If
TextBox4.Value = Replace(TextBox4.Value, "-", "")
If i /1000 it, may work, but it seems like it will alter the number, leaving it to fail.
Original: 25.1521584671082
/1000 : 2.51521584671082E-02
As you can see, it doesn't work as expected.
It should become 0.0251521584671082E-02 (And of course i don't want " E- " to be there.
So if it's possible to do this, but ONLY moving and not actually, dividing (i think excel is limited to how many numbers it can calculate with) then it will work.
Worth Noting, the numbers shown are actually less (shorter) then the real number for some reason, if i write it down to a text file, it becomes this:
2.51521584671082E-02251521584671082E-02
So as you can see, i probably hit a Wall in the calculation, so it changes to Characters (I know math uses those, but no idea what they mean, and they are useless for me anyway).
And i think it's because of them that it fails, not sure though.
EDIT:
Okay, by limiting to 15 decimals it will return the correct place, but i would like not to do this round about, should Excel really be limited to only 15 Decimals, or is it "Double,Float etc" that does this?
EDIT 2:
Tony's example provides this:
Original: 177.582010543847177582010543847
Tony's: 0.1775820110177582011
Round(15decimals): 0.1775820105438470177582010543847
Not really sure, but isn't it Incorrect, or perhaps i did something wrong?
If possible, i want ALL decimals and number to stay in place, as everything is already correct, it's just that they are in the wrong place.
/1000 solves this, but not in the best way, as it will recalculate instead of just moving.
Normally i wouldn't care, the results are the same, but here it does matter as you can see.
I can however think of a solution, where i till look for the position of the Dot, then cut out the last decimals, divide the Result by 1000, then later add the last decimals, though that is more of a hack , not sure if it will actually work.
I conclude that this is Answered, it does what i want, and the limitations is in the Calculation itself, not the Solution.
All Solutions work pretty much in the same way, but i chose Tony's as he was first to comment the solution in my question.
Many Thanks everyone!
It appears to me through all your edits that you want to divide the number by one thousand. To do this, you can simply use
TextBox4.Value = TextBox4.Value / 1000
You code sometimes fails because it does not handle every situation. You calculate TextPos so:
TextPos = InStr(1, TextBox4.Value, ".") - 2
You then handle TextPos being 4, 3, 2 or 1. But if, for example, TextBox4.Value = "2.22222" then TextPos = 0 and you have no code for that situation.
At 11:45 GMT, I suggested in a comment that dividing by 1000 would give you the result you seek. At 14:08 GMT, tunderblaster posted this as an answer. It is now 15:23 but you have not responded to either of us.
Having tried your code, I now know these answer do not match the result it gives when it works because you remove the sign. The following would give you almost the same as a working version of your code:
TextBox4.Value = Format(Abs(Val(TextBox4.Value)) / 1000, "#,##0.#########")
The only deficiency with this statement is that it does not preserve all the trailing decimal digits. Is this really important?
While I was preparing and posting this answer, you edited your question to state that dividing by 1000 would sometimes give the result in scientific format. My solution avoids that problem.
If you want to avoid floating point error when dividing by 10, you may use the Decimal type
Private Sub CommandButton1_Click()
Dim d As Variant
d = CDec(TextBox1.Text)
TextBox1.Text = d / 1000
End Sub
Is this what you are trying?
Sub Sample()
Dim sNum As String, SFinal As String
Dim sPref As String, sSuff As String
Dim nlen As Long
sNum = "234.123123123123123123131231231231223123123"
SFinal = sNum
If InStr(1, sNum, ".") Then
sPref = Trim(Split(sNum, ".")(0))
sSuff = Trim(Split(sNum, ".")(1))
nlen = Len(sPref)
Select Case nlen
Case 1: SFinal = ".00" & Val(sPref) & sSuff
Case 2: SFinal = ".0" & Val(sPref) & sSuff
Case 3: SFinal = "." & Val(sPref) & sSuff
End Select
End If
Debug.Print SFinal
End Sub
Try this. It works by initialy padding the string with leading 0's, shifting the DP, then removing any unnecassary remaining leading 0's
Function ShiftDecimalInString(str As String, Places As Long) As String
Dim i As Long
If Places > 0 Then
' Move DP to left
' Pad with leading 0's
str = Replace$(Space$(Places), " ", "0") & str
' Position of .
i = InStr(str, ".")
str = Replace$(str, ".", "")
If i > 0 Then
' Shift . to left
str = Left$(str, i - Places - 1) & "." & Mid$(str, i - Places)
' strip unnecassary leading 0's
Do While Left$(str, 2) = "00"
str = Mid$(str, 2)
Loop
If str Like "0[!.]*" Then
str = Mid$(str, 2)
End If
ShiftDecimalInString = str
Else
' No . in str
ShiftDecimalInString = str
End If
ElseIf Places < 0 Then
' ToDo: Handle moving DP to right
Else
' shift DP 0 places
ShiftDecimalInString = str
End If
End Function
This "answer" responses to the issue that you want as much precision in the result as possible.
When I was at school this was called spurious accuracy. I have forgotten most of my mathematics in this area but I will try to give you an understanding of the issue.
I have a value, 2.46, which is accurate to 2 decimal places. That is the true value is somewhere in the range 2.455 to 2.465.
If I feed these values into a square root function I will get:
sqrt(2.455) = 1.566843961599240
sqrt(2.46) = 1.568438714135810
sqrt(2.465) = 1.570031846810760
From this I see the square root of the true value is somewhere between 1.567 and 1.570; any extra decimal digits are spurious.
If I understand correctly, you wish to use these numbers to make adjustments. Are you capable of making adjustments to 15 decimal digits?
If you remember your calculus you can look up "propogation of error" for a better explanation.

Resources