313 lines
7.9 KiB
Python
313 lines
7.9 KiB
Python
# All the Courses related methods
|
|
|
|
# Python
|
|
from django.utils import timezone
|
|
from datetime import datetime
|
|
from datetime import timedelta
|
|
import time
|
|
from django.db import IntegrityError
|
|
import uuid
|
|
from django.conf import settings
|
|
|
|
import geocoder
|
|
|
|
from matplotlib import path
|
|
import xml.etree.ElementTree as et
|
|
|
|
import pandas as pd
|
|
import numpy as np
|
|
|
|
import dataprep
|
|
|
|
ns = {'opengis': 'http://www.opengis.net/kml/2.2'}
|
|
|
|
|
|
from rowers.models import (
|
|
Rower, Workout,
|
|
GeoPoint,GeoPolygon, GeoCourse,
|
|
)
|
|
|
|
# low level methods
|
|
class InvalidTrajectoryError(Exception):
|
|
def __init__(self,value):
|
|
self.value=value
|
|
|
|
def __str__(self):
|
|
return repr(self.value)
|
|
|
|
def polygon_coord_center(polygon):
|
|
|
|
points = GeoPoint.objects.filter(polygon=polygon).order_by("order_in_poly")
|
|
|
|
latitudes = pd.Series([p.latitude for p in points])
|
|
longitudes = pd.Series([p.longitude for p in points])
|
|
|
|
return latitudes.mean(), longitudes.mean()
|
|
|
|
def course_coord_center(course):
|
|
|
|
polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course")
|
|
|
|
latitudes = []
|
|
longitudes = []
|
|
|
|
for p in polygons:
|
|
latitude,longitude = polygon_coord_center(p)
|
|
latitudes.append(latitude)
|
|
longitudes.append(longitude)
|
|
|
|
latitude = pd.Series(latitudes).median()
|
|
longitude = pd.Series(longitudes).median()
|
|
|
|
coordinates = pd.DataFrame({
|
|
'latitude':latitudes,
|
|
'longitude':longitudes,
|
|
})
|
|
|
|
return latitude,longitude,coordinates
|
|
|
|
def course_coord_maxmin(course):
|
|
|
|
polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course")
|
|
|
|
latitudes = []
|
|
longitudes = []
|
|
|
|
for p in polygons:
|
|
latitude,longitude = polygon_coord_center(p)
|
|
latitudes.append(latitude)
|
|
longitudes.append(longitude)
|
|
|
|
lat_min = pd.Series(latitudes).min()
|
|
lat_max = pd.Series(latitudes).max()
|
|
long_min = pd.Series(longitudes).min()
|
|
long_max = pd.Series(longitudes).max()
|
|
|
|
|
|
return lat_min,lat_max,long_min,long_max
|
|
|
|
def polygon_to_path(polygon):
|
|
points = GeoPoint.objects.filter(polygon=polygon).order_by("order_in_poly")
|
|
s = []
|
|
for point in points:
|
|
s.append([point.latitude,point.longitude])
|
|
|
|
p = path.Path(s[:-1])
|
|
|
|
return p
|
|
|
|
def coordinate_in_path(latitude,longitude, p):
|
|
|
|
return p.contains_points([(latitude,longitude)])[0]
|
|
|
|
|
|
|
|
def time_in_path(df,p,maxmin='max'):
|
|
|
|
if df.empty:
|
|
return 0
|
|
|
|
latitude = df.latitude
|
|
longitude = df.longitude
|
|
|
|
f = lambda x: coordinate_in_path(x['latitude'],x['longitude'],p)
|
|
|
|
df['inpolygon'] = df.apply(f,axis=1)
|
|
|
|
if maxmin=='max':
|
|
b = (~df['inpolygon']).shift(-1)+df['inpolygon']
|
|
else:
|
|
b = (~df['inpolygon']).shift(1)+df['inpolygon']
|
|
|
|
if len(df[b==2]):
|
|
return df[b==2]['time'].min()
|
|
|
|
raise InvalidTrajectoryError("Trajectory doesn't go through path")
|
|
|
|
|
|
return 0
|
|
|
|
def crewnerdcourse(doc):
|
|
courses = []
|
|
for course in doc:
|
|
name = course.findall('.//opengis:name',ns)[0].text
|
|
try:
|
|
description = course.findall('.//opengis:description',ns)[0].text
|
|
except IndexError:
|
|
description = ''
|
|
|
|
polygonpms = course.findall('.//opengis:Placemark[opengis:Polygon]',ns)
|
|
polygons = get_polygons(polygonpms)
|
|
|
|
courses.append({
|
|
'name':name,
|
|
'description':description,
|
|
'polygons':polygons
|
|
})
|
|
|
|
return courses
|
|
|
|
def get_polygons(polygonpms):
|
|
polygons = []
|
|
for pm in polygonpms:
|
|
name = pm.findall('.//opengis:name',ns)[0].text
|
|
coordinates = pm.findall('.//opengis:coordinates',ns)
|
|
if coordinates:
|
|
cc = coordinates[0].text
|
|
else:
|
|
cc = ''
|
|
|
|
pointstring = cc.split()
|
|
|
|
points = []
|
|
for s in pointstring:
|
|
coordinates = s.split(',')
|
|
points.append({
|
|
'longitude':float(coordinates[0]),
|
|
'latitude':float(coordinates[1]),
|
|
})
|
|
|
|
polygons.append({
|
|
'name':name,
|
|
'points':points
|
|
})
|
|
|
|
return polygons
|
|
|
|
|
|
def kmltocourse(f):
|
|
doc = et.parse(f)
|
|
courses = doc.findall('.//opengis:Folder[opengis:Placemark]',ns)
|
|
|
|
if courses:
|
|
return crewnerdcourse(courses)
|
|
|
|
polygonpms = doc.findall('.//opengis:Placemark[opengis:Polygon]',ns)
|
|
|
|
return get_polygons(polygonpms)
|
|
|
|
|
|
|
|
|
|
def createcourse(
|
|
manager,name,polygons,notes=''):
|
|
|
|
c = GeoCourse(manager=manager,name=name,notes=notes)
|
|
c.save()
|
|
|
|
i = 0
|
|
for p in polygons:
|
|
pp = GeoPolygon(course=c,order_in_course=i,name=p['name'])
|
|
pp.save()
|
|
j = 0
|
|
for point in p['points']:
|
|
if i==0 and j==0:
|
|
latitude = point['latitude']
|
|
longitude = point['longitude']
|
|
g = geocoder.google([latitude,longitude],method='reverse')
|
|
if g.ok:
|
|
address = g.raw['address_components']
|
|
country = 'unknown'
|
|
for a in address:
|
|
if 'country' in a['types']:
|
|
country = a['long_name']
|
|
else:
|
|
country = 'unknown'
|
|
c.country = country
|
|
c.save()
|
|
obj = GeoPoint(
|
|
latitude = point['latitude'],
|
|
longitude = point['longitude'],
|
|
polygon = pp,
|
|
order_in_poly = j
|
|
)
|
|
obj.save()
|
|
j += 1
|
|
i += 1
|
|
|
|
return c
|
|
|
|
def coursetime_first(data,paths):
|
|
|
|
entrytime = data['time'].max()
|
|
coursecompleted = False
|
|
|
|
try:
|
|
entrytime = time_in_path(data,paths[0],maxmin='max')
|
|
coursecompleted = True
|
|
except InvalidTrajectoryError:
|
|
entrytime = data['time'].max()
|
|
coursecompleted = False
|
|
return entrytime, coursecompleted
|
|
|
|
def coursetime_paths(data,paths,finalmaxmin='min'):
|
|
|
|
entrytime = data['time'].max()
|
|
coursecompleted = False
|
|
|
|
# corner case - empty list of paths
|
|
if len(paths) == 0:
|
|
return 0,True
|
|
|
|
# end - just the Finish polygon
|
|
if len(paths) == 1:
|
|
try:
|
|
entrytime = time_in_path(data,paths[0],maxmin=finalmaxmin)
|
|
coursecompleted = True
|
|
except InvalidTrajectoryError:
|
|
entrytime = data['time'].max()
|
|
coursecompleted = False
|
|
return entrytime,coursecompleted
|
|
|
|
if len(paths) > 1:
|
|
try:
|
|
time = time_in_path(data, paths[0])
|
|
data = data[data['time']>time]
|
|
data['time'] = data['time']-time
|
|
timenext, coursecompleted = coursetime_paths(data,paths[1:])
|
|
return time+timenext, coursecompleted
|
|
except InvalidTrajectoryError:
|
|
entrytime = data['time'].max()
|
|
coursecompleted = False
|
|
|
|
return entrytime, coursecompleted
|
|
|
|
def get_time_course(ws,course):
|
|
coursetimeseconds = 0.0
|
|
coursecompleted = 0
|
|
|
|
w = ws[0]
|
|
columns = ['time',' latitude',' longitude']
|
|
rowdata = dataprep.getsmallrowdata_db(
|
|
columns,
|
|
ids = [w.id],
|
|
doclean=True,
|
|
workstrokesonly=False
|
|
)
|
|
rowdata.rename(columns = {
|
|
' latitude':'latitude',
|
|
' longitude':'longitude',
|
|
}, inplace=True)
|
|
|
|
|
|
rowdata['time'] = rowdata['time']/1000.
|
|
|
|
rowdata.fillna(method='backfill',inplace=True)
|
|
|
|
rowdata['time'] = rowdata['time']-rowdata.ix[0,'time']
|
|
# we may want to expand the time (interpolate)
|
|
|
|
polygons = GeoPolygon.objects.filter(course=course)
|
|
paths = []
|
|
for polygon in polygons:
|
|
path = polygon_to_path(polygon)
|
|
paths.append(path)
|
|
|
|
coursetimeseconds,coursecompleted = coursetime_paths(rowdata,paths)
|
|
coursetimefirst,firstcompleted = coursetime_first(
|
|
rowdata,paths)
|
|
|
|
coursetimeseconds = coursetimeseconds-coursetimefirst
|
|
|
|
return coursetimeseconds,coursecompleted
|