136 lines
4.5 KiB
Python
136 lines
4.5 KiB
Python
"""
|
|
Object-Document Map for bike model
|
|
"""
|
|
from mongoengine import Document, DateTimeField, GeoPointField, \
|
|
FloatField, connection
|
|
import datetime
|
|
from LatLon23 import LatLon, Latitude, Longitude
|
|
from geopy import distance
|
|
|
|
|
|
# TODO: instead of centroid use speed and bearing of flockers to create a vector, point rider towards it
|
|
# use time elapsed since last update, speed vector and current point to compute future location
|
|
class Flock:
|
|
"""
|
|
Flock object is created from a list of agents
|
|
and has a useful centroid
|
|
"""
|
|
def __init__(self, bikes):
|
|
lats = list()
|
|
lons = list()
|
|
speeds = list()
|
|
bearings = list()
|
|
n = 0
|
|
for b in bikes:
|
|
speeds.append(b['speed'])
|
|
bearings.append(b.get('bearing', 0))
|
|
lats.append(float(b['point'][1]))
|
|
lons.append(float(b['point'][0]))
|
|
n += 1
|
|
|
|
if n > 0:
|
|
self.size = n
|
|
self.mean_speed = sum(speeds) / float(len(speeds))
|
|
self.centroid = LatLon(Latitude(sum(lats) / len(lats)),
|
|
Longitude(sum(lons) / len(lons)))
|
|
else:
|
|
self.mean_speed = 0
|
|
self.size = 0
|
|
self.centroid = None
|
|
|
|
|
|
class Bike(Document):
|
|
point = GeoPointField()
|
|
destination = GeoPointField(required=True)
|
|
speed = FloatField(default=0)
|
|
bearing = FloatField(default=0)
|
|
last_update = DateTimeField(default=datetime.datetime.utcnow)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(Document, self).__init__(*args, **kwargs)
|
|
self.db = connection.get_db()
|
|
|
|
def update(self, lat, lon, speed, bearing):
|
|
self.point = (lon, lat)
|
|
self.speed = speed
|
|
self.bearing = bearing
|
|
self.save()
|
|
|
|
def heading_to(self, target):
|
|
s = LatLon(Latitude(self.point[1]),
|
|
Longitude(self.point[0]))
|
|
if type(target) is list:
|
|
t = LatLon(Latitude(target[0]),
|
|
Longitude(target[1]))
|
|
else:
|
|
t = target
|
|
|
|
return s.heading_initial(t)
|
|
|
|
def distance_to(self, point):
|
|
return distance.geodesic(self.get_point(),
|
|
(point[1],
|
|
point[0])).meters
|
|
|
|
def get_point(self):
|
|
return (self.point[1],
|
|
self.point[0])
|
|
|
|
def get_dest(self):
|
|
return (self.destination[1],
|
|
self.destination[0])
|
|
|
|
def find_flock(self, point_altruism=0.1, dest_altruism=0.2):
|
|
"""
|
|
:return: list of Bike objects
|
|
"""
|
|
|
|
trip_len = distance.geodesic(self.get_point(), self.get_dest()).meters
|
|
|
|
local_radius = trip_len * point_altruism
|
|
destination_radius = trip_len * dest_altruism
|
|
|
|
# TODO: filter by timestamp
|
|
|
|
# these are bikes around me
|
|
local = [bike for bike in
|
|
self.db.bike.find(
|
|
{'point':
|
|
{'$near':
|
|
{'$geometry': {'type': 'Point',
|
|
'coordinates': self.point},
|
|
'$maxDistance': local_radius}}})]
|
|
local_ids = set([bike['_id'] for bike in local])
|
|
|
|
# these are going near my destination
|
|
remote = [bike for bike in
|
|
self.db.bike.find(
|
|
{'destination':
|
|
{'$near':
|
|
{'$geometry': {'type': 'Point',
|
|
'coordinates': self.destination},
|
|
'$maxDistance': destination_radius}}})]
|
|
remote_ids = set([bike['_id'] for bike in remote])
|
|
|
|
# intersect them!
|
|
flock_ids = local_ids.intersection(remote_ids)
|
|
|
|
return (bike for bike in local if bike['_id'] in flock_ids)
|
|
|
|
def flock_data(self):
|
|
"""
|
|
:return: heading from point to flock centroid, in degrees
|
|
"""
|
|
flock = Flock(self.find_flock())
|
|
if flock.size > 1: # no flocking with self!
|
|
flock_heading = self.heading_to(flock.centroid)
|
|
flock_distance = distance.geodesic((self.point[1],
|
|
self.point[0]), (self.destination[1],
|
|
self.destination[0])).meters
|
|
return {'flock_heading': flock_heading,
|
|
'flock_distance': flock_distance,
|
|
'flock_avg_speed': flock.mean_speed,
|
|
'flock_size': flock.size}
|
|
else:
|
|
return {}
|