GPX downHill upHill berechnen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
Strawk
User
Beiträge: 227
Registriert: Mittwoch 15. Februar 2017, 11:42
Wohnort: Aachen
Kontaktdaten:

Hallo Leute!

Eine GPX-Datei enthält pro trackpoint auch die jeweilige Höhe. Ziel, ermitteln: Wie weit wurde jeweils hochgegangen, und wie weit jeweils runter? Folgender Code ist falsch:

Code: Alles auswählen

def up_down_hill(self):
        """
        comment ...
        """
        df = self.df
        maxTrkPoints = df.shape[0]
        upHill = 0
        downHill = 0
        for i in range(1, maxTrkPoints-1):
            if df['alt'][i] > df['alt'][i+1]:
                upHill = upHill + (df['alt'][i+1] - df['alt'][i])
            if df['alt'][i] < df['alt'][i+1]:
                downHill = downHill + (df['alt'][i+1] - df['alt'][i])
        
        return upHill, downHill
Wie muss er richtig lauten?
Strawk
Ich programmiere erfolglos, also bin ich nicht.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Strawk: wenn Du weißt, dass es falsch ist, was ist genau daran falsch?
Du benutzt anscheinend numpy. Da ist das Berechnen von up_hill und down_hill, ein Einzeiler, ohne Schleife:

Code: Alles auswählen

delta = np.diff(self.df)
up_hill = numpy.maximum(delta, 0).sum()
down_hill = numpy.minimum(delta, 0).sum()
Benutzeravatar
Strawk
User
Beiträge: 227
Registriert: Mittwoch 15. Februar 2017, 11:42
Wohnort: Aachen
Kontaktdaten:

Hallo Sirius3, falsch ist daran, dass er falsche Ergebnisse liefert. S.
Ich programmiere erfolglos, also bin ich nicht.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Aber woher weißt Du, dass es falsche Ergebnisse liefert, poste Beispieldaten mit Ergebnis und erwartetem Ergebnis.
Benutzeravatar
Strawk
User
Beiträge: 227
Registriert: Mittwoch 15. Februar 2017, 11:42
Wohnort: Aachen
Kontaktdaten:

Ich weiß das daher, weil ein Freund (Berufsprogrammierer) andere Ergebnise hat. Und weil die Addition nicht der Gesamthöhendifferenz entsprach. Ich habe jetzt die größer, kleiner Zeichen getauscht und die Schleife bei 0 statt bei 1 beginnen lassen ... passt!
Ich programmiere erfolglos, also bin ich nicht.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Ja, die i+1 hab ich übersehen, weil das ja so niemand auch macht. Nimm das, was numpy sowieso schon bietet. Vielleicht fruchtet das ja beim siebten mal, dass ich das schreibe.
Benutzeravatar
Strawk
User
Beiträge: 227
Registriert: Mittwoch 15. Februar 2017, 11:42
Wohnort: Aachen
Kontaktdaten:

Hallo Sirius3!
Keine Sorge, das hat schon beim ersten Mal 'gefruchtet'. Das kollidiert nur etwas mit meinem Ehrgeiz: selber machen, anstatt einfach die numpy-Funktionalität zu übernehmen. Vernünftig ist das nicht; ich weiß.
Strawk
Ich programmiere erfolglos, also bin ich nicht.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Das hat nichts mit selber machen zu tun, weil dann müßtest Du damit anfangen, Bits hin und her zu schupsen. Programmieren bedeutet, die richtigen Mittel einzusetzen, um ein Problem so einfach wie möglich zu lösen.
Benutzeravatar
Strawk
User
Beiträge: 227
Registriert: Mittwoch 15. Februar 2017, 11:42
Wohnort: Aachen
Kontaktdaten:

Hallo Sirius3,
hast ja recht. :)
Apropos: Kannst du mir deinen Code etwas erklären? Du schreibst sowohl np, als auch numpy. Mein Skript importiert so: import numpy as np. Wie muss ich den Code anpassen; er läuft bei mir noch nicht. Ich bekomme: delta = np.diff(self.df), TypeError: unsupported operand type(s) for -: 'Timestamp' and 'float'
Grüße, Strawk

Code komplett hier:

Code: Alles auswählen

# -*- coding: utf-8 -*-
"""
Created on Fri Jan  4 16:43:18 2019

@author: Karl Kraft
"""

from __future__ import print_function, division

import gpxpy
import pandas as pd
from geopy import distance
import matplotlib.pyplot as plt
import numpy as np
import numpy
import time
from glob import glob

class Trajectory():
    def __init__(self, gpxdata):
        """
        constructor, creates dataframe
        """
        self.gpx = gpxdata
        
        # initiation of booleans for properties of different types of gpx-files
        self.has_tracks = False
        self.has_routes = False
        self.has_elevation = False
        self.has_time = False
        
        if gpxdata.routes:
            self.has_routes = True
        
        if gpxdata.tracks:
            self.has_tracks = True
    
        if self.has_tracks == True:
            if gpxdata.tracks[0].segments[0].points[0].elevation:
                self.has_elevation = True
    
            if gpxdata.tracks[0].segments[0].points[0].time:
                self.has_time = True
 
            data = gpxdata.tracks[0].segments[0].points
            df = []
        
            self.df = pd.DataFrame()
            pts = gpxdata.tracks[0].segments[0].points
            self.df['lon'] = [pt.longitude for pt in pts]
            self.df['lat'] = [pt.latitude for pt in pts]

            # different cases of having altitude- and/or time-information
            if self.has_elevation:
                self.df['alt'] = [pt.elevation for pt in pts]
                
            if self.has_time:
                self.df['time'] = [pt.time for pt in pts]
            
        else:
            data = gpxdata.routes[0].points
            df = []
            for point in data:
                df.append({'lon': point.longitude, 'lat' : point.latitude})
            self.df = pd.DataFrame(df, columns=['lon', 'lat'])
            
    # public methods
    def total_distance(self):
        """
        Calculates the total distance of a list of geographic points
        
        Input: list of geographic points [(lat1,lon1), (lat2, lon2), ..., (lat_n, lon_n)]
        Output: total distance in km, i.e. d(p2,p1) + d(p3,p2) + .... + d(p_n,p_(n-1)), where d(x,y) is 
                the distance of points x and y
        """
        df = self.df
        geopoints = list(zip(df['lat'], df['lon']))
        Npoints = len(geopoints)
        total_distance = 0
    
        for i in range(1,Npoints):
            total_distance += distance.distance(geopoints[i], geopoints[i-1]).m
    
        return total_distance
    
    def start_end_elevation(self):
        """
        calculates the altitude at the beginning and at the end of a whole trip
        """
        Npoints = self.df.shape[0]
        startElev = self.df['alt'][0]
        endElev = self.df['alt'][Npoints-1]
        
        return startElev, endElev
    
    def min_max_elevation(self):
        """
        calculates the minimum and maximum altitude of a whole trip
        """
        minElev = self.df['alt'].min()
        maxElev = self.df['alt'].max()
        
        return minElev, maxElev
    
    def up_down_hill(self):
        """
        calculates the total uphill and downhill movement
        """
        '''
        df = self.df
        maxTrkPoints = df.shape[0]
        upHill = 0
        downHill = 0
        for i in range(0, maxTrkPoints-1):
            if df['alt'][i] < df['alt'][i+1]:
                upHill = upHill + (df['alt'][i+1] - df['alt'][i])
            if df['alt'][i] > df['alt'][i+1]:
                downHill = downHill + (df['alt'][i+1] - df['alt'][i])
        
        return upHill, downHill
        '''
        
        delta = np.diff(self.df)
        up_hill = numpy.maximum(delta, 0).sum()
        down_hill = numpy.minimum(delta, 0).sum()
        
        return up_hill, down_hill
        
    def total_elapsed_time(self):
        """
        calculates the total time elapsed of the track walked
        """
        Npoints = self.df.shape[0]
        # print(Npoints)
        startTime = self.df['time'][0]
        endTime = self.df['time'][Npoints-1]
        total_elapsed_time = endTime - startTime
        return total_elapsed_time
    
    def plot_velocities(self):
        """
        creates a graphical plot of all velocities between each of the two trackpoints
        """
        lstVelocities = self.__create_velocities()
        lstDistancesAddedTogether = self.__distances_cumul_sum()
               
        plt.plot(lstDistancesAddedTogether, lstVelocities)
        plt.xlabel('Distance [km]')
        plt.ylabel('Velocity [km/h]')
        plt.show()
        
    def plot_trip(self, gpxdata):
        """
        creates a graphical plot of the whole trip
        """
        # data = gpxdata.tracks[0].segments[0].points
        df = pd.DataFrame()
        
        if self.has_tracks:
            pts = gpxdata.tracks[0].segments[0].points
        if self.has_routes:
            pts = gpxdata.routes[0].points
        
        df['lon'] = [pt.longitude for pt in pts]
        df['lat'] = [pt.latitude for pt in pts]

        plt.plot(df['lon'], df['lat'])
        plt.show()
                            
    def print_summary(self):
        total_distance = self.total_distance()
        print('Total distance: {:.3f}'.format(total_distance/1000), 'kilometer')
        if self.has_time:
            total_elapsed_time = self.total_elapsed_time()
            print('Total elapsed time:', total_elapsed_time, 'hh:mm:ss')
        else:
            print("time and velocity not available")
        if self.has_elevation:
            
            startEndElevation = self.start_end_elevation()
            minMaxElevation = self.min_max_elevation()
            upDownHill = self.up_down_hill()
            
            print('start/end elevation m:', startEndElevation)
            print('min/max elevation m:', minMaxElevation)
            print('up/down hill m:', upDownHill)
            
            
        else:
            print("elevation-infos not available")
    
    # private methods    
    def __create_distances(self):
        """
        all single distances are calculated, necessary for velocity-calculation
        """
        df = self.df
        maxTrkPoints = df.shape[0]

        return [distance.distance((df['lat'][i], df['lon'][i]), (df['lat'][i+1], df['lon'][i+1])).km for i in range(1, maxTrkPoints-1)]
    
    def __create_velocities(self):
        """
        creates a list of all velocities based on seconds and appropriate distances
        """
        lstSeconds = self.__create_seconds()
        lstDistances = self.__create_distances()
                
        Npoints = self.df.shape[0]
        
        return [((lstDistances[i]*1000) / lstSeconds[i]*3.6) for i in range(0, Npoints-2)]
        
    def __create_seconds(self):
        """
        all single seconds are calculated, necessary for velocity-calculation
        """
        maxTrkPoints = self.df.shape[0]
        
        if self.has_time:
            lstSeconds = []
            for i in range(0, maxTrkPoints):
                 t = self.df['time'][i]
                 lstSeconds.append(time.mktime(t.timetuple()))
        else:
            raise Exception("time not available")
               
        lstTimeDifferenceSeconds = []
        
        for i in range(1, maxTrkPoints-1):
            timeDifference = lstSeconds[i+1] - lstSeconds[i]
            lstTimeDifferenceSeconds.append(timeDifference)
            
        return lstTimeDifferenceSeconds
        
    def __distances_cumul_sum(self):
        """
        creates distance values by using the cumulative sum of all distances
        """
        lstDistances = self.__create_distances()
        return np.cumsum(lstDistances)

class TrackCount():
    """    
    help functions, count elements only
    """
    def __init__(self, gpxdata):
        self.gpx = gpxdata
        
    def count_tracks(self):
        return len(self.gpx.tracks)
    
    def count_segments(self):
        nSegments = 0
        for track in self.gpx.tracks:
            for segment in track.segments:
                nSegments+=1
        return nSegments
    
    def count_points(self):
        nTrackpoints = 0
        for track in self.gpx.tracks:
            for segment in track.segments:
                for point in segment.points:
                    nTrackpoints +=1
        return nTrackpoints

class WaypointCount():
    """    
    help functions, count elements only
    """
    def __init__(self, gpxdata):
        self.gpx = gpxdata

    def count_waypoints(self):
        return len(self.gpx.waypoints)
    
class RouteCount():
    """    
    help functions, count elements only
    """
    def __init__(self, gpxdata):
        self.gpx = gpxdata

    def count_routes(self):
        return len(self.gpx.routes)
    
    def count_points(self):
        return sum([len(route.points) for route in self.gpx.routes])

def main():
    for filename in glob("data/*.gpx"):
        with open(filename) as gpx_file:
            gpxdata = gpxpy.parse(gpx_file)
            
            # help class TrackCount and its methods, instantiating an object
            myTrackCount = TrackCount(gpxdata)
            nTracks = myTrackCount.count_tracks()
            nSegments = myTrackCount.count_segments()
            nTrackpoints = myTrackCount.count_points()
            
            # help class WaypointCount and its methods, instantiating an object
            myWaypointCount = WaypointCount(gpxdata)
            nWaypoints = myWaypointCount.count_waypoints()
            
            # help class RouteCount and its methods, instantiating an object
            myRouteCount = RouteCount(gpxdata)
            nRoutes = myRouteCount.count_routes()
            nPoints = myRouteCount.count_points()
        
            if (nTracks == 1 and nSegments == 1 and nTrackpoints >= 1) or (nRoutes >= 1 and nPoints >=1):
                pass
            else:
                raise TypeError("unexpected file")
            print()
            print("results for:", filename)
            mytrack = Trajectory(gpxdata)
            mytrack.print_summary()
            if mytrack.has_time:
                mytrack.plot_velocities()
            mytrack.plot_trip(gpxdata)
            '''
            if mytrack.has_elevation:
                # mytrack.start_end_elevation()
                # mytrack.min_max_elevation()
                # mytrack.up_down_hill()
                pass
            ''' 
if __name__ == "__main__":
    tbegin = time.clock()
    main()
    computation_time = time.clock()-tbegin
    print("Executing the script took %6.3f seconds" % (computation_time))
Ich programmiere erfolglos, also bin ich nicht.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

__init__ ist kein Konstruktor, und sondern in Initialisierer. Man prüft nicht explizit auf == True.
Variablen werden klein_mit_unterstrich geschrieben. Npoints ist überflüssig, da man auch mit -1 von hinten her indizieren kann.
Warum benutzt total_distance nicht create_distances?
Es gibt keine privaten Methoden, die doppelten Unterstriche sind damit auch falsch.
In plot_trip gibt es einen NameError, wenn weder has_tracks noch has_routes True sind.
Warum erzeugst Du in `plot_trip` den Dataframe nochmal?
Die ganzen Hilfsklassen sind irgendwie überflüssig. Entweder funktioniert das Lesen, oder es gibt an der passenden Stelle einen Fehler automatisch.

Ungetestet:

Code: Alles auswählen

from __future__ import print_function, division
from glob import glob
import matplotlib.pyplot as plt
import pandas as pd
from geopy import distance
import gpxpy

class Trajectory():
    def __init__(self, gpxdata):
        """
        initializes dataframe
        """
        self.gpx = gpxdata
        
        # initiation of booleans for properties of different types of gpx-files
        self.has_tracks = bool(gpxdata.tracks)
        self.has_routes = bool(gpxdata.routes)
        self.has_elevation = False
        self.has_time = False
    
        if self.has_tracks:
            self.points = gpxdata.tracks[0].segments[0].points
            self.has_elevation = bool(self.points[0].elevation)
            self.has_time = bool(self.points[0].time)
        else:
            self.points = gpxdata.routes[0].points
            self.has_elevation = False
            self.has_time = False

        self.df = pd.DataFrame()
        self.df['lon'] = [pt.longitude for pt in self.points]
        self.df['lat'] = [pt.latitude for pt in self.points]

        # different cases of having altitude- and/or time-information
        if self.has_elevation:
            self.df['alt'] = [pt.elevation for pt in self.points]
        if self.has_time:
            self.df['time'] = [pt.time for pt in self.points]
            
    # public methods
    @property
    def total_distance(self):
        """
        Calculates the total distance of a list of geographic points
        
        Input: list of geographic points [(lat1,lon1), (lat2, lon2), ..., (lat_n, lon_n)]
        Output: total distance in km, i.e. d(p2,p1) + d(p3,p2) + .... + d(p_n,p_(n-1)), where d(x,y) is 
                the distance of points x and y
        """
        return self.get_distances().sum()
    
    @property
    def start_end_elevation(self):
        """
        calculates the altitude at the beginning and at the end of a whole trip
        """
        alt = self.df['alt']
        return alt[0], alt[-1]
    
    @property
    def min_max_elevation(self):
        """
        calculates the minimum and maximum altitude of a whole trip
        """
        alt = self.df['alt']
        return alt.min(), alt.max()
    
    @property    
    def up_down_hill(self):
        """
        calculates the total uphill and downhill movement
        """
        delta = self.df['alt'].diff()
        up_hill = diff.clip_lower(0).sum()
        down_hill = diff.clip_upper(0).sum()
        return up_hill, down_hill
        
    @property    
    def total_elapsed_time(self):
        """
        calculates the total time elapsed of the track walked
        """
        time = self.df['time']
        return time[-1] - time[0]
    
    def plot_velocities(self):
        """
        creates a graphical plot of all velocities between each of the two trackpoints
        """
        distances = self.get_distances()
        seconds = self.df['time'].diff()[1:].dt.total_seconds()
        velocities = distance/seconds / 3.6
        plt.plot(distances.cumsum(), velocities)
        plt.xlabel('Distance [km]')
        plt.ylabel('Velocity [km/h]')
        plt.show()
        
    def plot_trip(self):
        """
        creates a graphical plot of the whole trip
        """
        plt.plot(self.df['lon'], self.df['lat'])
        plt.show()
                            
    def print_summary(self):
        print('Total distance: {:.3f}'.format(self.total_distance/1000), 'kilometer')
        if self.has_time:
            print('Total elapsed time:', self.total_elapsed_time, 'hh:mm:ss')
        else:
            print("time and velocity not available")
        if self.has_elevation:
            print('start/end elevation m:', self.start_end_elevation)
            print('min/max elevation m:', self.min_max_elevation)
            print('up/down hill m:', self.up_down_hill)
        else:
            print("elevation-infos not available")
    
    # private methods
    def get_distances(self):
        """
        all single distances are calculated in meters, necessary for velocity-calculation
        """
        rows = df[['lat','lon']].iterrows()
        first = next(rows)
        distances = []
        for second in rows:
            distances.append(
                distance.distance(first, second).m
            )
            first = second
        return pd.Series(distances)
    
    def get_velocities(self):
        """
        creates a list of all velocities in km/h based on seconds and appropriate distances
        """
        distances = self.get_distances()
        seconds = self.df['time'].diff()[1:].dt.total_seconds()
        return distance/seconds / 3.6


def main():
    for filename in glob("data/*.gpx"):
        with open(filename) as gpx_file:
            gpxdata = gpxpy.parse(gpx_file)
            print("results for:", filename)
            trajectory = Trajectory(gpxdata)
            trajectory.print_summary()
            if trajectory.has_time:
                trajectory.plot_velocities()
            trajectory.plot_trip()

if __name__ == "__main__":
    main()
Benutzeravatar
Strawk
User
Beiträge: 227
Registriert: Mittwoch 15. Februar 2017, 11:42
Wohnort: Aachen
Kontaktdaten:

Hallo!

Gerne möchte ich eine GPX-Datei richtig einstufen. Mein Programm soll, wenn mehrere Tracks enthalten sind, nur den ersten verwenden/bearbeiten/analysieren. Wenn mehrere Routen enthalten sind, nur die erste Route. Sind weder Tracks, noch Routen enthalten, soll ein Fehler ausgegeben werden. Wie muss der Code lauten, der die Anzahl dieser genannten Elemente erkennt, bzw. wo werde ich dazu fündig?

@sirius3: Das sind sicher wertvolle Tipps. Was die Konventionen betrifft, hat mein Lehrer seine eigenen Vorstellungen, die sich mit deinen nicht immer decken.

Grüße
Strawk
Ich programmiere erfolglos, also bin ich nicht.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Bei Python gibt es keine eigenen Vorstellungen, weil es feste Regeln gibt. Zeige Deinem Lehrer https://www.python.org/dev/peps/pep-0008/. Da steht alles drin.

Zur Frage: Du mußt gar nichts machen. Wenn mehr als ein Track da ist, werden die anderen sowieso ignoriert. Wenn keiner da ist, dann liefert track[0] einen IndexError, dann ist auch klar, dass kein Track existiert.
Benutzeravatar
Strawk
User
Beiträge: 227
Registriert: Mittwoch 15. Februar 2017, 11:42
Wohnort: Aachen
Kontaktdaten:

Hallo!

Ich bräuchte eine GPX-Datei mit mehreren Tracks und in den Tracks mit mehreren Segmenten. Wo bekomme ich die her? Am besten mehrere Dateien.

Grüße
Strawk :)
Ich programmiere erfolglos, also bin ich nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Strawk: Am einfachsten erzeugst Du Dir die selber. Vorteil ist dann auch, dass Du ganz genau weisst was da drin steht, also zum Beispiel auch die genaue Anzahl an Höhenmetern die darin überwunden wurden.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
Strawk
User
Beiträge: 227
Registriert: Mittwoch 15. Februar 2017, 11:42
Wohnort: Aachen
Kontaktdaten:

Hallo!

Ziel: eine verschachtelte Liste. Obere Ebene: tracks. Untere Ebene: points per segment. Habe ich folgendermaßen implementiert:

Code: Alles auswählen

class TrackCount():
    """    
    help functions, count elements only
    """
    def __init__(self, gpxdata):
        self.gpx = gpxdata
        
    def count_tracks(self):
        return len(self.gpx.tracks)
    
    def count_segments(self):
        return sum([len(track.segments) for track in self.gpx.tracks])
    
    def count_points(self):
        return sum([sum([len(segment.points) for segment in track.segments]) for track in self.gpx.tracks])
    
    def count_points_nested(self):
        """
        returns a nested list with the points per segment
        """
        # print(len(self.count_tracks()[0]))
        # nTracks = self.count_tracks()
        # print(nTracks)
        

        lstTracks = []
        lstPointsNested = []
        
        for track in self.gpx.tracks:
            for segment in track.segments:
                lstPointsNested.append(len(segment.points))
            lstTracks.append(lstPointsNested)
        
        return lstTracks
Problem: Statt vier verschiedener Listen, erhalte ich 4x dieselbe Liste.
Tipps?
Grüße
Strawk
Ich programmiere erfolglos, also bin ich nicht.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

List vor der inneren Schleife anlegen. Oder gleich eine list comprehension benutzen, was du wenige Zeilen vorher ja noch beherrschst.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Strawk: schau Dir mal an, wo und wie oft Du lstPointsNested (schlechter Name) anlegst und wo Du diese Liste zu lstTracks (auch kein guter Name) hinzufügst.
Benutzeravatar
Strawk
User
Beiträge: 227
Registriert: Mittwoch 15. Februar 2017, 11:42
Wohnort: Aachen
Kontaktdaten:

Hi!
@_deets_: DANKE!!

Gruß, Strawk
Ich programmiere erfolglos, also bin ich nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Strawk: Zusätzlich zu den schon gegannten schlechten Namen `count_points_nested()` macht auch nicht das was der Name vermuten lässt. Und warum stecken diese ganzen Funktionen sinnloserweise in einer Klasse?
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten