[Python-modules-commits] [gpxpy] 01/07: Initial import.
Dominik George
natureshadow-guest at moszumanska.debian.org
Fri Nov 4 12:37:19 UTC 2016
This is an automated email from the git hooks/post-receive script.
natureshadow-guest pushed a commit to branch master
in repository gpxpy.
commit 871e030c94a3c22f56ca58114de0258f0dadc61e
Author: Dominik George <nik at naturalnet.de>
Date: Fri Nov 4 12:42:21 2016 +0100
Initial import.
---
PKG-INFO | 13 +
gpxinfo | 102 +++
gpxpy/__init__.py | 34 +
gpxpy/geo.py | 384 ++++++++
gpxpy/gpx.py | 2643 +++++++++++++++++++++++++++++++++++++++++++++++++++++
gpxpy/gpxfield.py | 406 ++++++++
gpxpy/gpxxml.py | 67 ++
gpxpy/parser.py | 221 +++++
gpxpy/utils.py | 116 +++
setup.py | 34 +
10 files changed, 4020 insertions(+)
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..672265f
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,13 @@
+Metadata-Version: 1.1
+Name: gpxpy
+Version: 1.1.1
+Summary: GPX file parser and GPS track manipulation library
+Home-page: http://www.trackprofiler.com/gpxpy/index.html
+Author: Tomo Krajina
+Author-email: tkrajina at gmail.com
+License: Apache License, Version 2.0
+Description: UNKNOWN
+Platform: UNKNOWN
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
diff --git a/gpxinfo b/gpxinfo
new file mode 100644
index 0000000..2a4b2ab
--- /dev/null
+++ b/gpxinfo
@@ -0,0 +1,102 @@
+#!/bin/env python
+
+"""
+Command line utility to extract basic statistics from a gpx file
+"""
+
+import pdb
+
+import sys as mod_sys
+import logging as mod_logging
+import math as mod_math
+
+import gpxpy as mod_gpxpy
+
+#mod_logging.basicConfig(level=mod_logging.DEBUG,
+# format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
+
+
+
+
+def format_time(time_s):
+ if not time_s:
+ return 'n/a'
+ minutes = mod_math.floor(time_s / 60.)
+ hours = mod_math.floor(minutes / 60.)
+
+ return '%s:%s:%s' % (str(int(hours)).zfill(2), str(int(minutes % 60)).zfill(2), str(int(time_s % 60)).zfill(2))
+
+
+def print_gpx_part_info(gpx_part, indentation=' '):
+ """
+ gpx_part may be a track or segment.
+ """
+ length_2d = gpx_part.length_2d()
+ length_3d = gpx_part.length_3d()
+ print('{}Length 2D: {:.3f}km'.format(indentation, length_2d / 1000.))
+ print('{}Length 3D: {:.3f}km'.format(indentation, length_3d / 1000.))
+
+ moving_time, stopped_time, moving_distance, stopped_distance, max_speed = gpx_part.get_moving_data()
+ print('%sMoving time: %s' % (indentation, format_time(moving_time)))
+ print('%sStopped time: %s' % (indentation, format_time(stopped_time)))
+ #print('%sStopped distance: %sm' % stopped_distance)
+ print('{}Max speed: {:.2f}m/s = {:.2f}km/h'.format(indentation, max_speed if max_speed else 0, max_speed * 60. ** 2 / 1000. if max_speed else 0))
+
+ uphill, downhill = gpx_part.get_uphill_downhill()
+ print('{}Total uphill: {:.2f}m'.format(indentation, uphill))
+ print('{}Total downhill: {:.2f}m'.format(indentation, downhill))
+
+ start_time, end_time = gpx_part.get_time_bounds()
+ print('%sStarted: %s' % (indentation, start_time))
+ print('%sEnded: %s' % (indentation, end_time))
+
+ points_no = len(list(gpx_part.walk(only_points=True)))
+ print('%sPoints: %s' % (indentation, points_no))
+
+ distances = []
+ previous_point = None
+ for point in gpx_part.walk(only_points=True):
+ if previous_point:
+ distance = point.distance_2d(previous_point)
+ distances.append(distance)
+ previous_point = point
+ print('{}Avg distance between points: {:.2f}m'.format(indentation, sum(distances) / len(list(gpx_part.walk()))))
+
+ print('')
+
+
+def print_gpx_info(gpx, gpx_file):
+ print('File: %s' % gpx_file)
+
+ if gpx.name:
+ print(' GPX name: %s' % gpx.name)
+ if gpx.description:
+ print(' GPX description: %s' % gpx.description)
+ if gpx.author_name:
+ print(' Author: %s' % gpx.author_name)
+ if gpx.author_email:
+ print(' Email: %s' % gpx.author_email)
+
+ print_gpx_part_info(gpx)
+
+ for track_no, track in enumerate(gpx.tracks):
+ for segment_no, segment in enumerate(track.segments):
+ print(' Track #%s, Segment #%s' % (track_no, segment_no))
+ print_gpx_part_info(segment, indentation=' ')
+
+def run(gpx_files):
+ if not gpx_files:
+ print('No GPX files given')
+ mod_sys.exit(1)
+
+ for gpx_file in gpx_files:
+ try:
+ gpx = mod_gpxpy.parse(open(gpx_file))
+ print_gpx_info(gpx, gpx_file)
+ except Exception as e:
+ mod_logging.exception(e)
+ print('Error processing %s' % gpx_file)
+ mod_sys.exit(1)
+
+if __name__ == '__main__':
+ run(mod_sys.argv[1:])
diff --git a/gpxpy/__init__.py b/gpxpy/__init__.py
new file mode 100644
index 0000000..996f229
--- /dev/null
+++ b/gpxpy/__init__.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2011 Tomo Krajina
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def parse(xml_or_file, parser=None):
+ """
+ Parse xml (string) or file object. This is just an wrapper for
+ GPXParser.parse() function.
+
+ parser may be 'lxml', 'minidom' or None (then it will be automatically
+ detected, lxml if possible).
+
+ xml_or_file must be the xml to parse or a file-object with the XML.
+ """
+
+ from . import gpx as mod_gpx
+ from . import parser as mod_parser
+
+ parser = mod_parser.GPXParser(xml_or_file, parser=parser)
+
+ return parser.parse()
diff --git a/gpxpy/geo.py b/gpxpy/geo.py
new file mode 100644
index 0000000..ac55805
--- /dev/null
+++ b/gpxpy/geo.py
@@ -0,0 +1,384 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2011 Tomo Krajina
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pdb
+
+import logging as mod_logging
+import math as mod_math
+
+from . import utils as mod_utils
+
+# Generic geo related function and class(es)
+
+# One degree in meters:
+ONE_DEGREE = 1000. * 10000.8 / 90.
+
+EARTH_RADIUS = 6371 * 1000
+
+
+def to_rad(x):
+ return x / 180. * mod_math.pi
+
+
+def haversine_distance(latitude_1, longitude_1, latitude_2, longitude_2):
+ """
+ Haversine distance between two points, expressed in meters.
+
+ Implemented from http://www.movable-type.co.uk/scripts/latlong.html
+ """
+ d_lat = to_rad(latitude_1 - latitude_2)
+ d_lon = to_rad(longitude_1 - longitude_2)
+ lat1 = to_rad(latitude_1)
+ lat2 = to_rad(latitude_2)
+
+ a = mod_math.sin(d_lat/2) * mod_math.sin(d_lat/2) + \
+ mod_math.sin(d_lon/2) * mod_math.sin(d_lon/2) * mod_math.cos(lat1) * mod_math.cos(lat2)
+ c = 2 * mod_math.atan2(mod_math.sqrt(a), mod_math.sqrt(1-a))
+ d = EARTH_RADIUS * c
+
+ return d
+
+
+def length(locations=None, _3d=None):
+ locations = locations or []
+ if not locations:
+ return 0
+ length = 0
+ for i in range(len(locations)):
+ if i > 0:
+ previous_location = locations[i - 1]
+ location = locations[i]
+
+ if _3d:
+ d = location.distance_3d(previous_location)
+ else:
+ d = location.distance_2d(previous_location)
+ if d != 0 and not d:
+ pass
+ else:
+ length += d
+ return length
+
+
+def length_2d(locations=None):
+ """ 2-dimensional length (meters) of locations (only latitude and longitude, no elevation). """
+ locations = locations or []
+ return length(locations, False)
+
+
+def length_3d(locations=None):
+ """ 3-dimensional length (meters) of locations (it uses latitude, longitude, and elevation). """
+ locations = locations or []
+ return length(locations, True)
+
+
+def calculate_max_speed(speeds_and_distances):
+ """
+ Compute average distance and standard deviation for distance. Extremes
+ in distances are usually extremes in speeds, so we will ignore them,
+ here.
+
+ speeds_and_distances must be a list containing pairs of (speed, distance)
+ for every point in a track segment.
+ """
+ assert speeds_and_distances
+ if len(speeds_and_distances) > 0:
+ assert len(speeds_and_distances[0]) == 2
+ # ...
+ assert len(speeds_and_distances[-1]) == 2
+
+ size = float(len(speeds_and_distances))
+
+ if size < 20:
+ mod_logging.debug('Segment too small to compute speed, size=%s', size)
+ return None
+
+ distances = list(map(lambda x: x[1], speeds_and_distances))
+ average_distance = sum(distances) / float(size)
+ standard_distance_deviation = mod_math.sqrt(sum(map(lambda distance: (distance-average_distance)**2, distances))/size)
+
+ # Ignore items where the distance is too big:
+ filtered_speeds_and_distances = filter(lambda speed_and_distance: abs(speed_and_distance[1] - average_distance) <= standard_distance_deviation * 1.5, speeds_and_distances)
+
+ # sort by speed:
+ speeds = list(map(lambda speed_and_distance: speed_and_distance[0], filtered_speeds_and_distances))
+ if not isinstance(speeds, list): # python3
+ speeds = list(speeds)
+ if not speeds:
+ return None
+ speeds.sort()
+
+ # Even here there may be some extremes => ignore the last 5%:
+ index = int(len(speeds) * 0.95)
+ if index >= len(speeds):
+ index = -1
+
+ return speeds[index]
+
+
+def calculate_uphill_downhill(elevations):
+ if not elevations:
+ return 0, 0
+
+ size = len(elevations)
+
+ def __filter(n):
+ current_ele = elevations[n]
+ if current_ele is None:
+ return False
+ if 0 < n < size - 1:
+ previous_ele = elevations[n-1]
+ next_ele = elevations[n+1]
+ if previous_ele is not None and current_ele is not None and next_ele is not None:
+ return previous_ele*.3 + current_ele*.4 + next_ele*.3
+ return current_ele
+
+ smoothed_elevations = list(map(__filter, range(size)))
+
+ uphill, downhill = 0., 0.
+
+ for n, elevation in enumerate(smoothed_elevations):
+ if n > 0 and elevation is not None and smoothed_elevations is not None:
+ d = elevation - smoothed_elevations[n-1]
+ if d > 0:
+ uphill += d
+ else:
+ downhill -= d
+
+ return uphill, downhill
+
+
+def distance(latitude_1, longitude_1, elevation_1, latitude_2, longitude_2, elevation_2,
+ haversine=None):
+ """
+ Distance between two points. If elevation is None compute a 2d distance
+
+ if haversine==True -- haversine will be used for every computations,
+ otherwise...
+
+ Haversine distance will be used for distant points where elevation makes a
+ small difference, so it is ignored. That's because haversine is 5-6 times
+ slower than the dummy distance algorithm (which is OK for most GPS tracks).
+ """
+
+ # If points too distant -- compute haversine distance:
+ if haversine or (abs(latitude_1 - latitude_2) > .2 or abs(longitude_1 - longitude_2) > .2):
+ return haversine_distance(latitude_1, longitude_1, latitude_2, longitude_2)
+
+ coef = mod_math.cos(latitude_1 / 180. * mod_math.pi)
+ x = latitude_1 - latitude_2
+ y = (longitude_1 - longitude_2) * coef
+
+ distance_2d = mod_math.sqrt(x * x + y * y) * ONE_DEGREE
+
+ if elevation_1 is None or elevation_2 is None or elevation_1 == elevation_2:
+ return distance_2d
+
+ return mod_math.sqrt(distance_2d ** 2 + (elevation_1 - elevation_2) ** 2)
+
+
+def elevation_angle(location1, location2, radians=False):
+ """ Uphill/downhill angle between two locations. """
+ if location1.elevation is None or location2.elevation is None:
+ return None
+
+ b = float(location2.elevation - location1.elevation)
+ a = location2.distance_2d(location1)
+
+ if a == 0:
+ return 0
+
+ angle = mod_math.atan(b / a)
+
+ if radians:
+ return angle
+
+ return 180 * angle / mod_math.pi
+
+
+def distance_from_line(point, line_point_1, line_point_2):
+ """ Distance of point from a line given with two points. """
+ assert point, point
+ assert line_point_1, line_point_1
+ assert line_point_2, line_point_2
+
+ a = line_point_1.distance_2d(line_point_2)
+
+ if a == 0:
+ return line_point_1.distance_2d(point)
+
+ b = line_point_1.distance_2d(point)
+ c = line_point_2.distance_2d(point)
+
+ s = (a + b + c) / 2.
+
+ return 2. * mod_math.sqrt(abs(s * (s - a) * (s - b) * (s - c))) / a
+
+
+def get_line_equation_coefficients(location1, location2):
+ """
+ Get line equation coefficients for:
+ latitude * a + longitude * b + c = 0
+
+ This is a normal cartesian line (not spherical!)
+ """
+ if location1.longitude == location2.longitude:
+ # Vertical line:
+ return float(0), float(1), float(-location1.longitude)
+ else:
+ a = float(location1.latitude - location2.latitude) / (location1.longitude - location2.longitude)
+ b = location1.latitude - location1.longitude * a
+ return float(1), float(-a), float(-b)
+
+
+def simplify_polyline(points, max_distance):
+ """Does Ramer-Douglas-Peucker algorithm for simplification of polyline """
+
+ if len(points) < 3:
+ return points
+
+ begin, end = points[0], points[-1]
+
+ # Use a "normal" line just to detect the most distant point (not its real distance)
+ # this is because this is faster to compute than calling distance_from_line() for
+ # every point.
+ #
+ # This is an approximation and may have some errors near the poles and if
+ # the points are too distant, but it should be good enough for most use
+ # cases...
+ a, b, c = get_line_equation_coefficients(begin, end)
+
+ tmp_max_distance = -1000000
+ tmp_max_distance_position = None
+ for point_no in range(len(points[1:-1])):
+ point = points[point_no]
+ d = abs(a * point.latitude + b * point.longitude + c)
+ if d > tmp_max_distance:
+ tmp_max_distance = d
+ tmp_max_distance_position = point_no
+
+ # Now that we have the most distance point, compute its real distance:
+ real_max_distance = distance_from_line(points[tmp_max_distance_position], begin, end)
+
+ if real_max_distance < max_distance:
+ return [begin, end]
+
+ return (simplify_polyline(points[:tmp_max_distance_position + 2], max_distance) +
+ simplify_polyline(points[tmp_max_distance_position + 1:], max_distance)[1:])
+
+
+class Location:
+ """ Generic geographical location """
+
+ latitude = None
+ longitude = None
+ elevation = None
+
+ def __init__(self, latitude, longitude, elevation=None):
+ self.latitude = latitude
+ self.longitude = longitude
+ self.elevation = elevation
+
+ def has_elevation(self):
+ return self.elevation or self.elevation == 0
+
+ def remove_elevation(self):
+ self.elevation = None
+
+ def distance_2d(self, location):
+ if not location:
+ return None
+
+ return distance(self.latitude, self.longitude, None, location.latitude, location.longitude, None)
+
+ def distance_3d(self, location):
+ if not location:
+ return None
+
+ return distance(self.latitude, self.longitude, self.elevation, location.latitude, location.longitude, location.elevation)
+
+ def elevation_angle(self, location, radians=False):
+ return elevation_angle(self, location, radians)
+
+ def move(self, location_delta):
+ self.latitude, self.longitude = location_delta.move(self)
+
+ def __add__(self, location_delta):
+ latitude, longitude = location_delta.move(self)
+ return Location(latitude, longitude)
+
+ def __str__(self):
+ return '[loc:%s,%s@%s]' % (self.latitude, self.longitude, self.elevation)
+
+ def __repr__(self):
+ if self.elevation is None:
+ return 'Location(%s, %s)' % (self.latitude, self.longitude)
+ else:
+ return 'Location(%s, %s, %s)' % (self.latitude, self.longitude, self.elevation)
+
+ def __hash__(self):
+ return mod_utils.hash_object(self, ('latitude', 'longitude', 'elevation'))
+
+
+class LocationDelta:
+ """
+ Intended to use similar to timestamp.timedelta, but for Locations.
+ """
+
+ NORTH = 0
+ EAST = 90
+ SOUTH = 180
+ WEST = 270
+
+ def __init__(self, distance=None, angle=None, latitude_diff=None, longitude_diff=None):
+ """
+ Version 1:
+ Distance (in meters).
+ angle_from_north *clockwise*.
+ ...must be given
+ Version 2:
+ latitude_diff and longitude_diff
+ ...must be given
+ """
+ if (distance is not None) and (angle is not None):
+ if (latitude_diff is not None) or (longitude_diff is not None):
+ raise Exception('No lat/lon diff if using distance and angle!')
+ self.distance = distance
+ self.angle_from_north = angle
+ self.move_function = self.move_by_angle_and_distance
+ elif (latitude_diff is not None) and (longitude_diff is not None):
+ if (distance is not None) or (angle is not None):
+ raise Exception('No distance/angle if using lat/lon diff!')
+ this.latitude_diff = latitude_diff
+ this.longitude_diff = longitude_diff
+ self.move_function = self.move_by_lat_lon_diff
+
+ def move(self, location):
+ """
+ Move location by this timedelta.
+ """
+ return self.move_function(location)
+
+ def move_by_angle_and_distance(self, location):
+ coef = mod_math.cos(location.latitude / 180. * mod_math.pi)
+ vertical_distance_diff = mod_math.sin((90 - self.angle_from_north) / 180. * mod_math.pi) / ONE_DEGREE
+ horizontal_distance_diff = mod_math.cos((90 - self.angle_from_north) / 180. * mod_math.pi) / ONE_DEGREE
+ lat_diff = self.distance * vertical_distance_diff
+ lon_diff = self.distance * horizontal_distance_diff / coef
+ return location.latitude + lat_diff, location.longitude + lon_diff
+
+ def move_by_lat_lon_diff(self, location):
+ return location.latitude + self.latitude_diff, location.longitude + self.longitude_diff
diff --git a/gpxpy/gpx.py b/gpxpy/gpx.py
new file mode 100644
index 0000000..a148cc1
--- /dev/null
+++ b/gpxpy/gpx.py
@@ -0,0 +1,2643 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2011 Tomo Krajina
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+GPX related stuff
+"""
+
+import pdb
+
+import logging as mod_logging
+import math as mod_math
+import collections as mod_collections
+import copy as mod_copy
+import datetime as mod_datetime
+
+from . import utils as mod_utils
+from . import geo as mod_geo
+from . import gpxfield as mod_gpxfield
+
+# GPX date format to be used when writing the GPX output:
+DATE_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
+
+# GPX date format(s) used for parsing. The T between date and time and Z after
+# time are allowed, too:
+DATE_FORMATS = [
+ '%Y-%m-%d %H:%M:%S',
+ '%Y-%m-%d %H:%M:%S.%f',
+ #'%Y-%m-%d %H:%M:%S%z',
+ #'%Y-%m-%d %H:%M:%S.%f%z',
+]
+# Used in smoothing, sum must be 1:
+SMOOTHING_RATIO = (0.4, 0.2, 0.4)
+
+# When computing stopped time -- this is the minimum speed between two points,
+# if speed is less than this value -- we'll assume it is zero
+DEFAULT_STOPPED_SPEED_THRESHOLD = 1
+
+# Fields used for all point elements (route point, track point, waypoint):
+GPX_10_POINT_FIELDS = [
+ mod_gpxfield.GPXField('latitude', attribute='lat', type=mod_gpxfield.FLOAT_TYPE, mandatory=True),
+ mod_gpxfield.GPXField('longitude', attribute='lon', type=mod_gpxfield.FLOAT_TYPE, mandatory=True),
+ mod_gpxfield.GPXField('elevation', 'ele', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('time', type=mod_gpxfield.TIME_TYPE),
+ mod_gpxfield.GPXField('magnetic_variation', 'magvar', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('geoid_height', 'geoidheight', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('name'),
+ mod_gpxfield.GPXField('comment', 'cmt'),
+ mod_gpxfield.GPXField('description', 'desc'),
+ mod_gpxfield.GPXField('source', 'src'),
+ mod_gpxfield.GPXField('link', 'url'),
+ mod_gpxfield.GPXField('link_text', 'urlname'),
+ mod_gpxfield.GPXField('symbol', 'sym'),
+ mod_gpxfield.GPXField('type'),
+ mod_gpxfield.GPXField('type_of_gpx_fix', 'fix', possible=('none', '2d', '3d', 'dgps', 'pps',)),
+ mod_gpxfield.GPXField('satellites', 'sat', type=mod_gpxfield.INT_TYPE),
+ mod_gpxfield.GPXField('horizontal_dilution', 'hdop', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('vertical_dilution', 'vdop', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('position_dilution', 'pdop', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('age_of_dgps_data', 'ageofdgpsdata', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('dgps_id', 'dgpsid'),
+]
+GPX_11_POINT_FIELDS = [
+ mod_gpxfield.GPXField('latitude', attribute='lat', type=mod_gpxfield.FLOAT_TYPE, mandatory=True),
+ mod_gpxfield.GPXField('longitude', attribute='lon', type=mod_gpxfield.FLOAT_TYPE, mandatory=True),
+ mod_gpxfield.GPXField('elevation', 'ele', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('time', type=mod_gpxfield.TIME_TYPE),
+ mod_gpxfield.GPXField('magnetic_variation', 'magvar', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('geoid_height', 'geoidheight', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('name'),
+ mod_gpxfield.GPXField('comment', 'cmt'),
+ mod_gpxfield.GPXField('description', 'desc'),
+ mod_gpxfield.GPXField('source', 'src'),
+ 'link',
+ mod_gpxfield.GPXField('link', attribute='href'),
+ mod_gpxfield.GPXField('link_text', tag='text'),
+ mod_gpxfield.GPXField('link_type', tag='type'),
+ '/link',
+ mod_gpxfield.GPXField('symbol', 'sym'),
+ mod_gpxfield.GPXField('type'),
+ mod_gpxfield.GPXField('type_of_gpx_fix', 'fix', possible=('none', '2d', '3d', 'dgps', 'pps',)),
+ mod_gpxfield.GPXField('satellites', 'sat', type=mod_gpxfield.INT_TYPE),
+ mod_gpxfield.GPXField('horizontal_dilution', 'hdop', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('vertical_dilution', 'vdop', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('position_dilution', 'pdop', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('age_of_dgps_data', 'ageofdgpsdata', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('dgps_id', 'dgpsid'),
+ mod_gpxfield.GPXExtensionsField('extensions'),
+]
+
+# GPX1.0 track points have two more fields after time
+# Note that this is not true for GPX1.1
+GPX_TRACK_POINT_FIELDS = GPX_10_POINT_FIELDS[:4] \
+ + [ \
+ mod_gpxfield.GPXField('course', type=mod_gpxfield.FLOAT_TYPE), \
+ mod_gpxfield.GPXField('speed', type=mod_gpxfield.FLOAT_TYPE) \
+ ] \
+ + GPX_10_POINT_FIELDS[4:]
+
+# When possible, the result of various methods are named tuples defined here:
+TimeBounds = mod_collections.namedtuple(
+ 'TimeBounds',
+ ('start_time', 'end_time'))
+MovingData = mod_collections.namedtuple(
+ 'MovingData',
+ ('moving_time', 'stopped_time', 'moving_distance', 'stopped_distance', 'max_speed'))
+UphillDownhill = mod_collections.namedtuple(
+ 'UphillDownhill',
+ ('uphill', 'downhill'))
+MinimumMaximum = mod_collections.namedtuple(
+ 'MinimumMaximum',
+ ('minimum', 'maximum'))
+NearestLocationData = mod_collections.namedtuple(
+ 'NearestLocationData',
+ ('location', 'track_no', 'segment_no', 'point_no'))
+PointData = mod_collections.namedtuple(
+ 'PointData',
+ ('point', 'distance_from_start', 'track_no', 'segment_no', 'point_no'))
+
+
+class GPXException(Exception):
+ """
+ Exception used for invalid GPX files. Is is used when the XML file is
+ valid but something is wrong with the GPX data.
+ """
+ pass
+
+
+class GPXBounds:
+ gpx_10_fields = gpx_11_fields = [
+ mod_gpxfield.GPXField('min_latitude', attribute='minlat', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('max_latitude', attribute='maxlat', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('min_longitude', attribute='minlon', type=mod_gpxfield.FLOAT_TYPE),
+ mod_gpxfield.GPXField('max_longitude', attribute='maxlon', type=mod_gpxfield.FLOAT_TYPE),
+ ]
+
+ __slots__ = ('min_latitude', 'max_latitude', 'min_longitude', 'max_longitude')
+
+ def __init__(self, min_latitude=None, max_latitude=None, min_longitude=None, max_longitude=None):
+ self.min_latitude = min_latitude
+ self.max_latitude = max_latitude
+ self.min_longitude = min_longitude
+ self.max_longitude = max_longitude
+
+ def __iter__(self):
+ return (self.min_latitude, self.max_latitude, self.min_longitude, self.max_longitude,).__iter__()
+
+ def __hash__(self):
+ return mod_utils.hash_object(self, self.__slots__)
+
+
+class GPXXMLSyntaxException(GPXException):
+ """
+ Exception used when the the XML syntax is invalid.
+
+ The __cause__ can be a minidom or lxml exception (See http://www.python.org/dev/peps/pep-3134/).
+ """
+ def __init__(self, message, original_exception):
+ GPXException.__init__(self, message)
+ self.__cause__ = original_exception
+
+
+class GPXWaypoint(mod_geo.Location):
+ gpx_10_fields = GPX_10_POINT_FIELDS
+ gpx_11_fields = GPX_11_POINT_FIELDS
+
+ __slots__ = ('latitude', 'longitude', 'elevation', 'time',
+ 'magnetic_variation', 'geoid_height', 'name', 'comment',
+ 'description', 'source', 'link', 'link_text', 'symbol',
+ 'type', 'type_of_gpx_fix', 'satellites',
+ 'horizontal_dilution', 'vertical_dilution',
+ 'position_dilution', 'age_of_dgps_data', 'dgps_id',
+ 'link_type', 'extensions')
+
+ def __init__(self, latitude=None, longitude=None, elevation=None, time=None,
+ name=None, description=None, symbol=None, type=None,
+ comment=None, horizontal_dilution=None, vertical_dilution=None,
+ position_dilution=None):
+ mod_geo.Location.__init__(self, latitude, longitude, elevation)
+
+ self.latitude = latitude
+ self.longitude = longitude
+ self.elevation = elevation
+ self.time = time
+ self.magnetic_variation = None
+ self.geoid_height = None
+ self.name = name
+ self.comment = comment
+ self.description = description
+ self.source = None
+ self.link = None
+ self.link_text = None
+ self.link_type = None
+ self.symbol = symbol
+ self.type = type
+ self.type_of_gpx_fix = None
+ self.satellites = None
+ self.horizontal_dilution = horizontal_dilution
+ self.vertical_dilution = vertical_dilution
+ self.position_dilution = position_dilution
+ self.age_of_dgps_data = None
+ self.dgps_id = None
+ self.extensions = None
+
+ def __str__(self):
+ return '[wpt{%s}:%s,%s@%s]' % (self.name, self.latitude, self.longitude, self.elevation)
+
+ def __repr__(self):
+ representation = '%s, %s' % (self.latitude, self.longitude)
+ for attribute in 'elevation', 'time', 'name', 'description', 'symbol', 'type', 'comment', \
+ 'horizontal_dilution', 'vertical_dilution', 'position_dilution':
+ value = getattr(self, attribute)
+ if value is not None:
+ representation += ', %s=%s' % (attribute, repr(value))
+ return 'GPXWaypoint(%s)' % representation
+
+ def get_max_dilution_of_precision(self):
+ """
+ Only care about the max dop for filtering, no need to go into too much detail
+ """
+ return max(self.horizontal_dilution, self.vertical_dilution, self.position_dilution)
+
+ def __hash__(self):
+ return mod_utils.hash_object(self, self.__slots__)
+
+
+class GPXRoutePoint(mod_geo.Location):
+ gpx_10_fields = GPX_10_POINT_FIELDS
+ gpx_11_fields = GPX_11_POINT_FIELDS
+
+ __slots__ = ('latitude', 'longitude', 'elevation', 'time',
+ 'magnetic_variation', 'geoid_height', 'name', 'comment',
+ 'description', 'source', 'link', 'link_text', 'symbol',
+ 'type', 'type_of_gpx_fix', 'satellites',
+ 'horizontal_dilution', 'vertical_dilution',
+ 'position_dilution', 'age_of_dgps_data', 'dgps_id',
+ 'link_type', 'extensions')
+
+ def __init__(self, latitude=None, longitude=None, elevation=None, time=None, name=None,
+ description=None, symbol=None, type=None, comment=None,
+ horizontal_dilution=None, vertical_dilution=None,
+ position_dilution=None):
+
+ mod_geo.Location.__init__(self, latitude, longitude, elevation)
+ self.latitude = latitude
+ self.longitude = longitude
+ self.elevation = elevation
+ self.time = time
+ self.magnetic_variation = None
+ self.geoid_height = None
+ self.name = name
+ self.comment = comment
+ self.description = description
+ self.source = None
+ self.link = None
+ self.link_text = None
+ self.symbol = symbol
+ self.type = type
+ self.type_of_gpx_fix = None
+ self.satellites = None
+ self.horizontal_dilution = horizontal_dilution
+ self.vertical_dilution = vertical_dilution
+ self.position_dilution = position_dilution
+ self.age_of_dgps_data = None
+ self.dgps_id = None
+ self.link_type = None
+ self.extensions = None
+
+ def __str__(self):
+ return '[rtept{%s}:%s,%s@%s]' % (self.name, self.latitude, self.longitude, self.elevation)
+
+ def __repr__(self):
+ representation = '%s, %s' % (self.latitude, self.longitude)
+ for attribute in 'elevation', 'time', 'name', 'description', 'symbol', 'type', 'comment', \
+ 'horizontal_dilution', 'vertical_dilution', 'position_dilution':
+ value = getattr(self, attribute)
+ if value is not None:
+ representation += ', %s=%s' % (attribute, repr(value))
+ return 'GPXRoutePoint(%s)' % representation
+
+ def __hash__(self):
+ return mod_utils.hash_object(self, self.__slots__)
+
+
+class GPXRoute:
+ gpx_10_fields = [
+ mod_gpxfield.GPXField('name'),
+ mod_gpxfield.GPXField('comment', 'cmt'),
+ mod_gpxfield.GPXField('description', 'desc'),
+ mod_gpxfield.GPXField('source', 'src'),
+ mod_gpxfield.GPXField('link', 'url'),
+ mod_gpxfield.GPXField('link_text', 'urlname'),
+ mod_gpxfield.GPXField('number', type=mod_gpxfield.INT_TYPE),
+ mod_gpxfield.GPXComplexField('points', tag='rtept', classs=GPXRoutePoint, is_list=True),
+ ]
+ gpx_11_fields = [
+ mod_gpxfield.GPXField('name'),
+ mod_gpxfield.GPXField('comment', 'cmt'),
+ mod_gpxfield.GPXField('description', 'desc'),
+ mod_gpxfield.GPXField('source', 'src'),
+ 'link',
+ mod_gpxfield.GPXField('link', attribute='href'),
+ mod_gpxfield.GPXField('link_text', tag='text'),
+ mod_gpxfield.GPXField('link_type', tag='type'),
+ '/link',
+ mod_gpxfield.GPXField('number', type=mod_gpxfield.INT_TYPE),
+ mod_gpxfield.GPXField('type'),
+ mod_gpxfield.GPXExtensionsField('extensions'),
+ mod_gpxfield.GPXComplexField('points', tag='rtept', classs=GPXRoutePoint, is_list=True),
+ ]
+
+ __slots__ = ('name', 'comment', 'description', 'source', 'link',
+ 'link_text', 'number', 'points', 'link_type', 'type',
+ 'extensions')
+
+ def __init__(self, name=None, description=None, number=None):
+ self.name = name
+ self.comment = None
+ self.description = description
+ self.source = None
+ self.link = None
+ self.link_text = None
+ self.number = number
+ self.points = []
+ self.link_type = None
+ self.type = None
+ self.extensions = None
+
+ def remove_elevation(self):
+ """ Removes elevation data from route """
+ for point in self.points:
+ point.remove_elevation()
+
+ def length(self):
+ """
+ Computes length (2-dimensional) of route.
+
+ Returns:
+ -----------
+ length: float
+ Length returned in meters
+ """
+ return mod_geo.length_2d(self.points)
+
+ def get_center(self):
+ """
+ Get the center of the route.
+
+ Returns
+ -------
+ center: Location
+ latitude: latitude of center in degrees
+ longitude: longitude of center in degrees
+ elevation: not calculated here
+ """
+ if not self.points:
+ return None
+
+ if not self.points:
+ return None
+
+ sum_lat = 0.
+ sum_lon = 0.
+ n = 0.
+
+ for point in self.points:
+ n += 1.
+ sum_lat += point.latitude
+ sum_lon += point.longitude
+
+ if not n:
+ return mod_geo.Location(float(0), float(0))
+
+ return mod_geo.Location(latitude=sum_lat / n, longitude=sum_lon / n)
+
+ def walk(self, only_points=False):
+ """
+ Generator for iterating over route points
+
+ Parameters
+ ----------
+ only_points: boolean
+ Only yield points (no index yielded)
+
+ Yields
+ ------
+ point: GPXRoutePoint
+ A point in the GPXRoute
+ point_no: int
+ Not included in yield if only_points is true
+ """
+ for point_no, point in enumerate(self.points):
+ if only_points:
+ yield point
+ else:
+ yield point, point_no
+
+ def get_points_no(self):
+ """
+ Get the number of points in route.
+
+ Returns
+ ----------
+ num_points : integer
+ Number of points in route
+ """
+ return len(self.points)
... 3098 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/gpxpy.git
More information about the Python-modules-commits
mailing list