TechnikTobi / little_exif

A little library for reading and writing EXIF data in pure Rust.
Apache License 2.0
22 stars 5 forks source link

unable to extract GPSLatitude / GPSLongitude works #49

Closed psytraxx closed 2 weeks ago

psytraxx commented 2 weeks ago

Hi There - I am trying to read EXIF geolocation from a sample jpeg -

It works after some research but only for the GPSLongitude tag - not GPSLatitude -

In the first case

let latitude = match metadata.get_tag(&ExifTag::GPSLatitude(Vec::new())).next() {
        Some(tag) => {
            let bytes = tag.value_as_u8_vec(&endian);
            bytes_to_geolocation(&bytes).ok()
        }
        None => {
            debug!("Tag does not exist");
            None
        }
    };

for

DSCN0029

returns 24 u8 values which i am able to convert to a decimal using

fn bytes_to_geolocation(vec: &[u8]) -> Result<f64> {
    if vec.len() != 24 {
        return Err(anyhow!("Error: Vector must contain exactly 24 u8 values."));
    }

    let mut coord = [0u32; 6];
    for i in 0..6 {
        coord[i] = u32::from_le_bytes(vec[i * 4..(i + 1) * 4].try_into().unwrap());
    }

    let degrees = coord[0] as f64 / coord[1] as f64;
    let minutes = coord[2] as f64 / coord[3] as f64;
    let seconds = coord[4] as f64 / coord[5] as f64;

    Ok(degrees + minutes / 60.0 + seconds / 3600.0)
}

this matches with the output from exiftool for the attached file

  "GPSLatitude": "43 deg 28' 5.68\" N",
  "GPSLongitude": "11 deg 52' 48.62\" E", (decimal 11.880171666638889)

for GPSLatitude i only receive 4 u8 values instead of 24 u8. this behavior is consistant -also with other jpegs

do you have an idea what i am doing wrong?

thank you

TechnikTobi commented 2 weeks ago

Hi,

Yes, I found the problem - however, I wouldn't say that you are doing something wrong ;-)

The reason you only get four u8 values is that you don't get a GPSLatitute tag with your code, even though you requested it, but get a InteroperabilityVersion tag instead. Why? Because some people when writing the EXIF standard and all the related documents thought it would be a good idea to give both tags the same hex value, 0x0001. My mistake that I forgot to check the group when requesting a tag via get_tag - will publish a fix for this!

Also: The way you are converting the values with bytes_to_geolocation feels a bit... "hacky", especially since I introduced the datatype for handling the rational values these tags store. What would, in your opinion, be a good function provided by little_exif to make accessing this data easier?

psytraxx commented 2 weeks ago

Wow - this was fast - thank you for your answer - I am aware of the "hackiness" of my solution :) it would be great to have a method similar to _value_as_u8vec - for example _value_as_rational64uvec - this would allow my to work with the now six RATIONAL64U values returned and convert those to my needed decimal representation of lat/long

TechnikTobi commented 2 weeks ago

Bugfix has been published!

it would be great to have a method similar to _value_as_u8vec - for example _value_as_rational64uvec - this would allow my to work with the now six RATIONAL64U values returned and convert those to my needed decimal representation of lat/long

Thanks for the input! I'll think about how to best implement this and will release something that goes into this direction soon.

psytraxx commented 2 weeks ago

great - thank you - i can confirm this is now fixed with 0.6.1