0
-from django.db import models
0
- import cPickle as pickle
0
-# Imports for Django stuff
0
+from geopy import distance as geopy_distance
0
from django.db import models
0
-from django.utils.encoding import smart_unicode
0
-from django.core.exceptions import ObjectDoesNotExist
0
from django.conf import settings
0
-# Imports for geo stuff
0
-from wt.generic.geo import misc as geo_misc
0
-from wt.generic.geo import fields as custom_fields
0
-from wt.generic.geo.dateutil import relativedelta
0
-from geopy import geocoders as geopy_geocoders, distance as geopy_distance
0
+from geo import fields as custom_fields
0
+from geo import geocoding, misc
0
+from geo.dateutil.relativedelta import relativedelta
0
class LocationManager(models.Manager):
0
def by_proximity_to_location(self, origin_location, radius_miles=None):
0
- """Returns a list of all
Location objects (excluding the origin_location)
0
+ """Returns a list of all
self.model objects (excluding the origin_location)
0
within radius_miles miles of the passed location if specified (otherwise
0
returns all other objects), ordered by ascending proximity to it."""
0
@@ -35,9 +27,9 @@ class LocationManager(models.Manager):
0
- results =
Location.objects.filter(latitude__range=(coord_set['latitude']['minimum'], coord_set['latitude']['maximum'])).filter(longitude__range=(coord_set['longitude']['minimum'], coord_set['longitude']['maximum']))
0
+ results =
self.model.objects.filter(latitude__range=(coord_set['latitude']['minimum'], coord_set['latitude']['maximum'])).filter(longitude__range=(coord_set['longitude']['minimum'], coord_set['longitude']['maximum']))
0
- results =
Location.objects.all()
0
+ results =
self.model.objects.all()
0
# Exclude any locations with exactly the same co-ordinates (GeoPy doesn't play nice with these)
0
results = list(results.exclude(latitude__exact=origin_location.latitude).exclude(longitude__exact=origin_location.longitude))
0
@@ -48,7 +40,7 @@ class LocationManager(models.Manager):
0
def proximity_cmp(current, previous, location=origin_location):
0
- return
geo_misc.base_cmp_by_proximity(current, previous, location.coords_tuple)
0
+ return
misc.base_cmp_by_proximity(current, previous, location.coords_tuple)
0
results.sort(proximity_cmp)
0
@@ -58,34 +50,30 @@ class LocationManager(models.Manager):
0
- """Returns all Location objects which have is_public set as True (convenience function)."""
0
- return Location.objects.filter(is_public=True)
0
+ """Returns all self.model objects which have is_public set as True (convenience function)."""
0
+ return self.model.objects.filter(is_public=True)
0
- """Returns all Location objects which have expired (convenience function)."""
0
- return Location.objects.filter(refreshed__lte=(datetime.datetime.now() - relativedelta.relativedelta(**settings.MAX_LOCATION_CACHE_AGE)))
0
+ """Returns all self.model objects which have expired (convenience function)."""
0
+ return self.model.objects.filter(refreshed__lte=(datetime.datetime.now() - relativedelta.relativedelta(**settings.MAX_LOCATION_CACHE_AGE)))
0
def within_bounds(self, north_west, south_east):
0
- """Returns a QuerySet of
Locations within the supplied lat/long two-tuples (the northwest
0
+ """Returns a QuerySet of
self.models within the supplied lat/long two-tuples (the northwest
0
and southwest-most corners bounding the segment of the earth in which to search)."""
0
- return
Location.objects.filter(latitude__range=(north_west[0], south_east[0])).filter(longitude__range=(north_west[1], south_east[1]))
0
+ return
self.model.objects.filter(latitude__range=(north_west[0], south_east[0])).filter(longitude__range=(north_west[1], south_east[1]))
0
class Location(models.Model):
0
- """Defines a location somewhere on the globe (presumably! [somewhere with two-
0
- dimensional space defined by latitude/longitude anyway!]). All that needs
0
- to be entered is a query, which is what will be geocoded (for example
0
- 'Penzance, UK'), and everything else will be taken care of automagically.
0
- Note that if the object is to be used before it is saved, then
0
- refresh_if_needed needs to be called."""
0
- query = models.TextField('Location', blank=False, null=False) # TextField in case it's over 250 characters
0
+ """A Location on the earth."""
0
+ query = models.CharField('Location', max_length=250, blank=False, null=False, unique=True)
0
friendly_name = models.CharField(max_length=250, blank=True, null=True, help_text='Use this to assign a friendly display-name to this location like \'Home\'.')
0
- geocoder = custom_fields.PickledObjectField(blank=True, null=False)
0
+ geocoded = models.BooleanField(default=True)
0
+ result = custom_fields.PickledObjectField(blank=True, null=True, editable=False)
0
latitude = models.FloatField(blank=True, null=False)
0
longitude = models.FloatField(blank=True, null=False)
0
- refreshed = models.DateTimeField(editable=False, blank=True, null=False)
0
- created = models.DateTimeField(editable=False, blank=True, null=True)
0
+ refreshed = models.DateTimeField(editable=False, blank=True, null=False, default=datetime.datetime.now())
0
+ extra = custom_fields.DictionaryField('A dictionary of additional information', blank=True, null=True, editable=False)
0
+ created = models.DateTimeField(editable=False, blank=True, null=True, default=datetime.datetime.now())
0
is_public = models.BooleanField(default=True)
0
objects = LocationManager()
0
@@ -94,73 +82,88 @@ class Location(models.Model):
0
list_display = ('__str__', 'latitude', 'longitude', 'created', 'refreshed')
0
list_filter = ('created', 'refreshed')
0
+ def save(self, *args, **kwargs):
0
+ return super(Location, self).save(*args, **kwargs)
0
+ def __unicode__(self):
0
+ return unicode(self.name)
0
+ def __getitem__(self, index):
0
+ """Gets either a latitude or longitude by indexing the coords_tuple."""
0
+ return self.coords_tuple[index]
0
+ def get_geocoder(self):
0
+ """Returns an instantiated geocoder for this object. Make sure you have settings.DEFAULT_GEOCODER set correctly."""
0
+ return geocoding.SHORT_NAME_MAPPINGS[settings.DEFAULT_GEOCODER]
0
- """A dictionary of latitude and longitude."""
0
- 'latitude': self.latitude,
0
- 'longitude': self.longitude,
0
+ if hasattr(self.result, 'coords'):
0
+ return self.result.coords
0
+ return geocoding.Coordinates(float(self.latitude or 0), float(self.longitude or 0))
0
+ def coords_tuple(self):
0
+ if not hasattr(self.result, 'coords'):
0
+ return (self.latitude, self.longitude)
0
+ return tuple(self.result.coords)
0
+ def coords_dict(self):
0
+ if not hasattr(self.result, 'coords'):
0
+ return {u'latitude': self.latitude, u'longitude': self.longitude}
0
+ return {u'longitude': self.result.coords.latitude, u'longitude': self.result.coords.longitude}
0
- """If it exists, returns the friendly_name, otherwise returns the query."""
0
return self.friendly_name or self.query
0
- def coords_tuple(self):
0
- """A two-tuple of latitude and longitude."""
0
- return (self.latitude, self.longitude)
0
- self.created = datetime.datetime.now()
0
- self.geocoder = getattr(geopy_geocoders, settings.DEFAULT_GEOCODER)
0
- self.refresh_if_needed(save=False)
0
- super(Location, self).save()
0
- def refresh(self, save=True):
0
- """Refreshes the geo-mapping."""
0
- geo_keys = settings.GEOCODING_KEYS
0
- except AttributeError:
0
- if self.geocoder.__name__ in geo_keys.keys():
0
- geocoder = self.geocoder(geo_keys[self.geocoder.__name__])
0
- geocoder = self.geocoder()
0
- place, (self.latitude, self.longitude) = geocoder.geocode(self.query)
0
- self.refreshed = datetime.datetime.now()
0
- raise geo_misc.GeocodingError, 'The location \'%s\' could not be geocoded.' % self.query
0
- def refresh_if_needed(self, *args, **kwargs):
0
- """Refreshes the geo-mapping if it has already expired, or we don't have any data
0
- if self.expired or not (self.latitude or self.longitude):
0
- return self.refresh(*args, **kwargs)
0
+ """Returns the datetime when this object will be deemed to have expired."""
0
+ return self.refreshed + relativedelta(**settings.MAX_LOCATION_CACHE_AGE)
0
- if datetime.datetime.now() > (self.created + relativedelta.relativedelta(**settings.MAX_LOCATION_CACHE_AGE)):
0
+ """Returns boolean as to whether this object has 'expired'. Always returns False if not geocoded."""
0
+ # This location hasn't been geocoded
0
+ elif datetime.datetime.now() >= self.expires:
0
+ # The location has expired
0
+ elif not (self.result and hasattr(self.result, 'coords')):
0
+ # The location hasn't yet been geocoded, but it should have been
0
+ # The location hasn't expired
0
+ def force_refresh(self):
0
+ """Forces a refresh of the geo-mapping by re-geocoding (if the location is geocoded)."""
0
+ self.result = self.get_geocoder()(self).geocode()
0
+ self.latitude, self.longitude = tuple(self.result.coords)[:2]
0
+ self.refreshed = datetime.datetime.now()
0
+ """Refreshes the geo-mapping it has already expired."""
0
def distance_between(self, other_location, units='miles'):
0
"""Calculates the distance between this Location object and another Location object.
0
- units should be a string containing the unit of measurement (default: miles) you
0
- would like the result returned in (kilometers, miles, feet or nautical)."""
0
+ units should be a string containing the unit of measurement (default: miles) you would like
0
+ the result returned in (kilometers, miles, feet or nautical)."""
0
if self.coords_tuple == other_location.coords_tuple:
0
dist_obj = geopy_distance.distance(self.coords_tuple, other_location.coords_tuple)
0
@@ -170,7 +173,7 @@ class Location(models.Model):
0
"""Given 2x two-tuples containing lat/long pairs (the northwest and southeast corners
0
bounding a segment of the earth), returns Boolean as to whether this Location falls
0
- if (north_west[0]
< self.latitude < south_east[0]) and (north_west[1] < self.longitude < south_east[1]):
0
+ if (north_west[0]
> self.latitude > south_east[0]) and (north_west[1] < self.longitude < south_east[1]):
0
\ No newline at end of file
Comments
No one has commented yet.