""" Object-Document Map for bike model """ from mongoengine import Document, DateTimeField, GeoPointField, \ FloatField, connection import bson import datetime from LatLon23 import LatLon, Latitude, Longitude from geopy import distance 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: # next point is determined by the distance travelled since the last update dt = datetime.datetime.now() - b['last_update'] progress = (b['speed'] * dt.seconds) / 1000 # in km point = LatLon(b['point'][1], b['point'][0]) next_point = point.offset(b['bearing'], progress) # collect all speeds, bearings and next points, to find centroid and average speed speeds.append(b['speed']) bearings.append(b.get('bearing', 0)) lats.append(float(next_point.lat)) lons.append(float(next_point.lon)) n += 1 self.mean_speed = 0 self.size = 0 self.centroid = None 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))) 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.last_update = datetime.datetime.now() 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, min_age=None): """ min_age of agents in database, in seconds :return: list of Bike objects """ if min_age is None: min_step = 100 # update at least every 100 meters if self.speed > 0: min_age = min_step / self.speed else: min_age = 60 trip_len = distance.geodesic(self.get_point(), self.get_dest()).meters local_radius = trip_len * point_altruism destination_radius = trip_len * dest_altruism # these are bikes recently known to be around me local = [bike for bike in self.db.bike.find( {'point': { '$near': { '$geometry': { 'type': 'Point', 'coordinates': self.point}, '$maxDistance': local_radius}}, 'last_update': { '$gte': datetime.datetime.now() - datetime.timedelta(seconds=min_age)}, '_id': {'$ne': bson.objectid.ObjectId(self.id)}})] 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}}, 'last_update': { '$gte': datetime.datetime.now() - datetime.timedelta(seconds=min_age)} })] 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 > 0: flock_lat = float(flock.centroid.lat) flock_lon = float(flock.centroid.lon) return {'flock_lat': flock_lat, 'flock_lon': flock_lon, 'flock_avg_speed': flock.mean_speed, 'flock_size': flock.size} else: return {}