-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodels.py
124 lines (106 loc) · 4.6 KB
/
models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import datetime
from geopy import distance as geopy_distance
from django.db import models
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from geo import geocoding, managers, fields as custom_fields
from geo.dateutil.relativedelta import relativedelta
class Location(models.Model):
"""A Location on the earth."""
query = models.CharField(_('Location'), max_length=250, blank=False, null=False, unique=True)
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\'.'))
geocoded = models.BooleanField(default=True)
result = custom_fields.PickledObjectField(blank=True, null=True, editable=False)
latitude = models.FloatField(blank=True, null=False)
longitude = models.FloatField(blank=True, null=False)
refreshed = models.DateTimeField(editable=False, blank=True, null=False, default=datetime.datetime.now())
extra = custom_fields.DictionaryField(_('A dictionary of additional information'), blank=True, null=True, editable=False)
created = models.DateTimeField(editable=False, blank=True, null=True, default=datetime.datetime.now())
is_public = models.BooleanField(default=True)
# Manager
objects = managers.LocationManager()
class Admin:
list_display = ('__str__', 'latitude', 'longitude', 'created', 'refreshed')
list_filter = ('created', 'refreshed')
def save(self, *args, **kwargs):
self.refresh()
return super(Location, self).save(*args, **kwargs)
# General
def __unicode__(self):
return unicode(self.name)
def __getitem__(self, index):
"""Gets either a latitude or longitude by indexing the coords_tuple."""
return self.coords_tuple[index]
def get_geocoder(self):
"""Returns an instantiated geocoder for this object. Make sure you have settings.DEFAULT_GEOCODER set correctly."""
return geocoding.SHORT_NAME_MAPPINGS[settings.DEFAULT_GEOCODER]
@property
def coords(self):
if hasattr(self.result, 'coords'):
return self.result.coords
else:
return geocoding.Coordinates(float(self.latitude or 0), float(self.longitude or 0))
@property
def coords_tuple(self):
if not hasattr(self.result, 'coords'):
return (self.latitude, self.longitude)
else:
return tuple(self.result.coords)
@property
def coords_dict(self):
if not hasattr(self.result, 'coords'):
return {u'latitude': self.latitude, u'longitude': self.longitude}
else:
return {u'longitude': self.result.coords.latitude, u'longitude': self.result.coords.longitude}
@property
def name(self):
return self.friendly_name or self.query
# Caching
@property
def expires(self):
"""Returns the datetime when this object will be deemed to have expired."""
return self.refreshed + relativedelta(**settings.MAX_LOCATION_CACHE_AGE)
@property
def expired(self):
"""Returns boolean as to whether this object has 'expired'. Always returns False if not geocoded."""
if not self.geocoded:
# This location hasn't been geocoded
return False
elif datetime.datetime.now() >= self.expires:
# The location has expired
return True
elif not (self.result and hasattr(self.result, 'coords')):
# The location hasn't yet been geocoded, but it should have been
return True
else:
# The location hasn't expired
return False
def force_refresh(self):
"""Forces a refresh of the geo-mapping by re-geocoding (if the location is geocoded)."""
if self.geocoded:
self.result = self.get_geocoder()(self).geocode()
self.latitude, self.longitude = tuple(self.result.coords)[:2]
self.refreshed = datetime.datetime.now()
return self
def refresh(self):
"""Refreshes the geo-mapping it has already expired."""
if self.expired:
self.force_refresh()
return self
# Conveniences
def distance_between(self, other_location, units='miles'):
"""Calculates the distance between this Location object and another Location object.
units should be a string containing the unit of measurement (default: miles) you would like
the result returned in (kilometers, miles, feet or nautical)."""
if self.coords_tuple == other_location.coords_tuple:
return 0
dist_obj = geopy_distance.distance(self.coords_tuple, other_location.coords_tuple)
return getattr(dist_obj, str(units))
def within_bounds(self, north_west, south_east):
"""Given 2x two-tuples containing lat/long pairs (the northwest and southeast corners
bounding a segment of the earth), returns Boolean as to whether this Location falls
inside the area."""
if (north_west[0] > self.latitude > south_east[0]) and (north_west[1] < self.longitude < south_east[1]):
return True
else:
return False