356 lines
9.5 KiB
Python
356 lines
9.5 KiB
Python
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
from __future__ import unicode_literals
|
|
|
|
# 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
|
|
|
|
from xml.etree.ElementTree import Element, SubElement, Comment, tostring
|
|
from xml.dom import minidom
|
|
|
|
from rowers.models import VirtualRace
|
|
|
|
# distance of course from lat_lon in km
|
|
def howfaris(lat_lon,course):
|
|
coords = course.coord
|
|
distance = geo_distance(lat_lon[0],lat_lon[1],coords[0],coords[1])[0]
|
|
|
|
return distance
|
|
|
|
#whatisnear = 150
|
|
|
|
# get nearest races
|
|
def getnearestraces(lat_lon,races,whatisnear=150):
|
|
newlist = []
|
|
counter = 0
|
|
for race in races:
|
|
if race.course is None: # pragma: no cover
|
|
newlist.append(race)
|
|
else:
|
|
c = race.course
|
|
coords = c.coord
|
|
distance = howfaris(lat_lon,c)
|
|
if distance < whatisnear:
|
|
newlist.append(race)
|
|
counter += 1
|
|
|
|
if counter>0:
|
|
races = newlist
|
|
else:
|
|
courseraces = races.exclude(course__isnull=True)
|
|
orders = [(c.id,howfaris(lat_lon,c.course)) for c in courseraces]
|
|
orders = sorted(orders,key = lambda tup:tup[1])
|
|
ids = [id for id,distance in orders[0:4]]
|
|
for id, distance in orders[5:]: # pragma: no cover
|
|
if distance<whatisnear:
|
|
ids.append(id)
|
|
|
|
for id in ids:
|
|
newlist.append(VirtualRace.objects.get(id=id))
|
|
races = newlist
|
|
|
|
return races
|
|
|
|
def getnearestcourses(lat_lon,courses,whatisnear=150,strict=False):
|
|
|
|
newlist = []
|
|
counter = 0
|
|
for c in courses:
|
|
coords = c.coord
|
|
distance = howfaris(lat_lon,c)
|
|
|
|
if distance < whatisnear:
|
|
newlist.append(c)
|
|
counter += 1
|
|
|
|
if counter>0:
|
|
courses = newlist
|
|
elif strict:
|
|
courses = newlist
|
|
else:
|
|
orders = [(c.id,howfaris(lat_lon,c)) for c in courses]
|
|
orders = sorted(orders,key = lambda tup:tup[1])
|
|
ids = [id for id,distance in orders[0:4]]
|
|
for id, distance in orders[5:]:
|
|
if distance<whatisnear: # pragma: no cover
|
|
ids.append(id)
|
|
|
|
for id in ids:
|
|
newlist.append(GeoCourse.objects.get(id=id))
|
|
courses = newlist
|
|
|
|
return courses
|
|
|
|
|
|
def prettify(elem):
|
|
"""Return a pretty-printed XML string for the Element.
|
|
"""
|
|
rough_string = tostring(elem, 'utf-8')
|
|
reparsed = minidom.parseString(rough_string)
|
|
return reparsed.toprettyxml(indent=" ")
|
|
|
|
import pandas as pd
|
|
import numpy as np
|
|
from timezonefinder import TimezoneFinder
|
|
|
|
import rowers.dataprep as dataprep
|
|
from rowers.utils import geo_distance
|
|
|
|
ns = {'opengis': 'http://www.opengis.net/kml/2.2'}
|
|
|
|
xmlns_uris_dict = {'gx': 'http://www.google.com/kml/ext/2.2',
|
|
'kml': 'http://www.google.com/kml/ext/2.2',
|
|
'atom': "http://www.w3.org/2005/Atom",
|
|
'': "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,
|
|
polygon_to_path,coordinate_in_path
|
|
)
|
|
|
|
|
|
from rowers.courseutils import coursetime_paths, coursetime_first,time_in_path
|
|
|
|
|
|
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: # pragma: no cover
|
|
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 coursetokml(course):
|
|
top = Element('kml')
|
|
for prefix, uri in xmlns_uris_dict.items():
|
|
if prefix != '':
|
|
top.attrib['xmlns:' + prefix] = uri
|
|
else:
|
|
top.attrib['xmlns'] = uri
|
|
|
|
document = SubElement(top,'Document')
|
|
name = SubElement(document, 'name')
|
|
name.text = 'Courses.kml'
|
|
folder = SubElement(document,'Folder')
|
|
foldername = SubElement(folder,'name')
|
|
foldername.text = 'Courses'
|
|
folder2 = SubElement(folder,'Folder')
|
|
coursename = SubElement(folder2,'name')
|
|
coursename.text = course.name
|
|
open = SubElement(folder2,'open')
|
|
open.text = '1'
|
|
|
|
polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course")
|
|
|
|
polygonsxml = []
|
|
|
|
for polygon in polygons:
|
|
placemark = SubElement(folder2,'Placemark')
|
|
polygonname = SubElement(placemark,'name')
|
|
polygonname.text = polygon.name
|
|
p = SubElement(placemark,'Polygon')
|
|
tessellate = SubElement(p,'tessellate')
|
|
tessellate.text = '1'
|
|
boundary = SubElement(p,'outerBoundaryIs')
|
|
ring = SubElement(boundary,'LinearRing')
|
|
coordinates = SubElement(ring,'coordinates')
|
|
coordinates.text = ''
|
|
points = GeoPoint.objects.filter(polygon=polygon).order_by("order_in_poly")
|
|
for point in points:
|
|
coordinates.text += '{lon},{lat},0 '.format(
|
|
lat = point.latitude,
|
|
lon = point.longitude,
|
|
)
|
|
coordinates.text += '{lon},{lat},0'.format(
|
|
lat = points[0].latitude,
|
|
lon = points[0].longitude,
|
|
)
|
|
|
|
return prettify(top)
|
|
|
|
def kmltocourse(f):
|
|
doc = et.parse(f)
|
|
courses = doc.findall('.//opengis:Folder[opengis:Placemark]',ns)
|
|
|
|
if not courses: # pragma: no cover
|
|
courses = doc.findall('.//opengis:Document[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) # pragma: no cover
|
|
|
|
return get_polygons(polygonpms) # pragma: no cover
|
|
|
|
|
|
|
|
|
|
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.osm([latitude,longitude],method='reverse')
|
|
if g.ok:
|
|
country = g.json['country']
|
|
else: # pragma: no cover
|
|
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
|
|
|
|
c.distance = int(course_length(c))
|
|
c.save()
|
|
|
|
return c
|
|
|
|
|
|
def get_time_course(ws,course): # pragma: no cover
|
|
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()
|
|
|
|
# create path
|
|
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
|