2020-08-05 19:09:38 +00:00
|
|
|
"""
|
|
|
|
|
Object-Document Map for bike model
|
|
|
|
|
"""
|
|
|
|
|
from mongoengine import Document, DateTimeField, GeoPointField, \
|
|
|
|
|
FloatField, connection
|
2020-08-20 18:16:32 +00:00
|
|
|
import bson
|
2020-08-05 19:09:38 +00:00
|
|
|
import datetime
|
2020-08-18 22:33:12 +00:00
|
|
|
from LatLon23 import LatLon, Latitude, Longitude
|
|
|
|
|
from geopy import distance
|
2020-08-05 19:09:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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()
|
2020-08-20 00:20:12 +00:00
|
|
|
bearings = list()
|
2020-08-05 19:09:38 +00:00
|
|
|
n = 0
|
|
|
|
|
for b in bikes:
|
2020-08-20 18:16:32 +00:00
|
|
|
|
2020-09-14 19:20:56 +00:00
|
|
|
# next point is determined by the distance travelled since the last update
|
2020-08-20 18:16:32 +00:00
|
|
|
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)
|
2020-09-14 19:20:56 +00:00
|
|
|
|
|
|
|
|
# collect all speeds, bearings and next points, to find centroid and average speed
|
2020-08-05 19:09:38 +00:00
|
|
|
speeds.append(b['speed'])
|
2020-08-20 00:20:12 +00:00
|
|
|
bearings.append(b.get('bearing', 0))
|
2020-08-20 18:16:32 +00:00
|
|
|
lats.append(float(next_point.lat))
|
|
|
|
|
lons.append(float(next_point.lon))
|
2020-08-05 19:09:38 +00:00
|
|
|
n += 1
|
|
|
|
|
|
2020-08-20 18:16:32 +00:00
|
|
|
|
|
|
|
|
self.mean_speed = 0
|
|
|
|
|
self.size = 0
|
|
|
|
|
self.centroid = None
|
2020-09-22 02:03:54 +00:00
|
|
|
|
2020-08-05 19:09:38 +00:00
|
|
|
if n > 0:
|
|
|
|
|
self.size = n
|
|
|
|
|
self.mean_speed = sum(speeds) / float(len(speeds))
|
2020-09-22 02:03:54 +00:00
|
|
|
|
2020-08-05 19:09:38 +00:00
|
|
|
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)
|
2020-08-20 00:20:12 +00:00
|
|
|
bearing = FloatField(default=0)
|
2020-08-05 19:09:38 +00:00
|
|
|
last_update = DateTimeField(default=datetime.datetime.utcnow)
|
|
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super(Document, self).__init__(*args, **kwargs)
|
|
|
|
|
self.db = connection.get_db()
|
|
|
|
|
|
2020-08-18 22:33:12 +00:00
|
|
|
def update(self, lat, lon, speed, bearing):
|
2020-08-05 19:09:38 +00:00
|
|
|
self.point = (lon, lat)
|
2020-08-18 22:33:12 +00:00
|
|
|
self.speed = speed
|
|
|
|
|
self.bearing = bearing
|
2020-08-20 18:16:32 +00:00
|
|
|
self.last_update = datetime.datetime.now()
|
2020-08-18 22:33:12 +00:00
|
|
|
self.save()
|
2020-09-22 02:03:54 +00:00
|
|
|
|
2020-08-20 00:20:12 +00:00
|
|
|
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
|
|
|
|
|
|
2020-08-18 22:33:12 +00:00
|
|
|
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])
|
2020-08-05 19:09:38 +00:00
|
|
|
|
2021-11-01 20:34:50 +00:00
|
|
|
def find_flock(self,
|
|
|
|
|
point_altruism=0.1,
|
|
|
|
|
dest_altruism=0.2,
|
|
|
|
|
min_age=None):
|
2020-08-05 19:09:38 +00:00
|
|
|
"""
|
2021-11-01 20:34:50 +00:00
|
|
|
min_age of agents in database, in seconds
|
2020-08-05 19:09:38 +00:00
|
|
|
:return: list of Bike objects
|
|
|
|
|
"""
|
|
|
|
|
|
2021-11-01 20:34:50 +00:00
|
|
|
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
|
|
|
|
|
|
2020-08-18 22:33:12 +00:00
|
|
|
trip_len = distance.geodesic(self.get_point(), self.get_dest()).meters
|
|
|
|
|
|
|
|
|
|
local_radius = trip_len * point_altruism
|
|
|
|
|
destination_radius = trip_len * dest_altruism
|
2020-08-05 19:09:38 +00:00
|
|
|
|
2021-11-01 20:34:50 +00:00
|
|
|
# these are bikes recently known to be around me
|
2020-08-05 19:09:38 +00:00
|
|
|
local = [bike for bike in
|
|
|
|
|
self.db.bike.find(
|
2020-08-20 18:16:32 +00:00
|
|
|
{'point': {
|
|
|
|
|
'$near': {
|
|
|
|
|
'$geometry': {
|
|
|
|
|
'type': 'Point',
|
|
|
|
|
'coordinates': self.point},
|
|
|
|
|
'$maxDistance': local_radius}},
|
|
|
|
|
'last_update': {
|
2021-11-01 20:34:50 +00:00
|
|
|
'$gte': datetime.datetime.now() - datetime.timedelta(seconds=min_age)},
|
2020-08-20 18:16:32 +00:00
|
|
|
'_id': {'$ne': bson.objectid.ObjectId(self.id)}})]
|
|
|
|
|
|
2020-08-05 19:09:38 +00:00
|
|
|
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':
|
2021-11-01 20:34:50 +00:00
|
|
|
{'$near':
|
|
|
|
|
{'$geometry': {'type': 'Point',
|
|
|
|
|
'coordinates': self.destination},
|
|
|
|
|
'$maxDistance': destination_radius}},
|
|
|
|
|
'last_update': {
|
|
|
|
|
'$gte': datetime.datetime.now() - datetime.timedelta(seconds=min_age)}
|
|
|
|
|
})]
|
2020-08-05 19:09:38 +00:00
|
|
|
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())
|
2020-09-22 02:03:54 +00:00
|
|
|
if flock.size > 0:
|
2020-09-22 01:29:07 +00:00
|
|
|
flock_lat = float(flock.centroid.lat)
|
|
|
|
|
flock_lon = float(flock.centroid.lon)
|
2020-09-22 02:03:54 +00:00
|
|
|
|
|
|
|
|
return {'flock_lat': flock_lat,
|
2020-09-22 01:29:07 +00:00
|
|
|
'flock_lon': flock_lon,
|
2020-08-05 19:09:38 +00:00
|
|
|
'flock_avg_speed': flock.mean_speed,
|
|
|
|
|
'flock_size': flock.size}
|
|
|
|
|
else:
|
|
|
|
|
return {}
|