from rowers.courseutils import coursetime_paths, coursetime_first, time_in_path import pandas as pd 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.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 distance = geo_distance(lat_lon[0], lat_lon[1], coords[0], coords[1])[0] 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: 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=" ") 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 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") 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") 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 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