Wohin mit der Funktion, welche auf auf Null testet

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 Profis!
Meine Klasse Track() errechnet von einem GPS-aufgezeichneten Weg (GPX-file im XML-Format) die Gesamtstrecke, die dazu benötigte Zeit und plottet die jeweiligen Geschwindigkeiten zwischen den waypoints. Ziel nun: Eine Funktion schreiben, die testet, ob die übergebene GPX-Datei evtl. 0 tracks oder 0 waypoints oder 0 routes hat. Wo soll ich diese Funktion ansiedeln? Der Konstruktor __init__(self, gpxdata) ist für das Erstellen eines Dataframes verantwortlich. Wo soll die Funktion aufgerufen werden?

Code: Alles auswählen

from __future__ import print_function, division

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

class Track():
    def __init__(self, gpxdata):
        """
        constructor, creates dataframe
        """
        data = gpxdata.tracks[0].segments[0].points
        df = []
        for point in data:
            df.append({'lon': point.longitude, 'lat' : point.latitude, 'alt' : point.elevation, 'time' : point.time})
        self.df = pd.DataFrame(df, columns=['lon', 'lat', 'alt', 'time'])
            
    # 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 total_elapsed_time(self):
        """
        calculates the total time elapsed of the track walked
        """
        Npoints = self.df.shape[0]
        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]')    
    
    def print_summary(self, total_distance, total_elapsed_time, plot):
        print('Total distance:', total_distance/1000, 'kilometer')
        print('Total elapsed time.', total_elapsed_time, 'hh:mm:ss')
        print('graph of velocities:')
    
    # private methods
    def __create_distances(self):
        """
        all single distances are calculated, necessary for velocity-calculation
        """
        df = self.df
        # lstDistances = []
        
        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].total_seconds()*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]
                   
        lstTimestamps = [(self.df['time'][i]) for i in range(0, maxTrkPoints)]
        
        return [(lstTimestamps[i+1] - lstTimestamps[i]) for i in range(1, maxTrkPoints-1)]
    
    def __distances_cumul_sum(self):
        """
        creates distance values by using the cumulative sum of all distances
        """
        lstDistances = self.__create_distances()
        
        return np.cumsum(lstDistances)
    
def main():
    filename, file_extension = os.path.splitext("Aljibe_Picacho_2017-03-08_10-32-52.gpx")
    try:
        gpx_file = open("Aljibe_Picacho_2017-03-08_10-32-52.gpx", "r") # gpx_file is opened for reading
    except file_extension != '.gpx':
        raise FileExistsError
    gpxdata = gpxpy.parse(gpx_file)
    mytrack = Track(gpxdata)
    # checkresult = mytrack.check_filetype(gpx_file)
    total_distance = mytrack.total_distance()
    total_elapsed_time = mytrack.total_elapsed_time()
    plot = mytrack.plot_velocities()
    mytrack.print_summary(total_distance, total_elapsed_time, plot)
    
if __name__ == "__main__":
    main()
Grüße
Strawk
Ich programmiere erfolglos, also bin ich nicht.
Benutzeravatar
sparrow
User
Beiträge: 4185
Registriert: Freitag 17. April 2009, 10:28

Ein Track kann ohne die entsprechenden GPX-Daten, die du ihm gibst nicht existieren?
Und wenn 0 Tracks, 0 Waypoints, 0 Routes in den Daten sind, dann kann das nicht funktionieren?
Dann würde ich in Track eine Funktion einfügen, die das prüft und die in __init__ aufrufen. Passen die übergebenen Daten nicht, würde ich eine Exception werfen. Ein entsprechendes Objekt kann mit ungültigen Daten nunmal nicht initialisiert werden.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Dahin wo sie gebraucht wird.

Zum restlichen Code: die Doppelten Unterstriche der Methoden sollten einer sein. Variablen schreibt man klein_mit_unterstrich. Datentypen gehören nicht in den Variablennamen.
Wie man die ganzen Rechnungen ohne for-Schleifen macht, hab ich Dir glaube ich schon ein paar mal anderswo gezeigt -> wenn Du Pandas nicht benutzt, dann benutz es auch nicht, ist nur eine unnötige Abhängigkeit.

Das Exception-Handing funktioniert so nicht, was hast Du Dir dabei gedacht?
Benutzeravatar
Strawk
User
Beiträge: 227
Registriert: Mittwoch 15. Februar 2017, 11:42
Wohnort: Aachen
Kontaktdaten:

Hallo!
Das war erst auch mein Ansatz gewesen. Problem 0: Obwohl die private Funktion __check_for_zero(self) in Track() definiert ist, will der Editor sie im __init__-Bereich nicht kennen.
undefined name __ckeck_for_zero
Problem 1: In welcher Weise sollen die zu überprüfenden Daten übergeben werden? Als Funktionsargument?
Grüße
Strawk
Ich programmiere erfolglos, also bin ich nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13069
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Strawk: Wenn es eine Funktion ist, dann gehört sie entweder nicht in die Klasse, oder aber man sollte sie als @staticmethod klar als ”statische Methode” kennzeichnen, damit sich kein Leser wundert warum da eine Funktion auf der Klasse definiert ist.

Und auch von mir auch noch einmal: mach aus den doppelten führenden Unterstrichen *einen*. Die doppelten sind nicht dazu da ”private” Attribute zu definieren. So etwas gibt es in Python nicht. Man kommt da trotzdem dran und Du missbrauchst diesen Mechanismus für etwas, für das er nicht gedacht ist. In Python gibt es die Konvention Implementierungsdetails mit *einem* führenden Unterstrich zu kennzeichnen.

Ad Problem 0: Es gibt ja auch keinen Namen `__ckeck_for_zero`, weder lokal in der `__init__()` noch auf Modulebene, und schon gar nicht bei den eingebauten Namen. Nur da wird nachgeschaut. Wenn Du eine Methode auf einem Objekt aufrufen möchtest, dann musst Du das explizit tun, also ``objekt.methode(eventuell_argumente)``.

Ich sehe gerade den ``from __future__``-Import: Wenn das auch in Python 2.x funktionieren soll, dann sollte `Track` explizit von `object` erben. Sonst hat man keine „new style“-Klasse und damit funktionieren dann zum Beispiel properties nicht wie erwartet.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten