Add geolocation cache for distances below 3km
By reusing lookup's the server need to be contacted a lot less Aslo names get a bit clustered and match together a bit better.
This commit is contained in:
parent
0c3cfa8e47
commit
8b30107a3e
|
@ -3,5 +3,6 @@ from os import path
|
||||||
debug = True
|
debug = True
|
||||||
application_directory = '{}/.elodie'.format(path.expanduser('~'))
|
application_directory = '{}/.elodie'.format(path.expanduser('~'))
|
||||||
hash_db = '{}/hash.json'.format(application_directory)
|
hash_db = '{}/hash.json'.format(application_directory)
|
||||||
|
location_db = '{}/location.json'.format(application_directory)
|
||||||
script_directory = path.dirname(path.dirname(path.abspath(__file__)))
|
script_directory = path.dirname(path.dirname(path.abspath(__file__)))
|
||||||
exiftool_config = '%s/configs/ExifTool_config' % script_directory
|
exiftool_config = '%s/configs/ExifTool_config' % script_directory
|
||||||
|
|
|
@ -9,6 +9,7 @@ import sys
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
from elodie import constants
|
from elodie import constants
|
||||||
|
from elodie.localstorage import Db
|
||||||
|
|
||||||
class Fraction(fractions.Fraction):
|
class Fraction(fractions.Fraction):
|
||||||
"""Only create Fractions from floats.
|
"""Only create Fractions from floats.
|
||||||
|
@ -84,17 +85,31 @@ def get_key():
|
||||||
return config.get('MapQuest', 'key')
|
return config.get('MapQuest', 'key')
|
||||||
|
|
||||||
def place_name(lat, lon):
|
def place_name(lat, lon):
|
||||||
|
|
||||||
|
# Try to get cached location first
|
||||||
|
db = Db()
|
||||||
|
# 3km distace radious for a match
|
||||||
|
cached_place_name = db.get_location_name(lat, lon,3000)
|
||||||
|
if(cached_place_name is not None):
|
||||||
|
return cached_place_name
|
||||||
|
|
||||||
|
lookup_place_name = None;
|
||||||
geolocation_info = reverse_lookup(lat, lon)
|
geolocation_info = reverse_lookup(lat, lon)
|
||||||
if(geolocation_info is not None):
|
if(geolocation_info is not None):
|
||||||
if('address' in geolocation_info):
|
if('address' in geolocation_info):
|
||||||
address = geolocation_info['address']
|
address = geolocation_info['address']
|
||||||
if('city' in address):
|
if('city' in address):
|
||||||
return address['city']
|
lookup_place_name = address['city']
|
||||||
elif('state' in address):
|
elif('state' in address):
|
||||||
return address['state']
|
lookup_place_name = address['state']
|
||||||
elif('country' in address):
|
elif('country' in address):
|
||||||
return address['country']
|
lookup_place_name = address['country']
|
||||||
return None
|
|
||||||
|
if(lookup_place_name is not None):
|
||||||
|
db.add_location(lat, lon, lookup_place_name)
|
||||||
|
# TODO: Maybe this should only be done on exit and not for every write.
|
||||||
|
db.update_location_db()
|
||||||
|
return lookup_place_name
|
||||||
|
|
||||||
|
|
||||||
def reverse_lookup(lat, lon):
|
def reverse_lookup(lat, lon):
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
from math import radians, cos, sqrt
|
||||||
from elodie import constants
|
from elodie import constants
|
||||||
|
|
||||||
class Db(object):
|
class Db(object):
|
||||||
|
@ -24,7 +24,22 @@ class Db(object):
|
||||||
self.hash_db = json.load(f)
|
self.hash_db = json.load(f)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# If the location db doesn't exist we create it.
|
||||||
|
# Otherwise we only open for reading
|
||||||
|
if not os.path.isfile(constants.location_db):
|
||||||
|
with open(constants.location_db, 'a'):
|
||||||
|
os.utime(constants.location_db, None)
|
||||||
|
|
||||||
|
self.location_db = []
|
||||||
|
|
||||||
|
# We know from above that this file exists so we open it for reading only.
|
||||||
|
with open(constants.location_db, 'r') as f:
|
||||||
|
try:
|
||||||
|
self.location_db = json.load(f)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
def add_hash(self, key, value, write=False):
|
def add_hash(self, key, value, write=False):
|
||||||
self.hash_db[key] = value
|
self.hash_db[key] = value
|
||||||
if(write == True):
|
if(write == True):
|
||||||
|
@ -55,3 +70,44 @@ class Db(object):
|
||||||
buf = f.read(blocksize)
|
buf = f.read(blocksize)
|
||||||
return hasher.hexdigest()
|
return hasher.hexdigest()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Location database
|
||||||
|
# Currently quite simple just a list of long/lat pairs with a name
|
||||||
|
# If it gets many entryes a lookup might takt to long and a better
|
||||||
|
# structure might be needed. Some speed up ideas:
|
||||||
|
# - Sort it and inter-half method can be used
|
||||||
|
# - Use integer part of long or lat as key to get a lower search list
|
||||||
|
# - Cache a smal number of lookups, photos is likey to be taken i clusters
|
||||||
|
# around a spot during import.
|
||||||
|
|
||||||
|
def add_location(self, latitude, longitude, place, write=False):
|
||||||
|
data = {}
|
||||||
|
data['lat'] = latitude
|
||||||
|
data['long'] = longitude
|
||||||
|
data['name'] = place
|
||||||
|
self.location_db.append(data)
|
||||||
|
if(write == True):
|
||||||
|
self.update_location_db()
|
||||||
|
|
||||||
|
def get_location_name(self, latitude, longitude,threshold_m):
|
||||||
|
for data in self.location_db:
|
||||||
|
# As threshold is quite smal use simple math
|
||||||
|
# From http://stackoverflow.com/questions/15736995/how-can-i-quickly-estimate-the-distance-between-two-latitude-longitude-points
|
||||||
|
# convert decimal degrees to radians
|
||||||
|
|
||||||
|
lon1, lat1, lon2, lat2 = map(radians, [longitude, latitude, data['long'], data['lat']])
|
||||||
|
|
||||||
|
R = 6371000 # radius of the earth in m
|
||||||
|
x = (lon2 - lon1) * cos( 0.5*(lat2+lat1) )
|
||||||
|
y = lat2 - lat1
|
||||||
|
d = R * sqrt( x*x + y*y )
|
||||||
|
# Use if closer then threshold_km reuse lookup
|
||||||
|
if(d<=threshold_m):
|
||||||
|
#print "Found in cached location dist: %d m" % d
|
||||||
|
return data['name'];
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def update_location_db(self):
|
||||||
|
with open(constants.location_db, 'w') as f:
|
||||||
|
json.dump(self.location_db, f)
|
||||||
|
|
Loading…
Reference in New Issue