Private
Public Access
1
0
Files
rowsandall/rowers/courses.py

438 lines
13 KiB
Python

from rowers.courseutils import coursetime_paths, coursetime_first, time_in_path
from rowers.models import (
Rower, Workout,
GeoPoint, GeoPolygon, GeoCourse,
course_length, course_coord_center, course_coord_maxmin,
course_coord_crewnerd_navigation,
polygon_coord_center, PlannedSession,
polygon_to_path, coordinate_in_path
)
from rowers.utils import geo_distance
import rowers.dataprep as dataprep
from timezonefinder import TimezoneFinder
import numpy as np
# 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 math
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
try:
distance = geo_distance(lat_lon[0], lat_lon[1], coords[0], coords[1])[0]
except TypeError:
return 10000000
return distance
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
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:
try:
distance = howfaris(lat_lon, c)
if distance < whatisnear:
newlist.append(c)
counter += 1
except TypeError:
pass
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=" ")
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"}
def get_polar_angle(point, reference_point):
"""
Calculate the polar angle of a point with respect to a reference point.
"""
try:
delta_x = point.longitude - reference_point.longitude
delta_y = point.latitude - reference_point.latitude
except AttributeError:
delta_x = point['longitude'] - reference_point.longitude
delta_y = point['latitude'] - reference_point.latitude
return math.atan2(delta_y, delta_x)
class Coordinate:
def __init__(self, latitude, longitude):
self.latitude = latitude
self.longitude = longitude
def get_coordinates_centerpoint(coordinates):
try:
centroid_latitude = sum(coord.latitude for coord in coordinates) / len(coordinates)
centroid_longitude = sum(coord.longitude for coord in coordinates) / len(coordinates)
centroid = Coordinate(centroid_latitude, centroid_longitude)
except AttributeError:
centroid_latitude = sum(coord['latitude'] for coord in coordinates) / len(coordinates)
centroid_longitude = sum(coord['longitude'] for coord in coordinates) / len(coordinates)
centroid = Coordinate(centroid_latitude, centroid_longitude)
return centroid
def sort_coordinates_ccw(coordinates):
"""
Sort coordinates in counterclockwise order.
"""
# Find the reference point (centroid)
centroid = get_coordinates_centerpoint(coordinates)
# Sort coordinates based on polar angle with respect to the centroid
sorted_coords = sorted(coordinates, key=lambda coord: get_polar_angle(coord, centroid))
return sorted_coords
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]),
})
points = sort_coordinates_ccw(points)
polygons.append({
'name': name,
'points': points
})
return polygons
def crewnerdify(polygons):
polygon_names = []
polygon_names.append("Start")
for i in range(1,len(polygons)-1):
polygon_name = 'WP{n}'.format(n=polygons[i].order_in_course)
polygon_names.append(polygon_name)
polygon_names.append("Finish")
return polygon_names
def removewhitespace(s):
regels = s.split('\n')
regels = [regel for regel in regels if regel.strip()]
return "\n".join(regels)
def getcoursefolder(course, document, cn=False):
folder2 = SubElement(document, 'Folder', id="{id}".format(id=course.id))
coursename = SubElement(folder2, 'name')
coursename.text = course.name
openst = SubElement(folder2, 'open')
openst.text = '1'
coursedescription = SubElement(folder2, 'description')
if len(removewhitespace(course.notes)):
coursedescription.text = "Rowsandall.com\n" + removewhitespace(course.notes)
else:
coursedescription.text = "Rowsandall.com " + course.name
polygons = GeoPolygon.objects.filter(
course=course).order_by("order_in_course")
polygon_names = [polygon.name for polygon in polygons]
if cn:
polygon_names = crewnerdify(polygons)
for index, polygon in enumerate(polygons):
placemark = SubElement(folder2, 'Placemark')
polygonname = SubElement(placemark, 'name')
polygonname.text = polygon_names[index]
polygondescription = SubElement(placemark, 'description')
polygondescription.text = polygon.name
polygonstyle = SubElement(placemark, 'styleUrl')
polygonstyle.text = "#default0"
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")
points = sort_coordinates_ccw(points)
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 folder2
def getstyle(document, id):
style = SubElement(document,'Style', id=id)
iconstyle = SubElement(style, 'IconStyle')
scale = SubElement(iconstyle, 'scale')
scale.text = "1.2"
linestyle = SubElement(style, 'LineStyle')
linestylecolor = SubElement(linestyle, 'color')
linestylecolor.text = "ff00ffff"
polystyle = SubElement(style, 'PolyStyle')
polystylecolor = SubElement(polystyle, 'color')
polystylecolor.text = "ff7fffff"
return style
def getstylemap(document, id):
stylemap = SubElement(document, 'StyleMap', id=id)
pair1 = SubElement(stylemap, 'Pair')
pair1key = SubElement(pair1, 'key')
pair1key.text = "normal"
pair1styleurl = SubElement(pair1, 'styleUrl')
pair1styleurl.text = "#default"
pair2 = SubElement(stylemap, 'Pair')
pair2key = SubElement(pair2, 'key')
pair2key.text = "highlight"
pair2styleurl = SubElement(pair2, 'styleUrl')
pair2styleurl.text = "#hl"
return stylemap
def coursestokml(courseids, cn=False):
courses = GeoCourse.objects.filter(id__in=courseids)
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'
opendoc = SubElement(document,'open')
opendoc.text = '1'
style = getstyle(document, "default")
stylemap = getstylemap(document, 'default0')
stylehl = getstyle(document, "hl")
for course in courses:
coursefolder = getcoursefolder(course, document, cn=cn)
return prettify(top)
def coursetokml(course,cn=False):
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'
opendoc = SubElement(document,'open')
opendoc.text = '1'
style = getstyle(document, "default")
stylemap = getstylemap(document, 'default0')
stylehl = getstyle(document, "hl")
coursefolder = getcoursefolder(course, document, cn=cn)
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.arcgis([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 replacecourse(course1, course2):
ps = PlannedSession.objects.filter(course=course1)
for p in ps:
p.course = course2
p.save()
course1.delete()
return 1