How do I convert longitude and latitude to GPS Exif byte array

I am trying to put latitude = 8°50'34.46" and longitude = 125° 9'50.82" into the exif file of an image. i'm using vb.net.

I'm not having problem converting the degrees and minutes into bytes because it is a whole number but when i convert the seconds (34.46") which has decimal values into bytes. it gives different result like 0.9856.

Please help me guys how to convert numbers with decimal values into bytes.

here the code:

Private Shared Function intToByteArray(ByVal int As Int32) As Byte()
    ' a necessary wrapper because of the cast to Int32
    Return BitConverter.GetBytes(int)
End Function

Private Shared Function doubleToByteArray(ByVal dbl As Double) As Byte()
    Return BitConverter.GetBytes(Convert.ToDecimal(dbl))
End Function

Private Shared Function doubleCoordinateToRationalByteArray(ByVal doubleVal As Double) As Byte()
    Dim temp As Double

    temp = Math.Abs(doubleVal)
    Dim degrees = Math.Truncate(temp)

    temp = (temp - degrees) * 60
    Dim minutes = Math.Truncate(temp)

    temp = (temp - minutes) * 60
    Dim seconds = temp

    Dim result(24) As Byte
    Array.Copy(intToByteArray(degrees), 0, result, 0, 4)
    Array.Copy(intToByteArray(1), 0, result, 4, 4)
    Array.Copy(intToByteArray(minutes), 0, result, 8, 4)
    Array.Copy(intToByteArray(1), 0, result, 12, 4)
    Array.Copy(doubleToByteArray(seconds), 0, result, 16, 4)
    Array.Copy(intToByteArray(1), 0, result, 20, 4)

    Return result
End Function

Upvotes: 2

Views: 1651

Answers (2)

Alex
Alex

Reputation: 13224

According to this specification, longitude and latitude are encoded as a

PropertyTagTypeRational

Specifies that the value data member is an array of pairs of unsigned long integers. Each pair represents a fraction; the first integer is the numerator and the second integer is the denominator.

The encoded layout should be (24 bytes total)

Byte Offset   Length   Encoding   Field
0             4        uint       Degrees Nominator
4             4        uint       Degrees Denominator
8             4        uint       Minutes Nominator
12            4        uint       Minutes Denominator
16            4        uint       Seconds Nominator
20            4        uint       Seconds Denominator

Given that your input is using whole degrees and minutes and not fractions, your encoding for those two will work fine, by using the value of 1 as the denominator.

For the seconds, that you have as a floating point value, this is not the case. You will have to encode it as a rational, using a nominator and denominator part.

I am not sure what the precision is that you would like to have, but given your example of 34.46 seconds, it would seem that multiplying by 1000 and using 1000 for the denominator would be more than good enough:

Dim secondsNominator = Math.Truncate(1000 * seconds)
Dim secondsDenoninator = 1000

Then your encoding function becomes:

Private Shared Function doubleCoordinateToRationalByteArray(ByVal doubleVal As Double) As Byte()
    Dim temp As Double

    temp = Math.Abs(doubleVal)
    Dim degrees = Math.Truncate(temp)

    temp = (temp - degrees) * 60
    Dim minutes = Math.Truncate(temp)

    temp = (temp - minutes) * 60
    Dim secondsNominator = Math.Truncate(1000 * temp)
    Dim secondsDenoninator = 1000

    Dim result(24) As Byte

    ' Degrees (nominator, and 1 for denominator)
    Array.Copy(intToByteArray(degrees), 0, result, 0, 4)
    Array.Copy(intToByteArray(1), 0, result, 4, 4)

    ' Minutes (nominator, and 1 for denominator)
    Array.Copy(intToByteArray(minutes), 0, result, 8, 4)
    Array.Copy(intToByteArray(1), 0, result, 12, 4)

    ' Seconds (1000 for denominator: ms resolution)
    Array.Copy(intToByteArray(secondsNominator), 0, result, 16, 4)
    Array.Copy(intToByteArray(secondsDenominator), 0, result, 20, 4)

    Return result
End Function

Upvotes: 5

xpda
xpda

Reputation: 15813

The GPS latitude and longitude for exif data are "rational" data type, or two 32-bit integers. To represent 34.46, for example, you could use the two 32-bit integers 3,446 (numerator) and 100 (denominator), or 344,600 and 10,000. For the integer value of degrees, for example you could use 8 with a denominator of 1.

You can get the exif specification here.

Upvotes: 1

Related Questions