diff --git a/rowers/courses.py b/rowers/courses.py index 9857b230..ab8bc77b 100644 --- a/rowers/courses.py +++ b/rowers/courses.py @@ -14,6 +14,9 @@ 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'} @@ -83,45 +86,45 @@ def course_coord_maxmin(course): return lat_min,lat_max,long_min,long_max def polygon_to_path(polygon): - points = GeoPoint.objects.filter(polygon==polygon).order_by("order_in_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(np.array(s)) + p = path.Path(s[:-1]) return p -def coordinate_in_polygon(latitude,longitude, polygon): - p = polygon_to_path(polygon) +def coordinate_in_path(latitude,longitude, p): return p.contains_points([(latitude,longitude)])[0] -def time_in_polygon(df,polygon,maxmin='max'): - # df has timestamp, latitude, longitude - p = polygon_to_path(polygon) +def time_in_path(df,p,maxmin='max'): + if df.empty: + return 0 + latitude = df.latitude longitude = df.longitude - f = lambda x: coordinate_in_polygon(x['latitude'],x['longitude'],polygon) + f = lambda x: coordinate_in_path(x['latitude'],x['longitude'],p) df['inpolygon'] = df.apply(f,axis=1) - mask = df['inpolygon'] == True - - if df[mask].empty(): - raise InvalidTrajectoryError - - if maxmin == 'max': - time = df[mask]['time'].max() + if maxmin=='max': + b = (~df['inpolygon']).shift(-1)+df['inpolygon'] else: - time = df[mask]['time'].min() + 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 time + return 0 def crewnerdcourse(doc): courses = [] @@ -200,8 +203,16 @@ def createcourse( j = 0 for point in p['points']: if i==0 and j==0: - loc = geolocator.reverse((point['latitude'],point['longitude'])) - country = loc.raw['address']['country'] + try: + loc = geolocator.reverse((point['latitude'],point['longitude'])) + country = loc.raw['address']['country'] + if isinstance(country,unicode): + country = country.encode('utf8') + elif isinstance(country, str): + country = country.decode('utf8') + except GeocoderInsufficientPrivileges: + country = "Unknown Country" + c.country = country c.save() obj = GeoPoint( @@ -215,3 +226,74 @@ def createcourse( i += 1 return c + +def coursetime_paths(data,paths): + + 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='min') + coursecompleted = True + print entrytime + 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 + print 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) + + print 'course time?',coursetimeseconds + + return coursetimeseconds,coursecompleted diff --git a/rowers/plannedsessions.py b/rowers/plannedsessions.py index 487226c5..a25ce578 100644 --- a/rowers/plannedsessions.py +++ b/rowers/plannedsessions.py @@ -24,6 +24,7 @@ from rowers.models import ( import metrics import numpy as np import dataprep +import courses # Low Level functions - to be called by higher level methods def add_workouts_plannedsession(ws,ps,r): @@ -47,7 +48,7 @@ def add_workouts_plannedsession(ws,ps,r): ids = [w.id for w in wold] + [w.id for w in ws] ids = list(set(ids)) - if len(ids)>1 and ps.sessiontype == 'test': + if len(ids)>1 and ps.sessiontype in ['test','coursetest']: errors.append('For tests, you can only attach one workout') return result,comments,errors @@ -233,8 +234,36 @@ def is_session_complete_ws(ws,ps): else: if not completiondate: completiondate = ws.reverse()[0].date - return ratio,'partial',completiondate - + return ratio,'partial',completiondate + elif ps.sessiontype == 'coursetest': + if ps.course: + coursetime,coursecompleted = courses.get_time_course(ws,ps.course) + if coursecompleted: + return 1.0,'completed',completiondate + else: + return ratio,'partial',completiondate + else: + if ps.criterium == 'exact': + if ratio == 1.0: + return ratio,'completed',completiondate + else: + if not completiondate: + completiondate = ws.reverse()[0].date + return ratio,'partial',completiondate + elif ps.criterium == 'minimum': + if ratio >= 1.0: + return ratio,'completed',completiondate + else: + if not completiondate: + completiondate = ws.reverse()[0].date + + return ratio,'partial',completiondate + else: + if ratio>cratiomin and ratio