# 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 from timezonefinder import TimezoneFinder import dataprep from rowers.utils import geo_distance ns = {'opengis': 'http://www.opengis.net/kml/2.2'} from rowers.models import ( Rower, Workout, GeoPoint,GeoPolygon, GeoCourse, course_length,course_coord_center,course_coord_maxmin, polygon_coord_center,PlannedSession ) from utils import geo_distance # low level methods class InvalidTrajectoryError(Exception): def __init__(self,value): self.value=value def __str__(self): return repr(self.value) def get_dir_vector(polygon1,polygon2): lat1,lon1 = polygon_coord_center(polygon1) lat2,lon2 = polygon_coord_center(polygon2) return [lat2-lat1,lon2-lon1] def get_delta(vector,polygon): x = pd.Series(range(10000))/9999. vlat = vector[0] vlon = vector[1] lat1,lon1 = polygon_coord_center(polygon) lat = x.apply(lambda x:lat1+x*vlat) lon = x.apply(lambda x:lon1+x*vlon) totdist,bearing = geo_distance(lat1,lon1,lat1+vlat,lon1+vlat) print totdist dist = x*totdist p = polygon_to_path(polygon) f = lambda x: coordinate_in_path(x['lat'],x['lon'],p) df = pd.DataFrame({'x':x, 'lat':lat, 'lon':lon, 'dist':dist, }) df['inpolygon'] = df.apply(f,axis=1) b = (~df['inpolygon']).shift(-1)+df['inpolygon'] if len(df[b==2]): return 1.0e3*df[b==2]['dist'].min() else: return 0 def get_delta_start(course): polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course") vector = get_dir_vector(polygons[0],polygons[1]) delta = get_delta(vector,polygons[0]) return delta def get_course_timezone(course): polygons = GeoPolygon.objects.filter(course = course) points = GeoPoint.objects.filter(polygon = polygons[0]) lat = points[0].latitude lon = points[0].longitude tf = TimezoneFinder() try: timezone_str = tf.timezone_at(lng=lon,lat=lat) except ValueError: timezone_str = 'UTC' if timezone_str is None: timezone_str = tf.closest_timezone_at(lng=lon,lat=lat) if timezone_str is None: timezone_str = 'UTC' return timezone_str 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(),df[b==2]['cum_dist'].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 not courses: courses = doc.findall('.//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() entrydistance = data['cum_dist'].max() coursecompleted = False try: entrytime,entrydistance = time_in_path(data,paths[0],maxmin='max') coursecompleted = True except InvalidTrajectoryError: entrytime = data['time'].max() entrydistance = data['cum_dist'].max() coursecompleted = False return entrytime, entrydistance, coursecompleted def coursetime_paths(data,paths,finalmaxmin='min'): entrytime = data['time'].max() entrydistance = data['cum_dist'].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, entrydistance ) = time_in_path(data,paths[0],maxmin=finalmaxmin) coursecompleted = True except InvalidTrajectoryError: entrytime = data['time'].max() entrydistance = data['cum_dist'].max() coursecompleted = False return entrytime,entrydistance,coursecompleted if len(paths) > 1: try: time,dist = time_in_path(data, paths[0]) data = data[data['time']>time] data['time'] = data['time']-time data['cum_dist'] = data['cum_dist']-dist ( timenext, distnext, coursecompleted ) = coursetime_paths(data,paths[1:]) return time+timenext, dist+distnext,coursecompleted except InvalidTrajectoryError: entrytime = data['time'].max() entrydistance = data['cum_dist'].max() coursecompleted = False return entrytime, entrydistance, coursecompleted def get_time_course(ws,course): coursetimeseconds = 0.0 coursecompleted = False w = ws[0] columns = ['time',' latitude',' longitude','cum_dist'] rowdata = dataprep.getsmallrowdata_db( columns, ids = [w.id], doclean=False, 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) rowdata['dt'] = rowdata['time'].apply( lambda x: timedelta(seconds=x) ) rowdata = rowdata.resample('100ms',on='dt').mean() rowdata = rowdata.interpolate() polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course") paths = [] for polygon in polygons: path = polygon_to_path(polygon) paths.append(path) ( coursetimeseconds, coursemeters, coursecompleted, ) = coursetime_paths(rowdata,paths) ( coursetimefirst, coursemetersfirst, firstcompleted ) = coursetime_first( rowdata,paths) coursetimeseconds = coursetimeseconds-coursetimefirst coursemeters = coursemeters-coursemetersfirst return coursetimeseconds,coursemeters,coursecompleted def replacecourse(course1,course2): ps = PlannedSession.objects.filter(course=course1) for p in ps: p.course = course2 p.save() course1.delete() return 1