Skip to content

Instantly share code, notes, and snippets.

@bczhc
Created March 25, 2024 13:33
Show Gist options
  • Save bczhc/5f60150e57f385b9f5cd2f7e21e6e3c0 to your computer and use it in GitHub Desktop.
Save bczhc/5f60150e57f385b9f5cd2f7e21e6e3c0 to your computer and use it in GitHub Desktop.
读Exif GPS数据,生成Google kml的关键xml
use std::collections::HashMap;
use std::fs::File;
use std::io::stdin;
use std::path::Path;
use rexif::{ExifEntry, ExifTag, TagValue, URational};
#[derive(Debug)]
struct GpsInfo {
latitude: f64,
longitude: f64,
altitude: f64,
}
type ExifMap<'a> = HashMap<ExifTag, &'a ExifEntry>;
fn main() -> Result<(), Box<dyn std::error::Error>> {
for line in stdin().lines() {
let path = line?;
let Ok(exif) = rexif::read_file(&mut File::open(&path).unwrap()) else {
continue;
};
let map = exif.entries.iter().map(|x| (x.tag, x)).collect::<ExifMap>();
let Some(gps_info) = get_gps_info(&map) else {
continue;
};
let filename = Path::new(&path).file_name().unwrap().to_string_lossy();
let xml = format!(
"<Placemark>
<name>{}</name>
<styleUrl>#icon-1899-0288D1-nodesc</styleUrl>
<Point>
<coordinates>
{},{},{}
</coordinates>
</Point>
</Placemark>",
filename, gps_info.longitude, gps_info.latitude, gps_info.altitude
);
if gps_info.longitude == 0.0 && gps_info.latitude == 0.0 {
continue;
}
println!("{}", xml);
}
Ok(())
}
fn get_gps_info(exif: &ExifMap) -> Option<GpsInfo> {
let get = |ref_tag: ExifTag, tag: ExifTag, multiplier_matcher: (&str, &str)| -> Option<f64> {
let multiplier = match exif.get(&ref_tag)?.value.to_string().as_str() {
x if x == multiplier_matcher.0 => 1.0,
x if x == multiplier_matcher.1 => -1.0,
_ => 0.0,
};
let value_rational = exif.get(&tag)?.value.as_rational()?;
let value = (value_rational[0].value()
+ value_rational[1].value() / 60.0
+ value_rational[2].value() / 3600.0)
* multiplier;
Some(value)
};
let latitude = get(ExifTag::GPSLatitudeRef, ExifTag::GPSLatitude, ("N", "S"))?;
let longitude = get(ExifTag::GPSLongitudeRef, ExifTag::GPSLongitude, ("E", "W"))?;
let altitude_multiplier = match exif.get(&ExifTag::GPSAltitudeRef)?.value.to_i64(0) {
None => 0.0,
Some(0) => 1.0,
Some(1) => -1.0,
Some(a) if a < 0 => -1.0,
_ => 0.0,
};
let mut altitude =
exif.get(&ExifTag::GPSAltitude)?.value.as_rational()?[0].value() * altitude_multiplier;
if altitude == -0.0 {
altitude = 0.0;
}
Some(GpsInfo {
altitude,
longitude,
latitude,
})
}
trait ExifValueExt {
fn as_rational(&self) -> Option<&Vec<URational>>;
}
impl ExifValueExt for TagValue {
fn as_rational(&self) -> Option<&Vec<URational>> {
if let TagValue::URational(v) = self {
Some(v)
} else {
None
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment