From b474373479ac6e0df75a730217a17fab421fda36 Mon Sep 17 00:00:00 2001 From: Rasmus Kaj Date: Sat, 9 Jul 2016 16:04:19 +0200 Subject: [PATCH] Use GPS position of photos. --- .../down.sql | 1 + .../up.sql | 12 ++++ src/findphotos.rs | 64 +++++++++++++++++-- src/main.rs | 22 +++++++ src/models.rs | 9 +++ templates/details.tpl | 19 ++++++ 6 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 migrations/20160708213522_create_positions_table/down.sql create mode 100644 migrations/20160708213522_create_positions_table/up.sql diff --git a/migrations/20160708213522_create_positions_table/down.sql b/migrations/20160708213522_create_positions_table/down.sql new file mode 100644 index 0000000..6f6cbcd --- /dev/null +++ b/migrations/20160708213522_create_positions_table/down.sql @@ -0,0 +1 @@ +DROP TABLE positions; diff --git a/migrations/20160708213522_create_positions_table/up.sql b/migrations/20160708213522_create_positions_table/up.sql new file mode 100644 index 0000000..5c697c7 --- /dev/null +++ b/migrations/20160708213522_create_positions_table/up.sql @@ -0,0 +1,12 @@ +-- Rather than using floating points or DECIMAL(8,5) or something like +-- that, lat and long are stored as signed microdegrees integer values. +CREATE TABLE positions ( + id SERIAL PRIMARY KEY, + photo_id INTEGER NOT NULL REFERENCES photos (id), + latitude INTEGER NOT NULL, + longitude INTEGER NOT NULL +); + +CREATE INDEX positions_photo_idx ON positions (photo_id); +CREATE INDEX positions_lat_idx ON positions (latitude); +CREATE INDEX positions_long_idx ON positions (longitude); diff --git a/src/findphotos.rs b/src/findphotos.rs index 4db8c88..a44e858 100644 --- a/src/findphotos.rs +++ b/src/findphotos.rs @@ -60,13 +60,45 @@ fn save_photo(db: &PgConnection, file_path: &str, exif: &ExifData) -> Result<(), FindPhotoError> { - match try!(Photo::create_or_set_basics(db, file_path, - Some(try!(find_date(&exif))), - try!(find_rotation(&exif)))) { - Modification::Created(photo) => info!("Created {:?}", photo), - Modification::Updated(photo) => info!("Modified {:?}", photo), - Modification::Unchanged(photo) => debug!("No change for {:?}", photo), - }; + let photo = + match try!(Photo::create_or_set_basics(db, file_path, + Some(try!(find_date(&exif))), + try!(find_rotation(&exif)))) { + Modification::Created(photo) => { + info!("Created {:?}", photo); + photo + }, + Modification::Updated(photo) => { + info!("Modified {:?}", photo); + photo + }, + Modification::Unchanged(photo) => { + debug!("No change for {:?}", photo); + photo + } + }; + if let Some((lat, long)) = try!(find_position(&exif)) { + debug!("Position for {} is {} {}", file_path, lat, long); + use rphotos::schema::positions::dsl::*; + if let Ok((pos, clat, clong)) = + positions.filter(photo_id.eq(photo.id)) + .select((id, latitude, longitude)) + .first::<(i32, i32, i32)>(db) + { + if (clat != (lat * 1e6) as i32) || (clong != (long * 1e6) as i32) { + panic!("TODO Should update position #{} from {} {} to {} {}", + pos, clat, clong, lat, long) + } + } else { + info!("Position for {} is {} {}", file_path, lat, long); + use rphotos::models::NewPosition; + diesel::insert(&NewPosition { + photo_id: photo.id, + latitude: (lat * 1e6) as i32, + longitude: (long * 1e6) as i32 + }).into(positions).execute(db).expect("Insert image position"); + } + } Ok(()) } @@ -123,6 +155,24 @@ fn find_date(exif: &ExifData) -> Result { } } +fn find_position(exif: &ExifData) + -> Result, FindPhotoError> +{ + if let (Some(lat), Some(long)) = (find_entry(exif, &ExifTag::GPSLatitude), + find_entry(exif, &ExifTag::GPSLongitude)) { + return Ok(Some((rat2float(&lat.value), rat2float(&long.value)))) + } + Ok(None) +} + +fn rat2float(val: &rexif::TagValue) -> f64 { + if let rexif::TagValue::URational(ref v) = *val { + v[0].value() + (v[1].value() + v[2].value() / 60.0) / 60.0 + } else { + 0.0 + } +} + fn find_entry<'a>(exif: &'a ExifData, tag: &ExifTag) -> Option<&'a ExifEntry> { for entry in &exif.entries { if entry.tag == *tag { diff --git a/src/main.rs b/src/main.rs index c116052..4ef1a41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -81,6 +81,12 @@ struct Group { photo: Photo, } +#[derive(Debug, Clone, RustcEncodable)] +struct Coord { + x: f64, + y: f64, +} + fn monthname(n: u8) -> &'static str { match n { 1 => "january", @@ -361,6 +367,22 @@ fn photo_details<'mw>(req: &mut Request, .filter(photo_id.eq(tphoto.id)))) .load(c).unwrap() }, + position: Option = { + use rphotos::schema::positions::dsl::*; + match positions.filter(photo_id.eq(tphoto.id)) + .select((latitude, longitude)) + .first::<(i32, i32)>(c) { + Ok((tlat, tlong)) => Some(Coord { + x: tlat as f64 / 1e6, + y: tlong as f64 / 1e6, + }), + Err(diesel::NotFound) => None, + Err(err) => { + error!("Failed to read position: {}", err); + None + } + } + }, time: String = match tphoto.date { Some(d) => d.format("%T").to_string(), None => "".to_string() diff --git a/src/models.rs b/src/models.rs index 2c13813..c753d98 100644 --- a/src/models.rs +++ b/src/models.rs @@ -319,6 +319,15 @@ pub struct NewPhotoPlace { pub place_id: i32, } +use super::schema::positions; +#[insertable_into(positions)] +#[derive(Debug, Clone)] +pub struct NewPosition { + pub photo_id: i32, + pub latitude: i32, + pub longitude: i32, +} + /* impl Entity for Place { fn id(&self) -> &ToValue { diff --git a/templates/details.tpl b/templates/details.tpl index 8251776..f179013 100644 --- a/templates/details.tpl +++ b/templates/details.tpl @@ -11,10 +11,29 @@

{{photo.path}}

+ + {{#position}} +
+ + + + {{/position}} + {{#photo.grade}}

Betyg: {{.}}

{{/photo.grade}} {{#photo}}

Tid: {{date}}

{{/photo}}

People: {{#people}}{{person_name}}, {{/people}}

Places: {{#places}}{{place_name}}, {{/places}}

Tags: {{#tags}}{{tag_name}}, {{/tags}}

+ {{#position}}

Position: {{x}} {{y}}

{{/position}}