""" 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 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() n = 0 for b in bikes: speeds.append(b['speed']) 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) 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, point): s = LatLon(Latitude(self.point[1]), Longitude(self.point[0])) t = LatLon(Latitude(point[1]), Longitude(point[0])) 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, self.destination).meters return {'flock_heading': flock_heading, 'flock_distance': flock_distance, 'flock_avg_speed': flock.mean_speed, 'flock_size': flock.size} else: return {}