Use GPS position of photos.
This commit is contained in:
parent
7da9bab282
commit
b474373479
@ -0,0 +1 @@
|
||||
DROP TABLE positions;
|
12
migrations/20160708213522_create_positions_table/up.sql
Normal file
12
migrations/20160708213522_create_positions_table/up.sql
Normal 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);
|
@ -60,13 +60,45 @@ fn save_photo(db: &PgConnection,
|
||||
file_path: &str,
|
||||
exif: &ExifData)
|
||||
-> Result<(), FindPhotoError> {
|
||||
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),
|
||||
Modification::Updated(photo) => info!("Modified {:?}", photo),
|
||||
Modification::Unchanged(photo) => debug!("No change for {:?}", photo),
|
||||
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 {
|
||||
|
22
src/main.rs
22
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<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()
|
||||
|
@ -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 {
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user