Use GPS position of photos.

This commit is contained in:
Rasmus Kaj 2016-07-09 16:04:19 +02:00
parent 7da9bab282
commit b474373479
6 changed files with 120 additions and 7 deletions

View File

@ -0,0 +1 @@
DROP TABLE positions;

View File

@ -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);

View File

@ -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<NaiveDateTime, FindPhotoError> {
}
}
fn find_position(exif: &ExifData)
-> Result<Option<(f64, f64)>, 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 {

View File

@ -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<Coord> = {
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()

View File

@ -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 {

View File

@ -11,10 +11,29 @@
<p><a href="/img/{{photo.id}}/l">{{photo.path}}</a></p>
<p><img src="/img/{{photo.id}}/m"></p>
{{#position}}
<div id="map" style="height: 8em;width: 40%;float: right;border: solid 1px #666;"> </div>
<link href="https://rasmus.krats.se/static/leaflet077c/leaflet.css" rel="stylesheet"/>
<script language="javascript" src="https://rasmus.krats.se/static/leaflet077c/leaflet.js" type="text/javascript">
</script>
<script language="javascript" type="text/javascript">
var pos = [{{x}}, {{y}}];
var map = document.getElementById('map');
map.style.height = 3 * map.clientWidth / 4 + "px";
var map = L.map('map').setView(pos, 16);
L.tileLayer('//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
L.marker(pos).addTo(map);
</script>
{{/position}}
{{#photo.grade}}<p>Betyg: {{.}}</p>{{/photo.grade}}
{{#photo}}<p>Tid: {{date}}</p>{{/photo}}
<p>People: {{#people}}<a href="/person/{{slug}}">{{person_name}}</a>, {{/people}}</p>
<p>Places: {{#places}}<a href="/place/{{slug}}">{{place_name}}</a>, {{/places}}</p>
<p>Tags: {{#tags}}<a href="/tag/{{slug}}">{{tag_name}}</a>, {{/tags}}</p>
{{#position}}<p>Position: {{x}} {{y}}</p>{{/position}}
</body>
</html>