VORBEREITENDES - First things fist


Mein System ist wie folgt aufgebaut. Ich importiere:

  1. Daten der Wiener Linien zu UBahn-Stationen. Es handelt sich um CSV-Daten die online abgerufen werden. Die wichtigen Daten für mich aus diesem Import sind: Stationsname, ID, Koordinaten
  2. Daten von Wikidata zum UBahn-Systen in Wien. Dies ist eine SPARQL Abfrage. Die wichtigen Daten aus der Abfrage: ID, benachbarte Stationen (um die Linie nachzubauen), Linien die mit der Station verbunden sind.
  3. weiter unten werden dann noch 2 weitere CSV-Dateien von data.gv.at importiert. Die Informationen zu Trinkwasserbrunnen in Wien und die Informationen zu Freibädern in Wien. Bei den Trinkbrunnen interessieren mich nur die Koordinaten und bei den Bädern Name und Koordinaten.

All das soll zusammengeführt werden, sodass sich die Möglichkeit ergibt zu sagen welche Station sich in der Nähe von einem Bad befindet wobei im Umkreis des Bades sich auch ein Trinkwasserbrunnen befinden soll. Das coole an dem Ganzen wird sein, dass man im Endeffekt angeben kann wo man startet und in welchem Bezirk man schwimmen gehen möchte. Ausgehend vom Standort wird das näheste Bad mit Trinkwasserbrunnen gewünschten Bezirk vorgeschlagen.

Weiters wird das Programm dazu in der Lage sein den Benutzer zur ausgegebenen Zielstation zu navigieren. Das Programm wird angeben welche Linie man nehmen muss, wieviele Stationen man fahren muss, wo und in welche Linie man umsteigen muss und auch in welche Richtung man jeweils fahren muss um zu seiner Zielstation zu kommen.

In [ ]:
 

Zuerst importiere ich einige Math Module die notwendig sein werden. Ich definiere eine Funktion die die Differenz (in Metern) zwischen 2 Koordinaten berechnet. Die Funktion erhält 4 String-Koordinaten die innerhalb der Funktion in int umgewandelt werden. Python sei dank, klappt typeconversion ja viel einfacher als zB in Java.

In [97]:
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:100% !important; }</style>"))
In [98]:
from math import radians, cos, sin, asin, sqrt
def haversine(coords):
    lat1 = float(coords[0])
    lon1 = float(coords[1])
    lat2 = float(coords[2])
    lon2 = float(coords[3])
    
        
    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
    # haversine formula 
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a)) 
    # Radius of earth in kilometers is 6378
    m = 6378* c * 1000
    return m

distance = haversine(['16.368679459080123', '48.200461908387354', '16.44325652441458', '48.25022254396772'])
In [99]:
print("Karlsplatz und Kagraner Platz sind " + str(round(distance, 2)) + " Meter voneinander entfernt")
Karlsplatz und Kagraner Platz sind 9856.64 Meter voneinander entfernt

WIe man hier sieht habe ich die Formel für Karlsplatz und Kagraner Platz getestet. Ein vielversprechendes Ergebnis.

ZWEITER TEIL - IMPORTING CSV FILE FROM "Echtzeitdaten Wr. Linien" bzw. data.gv.at


Jetzt wird pandas, csv and sqlite3 importiert. Alle 3 sind notwendig um die CSV Datei und die SPARQL Abfrage (später) durchzuführen. Natürlich wird auch die SQL Extansion geladen sowie die Verbindung zu einer DB namens "stations" hergestellt. Um die Tabellen importieren zu können wird noch die conn-Variable gesetzt.

In [100]:
import pandas as pd
import csv
import sqlite3
import codecs

%load_ext sql

%sql sqlite:///stations.db
conn = sqlite3.connect('stations.db')
The sql extension is already loaded. To reload it, use:
  %reload_ext sql

um sicherzustellen, dass das Notebook richtig arbeitet:

In [101]:
%%sql

PRAGMA foreign_keys = ON;
drop table if exists stations_blank
 * sqlite:///stations.db
Done.
Done.
Out[101]:
[]

Nun wird die CSV Datei von der Online Ressource importiert. (data.gv.at) data.gv.at hat immer wieder Aussetzer. Es kann manchmal vorkommen, dass Daten nicht geladen werden können. Sollte das länger der Fall sein habe ich alle CSV Dateien zur Not gesichert und kann Ihnen schnell das Notebook umschreiben, sodass es mit offline Daten arbeitet.

In [102]:
import requests

r = requests.get('https://data.wien.gv.at/daten/geo?service=WFS&request=GetFeature&version=1.1.0&typeName=ogdwien:UBAHNHALTOGD&srsName=EPSG:4326&outputFormat=csv')
#r = requests.get('D:/uni/SS2020/Data & Knowledge Engineering/Abschlussarbeit/UBAHNHALTOGD.csv')

f = open('stations_blank.csv', 'wb')
f.write(r.content)
f.close()
pd.read_csv('stations_blank.csv').to_sql('stations_blank', conn, index = False)

Arbeitet wie erwartet. Die Datei wurde in die SQLite Table "stations_blank" importiert.

In [103]:
queryStations = %sql select * from stations_blank limit 11
print(queryStations)
 * sqlite:///stations.db
Done.
+--------------------+----------+-----------------------------------------------+-------+-------+----------------------+------+-----------------+------------------+------------------+
|        FID         | OBJECTID |                     SHAPE                     | LINFO | HSTNR |         HTXT         | HBEM | EROEFFNUNG_JAHR | EROEFFNUNG_MONAT | SE_ANNO_CAD_DATA |
+--------------------+----------+-----------------------------------------------+-------+-------+----------------------+------+-----------------+------------------+------------------+
| UBAHNHALTOGD.63772 |  63772   |  POINT (16.324229809458245 48.16071538202971) |   6   |  None |    Am Schöpfwerk     | None |       1995      |        4         |       None       |
| UBAHNHALTOGD.63773 |  63773   |  POINT (16.37913094797084 48.206816355148845) |   3   |  None |      Stubentor       | None |       1991      |        4         |       None       |
| UBAHNHALTOGD.63774 |  63774   | POINT (16.420700222121553 48.169649232274296) |   3   |  None |      Simmering       | None |       2000      |        12        |       None       |
| UBAHNHALTOGD.63775 |  63775   |  POINT (16.32776477821712 48.18364499316398)  |   4   |  None | Meidling Hauptstraße | None |       1980      |        10        |       None       |
| UBAHNHALTOGD.63776 |  63776   |  POINT (16.36401162048291 48.22776723960448)  |   4   |  None |    Friedensbrücke    | None |       1976      |        5         |       None       |
| UBAHNHALTOGD.63777 |  63777   |  POINT (16.36581079439784 48.248451460447065) |   4   |  None |    Heiligenstadt     | None |       1976      |        5         |       None       |
| UBAHNHALTOGD.63778 |  63778   |  POINT (16.334943316802555 48.18480151423323) |   6   |  None |   Längenfeldgasse    | None |       1989      |        10        |       None       |
| UBAHNHALTOGD.63779 |  63779   |  POINT (16.30983753401893 48.13051520409918)  |   6   |  None |     Siebenhirten     | None |       1995      |        4         |       None       |
| UBAHNHALTOGD.63780 |  63780   |  POINT (16.368636832930342 48.23521713110054) |   6   |  None |     Jägerstraße      | None |       1996      |        5         |       None       |
| UBAHNHALTOGD.63781 |  63781   | POINT (16.316531068234443 48.141921600787924) |   6   |  None |    Erlaaer Straße    | None |       1995      |        4         |       None       |
| UBAHNHALTOGD.63782 |  63782   |  POINT (16.40044253339755 48.25620473105275)  |   6   |  None |     Floridsdorf      | None |       1996      |        5         |       None       |
+--------------------+----------+-----------------------------------------------+-------+-------+----------------------+------+-----------------+------------------+------------------+

Ich bin mit dem Ergebnis sehr zufrieden. Im nächsten Codeblock sortiere ich die Stationen nach der Ubahn-Linie. Das ist zwar nicht unbedingt notwendig aber ich finde es so cleaner.

In [104]:
def sortLines(unsortedStations):
    sortedLines = []
    for i in range(1, 7):
        for row in queryStations:
            if row[3] == i:
                sortedLines.append(row)
    return sortedLines
w = sortLines(queryStations)    
print(w)
[('UBAHNHALTOGD.63773', 63773, 'POINT (16.37913094797084 48.206816355148845)', 3, None, 'Stubentor', None, 1991, 4, None), ('UBAHNHALTOGD.63774', 63774, 'POINT (16.420700222121553 48.169649232274296)', 3, None, 'Simmering', None, 2000, 12, None), ('UBAHNHALTOGD.63775', 63775, 'POINT (16.32776477821712 48.18364499316398)', 4, None, 'Meidling Hauptstraße', None, 1980, 10, None), ('UBAHNHALTOGD.63776', 63776, 'POINT (16.36401162048291 48.22776723960448)', 4, None, 'Friedensbrücke', None, 1976, 5, None), ('UBAHNHALTOGD.63777', 63777, 'POINT (16.36581079439784 48.248451460447065)', 4, None, 'Heiligenstadt', None, 1976, 5, None), ('UBAHNHALTOGD.63772', 63772, 'POINT (16.324229809458245 48.16071538202971)', 6, None, 'Am Schöpfwerk', None, 1995, 4, None), ('UBAHNHALTOGD.63778', 63778, 'POINT (16.334943316802555 48.18480151423323)', 6, None, 'Längenfeldgasse', None, 1989, 10, None), ('UBAHNHALTOGD.63779', 63779, 'POINT (16.30983753401893 48.13051520409918)', 6, None, 'Siebenhirten', None, 1995, 4, None), ('UBAHNHALTOGD.63780', 63780, 'POINT (16.368636832930342 48.23521713110054)', 6, None, 'Jägerstraße', None, 1996, 5, None), ('UBAHNHALTOGD.63781', 63781, 'POINT (16.316531068234443 48.141921600787924)', 6, None, 'Erlaaer Straße', None, 1995, 4, None), ('UBAHNHALTOGD.63782', 63782, 'POINT (16.40044253339755 48.25620473105275)', 6, None, 'Floridsdorf', None, 1996, 5, None)]
In [105]:
count = 0;
for row in sortLines(queryStations):
    count += 1
    print(row, count)
    
('UBAHNHALTOGD.63773', 63773, 'POINT (16.37913094797084 48.206816355148845)', 3, None, 'Stubentor', None, 1991, 4, None) 1
('UBAHNHALTOGD.63774', 63774, 'POINT (16.420700222121553 48.169649232274296)', 3, None, 'Simmering', None, 2000, 12, None) 2
('UBAHNHALTOGD.63775', 63775, 'POINT (16.32776477821712 48.18364499316398)', 4, None, 'Meidling Hauptstraße', None, 1980, 10, None) 3
('UBAHNHALTOGD.63776', 63776, 'POINT (16.36401162048291 48.22776723960448)', 4, None, 'Friedensbrücke', None, 1976, 5, None) 4
('UBAHNHALTOGD.63777', 63777, 'POINT (16.36581079439784 48.248451460447065)', 4, None, 'Heiligenstadt', None, 1976, 5, None) 5
('UBAHNHALTOGD.63772', 63772, 'POINT (16.324229809458245 48.16071538202971)', 6, None, 'Am Schöpfwerk', None, 1995, 4, None) 6
('UBAHNHALTOGD.63778', 63778, 'POINT (16.334943316802555 48.18480151423323)', 6, None, 'Längenfeldgasse', None, 1989, 10, None) 7
('UBAHNHALTOGD.63779', 63779, 'POINT (16.30983753401893 48.13051520409918)', 6, None, 'Siebenhirten', None, 1995, 4, None) 8
('UBAHNHALTOGD.63780', 63780, 'POINT (16.368636832930342 48.23521713110054)', 6, None, 'Jägerstraße', None, 1996, 5, None) 9
('UBAHNHALTOGD.63781', 63781, 'POINT (16.316531068234443 48.141921600787924)', 6, None, 'Erlaaer Straße', None, 1995, 4, None) 10
('UBAHNHALTOGD.63782', 63782, 'POINT (16.40044253339755 48.25620473105275)', 6, None, 'Floridsdorf', None, 1996, 5, None) 11

Allright! First part is done. Der importiert hat sehr gut funktioniert. Die Stationen sind sortiert nach UBahn-Linie.

THIRD PART - SPARQL QUERY its time to import the sparql query


In [106]:
from SPARQLWrapper import SPARQLWrapper, JSON, CSV
sparql = SPARQLWrapper("https://query.wikidata.org/sparql")

Anmerkungen zur SPARQL-Abfrage. Die Abfrage ist in erster Linie wichtig um die benachbarten Stationen zu erhalten. Zuerst habe ich mir UBahn-Stationen herausgesucht die sich in Österreich befinden. EIne weitere Einschränkung ist nicht nötig weil ja nur in Wien eine UBahn vorhanden ist. Somit habe ich einmal alle UBahn-Stationen. Weiters werden die benachbarten Stationen einer Station importiert und diese werden gruppiert damit es keine Dubletten gibt. Außerdem werden auch die betreffenden Linien einer Station gruppiert. zB Praterstern = U1 und U2. Ich habe noch ein wenig zu den einzelnen SPARQL Zeilen kommentiert was genau passiert.

In [107]:
sparql.setQuery("""
#Metro Stations
#because there are more than just 1 adjacent station i will need to group the adjacent station as well as the lines
#else i would have 2 rows for every station and/or every line -> redundancies
SELECT ?item ?itemLabel (group_concat(distinct ?stationName;separator=',') as ?stationNames) ?id 
(group_concat(?nextStation) as ?nextStations)
(group_concat(?line) as ?lines)
(group_concat(distinct ?lineName;separator=',') as ?lineNames)
#to avoid duplicates in the nextStations as well as in the lines "array" i use the distinct keyword.
WHERE 
{
  #query all items which are instance of (P31) a metro station (q928830)
  #query only metro stations located in austia. gives back all 400 stations!
  #get the station id
    ?item wdt:P296 ?id .
#filter station id by length AND only if it not contains " " .
#because there are some other id's also in the wiki database like "E3 H2" or "DK"
#for my key i need the 5 digits number
    filter(((strlen(?id)) = 5) && !contains(?id, " ") ) .
#item has to be an instance of "metro station"
    ?item wdt:P31 wd:Q928830 .
#item has to be located in austria
    ?item ?austria wd:Q40 .
#give back the value of the adjacent stations. sometimes 1 most times 2.
    ?nextStation wdt:P197 ?item .
#filter only next stations which are a metro station and not eg. SBAHN Stations
    ?nextStation wdt:P31 wd:Q928830 .
    ?nextStation rdfs:label ?stationName .
    filter(lang(?stationName) = 'de') .
    ?item wdt:P81 ?line .
    ?line rdfs:label ?lineName .
    ?line wdt:P361 wd:Q209400 . 
    filter(lang(?lineName) = 'en') .
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
    

}
group by ?item ?itemLabel ?id
    LIMIT 150
""")
In [108]:
sparql.setReturnFormat(JSON)
stationsWithAdjacents = sparql.query().convert()

Mal sehen wie diese Query funktioniert hat. Alles so wie geplant. Um mit dem Outcome zu arbeiten exportiere ich die Daten in eine neue CSV File.

In [109]:
from IPython.display import HTML, display

tab = '<table>'
for res in stationsWithAdjacents["results"]["bindings"]:
    tab += '<tr><td>%s<td>%s<td>%s<td>%s' % (res["itemLabel"]["value"], 
                                       res["stationNames"]["value"],
                                        res["lineNames"]["value"],
                                       res["id"]["value"])
display(HTML(tab+'</table>'))
KendlerstraßeHütteldorfer Straße,Bahnhof Wien OttakringU363850
SchweglerstraßeWien Westbahnhof,JohnstraßeU363848
ZieglergasseWien Westbahnhof,NeubaugasseU363538
Burggasse-Stadthalle (Vienna U-Bahn)Wien Westbahnhof,ThaliastraßeU663816
Gumpendorfer StraßeWien Westbahnhof,LängenfeldgasseU663819
TaubstummengasseKarlsplatz,Wien HauptbahnhofU163852
KaisermühlenDonauinsel,Alte DonauU163825
Leopoldau (Vienna U-Bahn)GroßfeldsiedlungU163797
Bahnhof Meidling (Vienna U-Bahn)Niederhofstraße,TscherttegasseU663838
HardeggasseStadlau,DonauspitalU263788
SeestadtAspern NordU263783
Landstraße (Vienna U-Bahn)Rochusgasse,Stubentor,Schwedenplatz,StadtparkU3,U463831
Erlaaer StraßeAlterlaa,PerfektastraßeU663781
Praterstern (Vienna U-Bahn)Vorgartenstraße,Messe-Prater,Nestroyplatz,TaborstraßeU1,U263841
NestroyplatzSchwedenplatz,PratersternU163801
VolkstheaterU-Bahn-Station Rathaus,Museumsquartier,Herrengasse,NeubaugasseU2,U363856
SchlachthausgasseKardinal-Nagl-Platz,ErdbergU363847
SiebenhirtenPerfektastraßeU663779
Floridsdorf (Vienna U-Bahn)Neue DonauU663782
Dresdner StraßeJägerstraße,HandelskaiU663820
Spittelau (Vienna U-Bahn)Jägerstraße,U-Bahn-Station Friedensbrücke,Heiligenstadt,Nußdorfer StraßeU6,U463842
Kagraner PlatzRennbahnweg,KagranU163805
TroststraßeReumannplatz,U-Bahn-Station Altes LandgutU163812
StadionDonaumarina,KrieauU263792
DonauspitalAspernstraße,HardeggasseU263787
Josefstädter StraßeThaliastraße,Alser StraßeU663824
MichelbeuernAlser Straße,Währinger StraßeU663833
U-Bahnhof Neulaa (Wien)U-Bahn-Station Oberlaa,U-Bahn-Station AlaudagasseU163813
KarlsplatzStephansplatz,Museumsquartier,Stadtpark,Kettenbrückengasse,TaubstummengasseU1,U2,U463796
SchwedenplatzStephansplatz,Schottenring,Nestroyplatz,LandstraßeU1,U463846
StephansplatzKarlsplatz,Stubentor,Schwedenplatz,HerrengasseU1,U363851
MuseumsquartierKarlsplatz,VolkstheaterU263832
StubentorStephansplatz,LandstraßeU363773
TaborstraßeSchottenring,PratersternU263544
ZippererstraßeGasometer,EnkplatzU363818
KagranAlte Donau,Kagraner PlatzU163806
VorgartenstraßeDonauinsel,PratersternU163857
KeplerplatzWien Hauptbahnhof,ReumannplatzU163828
LängenfeldgasseNiederhofstraße,Meidling Hauptstraße,Gumpendorfer Straße,MargaretengürtelU6,U463778
Am SchöpfwerkTscherttegasse,AlterlaaU663772
HausfeldstraßeAspern Nord,AspernstraßeU263785
SchottenringSchottentor,Schwedenplatz,Roßauer Lände,TaborstraßeU2,U463845
KrieauMesse-Prater,StadionU263793
Währinger Straße-Volksoper (Vienna U-Bahn)Michelbeuern-AKH,Nußdorfer StraßeU663860
SimmeringEnkplatzU363774
BraunschweiggasseHietzing,Unter Sankt VeitU463815
JohnstraßeHütteldorfer Straße,SchweglerstraßeU363823
HerrengasseStephansplatz,VolkstheaterU363821
KettenbrückengasseKarlsplatz,PilgramgasseU463829
MargaretengürtelPilgramgasse,LängenfeldgasseU463546
SchönbrunnMeidling Hauptstraße,HietzingU463547
StadtparkKarlsplatz,LandstraßeU463849
Kardinal-Nagl-PlatzRochusgasse,SchlachthausgasseU363826
RochusgasseKardinal-Nagl-Platz,LandstraßeU363843
Handelskai (Vienna U-Bahn)Neue Donau,Dresdner StraßeU663541
ErdbergGasometer,SchlachthausgasseU363817
GasometerErdberg,ZippererstraßeU363542
Roßauer LändeSchottenring,U-Bahn-Station FriedensbrückeU463844
Unter St. VeitOber St. Veit,BraunschweiggasseU463855
Hütteldorf (Vienna U-Bahn)Ober St. VeitU463545
Aderklaaer StraßeGroßfeldsiedlung,RennbahnwegU163800
DonauinselVorgartenstraße,Kaisermühlen-VICU163803
DonaustadtbrückeStadlau,DonaumarinaU263789
RathausSchottentor,VolkstheaterU263795
Nußdorfer StraßeWähringer Straße,U-Bahn-Station SpittelauU663835
ThaliastraßeBurggasse-Stadthalle,Josefstädter StraßeU663853
Altes LandgutU-Bahn-Station Alaudagasse,U-Bahn-Station TroststraßeU163810
AlaudagasseU-Bahn-Station Altes Landgut,U-Bahn-Station NeulaaU163809
HietzingBraunschweiggasse,SchönbrunnU463822
NeubaugasseVolkstheater,ZieglergasseU363830
EnkplatzZippererstraße,SimmeringU363543
Alser StraßeJosefstädter Straße,Michelbeuern-AKHU663814
Alte DonauKaisermühlen-VIC,KagranU163799
Messe-PraterKrieau,PratersternU263794
SchottentorU-Bahn-Station Rathaus,SchottenringU263539
TscherttegasseAm Schöpfwerk,MeidlingU663854
AlterlaaAm Schöpfwerk,Erlaaer StraßeU663859
NiederhofstraßeLängenfeldgasse,MeidlingU663834
Südtiroler Platz-HauptbahnhofKeplerplatz,TaubstummengasseU163804
Reumannplatz StationKeplerplatz,U-Bahn-Station TroststraßeU163807
GroßfeldsiedlungAderklaaer Straße,U-Bahn-Station LeopoldauU163798
RennbahnwegAderklaaer Straße,Kagraner PlatzU163802
Neue DonauHandelskai,FloridsdorfU663540
Vienna West railway stationBurggasse-Stadthalle,Gumpendorfer Straße,Schweglerstraße,ZieglergasseU6,U363858
Aspern NordHausfeldstraße,SeestadtU263790
AspernstraßeHausfeldstraße,DonauspitalU263786
StadlauDonaustadtbrücke,HardeggasseU263784
DonaumarinaDonaustadtbrücke,StadionU263791
Meidling HauptstraßeLängenfeldgasse,SchönbrunnU463775
OberlaaU-Bahn-Station NeulaaU163811
Ober St. VeitHütteldorf,Unter Sankt VeitU463836
Friedensbrücke (metro station)U-Bahn-Station Spittelau,Roßauer LändeU463776
Heiligenstadt (Vienna U-Bahn)U-Bahn-Station SpittelauU463777
JägerstraßeDresdner Straße,U-Bahn-Station SpittelauU663780
Perfektastraße metro stationErlaaer Straße,SiebenhirtenU663837
Hütteldorfer StraßeJohnstraße,KendlerstraßeU363840
Wien Ottakring railway stationKendlerstraßeU363808
PilgramgasseKettenbrückengasse,MargaretengürtelU463839
In [110]:
#create new table
f = open('stationsWithAdjacents.csv', "w", encoding='utf8')
f.write("station;adjacent;line;key\n")
for res in stationsWithAdjacents["results"]["bindings"]:
    f.write(res["itemLabel"]["value"] + ";" + res["stationNames"]["value"] + ";" + res["lineNames"]["value"] + ";" + res["id"]["value"] + "\n")
f.close()

%sql drop table if exists stationsWithAdjacents;
#csv to sql
pd.read_csv('stationsWithAdjacents.csv', delimiter=";").to_sql('stationsWithAdjacents', conn, index = False)

# new variable with the selection of all items in the new sql table
stationsWithAdjacents = %sql SELECT * from stationsWithAdjacents
 * sqlite:///stations.db
Done.
 * sqlite:///stations.db
Done.

Durch die Gruppierungen habe ich bereits quasi "Arrays" für die Nachbarsstationen und die Linien. Das finde ich sehr cool. Um mit den Daten dann weiterzuarbeiten ist das eine große Hilfe. Die letzte Spalte ist für den Schlüssel. Ich möchte nun die bereits importierte CSV von data.gv.at mit dieser Tabelle vereinen. Das gelingt über den Schlüssel. Damit habe ich dann die Koordinaten und die benachbarten Stationen in einer Tabelle. Dort wollte ich hin. Die OBJECTID aus der 1. csv Tabelle war auf wikidata leider nicht vorhanden. Ich habe lange überlegt wie ich die beiden Tabellen ohne exakten Schlüssel verbinden kann. Alle recherchierten Varianten schienen aber eher schlecht als recht zu sein. Hier wäre ich über ein Feedback sehr dankbar wie man solche Dinge zusammenführen kann. Meine Gedanken gingen in Richtung Teilstring des Stationsnamen abgleichen mit dem Stationsnamen aus Wikidata. Ich glaube das hätte nicht sonderlich gut funktioniert.

Ich habe mich schlussendlich dazu entschieden der Allgemeinheit (und mir) einen Gefallen zu tun und habe die OBJECTID auf Wikidata implementiert. Ich hoffe andere Nutzer können von diesen Informationen profitieren. Dazu habe ich die OBJECTID von den Wiener Linien verwendet und auf Wikidata auch die Echtzeitdaten der Wiener Linien als Referenz angegeben.

Es ist zwar nicht ganz sauber aber ich denke diese Vorgehensweise könnte sich auch bei einem echten Projekt als sinnvoll herausstellen zumal die Daten ja überschaubar sind. Wie gesagt wäre ich aber froh hier zu erfahren wie man das besser macht. Ich gehe davon aus, dass ich in meiner zukünftigen Tätigkeit öfters mit solchen Problemen konfrontiert bin.

UPDATE zur OBJECTID: Ich habe mich mit den Wiener Linien dbzgl. zusammen gesprochen. Die OBJECTID ist leider nicht als Referenzschlüssel geeignet da sich dieser ab und an ändern kann. Sollte sich die OBJECTID während dem Benoten meiner Arbeit ändern und somit alles nicht mehr funktionieren geben Sie mir bitte Bescheid. Dann ändere ich die OBJECTID nochmal kurz zum Benoten. Nach der Benotung werde ich die OBJECTID wieder von Wikidata löschen. Ich habe noch ein Telefongespräch nächste Woche mit den Wiener Linien dbzgl. Hier wird mir hoffentlich mitgeteilt was man als Schlüssel verwenden kann. Ich werde das weiter verfolgen und einen entsprechenden Key auf Wikidata einfügen.

In [ ]:
 
In [ ]:
 

FOURTH PART - COMBINING THE 2 TABLES stations_blank and stationsWithAdjacents


In [111]:
%%sql

select HTXT, adjacent, line, SHAPE, OBJECTID from stations_blank JOIN stationsWithAdjacents on OBJECTID = key;
 * sqlite:///stations.db
Done.
Out[111]:
HTXT adjacent line SHAPE OBJECTID
Am Schöpfwerk Tscherttegasse,Alterlaa U6 POINT (16.324229809458245 48.16071538202971) 63772
Stubentor Stephansplatz,Landstraße U3 POINT (16.37913094797084 48.206816355148845) 63773
Simmering Enkplatz U3 POINT (16.420700222121553 48.169649232274296) 63774
Meidling Hauptstraße Längenfeldgasse,Schönbrunn U4 POINT (16.32776477821712 48.18364499316398) 63775
Friedensbrücke U-Bahn-Station Spittelau,Roßauer Lände U4 POINT (16.36401162048291 48.22776723960448) 63776
Heiligenstadt U-Bahn-Station Spittelau U4 POINT (16.36581079439784 48.248451460447065) 63777
Längenfeldgasse Niederhofstraße,Meidling Hauptstraße,Gumpendorfer Straße,Margaretengürtel U6,U4 POINT (16.334943316802555 48.18480151423323) 63778
Siebenhirten Perfektastraße U6 POINT (16.30983753401893 48.13051520409918) 63779
Jägerstraße Dresdner Straße,U-Bahn-Station Spittelau U6 POINT (16.368636832930342 48.23521713110054) 63780
Erlaaer Straße Alterlaa,Perfektastraße U6 POINT (16.316531068234443 48.141921600787924) 63781
Floridsdorf Neue Donau U6 POINT (16.40044253339755 48.25620473105275) 63782
Seestadt Aspern Nord U2 POINT (16.508438129989 48.22625417891032) 63783
Stadlau Donaustadtbrücke,Hardeggasse U2 POINT (16.449662667515202 48.21981735909775) 63784
Hausfeldstraße Aspern Nord,Aspernstraße U2 POINT (16.48577262697568 48.23340976850327) 63785
Aspernstraße Hausfeldstraße,Donauspital U2 POINT (16.474871718755562 48.222523966126055) 63786
Donauspital Aspernstraße,Hardeggasse U2 POINT (16.46661216109877 48.219419110545104) 63787
Hardeggasse Stadlau,Donauspital U2 POINT (16.457749951315215 48.220896078368334) 63788
Donaustadtbrücke Stadlau,Donaumarina U2 POINT (16.44012508990889 48.21176860347998) 63789
Aspern Nord Hausfeldstraße,Seestadt U2 POINT (16.50449379088435 48.23450791505335) 63790
Donaumarina Donaustadtbrücke,Stadion U2 POINT (16.431617486203894 48.206465026547114) 63791
Stadion Donaumarina,Krieau U2 POINT (16.420255255579608 48.21067140288552) 63792
Krieau Messe-Prater,Stadion U2 POINT (16.413752066012034 48.2146862716321) 63793
Messe Prater Krieau,Praterstern U2 POINT (16.404963120853363 48.21779201067202) 63794
Rathaus Schottentor,Volkstheater U2 POINT (16.355395013326156 48.21065895425584) 63795
Karlsplatz Stephansplatz,Museumsquartier,Stadtpark,Kettenbrückengasse,Taubstummengasse U1,U2,U4 POINT (16.370610432010736 48.200521134303294) 63796
Leopoldau Großfeldsiedlung U1 POINT (16.452139552735247 48.27751786569251) 63797
Großfeldsiedlung Aderklaaer Straße,U-Bahn-Station Leopoldau U1 POINT (16.447882377130643 48.27101076563699) 63798
Alte Donau Kaisermühlen-VIC,Kagran U1 POINT (16.424901236767635 48.23833798473269) 63799
Aderklaaer Straße Großfeldsiedlung,Rennbahnweg U1 POINT (16.45162591874024 48.26342048389046) 63800
Nestroyplatz Schwedenplatz,Praterstern U1 POINT (16.385113644869403 48.21509144905808) 63801
Rennbahnweg Aderklaaer Straße,Kagraner Platz U1 POINT (16.449641645714358 48.257602853521924) 63802
Donauinsel Vorgartenstraße,Kaisermühlen-VIC U1 POINT (16.411360087570586 48.22922767362301) 63803
Südtiroler Platz Keplerplatz,Taubstummengasse U1 POINT (16.373613136637328 48.187145905485394) 63804
Kagraner Platz Rennbahnweg,Kagran U1 POINT (16.44325652441458 48.25022254396772) 63805
Kagran Alte Donau,Kagraner Platz U1 POINT (16.432858206625806 48.24295921334905) 63806
Reumannplatz Keplerplatz,U-Bahn-Station Troststraße U1 POINT (16.377905052346666 48.174989952355354) 63807
Ottakring Kendlerstraße U3 POINT (16.311366713192395 48.211059149435854) 63808
Alaudagasse U-Bahn-Station Altes Landgut,U-Bahn-Station Neulaa U1 POINT (16.382365087229942 48.15363209110488) 63809
Altes Landgut U-Bahn-Station Alaudagasse,U-Bahn-Station Troststraße U1 POINT (16.383229270853718 48.16194885771284) 63810
Oberlaa U-Bahn-Station Neulaa U1 POINT (16.40043020156634 48.14219145152564) 63811
Troststraße Reumannplatz,U-Bahn-Station Altes Landgut U1 POINT (16.380315900921804 48.169162765595985) 63812
Neulaa U-Bahn-Station Oberlaa,U-Bahn-Station Alaudagasse U1 POINT (16.386554293302417 48.145589417483) 63813
Alser Straße Josefstädter Straße,Michelbeuern-AKH U6 POINT (16.341815248726135 48.216775002793646) 63814
Braunschweiggasse Hietzing,Unter Sankt Veit U4 POINT (16.29580355349017 48.18941236062045) 63815
Burggasse-Stadthalle Wien Westbahnhof,Thaliastraße U6 POINT (16.33719289344223 48.203365085364936) 63816
Erdberg Gasometer,Schlachthausgasse U3 POINT (16.41414054605958 48.19142511439878) 63817
Zippererstraße Gasometer,Enkplatz U3 POINT (16.41206582947239 48.18043924624322) 63818
Gumpendorfer Straße Wien Westbahnhof,Längenfeldgasse U6 POINT (16.337511420914975 48.190867982179235) 63819
Dresdner Straße Jägerstraße,Handelskai U6 POINT (16.380131011741522 48.23725563216408) 63820
Herrengasse Stephansplatz,Volkstheater U3 POINT (16.365412433048974 48.209344770374535) 63821
Hietzing Braunschweiggasse,Schönbrunn U4 POINT (16.30503108467237 48.18754665190197) 63822
Johnstraße Hütteldorfer Straße,Schweglerstraße U3 POINT (16.32008004833777 48.19764223975556) 63823
Josefstädter Straße Thaliastraße,Alser Straße U6 POINT (16.339161406619063 48.211550222471374) 63824
Kaisermühlen-VIC Donauinsel,Alte Donau U1 POINT (16.41649683213335 48.23286184587308) 63825
Kardinal-Nagl-Platz Rochusgasse,Schlachthausgasse U3 POINT (16.399882982436093 48.19755862191532) 63826
Keplerplatz Wien Hauptbahnhof,Reumannplatz U1 POINT (16.376183634036693 48.179192090746085) 63828
Kettenbrückengasse Karlsplatz,Pilgramgasse U4 POINT (16.357976518583797 48.19661493812079) 63829
Neubaugasse Volkstheater,Zieglergasse U3 POINT (16.352275108835354 48.19917250513438) 63830
Landstraße / Wien Mitte (ÖBB) Rochusgasse,Stubentor,Schwedenplatz,Stadtpark U3,U4 POINT (16.38391581861814 48.206301017717365) 63831
Museumsquartier Karlsplatz,Volkstheater U2 POINT (16.361344763165963 48.20257252137882) 63832
Michelbeuern-AKH Alser Straße,Währinger Straße U6 POINT (16.344276994169583 48.22128112120785) 63833
Niederhofstraße Längenfeldgasse,Meidling U6 POINT (16.331180131750827 48.18077624910096) 63834
Nußdorfer Straße Währinger Straße,U-Bahn-Station Spittelau U6 POINT (16.35243572368213 48.23132346058086) 63835
Ober Sankt Veit Hütteldorf,Unter Sankt Veit U4 POINT (16.276205909609526 48.19223954097488) 63836
Perfektastraße Erlaaer Straße,Siebenhirten U6 POINT (16.313447385677797 48.13688607820423) 63837
Philadelphiabrücke bzw. Meidling (ÖBB) Niederhofstraße,Tscherttegasse U6 POINT (16.33149612870122 48.17420177040132) 63838
Pilgramgasse Kettenbrückengasse,Margaretengürtel U4 POINT (16.354241098311263 48.19203130290391) 63839
Hütteldorfer Straße Johnstraße,Kendlerstraße U3 POINT (16.311393949424332 48.19979659129259) 63840
Praterstern bzw. Wien Nord (ÖBB) Vorgartenstraße,Messe-Prater,Nestroyplatz,Taborstraße U1,U2 POINT (16.391381828582844 48.21887130142808) 63841
Spittelau Jägerstraße,U-Bahn-Station Friedensbrücke,Heiligenstadt,Nußdorfer Straße U6,U4 POINT (16.35843343079004 48.235631277842735) 63842
Rochusgasse Kardinal-Nagl-Platz,Landstraße U3 POINT (16.39225381969448 48.20208526138576) 63843
Roßauer Lände Schottenring,U-Bahn-Station Friedensbrücke U4 POINT (16.367568667360377 48.222256371270355) 63844
Schottenring Schottentor,Schwedenplatz,Roßauer Lände,Taborstraße U2,U4 POINT (16.37124498935575 48.217146092964356) 63845
Schwedenplatz Stephansplatz,Schottenring,Nestroyplatz,Landstraße U1,U4 POINT (16.37836617290789 48.211805714655966) 63846
Schlachthausgasse Kardinal-Nagl-Platz,Erdberg U3 POINT (16.406748730417295 48.19441401745851) 63847
Schweglerstraße Wien Westbahnhof,Johnstraße U3 POINT (16.32851868958135 48.19780081279791) 63848
Stadtpark Karlsplatz,Landstraße U4 POINT (16.37957846760191 48.2028085790183) 63849
Kendlerstraße Hütteldorfer Straße,Bahnhof Wien Ottakring U3 POINT (16.309147428955267 48.204540181864424) 63850
Stephansplatz Karlsplatz,Stubentor,Schwedenplatz,Herrengasse U1,U3 POINT (16.3715306120903 48.20804683419944) 63851
Taubstummengasse Karlsplatz,Wien Hauptbahnhof U1 POINT (16.370341054127362 48.19391607679164) 63852
Thaliastraße Burggasse-Stadthalle,Josefstädter Straße U6 POINT (16.338015523652427 48.2078009691215) 63853
Tscherttegasse Am Schöpfwerk,Meidling U6 POINT (16.32766877491165 48.16485969016559) 63854
Unter Sankt Veit Ober St. Veit,Braunschweiggasse U4 POINT (16.2859483869923 48.19112786327868) 63855
Volkstheater U-Bahn-Station Rathaus,Museumsquartier,Herrengasse,Neubaugasse U2,U3 POINT (16.3582601366227 48.2049156582831) 63856
Vorgartenstraße Donauinsel,Praterstern U1 POINT (16.401076559650786 48.223629267264364) 63857
Westbahnhof Burggasse-Stadthalle,Gumpendorfer Straße,Schweglerstraße,Zieglergasse U6,U3 POINT (16.33960167546456 48.1958382554576) 63858
Alterlaa Am Schöpfwerk,Erlaaer Straße U6 POINT (16.31687592693336 48.15072742526464) 63859
Währinger Straße-Volksoper Michelbeuern-AKH,Nußdorfer Straße U6 POINT (16.349500070214873 48.22558683917439) 63860
Zieglergasse Wien Westbahnhof,Neubaugasse U3 POINT (16.345980217468753 48.197080371136785) 63538
Schottentor U-Bahn-Station Rathaus,Schottenring U2 POINT (16.36183617851763 48.214526571261) 63539
Neue Donau Handelskai,Floridsdorf U6 POINT (16.39483444193669 48.24640576287346) 63540
Handelskai Neue Donau,Dresdner Straße U6 POINT (16.385702894060262 48.24186053583507) 63541
Gasometer Erdberg,Zippererstraße U3 POINT (16.41746712138183 48.18514301538456) 63542
Enkplatz Zippererstraße,Simmering U3 POINT (16.414796672573672 48.17471873608361) 63543
Taborstraße Schottenring,Praterstern U2 POINT (16.381291851267058 48.219130313111236) 63544
Hütteldorf Ober St. Veit U4 POINT (16.260835508353725 48.19695701497334) 63545
Margaretengürtel Pilgramgasse,Längenfeldgasse U4 POINT (16.34295439767767 48.18848396099265) 63546
Schönbrunn Meidling Hauptstraße,Hietzing U4 POINT (16.318955607133436 48.18604725454002) 63547

Nachdem der Code ja eindeutig ist, ist die Verbindung der beiden Tabellen kein Problem mehr. Es handelt sich quasi um die finalen Daten. Deshalb hat die Abfrage den Namen finalQuery erhalten. Alles was ich für die weitere Bearbeitung benötige habe ich nun. Ich habe nur die Daten selektiert die ich in weiterer Folge benötigen werde.

In [112]:
finalQuery = %sql select HTXT, adjacent, line, SHAPE, OBJECTID from stations_blank JOIN stationsWithAdjacents on OBJECTID = key
 * sqlite:///stations.db
Done.

Ich prüfe nun ob alle Stationen tatsächlich vorhanden sind. Die originale CSV Datei von den Wr Linien hat 99 Einträge aber es gibt einen redundanten Eintrag in dieser Liste. Die Station Karlsplatz ist 2x vorhanden. Meine Tabelle ist deshalb korrekt mit 98 Einträgen. Karlsplatz hat auch 2 OBJECTID's in der CSV Datei der Wiener Linien. Ich werde versuchen dies den Verantwortlichen der Wiener Linien mitzuteilen weil dies bestimmt nicht beabsichtigt ist.

In [113]:
countFinal = 0
for row in finalQuery:
    countFinal += 1
    print(row[0], row[1], row[2], row[4], countFinal)
Am Schöpfwerk Tscherttegasse,Alterlaa U6 63772 1
Stubentor Stephansplatz,Landstraße U3 63773 2
Simmering Enkplatz U3 63774 3
Meidling Hauptstraße Längenfeldgasse,Schönbrunn U4 63775 4
Friedensbrücke U-Bahn-Station Spittelau,Roßauer Lände U4 63776 5
Heiligenstadt U-Bahn-Station Spittelau U4 63777 6
Längenfeldgasse Niederhofstraße,Meidling Hauptstraße,Gumpendorfer Straße,Margaretengürtel U6,U4 63778 7
Siebenhirten Perfektastraße U6 63779 8
Jägerstraße Dresdner Straße,U-Bahn-Station Spittelau U6 63780 9
Erlaaer Straße Alterlaa,Perfektastraße U6 63781 10
Floridsdorf Neue Donau U6 63782 11
Seestadt Aspern Nord U2 63783 12
Stadlau Donaustadtbrücke,Hardeggasse U2 63784 13
Hausfeldstraße Aspern Nord,Aspernstraße U2 63785 14
Aspernstraße Hausfeldstraße,Donauspital U2 63786 15
Donauspital Aspernstraße,Hardeggasse U2 63787 16
Hardeggasse Stadlau,Donauspital U2 63788 17
Donaustadtbrücke Stadlau,Donaumarina U2 63789 18
Aspern Nord Hausfeldstraße,Seestadt U2 63790 19
Donaumarina Donaustadtbrücke,Stadion U2 63791 20
Stadion Donaumarina,Krieau U2 63792 21
Krieau Messe-Prater,Stadion U2 63793 22
Messe Prater Krieau,Praterstern U2 63794 23
Rathaus Schottentor,Volkstheater U2 63795 24
Karlsplatz Stephansplatz,Museumsquartier,Stadtpark,Kettenbrückengasse,Taubstummengasse U1,U2,U4 63796 25
Leopoldau Großfeldsiedlung U1 63797 26
Großfeldsiedlung Aderklaaer Straße,U-Bahn-Station Leopoldau U1 63798 27
Alte Donau Kaisermühlen-VIC,Kagran U1 63799 28
Aderklaaer Straße Großfeldsiedlung,Rennbahnweg U1 63800 29
Nestroyplatz Schwedenplatz,Praterstern U1 63801 30
Rennbahnweg Aderklaaer Straße,Kagraner Platz U1 63802 31
Donauinsel Vorgartenstraße,Kaisermühlen-VIC U1 63803 32
Südtiroler Platz Keplerplatz,Taubstummengasse U1 63804 33
Kagraner Platz Rennbahnweg,Kagran U1 63805 34
Kagran Alte Donau,Kagraner Platz U1 63806 35
Reumannplatz Keplerplatz,U-Bahn-Station Troststraße U1 63807 36
Ottakring Kendlerstraße U3 63808 37
Alaudagasse U-Bahn-Station Altes Landgut,U-Bahn-Station Neulaa U1 63809 38
Altes Landgut U-Bahn-Station Alaudagasse,U-Bahn-Station Troststraße U1 63810 39
Oberlaa U-Bahn-Station Neulaa U1 63811 40
Troststraße Reumannplatz,U-Bahn-Station Altes Landgut U1 63812 41
Neulaa U-Bahn-Station Oberlaa,U-Bahn-Station Alaudagasse U1 63813 42
Alser Straße Josefstädter Straße,Michelbeuern-AKH U6 63814 43
Braunschweiggasse Hietzing,Unter Sankt Veit U4 63815 44
Burggasse-Stadthalle Wien Westbahnhof,Thaliastraße U6 63816 45
Erdberg Gasometer,Schlachthausgasse U3 63817 46
Zippererstraße Gasometer,Enkplatz U3 63818 47
Gumpendorfer Straße Wien Westbahnhof,Längenfeldgasse U6 63819 48
Dresdner Straße Jägerstraße,Handelskai U6 63820 49
Herrengasse Stephansplatz,Volkstheater U3 63821 50
Hietzing Braunschweiggasse,Schönbrunn U4 63822 51
Johnstraße Hütteldorfer Straße,Schweglerstraße U3 63823 52
Josefstädter Straße Thaliastraße,Alser Straße U6 63824 53
Kaisermühlen-VIC Donauinsel,Alte Donau U1 63825 54
Kardinal-Nagl-Platz Rochusgasse,Schlachthausgasse U3 63826 55
Keplerplatz Wien Hauptbahnhof,Reumannplatz U1 63828 56
Kettenbrückengasse Karlsplatz,Pilgramgasse U4 63829 57
Neubaugasse Volkstheater,Zieglergasse U3 63830 58
Landstraße / Wien Mitte (ÖBB) Rochusgasse,Stubentor,Schwedenplatz,Stadtpark U3,U4 63831 59
Museumsquartier Karlsplatz,Volkstheater U2 63832 60
Michelbeuern-AKH Alser Straße,Währinger Straße U6 63833 61
Niederhofstraße Längenfeldgasse,Meidling U6 63834 62
Nußdorfer Straße Währinger Straße,U-Bahn-Station Spittelau U6 63835 63
Ober Sankt Veit Hütteldorf,Unter Sankt Veit U4 63836 64
Perfektastraße Erlaaer Straße,Siebenhirten U6 63837 65
Philadelphiabrücke bzw. Meidling (ÖBB) Niederhofstraße,Tscherttegasse U6 63838 66
Pilgramgasse Kettenbrückengasse,Margaretengürtel U4 63839 67
Hütteldorfer Straße Johnstraße,Kendlerstraße U3 63840 68
Praterstern bzw. Wien Nord (ÖBB) Vorgartenstraße,Messe-Prater,Nestroyplatz,Taborstraße U1,U2 63841 69
Spittelau Jägerstraße,U-Bahn-Station Friedensbrücke,Heiligenstadt,Nußdorfer Straße U6,U4 63842 70
Rochusgasse Kardinal-Nagl-Platz,Landstraße U3 63843 71
Roßauer Lände Schottenring,U-Bahn-Station Friedensbrücke U4 63844 72
Schottenring Schottentor,Schwedenplatz,Roßauer Lände,Taborstraße U2,U4 63845 73
Schwedenplatz Stephansplatz,Schottenring,Nestroyplatz,Landstraße U1,U4 63846 74
Schlachthausgasse Kardinal-Nagl-Platz,Erdberg U3 63847 75
Schweglerstraße Wien Westbahnhof,Johnstraße U3 63848 76
Stadtpark Karlsplatz,Landstraße U4 63849 77
Kendlerstraße Hütteldorfer Straße,Bahnhof Wien Ottakring U3 63850 78
Stephansplatz Karlsplatz,Stubentor,Schwedenplatz,Herrengasse U1,U3 63851 79
Taubstummengasse Karlsplatz,Wien Hauptbahnhof U1 63852 80
Thaliastraße Burggasse-Stadthalle,Josefstädter Straße U6 63853 81
Tscherttegasse Am Schöpfwerk,Meidling U6 63854 82
Unter Sankt Veit Ober St. Veit,Braunschweiggasse U4 63855 83
Volkstheater U-Bahn-Station Rathaus,Museumsquartier,Herrengasse,Neubaugasse U2,U3 63856 84
Vorgartenstraße Donauinsel,Praterstern U1 63857 85
Westbahnhof Burggasse-Stadthalle,Gumpendorfer Straße,Schweglerstraße,Zieglergasse U6,U3 63858 86
Alterlaa Am Schöpfwerk,Erlaaer Straße U6 63859 87
Währinger Straße-Volksoper Michelbeuern-AKH,Nußdorfer Straße U6 63860 88
Zieglergasse Wien Westbahnhof,Neubaugasse U3 63538 89
Schottentor U-Bahn-Station Rathaus,Schottenring U2 63539 90
Neue Donau Handelskai,Floridsdorf U6 63540 91
Handelskai Neue Donau,Dresdner Straße U6 63541 92
Gasometer Erdberg,Zippererstraße U3 63542 93
Enkplatz Zippererstraße,Simmering U3 63543 94
Taborstraße Schottenring,Praterstern U2 63544 95
Hütteldorf Ober St. Veit U4 63545 96
Margaretengürtel Pilgramgasse,Längenfeldgasse U4 63546 97
Schönbrunn Meidling Hauptstraße,Hietzing U4 63547 98
In [ ]:
 
In [ ]:
 

FIFTH PART - Create the Class STATION


Für diesen Part ist der Plan eine Stationsklasse zu erstellen. Die Klasse soll dann als Fields alle Daten haben die ich importiert habe. Für jede Station wird automatisch ein Objekt erstellt. Das field "name" der jeweiligen Station wird automatisch generiert.

In [114]:
class Station:
    def __init__(self, name, objectId=None, line=None, coords=None, changeLine=None, adjacentStation=None, hasWater=None, hasPool=None):
        self.name = name.lower()
        self.objectId = objectId or 123456
        self.line = line or "UBAHN not defined"
        self.coords = coords or ["12.34567890", "12.34567890"]
        self.changeLine = changeLine or []
        self.adjacentStation = adjacentStation or ["testStation1", "testStation2"]
        self.hasWater = hasWater or False
        self.hasPool = hasPool or False

Hier wird die Klasse konstruiert. Untenstehend wird ein Array erstellt aus allen Namen (Spalte HTXT aus der Abfrage).

In [115]:
classNamesStation = []
for i in range(len(finalQuery)):
    classNamesStation.append(finalQuery[i][0])
print(classNamesStation)
['Am Schöpfwerk', 'Stubentor', 'Simmering', 'Meidling Hauptstraße', 'Friedensbrücke', 'Heiligenstadt', 'Längenfeldgasse', 'Siebenhirten', 'Jägerstraße', 'Erlaaer Straße', 'Floridsdorf', 'Seestadt', 'Stadlau', 'Hausfeldstraße', 'Aspernstraße', 'Donauspital', 'Hardeggasse', 'Donaustadtbrücke', 'Aspern Nord', 'Donaumarina', 'Stadion', 'Krieau', 'Messe Prater', 'Rathaus', 'Karlsplatz', 'Leopoldau', 'Großfeldsiedlung', 'Alte Donau', 'Aderklaaer Straße', 'Nestroyplatz', 'Rennbahnweg', 'Donauinsel', 'Südtiroler Platz', 'Kagraner Platz', 'Kagran', 'Reumannplatz', 'Ottakring', 'Alaudagasse', 'Altes Landgut', 'Oberlaa', 'Troststraße', 'Neulaa', 'Alser Straße', 'Braunschweiggasse', 'Burggasse-Stadthalle', 'Erdberg', 'Zippererstraße', 'Gumpendorfer Straße', 'Dresdner Straße', 'Herrengasse', 'Hietzing', 'Johnstraße', 'Josefstädter Straße', 'Kaisermühlen-VIC', 'Kardinal-Nagl-Platz', 'Keplerplatz', 'Kettenbrückengasse', 'Neubaugasse', 'Landstraße / Wien Mitte (ÖBB)', 'Museumsquartier', 'Michelbeuern-AKH', 'Niederhofstraße', 'Nußdorfer Straße', 'Ober Sankt Veit', 'Perfektastraße', 'Philadelphiabrücke bzw. Meidling (ÖBB)', 'Pilgramgasse', 'Hütteldorfer Straße', 'Praterstern bzw. Wien Nord (ÖBB)', 'Spittelau', 'Rochusgasse', 'Roßauer Lände', 'Schottenring', 'Schwedenplatz', 'Schlachthausgasse', 'Schweglerstraße', 'Stadtpark', 'Kendlerstraße', 'Stephansplatz', 'Taubstummengasse', 'Thaliastraße', 'Tscherttegasse', 'Unter Sankt Veit', 'Volkstheater', 'Vorgartenstraße', 'Westbahnhof', 'Alterlaa', 'Währinger Straße-Volksoper', 'Zieglergasse', 'Schottentor', 'Neue Donau', 'Handelskai', 'Gasometer', 'Enkplatz', 'Taborstraße', 'Hütteldorf', 'Margaretengürtel', 'Schönbrunn']

Weiters wird dann über dieses Array iteriert und mit den Namen alle Stationsobjekte erstellt. Die anderen Felder haben erstmal Standardwerte. Die korrekten Werte werden später eingetragen.

In [116]:
stationsObj = []
for name in classNamesStation:
    stationsObj.append(Station(name))

Untenstehend werden folgende Schritte ausgeführt: Die Linien und die Koordinaten werden in alle Stationen eingefügt. Für die Koordinaten wird ein Substring verwendet der dann auch noch gesplittet wird. Somit erhält meine Instanz der Stationsklasse in den Korrdinaten ein Array mit 2 Strings. Die Daten sind bereits so formatiert, dass sie an meine Formel zur Berechnung der Distanz übergeben werden können. Auch die benachbarten Stationen werden gleich gesplittet und sind somit als String Array enthalten. changeLine = Line. Eigentlich wäre das ja somit nicht notwendig. Evtl. nehme ich das also noch raus. (das ist mir beim Kommentieren aufgefallen)

In [ ]:
 
In [117]:
for i in range(len(finalQuery)):
    stationsObj[i].line = (finalQuery[i][2]).split(",")
    stationsObj[i].coords = ((((finalQuery[i][3])[7:])[:-1]).split(" "))
    stationsObj[i].adjacentStation = (finalQuery[i][1]).split(",")
    stationsObj[i].changeLine = stationsObj[i].line
    stationsObj[i].objectId = int(finalQuery[i][4])

Untenstehend ein Test. WIe Sie sehen wurde die Station Praterstern korrekt implementiert. Ein Test über Google Maps hat gezeigt, dass auch die Koordinaten stimmen.

In [118]:
print(stationsObj[47].name)
print(stationsObj[47].line)
print(stationsObj[68].coords)
print(stationsObj[68].adjacentStation)
print(stationsObj[47].changeLine)
gumpendorfer straße
['U6']
['16.391381828582844', '48.21887130142808']
['Vorgartenstraße', 'Messe-Prater', 'Nestroyplatz', 'Taborstraße']
['U6']
In [119]:
stationsObj[51].adjacentStation
Out[119]:
['Hütteldorfer Straße', 'Schweglerstraße']
In [ ]:
 

SIXTH PART - STATIONSOBJEKTE ALS benachbarte Stationen einfügen


Das Ziel ist es in diesem Abschnitt die String-Stationen im Array adjacentStation mit den Objekten der Stationen selbst zu ersetzen. Jetzt sind leider ein paar händische Korrekturen notwendig. Die benachbarten Stationen beginnen manchmal mit "Wien" oder "U-Bahn-Station" Um das auszugleichen wird das hier entfernt.

TEIL A - vorbereitung und Korrekturen der Daten

Das ist nicht besonders schön und man kann das sicher besser machen. Da mein Hauptaugenmerk aber nicht daruaf liegt belasse ich dies so und gebe mich damit zufrieden.

In [120]:
for i in range(len(stationsObj)):
    adjacent = len(stationsObj[i].adjacentStation)
    for station in range(adjacent):
        stationsObj[i].adjacentStation[station] = stationsObj[i].adjacentStation[station].lower()
        if "u-bahn-station" in stationsObj[i].adjacentStation[station]:
            print(stationsObj[i].adjacentStation[station])
            stationsObj[i].adjacentStation[station] = stationsObj[i].adjacentStation[station][15:]
for i in range(len(stationsObj)):
    adjacent = len(stationsObj[i].adjacentStation)
    for station in range(adjacent):
        if "ottakring" in stationsObj[i].adjacentStation[station] :
            print(stationsObj[i].adjacentStation[station])
            stationsObj[i].adjacentStation[station] = stationsObj[i].adjacentStation[station][13:]
for i in range(len(stationsObj)):
    adjacent = len(stationsObj[i].adjacentStation)
    for station in range(adjacent):
        if "wien " in stationsObj[i].adjacentStation[station]:
            print(stationsObj[i].adjacentStation[station])
            stationsObj[i].adjacentStation[station] = stationsObj[i].adjacentStation[station][5:]
u-bahn-station spittelau
u-bahn-station spittelau
u-bahn-station spittelau
u-bahn-station leopoldau
u-bahn-station troststraße
u-bahn-station altes landgut
u-bahn-station neulaa
u-bahn-station alaudagasse
u-bahn-station troststraße
u-bahn-station neulaa
u-bahn-station altes landgut
u-bahn-station oberlaa
u-bahn-station alaudagasse
u-bahn-station spittelau
u-bahn-station friedensbrücke
u-bahn-station friedensbrücke
u-bahn-station rathaus
u-bahn-station rathaus
bahnhof wien ottakring
wien westbahnhof
wien westbahnhof
wien hauptbahnhof
wien westbahnhof
wien hauptbahnhof
wien westbahnhof

Hier werden noch einige Namensprobleme beseitigt. Erneut, hier gibt es Potential um das besser zu machen. Vor allem automatisiert aber ich wollte nicht zu viel Zeit dafür vergeuden.

In [121]:
stationsObj[68].name = "praterstern"
stationsObj[32].name = "hauptbahnhof"
stationsObj[22].name = "messe-prater"
stationsObj[58].name = "landstraße"
stationsObj[65].name = "meidling"
stationsObj[87].name = "währinger straße"
stationsObj[63].name = "ober st. veit"
stationsObj[36].name = "ottakring"
In [122]:
stationsObj[36].name
Out[122]:
'ottakring'
In [123]:
for station in stationsObj[88].adjacentStation:
    if type(station) != str:
        print(station.name)
In [ ]:
 

TEIL B - Überführen der Stationsobjekte als benachbarte Stationen, bis hier waren es ja nur Strings

In [124]:
stationsObj[0].name
stationsObj[0].adjacentStation
for testi in range(len(stationsObj)):
    for station in range(len(stationsObj[testi].adjacentStation)):
        for i in range(len(stationsObj)):
            if stationsObj[testi].adjacentStation[station] == stationsObj[i].name:
                stationsObj[testi].adjacentStation[station] = stationsObj[i]

Untenstehend soll geprüft werden ob tatsächlich alles funktioniert hat. Beim unten stehenden Beispiel sieht man schön, dass die Station Ottakring als benachbarte Stationen nun 2 Objekte hat. Ich habe alle Stationen durchgetestet und bin dabei auf einige Namensprobleme gestoßen. Deshalb weiter oben die Korrekturen. Nachdem alle Stationen durchgetetstet sind bin ich sehr froh über das Ergebnis. Ich bin meinem Ziel, dass U-Bahn-System Wiens nachzubauen ein sehr großes Stück näher gekommen. Jetzt kann ich mich daran machen die Linien zu bauen.

In [125]:
print(stationsObj[4].adjacentStation)
for station in stationsObj[4].adjacentStation:
   print(station)
   print(station.name)
[<__main__.Station object at 0x23163F58>, <__main__.Station object at 0x23163F28>]
<__main__.Station object at 0x23163F58>
spittelau
<__main__.Station object at 0x23163F28>
roßauer lände
In [126]:
#automated test if every station has a object as adjacent Sation and not a string
for i in range(len(stationsObj)):
    for station in stationsObj[i].adjacentStation:
        print(type(station), stationsObj[i].name)
        
<class '__main__.Station'> am schöpfwerk
<class '__main__.Station'> am schöpfwerk
<class '__main__.Station'> stubentor
<class '__main__.Station'> stubentor
<class '__main__.Station'> simmering
<class '__main__.Station'> meidling hauptstraße
<class '__main__.Station'> meidling hauptstraße
<class '__main__.Station'> friedensbrücke
<class '__main__.Station'> friedensbrücke
<class '__main__.Station'> heiligenstadt
<class '__main__.Station'> längenfeldgasse
<class '__main__.Station'> längenfeldgasse
<class '__main__.Station'> längenfeldgasse
<class '__main__.Station'> längenfeldgasse
<class '__main__.Station'> siebenhirten
<class '__main__.Station'> jägerstraße
<class '__main__.Station'> jägerstraße
<class '__main__.Station'> erlaaer straße
<class '__main__.Station'> erlaaer straße
<class '__main__.Station'> floridsdorf
<class '__main__.Station'> seestadt
<class '__main__.Station'> stadlau
<class '__main__.Station'> stadlau
<class '__main__.Station'> hausfeldstraße
<class '__main__.Station'> hausfeldstraße
<class '__main__.Station'> aspernstraße
<class '__main__.Station'> aspernstraße
<class '__main__.Station'> donauspital
<class '__main__.Station'> donauspital
<class '__main__.Station'> hardeggasse
<class '__main__.Station'> hardeggasse
<class '__main__.Station'> donaustadtbrücke
<class '__main__.Station'> donaustadtbrücke
<class '__main__.Station'> aspern nord
<class '__main__.Station'> aspern nord
<class '__main__.Station'> donaumarina
<class '__main__.Station'> donaumarina
<class '__main__.Station'> stadion
<class '__main__.Station'> stadion
<class '__main__.Station'> krieau
<class '__main__.Station'> krieau
<class '__main__.Station'> messe-prater
<class '__main__.Station'> messe-prater
<class '__main__.Station'> rathaus
<class '__main__.Station'> rathaus
<class '__main__.Station'> karlsplatz
<class '__main__.Station'> karlsplatz
<class '__main__.Station'> karlsplatz
<class '__main__.Station'> karlsplatz
<class '__main__.Station'> karlsplatz
<class '__main__.Station'> leopoldau
<class '__main__.Station'> großfeldsiedlung
<class '__main__.Station'> großfeldsiedlung
<class '__main__.Station'> alte donau
<class '__main__.Station'> alte donau
<class '__main__.Station'> aderklaaer straße
<class '__main__.Station'> aderklaaer straße
<class '__main__.Station'> nestroyplatz
<class '__main__.Station'> nestroyplatz
<class '__main__.Station'> rennbahnweg
<class '__main__.Station'> rennbahnweg
<class '__main__.Station'> donauinsel
<class '__main__.Station'> donauinsel
<class '__main__.Station'> hauptbahnhof
<class '__main__.Station'> hauptbahnhof
<class '__main__.Station'> kagraner platz
<class '__main__.Station'> kagraner platz
<class '__main__.Station'> kagran
<class '__main__.Station'> kagran
<class '__main__.Station'> reumannplatz
<class '__main__.Station'> reumannplatz
<class '__main__.Station'> ottakring
<class '__main__.Station'> alaudagasse
<class '__main__.Station'> alaudagasse
<class '__main__.Station'> altes landgut
<class '__main__.Station'> altes landgut
<class '__main__.Station'> oberlaa
<class '__main__.Station'> troststraße
<class '__main__.Station'> troststraße
<class '__main__.Station'> neulaa
<class '__main__.Station'> neulaa
<class '__main__.Station'> alser straße
<class '__main__.Station'> alser straße
<class '__main__.Station'> braunschweiggasse
<class '__main__.Station'> braunschweiggasse
<class '__main__.Station'> burggasse-stadthalle
<class '__main__.Station'> burggasse-stadthalle
<class '__main__.Station'> erdberg
<class '__main__.Station'> erdberg
<class '__main__.Station'> zippererstraße
<class '__main__.Station'> zippererstraße
<class '__main__.Station'> gumpendorfer straße
<class '__main__.Station'> gumpendorfer straße
<class '__main__.Station'> dresdner straße
<class '__main__.Station'> dresdner straße
<class '__main__.Station'> herrengasse
<class '__main__.Station'> herrengasse
<class '__main__.Station'> hietzing
<class '__main__.Station'> hietzing
<class '__main__.Station'> johnstraße
<class '__main__.Station'> johnstraße
<class '__main__.Station'> josefstädter straße
<class '__main__.Station'> josefstädter straße
<class '__main__.Station'> kaisermühlen-vic
<class '__main__.Station'> kaisermühlen-vic
<class '__main__.Station'> kardinal-nagl-platz
<class '__main__.Station'> kardinal-nagl-platz
<class '__main__.Station'> keplerplatz
<class '__main__.Station'> keplerplatz
<class '__main__.Station'> kettenbrückengasse
<class '__main__.Station'> kettenbrückengasse
<class '__main__.Station'> neubaugasse
<class '__main__.Station'> neubaugasse
<class '__main__.Station'> landstraße
<class '__main__.Station'> landstraße
<class '__main__.Station'> landstraße
<class '__main__.Station'> landstraße
<class '__main__.Station'> museumsquartier
<class '__main__.Station'> museumsquartier
<class '__main__.Station'> michelbeuern-akh
<class '__main__.Station'> michelbeuern-akh
<class '__main__.Station'> niederhofstraße
<class '__main__.Station'> niederhofstraße
<class '__main__.Station'> nußdorfer straße
<class '__main__.Station'> nußdorfer straße
<class '__main__.Station'> ober st. veit
<class '__main__.Station'> ober st. veit
<class '__main__.Station'> perfektastraße
<class '__main__.Station'> perfektastraße
<class '__main__.Station'> meidling
<class '__main__.Station'> meidling
<class '__main__.Station'> pilgramgasse
<class '__main__.Station'> pilgramgasse
<class '__main__.Station'> hütteldorfer straße
<class '__main__.Station'> hütteldorfer straße
<class '__main__.Station'> praterstern
<class '__main__.Station'> praterstern
<class '__main__.Station'> praterstern
<class '__main__.Station'> praterstern
<class '__main__.Station'> spittelau
<class '__main__.Station'> spittelau
<class '__main__.Station'> spittelau
<class '__main__.Station'> spittelau
<class '__main__.Station'> rochusgasse
<class '__main__.Station'> rochusgasse
<class '__main__.Station'> roßauer lände
<class '__main__.Station'> roßauer lände
<class '__main__.Station'> schottenring
<class '__main__.Station'> schottenring
<class '__main__.Station'> schottenring
<class '__main__.Station'> schottenring
<class '__main__.Station'> schwedenplatz
<class '__main__.Station'> schwedenplatz
<class '__main__.Station'> schwedenplatz
<class '__main__.Station'> schwedenplatz
<class '__main__.Station'> schlachthausgasse
<class '__main__.Station'> schlachthausgasse
<class '__main__.Station'> schweglerstraße
<class '__main__.Station'> schweglerstraße
<class '__main__.Station'> stadtpark
<class '__main__.Station'> stadtpark
<class '__main__.Station'> kendlerstraße
<class '__main__.Station'> kendlerstraße
<class '__main__.Station'> stephansplatz
<class '__main__.Station'> stephansplatz
<class '__main__.Station'> stephansplatz
<class '__main__.Station'> stephansplatz
<class '__main__.Station'> taubstummengasse
<class '__main__.Station'> taubstummengasse
<class '__main__.Station'> thaliastraße
<class '__main__.Station'> thaliastraße
<class '__main__.Station'> tscherttegasse
<class '__main__.Station'> tscherttegasse
<class '__main__.Station'> unter sankt veit
<class '__main__.Station'> unter sankt veit
<class '__main__.Station'> volkstheater
<class '__main__.Station'> volkstheater
<class '__main__.Station'> volkstheater
<class '__main__.Station'> volkstheater
<class '__main__.Station'> vorgartenstraße
<class '__main__.Station'> vorgartenstraße
<class '__main__.Station'> westbahnhof
<class '__main__.Station'> westbahnhof
<class '__main__.Station'> westbahnhof
<class '__main__.Station'> westbahnhof
<class '__main__.Station'> alterlaa
<class '__main__.Station'> alterlaa
<class '__main__.Station'> währinger straße
<class '__main__.Station'> währinger straße
<class '__main__.Station'> zieglergasse
<class '__main__.Station'> zieglergasse
<class '__main__.Station'> schottentor
<class '__main__.Station'> schottentor
<class '__main__.Station'> neue donau
<class '__main__.Station'> neue donau
<class '__main__.Station'> handelskai
<class '__main__.Station'> handelskai
<class '__main__.Station'> gasometer
<class '__main__.Station'> gasometer
<class '__main__.Station'> enkplatz
<class '__main__.Station'> enkplatz
<class '__main__.Station'> taborstraße
<class '__main__.Station'> taborstraße
<class '__main__.Station'> hütteldorf
<class '__main__.Station'> margaretengürtel
<class '__main__.Station'> margaretengürtel
<class '__main__.Station'> schönbrunn
<class '__main__.Station'> schönbrunn

Abschließend, da jetzt alle Stationsobjekte fertig sind noch ein kurzer Test ob die Formel von mir funktionieren wird:

In [127]:
stationsObj[77].adjacentStation
Out[127]:
[<__main__.Station at 0x23163f88>, <__main__.Station at 0x22c91928>]
In [128]:
haversine(stationsObj[34].coords + stationsObj[0].coords)
Out[128]:
14945.678480020293

Great! Das sind die Punkte: Am Schöpfwerk und Kagran. Ich habe in einem Online Berechnungsprogramm für Distanzen mein Ergebnis verglichen. Meine Kalkulation liegt nur um einen Meter neben der Online Berechnung. Ich bin mit dem Ergebnis sehr zufrieden und denke, dass mein Projekt ein Erfolg wird :)

In [ ]:
 
In [ ]:
 

SIXTH PART - DOUBLY LINKED LIST ERSTELLEN


In diesem Part möchte ich die Linien erstellen. Dazu benötige ich allerdings 2 neue Klassen um eine doppelt verkettete Liste zu erstellen. Ich denke für eine UBahn Linie macht das Konzept der doubly linked list viel Sinn! Die erste Klasse beschreibt einen Knoten. Die doubly linked list erhält dann einige Funktionen die dann wichtig sind. Ich habe also eine Endstation (oder Startstation, je nach Sichtweise) die nur eine benachbarte Station hat. Von diesem Startpunkt aus folge ich den benachbarten Stationen bis ich am Ende der Linie ankomme. Das ist dann der Fall wenn auf der selben Linie keine weitere benachbarte Station mehr vorhanden ist.

Anmerkung: Die Klasse DoublyLinkedList wurde im Verlauf der weiteren Bearbeitung um immer mehr Funktionen erweitert.

In [129]:
class Node:
    def __init__(self, station):
        self.station = station
        self.next = None
        self.prev = None
In [130]:
class DoublyLinkedList:
    def __init__(self):
        self.start_node = None

#insert a new node in an empty list
    def insert_in_emptylist(self, data):
        if self.start_node is None:
            new_node = Node(data)
            self.start_node = new_node
        else:
            print("list is not empty")

#insert a new node at the end of the list
    def insert_at_end(self, data):
        if self.start_node is None:
            new_node = Node(data)
            self.start_node = new_node
            return
        n = self.start_node
        
        while n.next is not None:
            n = n.next
       # for adjacent in n.station.adjacentStation:
        #    if adjacent == data.name:
        new_node = Node(data)
        n.next = new_node
        new_node.prev = n
    
    def get_lastStation(self):
        if self.start_node is None:
            print("Nothin in List")
        n = self.start_node
        while n.next is not None:
            n = n.next
        return n
    
    def get_nextLastStation(self, startStation):
        current = self.search_node(startStation)
        while current.next is not None:
            current = current.next
        return current.station
    
    def get_prevLastStation(self, startStation):
        current = self.search_node(startStation)
        while current.prev is not None:
            current = current.prev
        return current.station
    
    def get_prevStation(self, searchedStation):
        if self.start_node is None:
            print("Nothin in List")
        n = self.start_node
        while n.next is not None:
            if n.station == searchedStation:
                return n.station.prev.station.name
            n = n.next
        if n is None:
            print("not in list")
    

    def traverse_list(self):
        if self.start_node is None:
            print("List has no element")
            return
        else:
            n = self.start_node
            while n is not None:
                print(n.station.name , " ")
                n = n.next
    
    def search_list(self, element):
        current = self.start_node
        index = 1
        while current != None:
            if current.station == element:
                return index
            current = current.next
            index += 1
        return - 1
    ######################################################### Start Navigation Algorithm
    def search_node(self, element):
        current = self.start_node
        while current != None:
            if current.station == element:
                return current
            current = current.next
    
    def search_lineNext(self, line, startStation):
        #the var line is the line which i am looking for. i want to find a station where it is possible to change to the needed line. eg im searching for U2 if i want to get to WU Wien from U1.
        #current node = Result of search node. Eg. Praterstern is in the middle of the Line. I want to start my search from Praterstern not from Leopoldau or Oberlaa
        current = self.search_node(startStation)
        index = 0
        travelTo = []
        while current != None:
            for changeline in current.station.line:
                if changeline == line:
                    travelTo.append(current.station)
                    travelTo.append(index)
                    return travelTo
            current = current.next
            index += 1
        return -1
    
    def search_linePrev(self, line, startStation):
        #the var line is the line which i am looking for. i want to find a station where it is possible to change to the needed line. eg im searching for U2 if i want to get to WU Wien from U1.
        #current node = Result of search node. Eg. Praterstern is in the middle of the Line. I want to start my search from Praterstern not from Leopoldau or Oberlaa
        current = self.search_node(startStation)
        index = 0
        travelTo = []
        while current != None:
            for changeline in current.station.line:
                if changeline == line:
                    travelTo.append(current.station)
                    travelTo.append(index)
                    return travelTo
            current = current.prev
            index += 1
        return -1

    def search_stationNext(self, startStation, targetStation):
        current = self.search_node(startStation)
        index = 0
        travelTo = []
        while current is not None:
            if current.station == targetStation:
                travelTo.append(index)
                return index
            current = current.next
            index += 1
        return -1
    
    def search_stationPrev(self, startStation, targetStation):
        current = self.search_node(startStation)
        index = 0
        travelTo = []
        while current is not None:
            if current.station == targetStation:
                travelTo.append(index)
                return index
            current = current.prev
            index += 1
        return -1
    
    def search_knotNext(self, startStation):
        current = self.search_node(startStation)
        index = 0
        travelTo = []
        while current is not None:
            if len(current.station.line) > 1 and startStation != current.station:
                travelTo.append(current.station)
                travelTo.append(index)
                return travelTo
            current = current.next
            index += 1
        travelTo = [-1]
        return travelTo
    
    def search_knotPrev(self, startStation):
        current = self.search_node(startStation)
        index = 0
        travelTo = []
        while current is not None:
            if len(current.station.line) > 1 and startStation != current.station:
                travelTo.append(current.station)
                travelTo.append(index)
                return travelTo
            current = current.prev
            index += 1
        travelTo = [-1]
        return travelTo
                
In [ ]:
 
In [ ]:
 

SEVENTH PART - FUNKTION UM LINIEN AUTOMATISCH ZU ERSTELLEN


Hier geht es jetzt ziemlich ans Eingemachte. Mein Ziel war es eine Linie als String zu übergeben und für diese Linie dann automatisch die komplette Linie zu erstellen. Zurückgegeben wird eine fix und fertige Linked List für zb die U1. Jede Linie klappte eigentlich gut. Ich hatte aber ein Problem mit der Linie U2. Die Endstation Karlsplatz stellte mich Herausforderungen. Meine While-Schleife geht so lange bis eine Station nur mehr eine Nachbarstation hat. Das klingt zwar vernünftig weil ja jede Endstation normalerweise nur einen Nachbarn hat. Das klappt aber nicht bei Karlsplatz denn der hat 5 Nachbarstationen ist aber dennoch Endstation der U2. Ich habe dann eine Kondition implementiert die mitzählt wie oft die Linie (in diesem Fall die U2) NICHT in den Nachbarstationen vorkommt. Wenn dies für alle Nachbarstationen bis auf eine zutrifft und der Counter somit gleich groß ist wie die Anzahl der (Nachbarstationen)-1 ist klar, dass es sich hier um eine Endstation handelt. Und somit habe ich auch das geschafft. Die Linien können nun automatisiert erstellt werden.

In [131]:
#insert the first Node. I search for a station with only one adjacent station. This has to be an endstation.
#the if loop breaks when found one. i dont need the second endstation. from here on i will walk trough
#the adjacent stations and insert the next adjacent station into the linked list till there are no more
#stations to add

def createLine(line):
    u = DoublyLinkedList()
    for i in range(len(stationsObj)):
        if ((len(stationsObj[i].adjacentStation) < 2) and (line in stationsObj[i].line) and u.start_node == None):
            u.insert_in_emptylist(stationsObj[i])

    if len(u.get_lastStation().station.adjacentStation) < 2 and u.get_lastStation().prev == None:
        u.insert_at_end(u.get_lastStation().station.adjacentStation[0])
    
    lastStation = u.get_lastStation()    
    while len(lastStation.station.adjacentStation) > 1 and lastStation.station.adjacentStation:
        for station in lastStation.station.adjacentStation:
            #the search function gives back -1 if the new station is not already in the linked list.
            #so the next station which follows must fulfill 2 conditions: it is not in the list already and the current line is in the line field of the station.
            #this could only be true for exact one adjacent station. every other station cannot have the same line in its line field.
            if u.search_list(station) < 0 and line in station.line:
                u.insert_at_end(station)
                lastStation = u.get_lastStation()
                #in case a endstation has more than 1 adjacent station. eg karlsplatz. i need another condition.
                #else i would be stuck in an endless loop cause there are always more than 1 adjacentstation in this last station
                counter = 0
                for everyAdjacent in lastStation.station.adjacentStation:
                    if line not in everyAdjacent.line:
                        counter += 1
                if counter == len(lastStation.station.adjacentStation)-1:    
                    return u
    return u
    


    


    

Ich wollte es unbedingt schaffen die Linien automatisch zu erstellen. Dazu habe ich eine kleine Schleife geschrieben die ein Array mit allen Linien erstellt. Danach wird anhand dieses Arrays ein dictionary erstellt mit den Linien als Key und der Funktion die eine Linie erstellt als value. Der Key wird gleichzeitig als Argument übergeben weil dieses für die Erstellung der Linie ja benötigt wird. Die Funktion gibt die fertige Linie zurück und alle Linien sind fertig. Nun habe ich ein navigierbares UBahnsystem.

In [132]:
baseLines = []
for station in stationsObj:
    for line in station.line:
        if line not in baseLines:
            baseLines.append(line)


finalLines = { i : createLine(i) for i in baseLines }
print(finalLines)
{'U6': <__main__.DoublyLinkedList object at 0x231637C0>, 'U3': <__main__.DoublyLinkedList object at 0x23163C40>, 'U4': <__main__.DoublyLinkedList object at 0x006EF148>, 'U2': <__main__.DoublyLinkedList object at 0x006EF100>, 'U1': <__main__.DoublyLinkedList object at 0x006EF8E0>}

Beispielhaft die U4 sowie die U6:

In [133]:
print("UBAHN LINIE U4")
finalLines["U4"].traverse_list()
print(" ")
print("UBAHN LINIE U6")
finalLines["U6"].traverse_list()
UBAHN LINIE U4
heiligenstadt  
spittelau  
friedensbrücke  
roßauer lände  
schottenring  
schwedenplatz  
landstraße  
stadtpark  
karlsplatz  
kettenbrückengasse  
pilgramgasse  
margaretengürtel  
längenfeldgasse  
meidling hauptstraße  
schönbrunn  
hietzing  
braunschweiggasse  
unter sankt veit  
ober st. veit  
hütteldorf  
 
UBAHN LINIE U6
siebenhirten  
perfektastraße  
erlaaer straße  
alterlaa  
am schöpfwerk  
tscherttegasse  
meidling  
niederhofstraße  
längenfeldgasse  
gumpendorfer straße  
westbahnhof  
burggasse-stadthalle  
thaliastraße  
josefstädter straße  
alser straße  
michelbeuern-akh  
währinger straße  
nußdorfer straße  
spittelau  
jägerstraße  
dresdner straße  
handelskai  
neue donau  
floridsdorf  
In [134]:
 
Out[134]:
[<__main__.Station at 0x22c91b38>, 0]
In [ ]:
 

EIGHTH PART - Import Koordinaten der Trinkbrunnen und der öffentlichen Bäder

Die Dateien gibt es wieder online und zwar in mehreren Formaten. Ich importiere erneut im csv Format.

In [135]:
%%sql sqlite:///stations.db
Out[135]:
'Connected: @stations.db'
In [136]:
%sql drop table if exists water
 * sqlite:///stations.db
Done.
Out[136]:
[]
In [137]:
%sql drop table if exists communalPool
 * sqlite:///stations.db
Done.
Out[137]:
[]
In [138]:
conn = sqlite3.connect('stations.db')
In [139]:
r = requests.get('https://data.wien.gv.at/daten/geo?service=WFS&request=GetFeature&version=1.1.0&typeName=ogdwien:TRINKBRUNNENOGD&srsName=EPSG:4326&outputFormat=csv')
f = open('water.csv', 'wb')
f.write(r.content)
f.close()
pd.read_csv('water.csv').to_sql('water', conn, index = False)
In [140]:
%sql SELECT * FROM water limit 10
 * sqlite:///stations.db
Done.
Out[140]:
FID SHAPE NAME TYP_NUM SE_SDO_ROWID SE_ANNO_CAD_DATA
TRINKBRUNNENOGD.1991014 POINT (16.49713368840875 48.21181542949965) Trinkhydrant 10 1991014 None
TRINKBRUNNENOGD.1991015 POINT (16.40279783706037 48.2547039641694) Trinkhydrant 10 1991015 None
TRINKBRUNNENOGD.1991016 POINT (16.32991676262219 48.21191054696919) Trinkhydrant 10 1991016 None
TRINKBRUNNENOGD.1991017 POINT (16.432911486446727 48.202504528916975) Auslaufbrunnen 7 1991017 None
TRINKBRUNNENOGD.1991018 POINT (16.45169185228936 48.28262242457724) Auslaufbrunnen 7 1991018 None
TRINKBRUNNENOGD.1991019 POINT (16.485733142479347 48.20238106739251) Auslaufbrunnen 7 1991019 None
TRINKBRUNNENOGD.1991020 POINT (16.380933200848634 48.30114626194257) Auslaufbrunnen 7 1991020 None
TRINKBRUNNENOGD.1991021 POINT (16.38523341414596 48.29783397485897) Auslaufbrunnen 7 1991021 None
TRINKBRUNNENOGD.1991022 POINT (16.387358186668454 48.30090265500682) Auslaufbrunnen 7 1991022 None
TRINKBRUNNENOGD.1991023 POINT (16.40538091115044 48.30407622294487) Auslaufbrunnen 7 1991023 None
In [141]:
water = %sql SELECT SHAPE, NAME from water
 * sqlite:///stations.db
Done.

Das Funktioniert ausgezeichnet. Schleife unten gibt Die UBahn Stationen aus in deren Umkreis von 250m ein Wasserbrunnen ist. Die Stationsobjekte bekommen ein "True" in ihrem hasWater-field

In [142]:
for i in range(len(stationsObj)):
    for row in range(len(water)):
        if haversine(stationsObj[i].coords + water[row][0][7:][:-1].split(" ")) < 250:
            stationsObj[i].hasWater = True
            print(str(int(haversine(stationsObj[i].coords + water[row][0][7:][:-1].split(" ")))) + " meter" )
            print(stationsObj[i].name)
            print(water[row][1])
            print("")
19 meter
am schöpfwerk
Trinkbrunnen

84 meter
am schöpfwerk
Trinkbrunnen

106 meter
stubentor
Auslaufbrunnen

71 meter
stubentor
Auslaufbrunnen

71 meter
stubentor
Auslaufbrunnen

249 meter
stubentor
Zierbrunnen

161 meter
stubentor
Sprühnebeldusche

241 meter
simmering
Trinkbrunnen mit Tränke

148 meter
meidling hauptstraße
Trinkbrunnen mit Tränke

149 meter
meidling hauptstraße
Trinkbrunnen mit Tränke

44 meter
längenfeldgasse
Trinkbrunnen

231 meter
längenfeldgasse
Trinkbrunnen mit Tränke

239 meter
längenfeldgasse
Spielbrunnen

10 meter
längenfeldgasse
Sprühnebeldusche

64 meter
siebenhirten
Trinkhydrant

242 meter
jägerstraße
Trinkbrunnen mit Tränke

76 meter
jägerstraße
Spielbrunnen

116 meter
jägerstraße
Trinkbrunnen mit Tränke

232 meter
erlaaer straße
Trinkbrunnen mit Tränke

203 meter
floridsdorf
Auslaufbrunnen

143 meter
floridsdorf
Auslaufbrunnen

150 meter
floridsdorf
Trinkbrunnen mit Tränke

242 meter
seestadt
Trinkbrunnen mit Tränke

88 meter
seestadt
Trinkbrunnen mit Tränke

67 meter
aspernstraße
Wasserspielmöglichkeit

147 meter
donaustadtbrücke
Trinkbrunnen

237 meter
donaumarina
Auslaufbrunnen

179 meter
donaumarina
Auslaufbrunnen

176 meter
stadion
Mobiler Trinkbrunnen mit Sprühnebelfunktion

99 meter
krieau
Trinkbrunnen mit Tränke

181 meter
messe-prater
Auslaufbrunnen

181 meter
messe-prater
Auslaufbrunnen

107 meter
messe-prater
Trinkbrunnen

111 meter
messe-prater
Sprühnebeldusche

102 meter
messe-prater
Wasserspielmöglichkeit

177 meter
karlsplatz
Mobiler Trinkbrunnen mit Sprühnebelfunktion

156 meter
karlsplatz
Trinkbrunnen mit Tränke

90 meter
karlsplatz
Sprühnebeldusche

122 meter
großfeldsiedlung
Trinkbrunnen mit Tränke

159 meter
großfeldsiedlung
Trinkbrunnen

212 meter
großfeldsiedlung
Trinkbrunnen

247 meter
großfeldsiedlung
Trinkbrunnen

159 meter
großfeldsiedlung
Sprühnebeldusche

198 meter
großfeldsiedlung
Spielbrunnen

247 meter
nestroyplatz
Trinkbrunnen

198 meter
nestroyplatz
Trinkbrunnen

201 meter
nestroyplatz
Wasserspielmöglichkeit

153 meter
donauinsel
Sommerspritzer

186 meter
donauinsel
Sommerspritzer

248 meter
donauinsel
Trinkbrunnen

76 meter
hauptbahnhof
Mobiler Trinkbrunnen mit Sprühnebelfunktion

205 meter
reumannplatz
Auslaufbrunnen

247 meter
reumannplatz
Trinkbrunnen mit Tränke

110 meter
reumannplatz
Trinkbrunnen mit Tränke

182 meter
reumannplatz
Trinkbrunnen mit Tränke

158 meter
ottakring
Auslaufbrunnen

101 meter
ottakring
Mobiler Trinkbrunnen mit Sprühnebelfunktion

30 meter
alaudagasse
Sommerspritzer

176 meter
alaudagasse
Trinkbrunnen mit Tränke

104 meter
troststraße
Auslaufbrunnen

230 meter
troststraße
Trinkbrunnen mit Tränke

102 meter
neulaa
Trinkbrunnen mit Tränke

226 meter
burggasse-stadthalle
Auslaufbrunnen

196 meter
burggasse-stadthalle
Auslaufbrunnen

197 meter
burggasse-stadthalle
Auslaufbrunnen

175 meter
burggasse-stadthalle
Auslaufbrunnen

232 meter
burggasse-stadthalle
Mobiler Trinkbrunnen mit Sprühnebelfunktion

222 meter
burggasse-stadthalle
Trinkbrunnen

228 meter
burggasse-stadthalle
Trinkbrunnen

203 meter
burggasse-stadthalle
Spielbrunnen

69 meter
erdberg
Mobiler Trinkbrunnen mit Sprühnebelfunktion

192 meter
zippererstraße
Trinkbrunnen mit Tränke

169 meter
zippererstraße
Trinkbrunnen mit Tränke

146 meter
zippererstraße
Trinkbrunnen mit Tränke

231 meter
gumpendorfer straße
Spielbrunnen

34 meter
johnstraße
Sommerspritzer

85 meter
johnstraße
Auslaufbrunnen

137 meter
johnstraße
Trinkbrunnen mit Tränke

168 meter
johnstraße
Trinkbrunnen mit Tränke

29 meter
josefstädter straße
Auslaufbrunnen

220 meter
josefstädter straße
Trinkhydrant

202 meter
kaisermühlen-vic
Trinkbrunnen mit Tränke

198 meter
kaisermühlen-vic
Trinkbrunnen

242 meter
kardinal-nagl-platz
Trinkbrunnen mit Tränke

133 meter
kardinal-nagl-platz
Trinkbrunnen mit Tränke

160 meter
kardinal-nagl-platz
Trinkbrunnen mit Tränke

240 meter
keplerplatz
Auslaufbrunnen

240 meter
keplerplatz
Auslaufbrunnen

237 meter
keplerplatz
Trinkbrunnen

121 meter
keplerplatz
Trinkbrunnen mit Tränke

126 meter
neubaugasse
Trinkhydrant

145 meter
neubaugasse
Trinkbrunnen mit Tränke

172 meter
neubaugasse
Trinkbrunnen mit Tränke

128 meter
neubaugasse
Trinkbrunnen mit Tränke

243 meter
neubaugasse
Trinkhydrant

197 meter
neubaugasse
Trinkhydrant

236 meter
neubaugasse
Trinkhydrant

204 meter
neubaugasse
Trinkbrunnen mit Tränke

173 meter
neubaugasse
Trinkbrunnen mit Tränke

192 meter
neubaugasse
Spielbrunnen

196 meter
museumsquartier
Trinkhydrant

137 meter
museumsquartier
Trinkhydrant

85 meter
museumsquartier
Sommerspritzer

80 meter
museumsquartier
Mobiler Trinkbrunnen mit Sprühnebelfunktion

190 meter
michelbeuern-akh
Trinkbrunnen mit Tränke

241 meter
niederhofstraße
Auslaufbrunnen

157 meter
niederhofstraße
Auslaufbrunnen

188 meter
niederhofstraße
Auslaufbrunnen

216 meter
niederhofstraße
Auslaufbrunnen

165 meter
niederhofstraße
Auslaufbrunnen

188 meter
niederhofstraße
Auslaufbrunnen

216 meter
niederhofstraße
Auslaufbrunnen

165 meter
niederhofstraße
Auslaufbrunnen

226 meter
niederhofstraße
Trinkbrunnen mit Tränke

162 meter
nußdorfer straße
Spielbrunnen

170 meter
nußdorfer straße
Trinkbrunnen

178 meter
meidling
Trinkbrunnen mit Tränke

116 meter
pilgramgasse
Auslaufbrunnen

243 meter
pilgramgasse
Trinkbrunnen

159 meter
hütteldorfer straße
Mobiler Trinkbrunnen mit Sprühnebelfunktion

238 meter
hütteldorfer straße
Trinkbrunnen mit Tränke

245 meter
praterstern
Auslaufbrunnen

225 meter
praterstern
Mobiler Trinkbrunnen mit Sprühnebelfunktion

248 meter
rochusgasse
Auslaufbrunnen

143 meter
rochusgasse
Auslaufbrunnen

248 meter
rochusgasse
Auslaufbrunnen

143 meter
rochusgasse
Auslaufbrunnen

115 meter
rochusgasse
Trinkbrunnen mit Tränke

180 meter
roßauer lände
Trinkbrunnen mit Tränke

125 meter
roßauer lände
Trinkbrunnen mit Tränke

149 meter
roßauer lände
Trinkbrunnen mit Tränke

189 meter
roßauer lände
Trinkbrunnen mit Tränke

177 meter
schottenring
Trinkbrunnen mit Tränke

189 meter
schwedenplatz
Auslaufbrunnen

109 meter
schwedenplatz
Mobiler Trinkbrunnen mit Sprühnebelfunktion

247 meter
schweglerstraße
Trinkbrunnen mit Tränke

72 meter
stadtpark
Sommerspritzer

205 meter
stadtpark
Zierbrunnen

175 meter
stadtpark
Trinkbrunnen

236 meter
stadtpark
Zierbrunnen

147 meter
stadtpark
Trinkbrunnen

247 meter
stadtpark
Sprühnebeldusche

207 meter
stadtpark
Sprühnebeldusche

106 meter
stephansplatz
Auslaufbrunnen

198 meter
stephansplatz
Trinkhydrant

126 meter
stephansplatz
Trinkhydrant

231 meter
stephansplatz
Sommerspritzer

88 meter
stephansplatz
Mobiler Trinkbrunnen mit Sprühnebelfunktion

161 meter
taubstummengasse
Trinkbrunnen mit Tränke

46 meter
thaliastraße
Trinkbrunnen mit Tränke

199 meter
thaliastraße
Trinkhydrant

201 meter
tscherttegasse
Sommerspritzer

85 meter
tscherttegasse
Sommerspritzer

125 meter
tscherttegasse
Trinkbrunnen

20 meter
tscherttegasse
Trinkbrunnen

38 meter
tscherttegasse
Sprühnebeldusche

224 meter
volkstheater
Trinkbrunnen mit Tränke

237 meter
vorgartenstraße
Auslaufbrunnen

237 meter
vorgartenstraße
Auslaufbrunnen

145 meter
westbahnhof
Mobiler Trinkbrunnen mit Sprühnebelfunktion

192 meter
westbahnhof
Auslaufbrunnen

154 meter
westbahnhof
Auslaufbrunnen

41 meter
alterlaa
Mobiler Trinkbrunnen mit Sprühnebelfunktion

225 meter
alterlaa
Trinkbrunnen

126 meter
währinger straße
Auslaufbrunnen

27 meter
zieglergasse
Trinkhydrant

198 meter
zieglergasse
Trinkhydrant

185 meter
zieglergasse
Sommerspritzer

246 meter
zieglergasse
Trinkbrunnen

205 meter
schottentor
Sommerspritzer

248 meter
schottentor
Auslaufbrunnen

248 meter
schottentor
Auslaufbrunnen

190 meter
schottentor
Trinkbrunnen

151 meter
schottentor
Sprühnebeldusche

139 meter
neue donau
Trinkbrunnen

132 meter
neue donau
Trinkbrunnen

101 meter
handelskai
Auslaufbrunnen

204 meter
handelskai
Mobiler Trinkbrunnen mit Sprühnebelfunktion

41 meter
gasometer
Mobiler Trinkbrunnen mit Sprühnebelfunktion

208 meter
gasometer
Trinkbrunnen mit Tränke

187 meter
enkplatz
Sprühnebeldusche

152 meter
hütteldorf
Trinkbrunnen mit Tränke

201 meter
margaretengürtel
Auslaufbrunnen

243 meter
margaretengürtel
Mobiler Trinkbrunnen mit Sprühnebelfunktion

166 meter
margaretengürtel
Trinkbrunnen mit Tränke

90 meter
margaretengürtel
Trinkbrunnen mit Tränke

189 meter
margaretengürtel
Trinkbrunnen mit Tränke

49 meter
margaretengürtel
Trinkbrunnen

143 meter
margaretengürtel
Sprühnebeldusche

In [143]:
r = requests.get('https://data.wien.gv.at/daten/geo?service=WFS&request=GetFeature&version=1.1.0&typeName=ogdwien:SCHWIMMBADOGD&srsName=EPSG:4326&outputFormat=csv')
f = open('communalPool.csv', 'wb')
f.write(r.content)
f.close()
pd.read_csv('communalPool.csv').to_sql('communalPool', conn, index = False)
In [144]:
%sql select SHAPE, NAME, BEZIRK, AUSLASTUNG_AMPEL_KAT_TXT_0, WEBLINK1 from communalPool limit 10
 * sqlite:///stations.db
Done.
Out[144]:
SHAPE NAME BEZIRK AUSLASTUNG_AMPEL_KAT_TXT_0 WEBLINK1
POINT (16.315148630751874 48.179334835672364) Schönbrunner Bad 13 None http://www.schoenbrunnerbad.at/
POINT (16.333372262814034 48.2090876212682) Familienbad Hofferplatz 16 Noch Platz http://www.wien.gv.at/freizeit/baeder/uebersicht/familienbaeder.html
POINT (16.33469197737579 48.23981963914403) Familienbad-Hugo-Wolf-Park 19 Noch Platz http://www.wien.gv.at/freizeit/baeder/uebersicht/familienbaeder.html
POINT (16.389868434937853 48.17579082647462) Familienbad Gudrunstraße 10 Noch Platz http://www.wien.gv.at/freizeit/baeder/uebersicht/familienbaeder.html
POINT (16.42221061627191 48.203712376002365) Stadionbad 2 None http://www.wienersportstaetten.at/home/stadionbad
POINT (16.409846482765538 48.17319850233019) Familienbad Herderplatz 11 Noch Platz http://www.wien.gv.at/freizeit/baeder/uebersicht/familienbaeder.html
POINT (16.331705756702743 48.20140726541739) Stadthallenbad 15 None http://www.wienersportstaetten.at/home/stadthallenbad
POINT (16.348841199156727 48.23125771081222) Familienbad Währinger Park 18 Noch Platz http://www.wien.gv.at/freizeit/baeder/uebersicht/familienbaeder.html
POINT (16.375414747417764 48.228100489892455) Familienbad Augarten 2 Derzeit voll http://www.wien.gv.at/freizeit/baeder/uebersicht/familienbaeder.html
POINT (16.382021141856153 48.21180199274544) Badeschiff Wien 1 None http://www.badeschiff.at/
In [ ]:
 
In [145]:
pools = %sql select SHAPE, NAME, BEZIRK, AUSLASTUNG_AMPEL_KAT_TXT_0, WEBLINK1 from communalPool
 * sqlite:///stations.db
Done.

Nun kann auch schon die Kombination erfolgen. Ich gleiche alle Stationskoordinaten mit allen Koordinaten der Bäder ab. Die Entfernung soll unter 1000m liegen und der Bezirk kann mittels Argument übergeben werden wenn die Funktion aufgerufen wird. Der Output passt super. Ich habe das wieder mittels Google Maps verglichen und meine Ergebnisse sind aussagekräftig. Ich packe das in eine Funktion weil ich das dann später noch verwende! Außerdem füge ich den Stationsobjekten noch im Field hasPool ein True hinzu.

In [146]:
for i in range(len(stationsObj)):
        for row in range(len(pools)):
            if haversine(stationsObj[i].coords + pools[row][0][7:][:-1].split(" ")) < 1000:
                stationsObj[i].hasPool = True
                print(str(int(haversine(stationsObj[i].coords + pools[row][0][7:][:-1].split(" ")))) + " meter" )
                print(stationsObj[i].name)
                print(pools[row][1])
                print("")
622 meter
stubentor
Badeschiff Wien

799 meter
stubentor
Dianabad

195 meter
meidling hauptstraße
Theresienbad

838 meter
friedensbrücke
Hallenbad Brigittenau

901 meter
heiligenstadt
Kombibad Döbling

829 meter
längenfeldgasse
Theresienbad

411 meter
jägerstraße
Hallenbad Brigittenau

286 meter
floridsdorf
Hallenbad Floridsdorf

857 meter
floridsdorf
Strandbad Angelibad

879 meter
hardeggasse
Strandbad Stadlau

774 meter
stadion
Stadionbad

544 meter
leopoldau
Kombibad Großfeldsiedlung

334 meter
großfeldsiedlung
Kombibad Großfeldsiedlung

989 meter
alte donau
Strandbad Alte Donau

438 meter
alte donau
Bundesbad Alte Donau

491 meter
nestroyplatz
Badeschiff Wien

850 meter
nestroyplatz
Dianabad

793 meter
kagraner platz
Kombibad Donaustadt

140 meter
reumannplatz
Amalienbad

665 meter
alaudagasse
Laaerbergbad

324 meter
altes landgut
Laaerbergbad

124 meter
oberlaa
Therme Wien

566 meter
troststraße
Amalienbad

771 meter
alser straße
Jörgerbad

858 meter
braunschweiggasse
Penzinger Bad

744 meter
burggasse-stadthalle
Familienbad Hofferplatz

645 meter
burggasse-stadthalle
Stadthallenbad

811 meter
zippererstraße
Familienbad Herderplatz

967 meter
johnstraße
Familienbad Reinlgasse

696 meter
josefstädter straße
Familienbad Hofferplatz

840 meter
josefstädter straße
Jörgerbad

997 meter
kaisermühlen-vic
Strandbad Alte Donau

941 meter
kaisermühlen-vic
Bundesbad Alte Donau

388 meter
kardinal-nagl-platz
Apostelbad

609 meter
keplerplatz
Amalienbad

677 meter
neubaugasse
Hermannbad

624 meter
landstraße
Badeschiff Wien

366 meter
niederhofstraße
Theresienbad

400 meter
nußdorfer straße
Familienbad Währinger Park

901 meter
meidling
Theresienbad

869 meter
pilgramgasse
Einsiedlerbad

369 meter
hütteldorfer straße
Familienbad Reinlgasse

726 meter
spittelau
Hallenbad Brigittenau

595 meter
rochusgasse
Apostelbad

771 meter
schottenring
Dianabad

406 meter
schwedenplatz
Badeschiff Wien

263 meter
schwedenplatz
Dianabad

523 meter
schweglerstraße
Stadthallenbad

998 meter
stadtpark
Badeschiff Wien

914 meter
kendlerstraße
Familienbad Reinlgasse

931 meter
stephansplatz
Dianabad

534 meter
thaliastraße
Familienbad Hofferplatz

979 meter
thaliastraße
Stadthallenbad

610 meter
währinger straße
Familienbad Währinger Park

641 meter
zieglergasse
Hermannbad

980 meter
neue donau
Strandbad Angelibad

574 meter
enkplatz
Familienbad Herderplatz

786 meter
taborstraße
Badeschiff Wien

679 meter
taborstraße
Dianabad

737 meter
hütteldorf
Hallenbad Hütteldorf

777 meter
margaretengürtel
Einsiedlerbad

832 meter
schönbrunn
Schönbrunner Bad

In [ ]:
 
In [147]:
def searchForPoolsWithWater(district):
    result = []
    for i in range(len(stationsObj)):
        for row in range(len(pools)):
            if haversine(stationsObj[i].coords + pools[row][0][7:][:-1].split(" ")) < 1000 and pools[row][2] == district:
                stationsObj[i].hasPool = True
                #print(str(int(haversine(stationsObj[i].coords + pools[row][0][7:][:-1].split(" ")))) + " meter" )
                #print(stationsObj[i].name)
                #print(pools[row][1])
                #print("")
                result.append([stationsObj[i], pools[row][1]])         
    return result

Unten ein Test der Funktion. Die Funktion retourniert das station Objekt samt Name des Bades in einem Array von Arrays.

In [148]:
searchForPoolsWithWater(13)
Out[148]:
[[<__main__.Station at 0x23163cb8>, 'Schönbrunner Bad']]
In [149]:
def calcDistancePools(district):
    stationsPools = []
    for i in range(len(stationsObj)):
        for row in range(len(pools)):
            if haversine(stationsObj[i].coords + pools[row][0][7:][:-1].split(" ")) < 1000:
                #print(stationsObj[i].name)
                #print(pools[row][1])
                stationsPools.append(stationsObj[i])
    return stationsPools

stationsWithWaterAndPoolONE = []
stationsWithWaterAndPoolALL = []
m = (calcDistancePools(10))   
for row in m:
    if row.hasWater == True:
        if row not in stationsWithWaterAndPoolONE:
            stationsWithWaterAndPoolONE.append(row)
        stationsWithWaterAndPoolALL.append(row)
In [150]:
for stations in stationsWithWaterAndPoolONE:
    print(stations.name)
print(len(stationsWithWaterAndPoolONE))
stubentor
meidling hauptstraße
längenfeldgasse
jägerstraße
floridsdorf
stadion
großfeldsiedlung
nestroyplatz
reumannplatz
alaudagasse
troststraße
burggasse-stadthalle
zippererstraße
johnstraße
josefstädter straße
kaisermühlen-vic
kardinal-nagl-platz
keplerplatz
neubaugasse
niederhofstraße
nußdorfer straße
meidling
pilgramgasse
hütteldorfer straße
rochusgasse
schottenring
schwedenplatz
schweglerstraße
stadtpark
stephansplatz
thaliastraße
währinger straße
zieglergasse
neue donau
enkplatz
hütteldorf
margaretengürtel
37

Diese Liste spiegelt UBahn Stationen wieder die im Umkreis von 1000m ein Freibad haben. Außerdem befindet sich im Umkreis von 250m auch ein Wasserbrunnen. Die 37 Bäder sind für meine Navigationsfunktion relevant. Nur zu einer von diesen Stationen führt mich die Navigation.

Hier berechne ich wieviele Stationen einen Trinkbrunnen im Umkreis von 250 Meter haben 67 ist ein guter Wert. Ich lasse die Distanz so eingestellt.

In [151]:
waterCount = 0
for station in stationsObj:
    if station.hasWater == True:
        waterCount += 1

print(waterCount)
67
In [ ]:
 

NINTH PART - create new ONTOLOGY + knowledge Graph (Grakn)


Hier soll ein neuer knowledge Graph erstellt werden. Ich wusste zu Beginn nicht wie Grakn genau funktioniert. Ich dachte ich kann einen KG einfach in dem uns bekannten Format ala n3 importieren. Dem ist nicht so. Ich hatte zu diesem Zeitpunkt aber schon mit einem KG begonnen weshalb ich Ihnen das nicht vorenthalten will. Weiter unten dann die Implementierung eines Grakn Schemas.

In [152]:
summerOnt = """
@prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@base <http://my.org/> .
@prefix : <#> .
"""
In [153]:
summerOnt += """

:Station a rdfs:Class .
:Pool a rdfs:Class .
:Fountain a rdfs:Class .

:adjacent a rdfs:Property .
:isNearby a rdfs:Property .
:hasWater a rdfs:Property .
:located a rdfs:Property .
"""
In [154]:
for station in stationsWithWaterAndPoolONE:
    summerOnt += """
    :{} a :Station .
    :{} :adjacent :Fountain .
    :{} :adjacent :Pool .
    """.format("".join(station.name.split()), "".join(station.name.split()), "".join(station.name.split()))
In [155]:
import rdflib

g = rdflib.Graph()

g.parse(data=summerOnt, format='turtle')
print(len(g))
118

Wie man sieht hat das einfügen funktioniert. 118 Einträge sind nun in meinem Graphen zu finden. AN dieser Stelle breche ich das hier ab und gehe weiter zu Grakn.

In [ ]:
 

Hier nun die GRAKN Implementierung:


Erstellen 2 neuer CSV Dateien mit den Daten aus den Stationsobjekten. Diese Dateien werden dann in VS-Code und Grakn Console und Grakn Base weiter bearbeitet.

Die Dateien wurden exportiert. Ich spare mir jetzt das erneute importieren und lasse Ihnen das Ergebnis meiner GRAKN-Abfrage stattdessen zukommen!

In [156]:
with open('stationWithWaterPool.csv', 'w', newline='', encoding='utf-8') as file:
    writer = csv.writer(file)
    writer.writerow(['station', 'objectID', 'hasWater', 'hasPool'])
    for station in stationsObj:
            writer.writerow([station.name, int(station.objectId), str(station.hasWater).lower(), str(station.hasPool).lower() + "'"])
In [157]:
with open('lineHas.csv', 'w', newline='', encoding='utf-8') as file:
    writer = csv.writer(file)
    writer.writerow(['line', 'objectID'])
    for station in stationsObj:
        for line in station.line:
            writer.writerow([line, int(station.objectId)])

Unten die Abbildung des Schemas.

schema

Hier das Ergebnis im Workbase mit der Abfrage nach Stationen an der U1 mit Zugang zu einem Freibad und Zugang zu einem Wasserbrunnen. Insgesamt 9 Stationen.

u1

Eine zweite Abfrage für die U2. Hier nur Zugang zu Bädern da es die Kombination hier nur 2 mal gibt. 4 Stationen aber mit Zugang zu Bädern.

u2

10th PART - letzter Teil, NAVIGATION


Das Ziel für diesen Teil ist klar. Ich möchte eine beliebige Station eingeben und mein Algorithmus soll mich zum nächst gelegenen Bad mit Trinkwasserbrunnen in dem von mir gewählten Bezirk führen. Das Programm liefert die Wegstrecke zu der nähesten UBahn-Station im gewählten Bezirk welche beide Kriterien erfüllt. Die Entfernung wird dabei vom aktuellen Standpunkt berechnet (Luftlinie). Der Algorithmus sucht dann in meinen Linien nach der Station und navigiert mich zur Zielstation.

Als erstes werden einige Funktionen für die Pfadsuche erstellt. Insgesamt soll versucht werden, einen möglichst kurzen Weg, gemessen an den Stationen (häufiges Umsteigen wird nicht berücksichtigt) zu finden. Anmerkung zu der Endfunktionalität. Da es nicht so viele Stationen mit Wasserzugang und Bad gibt in Wien (37) kann die volle Vielfalt des Programms nicht ausgespielt werden. Es werden natürlich bei weitem nicht alle Stationen angefahren. Aber ich habe alle Stationen auf Funktionalität überprüft. Jede mögliche Kombination von Start und Endstation hat geklappt. Vor allem schwierig war das bei Praterstern und Gumpendorfer Straße. Beide Stationen bieten die Möglichkeit in 2 Richtungen zu starten falls sich die Zielstation nicht auf der selben Linie befindet da der nächste Knotenpunkt sowohl vorwärts als auch rückwärts genau gleich weit entfernt ist. Es gäbe somit grundsätzlich 2 gültige Varianten. Überhaupt schwierig ist es von Praterstern zur U6. Da gibt es erstmal 2 Varianten. Nämlich U2 und U1. Beide bringen einen zur U4 die ich benötige um zur U6 zu gelangen. An der U4 angelangt bieten beide Varianten aber jeweils 2 weitere Varianten um zur U6 zu kommen. Alle könnten die Kürzesten sein also müssen alle berücksichtigt werden. Das hat mir am meisten Kopfzerbrechen bereitet.

Über ipywidgets war es mir möglich eine kleine Benutzeroberfläche zu basteln. Damit kann man das Programm ganz gut bedienen. Die Eingabemaske ist ganz am Ende des Notebooks. Nun zu den Funktionen!

In [158]:
from ipywidgets import widgets

Die checkIfOnSameLine macht ihrem Namen alle Ehre. Sie überprüft ob die Linien der Startstation sich mit den Linien der Zielstation kreuzen. Ist das der Fall befindet sich die Zielstation auf der selben Linie und man muss nicht umsteigen. Es wird dann einfach die entspprechende Funktion in der doubly linked list aufgerufen. Das macht dann auch schon die 2 Funktion. Nämlich onSameLine. Die kann also aufgerufen werden wenn ich weiß, dass ich auf der selben Linie bin. Zurück kommt die Anzahl an Stationen die ich zurücklegen muss aber auch die Endstation der Linie. Dies um im Endeffekt auszugeben in welche Richtung ich fahren soll.

In [159]:
def checkIfOnSameLine(targetLines, startLines):
    sameLine = [False, "Ux"]
    for targetLine in targetLines:
        if targetLine in startLines:
            sameLine[0] = True
            sameLine[1] = targetLine
    return sameLine
In [160]:
def onSameLine(startStation, targetStation, targetLine):
    searchNext = finalLines[targetLine].search_stationNext(startStation, targetStation)
    searchPrev = finalLines[targetLine].search_stationPrev(startStation, targetStation)
    if searchNext != -1:
        endstation = (finalLines[targetLine].get_nextLastStation(startStation).name)
        return([[startStation, searchNext], targetStation, targetLine, endstation])
    if searchPrev != -1:
        endstation = (finalLines[targetLine].get_prevLastStation(startStation).name)
        return([[startStation, searchPrev], targetStation, targetLine, endstation])

Ist man bereits auf der richtigen Linie ist man auch schon am Ende angekommen. Es wird dann diese Funktion ausgeführt und man erfährt wieviele Stationen man in welche Richtung fahren muss um sein Ziel zu erreichen.

In [161]:
def onSameLineDepth0and1(startStation, targetStation, targetLine):
    searchNext = finalLines[targetLine].search_stationNext(startStation, targetStation)
    searchPrev = finalLines[targetLine].search_stationPrev(startStation, targetStation)
    if searchNext != -1:
        endstation = (finalLines[targetLine].get_nextLastStation(startStation).name)
        return(startStation.name + " ist auf der selben Linie wie " + targetStation.name + ". Nehmen Sie die " + targetLine + " und fahren Sie für " + str(searchNext) + " Stationen zu " + targetStation.name + " in Richtung " + endstation)
    if searchPrev != -1:
        endstation = (finalLines[targetLine].get_prevLastStation(startStation).name)
        return(startStation.name + " ist auf der selben Linie wie " + targetStation.name + ". Nehmen Sie die " + targetLine + " und fahren Sie für " + str(searchPrev) + " Stationen zu " + targetStation.name + " in Richtung " + endstation)

An meiner Namensgebung muss ich arbeiten aber hier soll überprüft werden welcher Weg der Kürzeste ist. Allerdings ist mit dem shortestHalwayPath gemeint, dass hier die Hälfte bereits zurückgelegt ist und ich schon weiß, dass ich mit dem Umsteigen auf der Linie der Zielstation bin. Hier findet keine Gesamtüberprüfung der Länge der Route statt da sich herausgestellt hat, dass in diesen Fällen zwar zu ein paar schlechteren Ergebnissen kommen kann aber es hält sich sehr in Grenzen.

In [162]:
def shortestHalfwayPath(paths):
    currentLowestNumber = 100
    currentLowestPath = [-1, -1]
    for entry in paths:
        if entry == -1:
            break
        if entry[1] < currentLowestNumber:
            currentLowestNumber = entry[1]
            currentLowestPath[0] = entry[0]
            currentLowestPath[1] = entry[1]
    return currentLowestPath

Der Name ist wieder besser gewählt. Wurde festgestellt, dass die Zielstation nicht auf der selben Linie ist wird diese Funktion ausgeführt. Sie gibt zurück die nächste Kreuzung die meiner Ziellinie entspricht. Falls dies nicht der Fall ist, die Ziellinie also überhaupt nicht erreicht werden kann über die Startlinie wird -1 zurückgegeben. Und man muss tiefer suchen.

In [163]:
def notOnSameLine(startStation, targetStation, startLines, targetLines):
    print(startStation.name + " ist NICHT auf der selben Linie wie " + targetStation.name + "!")
    searchNext = []
    searchPrev = []
    for startLine in startLines:
        for i in range(len(targetLines)):
            #append every result to the next or prev array. if there is one positive number in any of the 2 arrays the line is found on a knot on the current line
            #and we found our target Line. If that the case the searchStationNext and searchStationPrev will be called.
            searchNext.append(finalLines[startLine].search_lineNext(targetLines[i], startStation))
            searchPrev.append(finalLines[startLine].search_linePrev(targetLines[i], startStation))
    return searchNext, searchPrev

die FlattenList ist dazu da um eine verschachtelte Liste wieder schlanker zu machen. Ich arbeite viel mit ineinander verschachtelten Arrays und hierbei ist diese Funktion sehr hilfreich gewesen. Um ehrlich zu sein hab ich dann im späteren Verlauf den Überblick verloren und mir ist nicht mehr ganz klar wieso in manchen Fällen derart verschachtelte Infos zurückgegeben wurden. Aber mit dieser Funktion kann man das Problem lösen. Hier zeigt sich das bei größeren Projekte eine gewissenhafte Vorausplanung essentiell ist. Damit kann man solche Probleme gleich entschärfen aber naja, learning by doing.

In [164]:
def flattenList(listToFlat):
    flattenedList = []
    if "FAILURE" in listToFlat:
        flattenedList.append(-1)
        return flattenedList
    for sublist in listToFlat:
        for item in sublist:
            flattenedList.append(item)
    return flattenedList

wenns also tiefer hinein geht in die Suche kann diese Funktion verwendet werden um den kürzesten Pfad zu berechnen. Alle möglichen Routen werden ihr übergeben und returned wird die Kürzeste Route.

In [165]:
def shortestPathDepth2(routes):
    shortestNumber = 100
    shortestRoute = []
    for route in routes:
        if (route[0][1] + route[1][1] + route[2][1]) < shortestNumber:
            shortestNumber = route[0][1] + route[1][1] + route[2][1]
            shortestRoute = route
    return shortestRoute

Diese Funktion hängt sehr stark mit einer Funktion 3 Zellen weiter unten zusammen. Dies ist die Folgefunktion der Funktion searchFurther. SearchFurther hat die Aufgabe nach der nächsten Station zu suchen die ein Knoten ist. Nämlich dann, wenn man weder auf der Ziellinie ist und die aktuelle Linie auch nicht die Ziellinie kreuzt. Dann wird also einfach nach dem nächsten Knoten gesucht. Wenn es da mehrere Möglichkeiten gibt werden alle berücksichtigt. Also maximal 2 natürlich nur. Bis eben eine Kreuzung gefunden wurde in beide Richtungen. Danach wird dann searchDeeper aufgerufen. Hiermit ist gemeint, dass nun von dem gefundenen Knoten aus auf den dort befindlichen Linien nach der Ziellinie gesucht wird.

In [166]:
def searchDeeper(linesNextKnot, targetLines, newStartStation):
    testNext = []
    testPrev = []
    if -1 in linesNextKnot:
        return "FAILURE"
    for startLine in linesNextKnot[0].line:
        for i in range(len(targetLines)):
        #append every result to the next or prev array. if there is one positive number in any of the 2 arrays the line is found on a knot on the current line
        #and we found our target Line. If that the case the searchStationNext and searchStationPrev will be called.
            n = finalLines[startLine].search_lineNext(targetLines[i], newStartStation)
            p = finalLines[startLine].search_linePrev(targetLines[i], newStartStation)
            if n not in testNext:
                testNext.append(n)
            if p not in testPrev:
                testPrev.append(p)
    return testNext, testPrev

Hier wird jetzt das Ergebnis aus notOnSameLine weiter verarbeitet. Ein loggedPath wird returniert.

In [167]:
def searchStationWithTargetLineOnCurrentLine(start, target, startLines, targetLines):
    temp = flattenList(notOnSameLine(start, target, startLines, targetLines))
    searchStationWithTargetLine = []
    for entry in temp:
        if entry != -1:
            searchStationWithTargetLine.append(entry)   
    loggedPath = (shortestHalfwayPath(searchStationWithTargetLine))
    return loggedPath

stuckLine betrifft einen Spezialfall. Nämlich den, dass vom nächsten Knoten aus keine Verbindung zu Ziellinie gefunden werden kann. Da kommt nur einmal vor in der Wiener UBahn. Nämlich wenn man von der U2 startet und zwar östlich von Praterstern und man zur U6 will. Also alles ab Messe-Prater. Denn die U2 bietet keine Verbindung zu der U6. Somit wird der nächste Knoten gesucht. Das ist Praterstern aber auch vom Praterstern aus kann man die U6 nicht finden. Somit wird hier einfach erneut zum nächsten Knoten gesprungen. Im Fall von Praterstern und der U2 ist es dann Schottenring. Schottenring hat eine Verbindung zur U6.

In [168]:
def stuckLine(loggedPath, startLines, targetLines):
    newLoggedPath = []
    newLoggedPath.append(flattenList(loggedPath))
    tempNext = []
    tempPrev = []   
    for startLine in startLines:
        tempNext.append(finalLines[startLine].search_knotNext(loggedPath[0][0]))
        tempPrev.append(finalLines[startLine].search_knotPrev(loggedPath[0][0]))
    flattenList(tempNext)
    flattenList(tempPrev)
    if tempNext[0][0] != -1:
        newLoggedPath.append(flattenList(tempNext))
    if tempPrev[0][0] != -1:
        newLoggedPath.append(flattenList(tempPrev))
    return newLoggedPath
In [169]:
def searchFurther(loggedPath, startLines, targetLines, startStationObj, targetStationObj):
    loggedPath = []
    tempNext = []
    tempPrev = []   
    for startLine in startLines:
        tempNext.append(finalLines[startLine].search_knotNext(startStationObj))
        tempPrev.append(finalLines[startLine].search_knotPrev(startStationObj))
    searchNextKnot = tempNext
    searchPrevKnot = tempPrev        
    for entry in searchNextKnot:
        if -1 not in entry:
            loggedPath.append(entry)
    for entry in searchPrevKnot:
        if -1 not in entry:
            loggedPath.append(entry)

    allPossiblePaths = []
    for i in range(len(loggedPath)):

        allPossiblePaths.append(flattenList(searchDeeper(loggedPath[i], targetLines, loggedPath[i][0])))

    deeperPath = []
    for entry in flattenList(allPossiblePaths):

        if entry != -1:
            loggedPath.append(entry)

    if (startStationObj == (stationsObj[47]) and targetStationObj == stationsObj[68]):
        del loggedPath[2]
    return loggedPath

Die calcRoute ist eine der wichtigsten Funktionen. Die hat mich viel Zeit gekostet und Hirnschmalz auch glaube ich. Ganz astrein und sauber ist das da alles nicht. Das ist mir bewusst aber besser hab ich es jetzt nicht mehr hinbekommen. Der Code gehört sowieso refactored. Die Funktion betrifft nur Routen bei denen man 2 mal umsteigen muss. >=4 bedeutet, dass es mehr als eine Möglichkeit gibt. ==2 bedeutet, dass es für nur eine Route gibt. Mehrere mögliche Routen werden hier verglichen und die beste Route ausgewählt. Dieser Funktion geht die Funktion darunter vor.

In [170]:
def calcRoute(routes, targetLines, startStationObj, targetStationObj):
    correctLines = []
    if len(routes) >= 4:

        temp1 = [routes[0], routes[1]]
        temp2 = [routes[2], routes[3]]
        routes = [temp1, temp2]

        for route in routes:
            correctLines.append(checkIfOnSameLine(targetLines, route[1][0].line))
        #append all the information to all the routes. Path for first, second and third line. StartingStation, Endline and Endstation of Endline to give back the correct direction.

        for route in range(len(routes)):
            #print(targetStationObj.name
            temp = onSameLine(routes[route][1][0], targetStationObj, correctLines[route][1])
            #print((routes[route][1][0].name, targetStationObj.name, correctLines[route][1]))
                    
            for entry in temp:
                routes[route].append(entry)
                #print(entry)
        shortestPath = (shortestPathDepth2(routes))
        routes = []  
    #print(shortestPathDepth2)
    if len(routes) == 2:
        correctLines.append(checkIfOnSameLine(targetLines, routes[1][0].line))
        temp = onSameLine(routes[1][0], targetStationObj, correctLines[0][1])
        for entry in temp:
            routes.append(entry)
        shortestPath = routes
        
    isSameLineDepth1 = checkIfOnSameLine(shortestPath[0][0].line, startStationObj.line)
    isSameLineDepth2 = checkIfOnSameLine(shortestPath[1][0].line, shortestPath[0][0].line)
    isSameLineDepth3 = checkIfOnSameLine(targetStationObj.line, shortestPath[2][0].line)

    firstSection = onSameLine(startStationObj, shortestPath[0][0], isSameLineDepth1[1])

    secondSection = onSameLine(shortestPath[0][0], shortestPath[1][0], isSameLineDepth2[1])

    thirdSection = onSameLine(shortestPath[1][0], targetStationObj, isSameLineDepth3[1])

    print("")
    print("Sie müssen zweimal Umsteigen:")
    print("Nehmen Sie als erstes die {0} in Richtung {1}. Fahren Sie für {2} Stationen bis zur Station {3}.".format(firstSection[2], firstSection[3], firstSection[0][1], secondSection[0][0].name))
    print("")
    print("Nun fahren Sie mit der {0} in Richtung {1}. Fahren Sie für {2} Stationen bis zur Station {3}.".format(secondSection[2], secondSection[3], secondSection[0][1], thirdSection[0][0].name))
    print("")
    print("Ein letztes mal umsteigen. Nehmen Sie die {0} in Richtung {1}. Fahren Sie für {2} Stationen bis zu Ihrem Ziel {3}.".format(thirdSection[2], thirdSection[3], thirdSection[0][1], targetStationObj.name))

Hier werden alle möglichen Routen berechnet. Besser ausgedrückt werden sie zusammengestellt. Hier blieb mir ncihts andere übrig als viele Szenarien zu berücksichtigen. Ich hätte das gerna automatisiert aber das war mir nicht möglich in der Zeit. Da hätte ich nochmal von vorne beginnen müssen. Deshalb auch hier die ein oder andere Unschärfe. Am Ende der Funktion hat man ein Array mit den diversen Routen. Das wird dann an die calcRoute Funktion übergeben die sich dann darum kümmert den besten, kürzesten Pfad zu finden.

In [171]:
def searchSecondDepth(loggedPath, startLines, targetLines, startStationObj, targetStationObj):

    if len(loggedPath) == 1:
    #U2 wants to U6 AND IS STUCK ON PRATERSTERN CAUSE U1 not CONNECTED TO U6! NEED TO SEARCH FOR THE NEXT KNOT AGAIN!

        #search for connection to targetLine on next Knot (from Startline). if the current Line has no next knot it would not be possible to access the targetLine from this line. so this scenario
        #is only theoretical because every station is accessible from every station in the vienna ubahn
        stuck = flattenList(stuckLine(loggedPath, startLines, targetLines))
        countStations = stuck[1] + stuck[3]
        correctKnot = stuck[2]
        loggedPath = [correctKnot, countStations]

        temp = flattenList(searchDeeper(loggedPath, targetLines, loggedPath[0]))
        for entry in temp:
            if entry != -1:
                loggedPath.append(entry)   
        
        if isinstance(loggedPath[1], int):
            loggedPath[0] = [loggedPath[0], loggedPath[1]]
            del loggedPath[1]
        
        
    if len(loggedPath) == 2:
        calcRoute(loggedPath, targetLines, startStationObj, targetStationObj)
    
    if len(loggedPath) == 3:

        route1 = []
        route1.append(loggedPath[0])
        route1.append(loggedPath[1])

        route2 = []
        route2.append(loggedPath[0])
        route2.append(loggedPath[2])
        
        routes = [route1, route2]
        calcRoute(flattenList(routes), targetLines, startStationObj, targetStationObj)
        
    if len(loggedPath) == 4 and isinstance(loggedPath[-1], list):
        
        if loggedPath[3][1] == loggedPath[2][1]:
            route1 = []
            route1.append(loggedPath[0])
            route1.append(loggedPath[1])

            route2 = []
            route2.append(loggedPath[2])
            route2.append(loggedPath[3])
            
        if loggedPath[3][1] != loggedPath[2][1]:
            route1 = []
            route1.append(loggedPath[0])
            route1.append(loggedPath[2])

            route2 = []
            route2.append(loggedPath[1])
            route2.append(loggedPath[3])
        
        routes = [route1, route2]

        calcRoute(flattenList(routes), targetLines, startStationObj, targetStationObj)
    
    if len(loggedPath) == 5 and isinstance(loggedPath[-1], list):

        route1 = []
        route1.append(loggedPath[0])
        route1.append(loggedPath[2])

        route2 = []
        route2.append(loggedPath[1])
        route2.append(loggedPath[3])

        route3 = []
        route3.append(loggedPath[1])
        route3.append(loggedPath[4])
        
        routes = [route1, route2, route3]
        
        
        
        calcRoute(flattenList(routes), targetLines, startStationObj, targetStationObj)
        
    if len(loggedPath) == 6 and isinstance(loggedPath[-1], list):

        route1 = []
        route1.append(loggedPath[0])
        route1.append(loggedPath[2])

        route2 = []
        route2.append(loggedPath[0])
        route2.append(loggedPath[3])

        route3 = []
        route3.append(loggedPath[1])
        route3.append(loggedPath[4])
                
        route4 = []
        route4.append(loggedPath[1])
        route4.append(loggedPath[5])
                
        routes = [route1, route2, route3, route4]

        calcRoute(flattenList(routes), targetLines, startStationObj, targetStationObj)

Und das ist die Startfunktion. Ihr werden die Startstation und die Zielstation als String übergeben. Als erstes sucht sich die Funktion ein paar Variablen und checkt dann gleich mal ob man sich bereits auf der richtigen Linie befindet. Falls ja wird der korrekt Weg zur Zielstation, bzw. die Stationen dahin, ausgegeben.

Falls nicht wird der weitere Code in Gang gesetzt.

In [172]:
def searchPath(startingStation, target):
    linesStart = []
    linesTarget = []
    startStationObj = -1
    targetStationObj = -1
    collectAll = [linesTarget, linesStart, startStationObj]
    
    for i in range(len(stationsObj)):
        if stationsObj[i].name == startingStation:
            startStationObj = stationsObj[i]
            for line in stationsObj[i].line:
                linesStart.append(line)

    for i in range(len(stationsObj)):
        if stationsObj[i].name == target:
            targetStationObj = stationsObj[i]
            for line in stationsObj[i].line:
                linesTarget.append(line)          
    
    sameLine = checkIfOnSameLine(collectAll[0], collectAll[1])
    if sameLine[0] == True:
        print(onSameLineDepth0and1(startStationObj, targetStationObj, sameLine[1]))
    
    #case start and target are NOT on same line 
    if sameLine[0] == False:
        #loggedPath will be calculated. loggedPath will be -1 if no knot is found at wich you can change to the targetLine.
        #this means that i have to search deeper. if it is not -1 a path to the target line was found.
        loggedPath = searchStationWithTargetLineOnCurrentLine(startStationObj, targetStationObj, collectAll[1], collectAll[0])
        #serch for the shortest path in both directions.

        if loggedPath[1] != -1:
            sameLineHalfWay = checkIfOnSameLine(loggedPath[0].line, collectAll[1])
            arrivedAtCorrectLine = checkIfOnSameLine(collectAll[0], loggedPath[0].line)
            print("")
            print("Sie müssen einmal Umsteigen:")
            print(onSameLineDepth0and1(startStationObj, loggedPath[0], sameLineHalfWay[1]))
            print("")
            print("Steigen Sie nun um in die " + arrivedAtCorrectLine[1])
            print(onSameLineDepth0and1(loggedPath[0], targetStationObj, arrivedAtCorrectLine[1]))
            print("")
            print("Sie haben Ihr Ziel erreicht")
    
    #case NO connection to targetLine on next line. in this case i will search for the nearest knot. from where the targetLine is reachable.
    #you can reach every station from everywhere if you change the line for two times.  

        if loggedPath[1] == -1:
            loggedPath = searchFurther(loggedPath, collectAll[1], collectAll[0], startStationObj, targetStationObj)
        
            searchFurther(loggedPath, collectAll[1], collectAll[0], startStationObj, targetStationObj)
            searchSecondDepth((loggedPath), collectAll[1], collectAll[0], startStationObj, targetStationObj)

       

Und das war es auch schon mit der Navigationsfunktion. Unten noch eine Funktion die ausgeführt werden wenn man dann nach einem Bad sucht. Sie sucht nach der nähesten Station im gewählten Bezirk. Ist in einem Bezirk keine Station mit den genannten Kriterien verfügbar wird im Bezirk +1 gesucht. Danach im Bezirk -1. Sollte auch dann kein Bad gefunden werden muss ein neuer Bezirk eingegeben werden. Spezialfall 23. Bezirk. Hier wird natürlich nicht im 24. Bezirk gesucht sondern im 14. Ich habe ein paar Varianten durchgespielt und der 14. scheint sinnvoll zu sein. Der 10. würde natürlich auch gehen.

In [182]:
def startSearchToPool(startingStation, district):
    startObj = None
    for station in stationsObj:
        if station.name == startingStation:
            startObj = station
    startCoords = startObj.coords
    nearestStationInNeededDistrict = None
    lowestDistance = 50000
    possiblePoolsWithStations = searchForPoolsWithWater(int(district))
    if len(possiblePoolsWithStations) < 1 and district != "23":
        print("Es konnten keine entsprecheden Bäder im {}. gefunden werden. Es wird im {}. Bezirk gesucht...".format(district, str(int(district)+1)))
        possiblePoolsWithStations = searchForPoolsWithWater(int(district) + 1)
    if len(possiblePoolsWithStations) < 1 and district != "23":
        print("Auch im {}. Bezirk wurde kein entprechender Bäder gefunden. Es wird im {}. Bezirk gesucht...".format(str(int(district)+1), str(int(district)-1)))
        possiblePoolsWithStations = searchForPoolsWithWater(int(district) - 1)
    if len(possiblePoolsWithStations) < 1 and district == "23":
        print("Es konnten keine entsprecheden Bäder im {}. gefunden werden. Es wird im {}. Bezirk gesucht...".format(district, str(int(district)-9)))
        possiblePoolsWithStations = searchForPoolsWithWater(int(district) - 9)
    if len(possiblePoolsWithStations) < 1:
        print("Immer noch nichts gefunden. Bitte wählen Sie einen anderen Bezirk und starten die Abfrage erneut")
    if len(possiblePoolsWithStations) >= 1:
    
        for possiblePoolStation in possiblePoolsWithStations:
            temp = haversine(possiblePoolStation[0].coords + startObj.coords)
            if nearestStationInNeededDistrict == None or lowestDistance > temp:
                lowestDistance = temp
                nearestStationInNeededDistrict = possiblePoolStation
        print("")
        print("Von Ihrem Standpunkt {} aus ist das nächst gelegene Bad (mit Wasserbrunnen in der Nähe) das {}. Dazu fahren Sie bis zur Station {}. Untenstehend eine Wegbeschreibung:".format(startingStation, nearestStationInNeededDistrict[1], nearestStationInNeededDistrict[0].name))
        print("")
        searchPath(startingStation, nearestStationInNeededDistrict[0].name)

Das Untenstehende ist jetzt nur mehr für das User Interface notwendig.

In [183]:
#todo evtl ein filter für die stationen damit es keine probleme gibt beim eintippen
print("Bitte tippen Sie ihre Start-Ubahn-Station ein.")
print("Achtung! Sollte es zu Problemen mit dem Namen kommen versuchen Sie bitte eine andere Schreibweise. Groß und Kleinschreibung kann unberücksichtigt bleiben.")
print("")
print("Schwierige Stationen:")
print("Südtiroler Platz bitte Hauptbahnhof verwenden")
print("Schottentor-Universität bitte Schottentor verwenden")
print("Messe Prater bitte Messe-Prater verwenden")
text = widgets.Text()
display(text)
print("In welchem Bezirk möchten Sie schwimmen gehen?")
district = widgets.Dropdown(
    options=['1010', '1020', '1030', '1040', '1050', '1060', '1070', '1080', '1090', '1100', '1110', '1120', '1130', '1140', '1150', '1160', '1170', '1180', '1190', '1200', '1210', '1220', '1230'],
    value='1010',
    disabled=False,
)
display(district)

def handle_submit(sender):
    station = text.value.lower()
    dist = district.value
    if dist[1] == "0":
        startSearchToPool(station, dist[2])
    else: startSearchToPool(station, dist[1:][:-1])
#text.on_submit(handle_submit)




button = widgets.Button(description="Suche starten")
display(button)
def on_button_clicked(b):
    handle_submit(text)
button.on_click(on_button_clicked)
    
Bitte tippen Sie ihre Start-Ubahn-Station ein.
Achtung! Sollte es zu Problemen mit dem Namen kommen versuchen Sie bitte eine andere Schreibweise. Groß und Kleinschreibung kann unberücksichtigt bleiben.

Schwierige Stationen:
Südtiroler Platz bitte Hauptbahnhof verwenden
Schottentor-Universität bitte Schottentor verwenden
Messe Prater bitte Messe-Prater verwenden
In welchem Bezirk möchten Sie schwimmen gehen?
Es konnten keine entsprecheden Bäder im 23. gefunden werden. Es wird im 14. Bezirk gesucht...

Von Ihrem Standpunkt alser straße aus ist das nächst gelegene Bad (mit Wasserbrunnen in der Nähe) das Familienbad Reinlgasse. Dazu fahren Sie bis zur Station johnstraße. Untenstehend eine Wegbeschreibung:

alser straße ist NICHT auf der selben Linie wie johnstraße!

Sie müssen einmal Umsteigen:
alser straße ist auf der selben Linie wie westbahnhof. Nehmen Sie die U6 und fahren Sie für 4 Stationen zu westbahnhof in Richtung siebenhirten

Steigen Sie nun um in die U3
westbahnhof ist auf der selben Linie wie johnstraße. Nehmen Sie die U3 und fahren Sie für 2 Stationen zu johnstraße in Richtung ottakring

Sie haben Ihr Ziel erreicht

falls Sie möchten können Sie mit dem Funktionsaufruf unten eigene Stationen eingeben. Dazu muss die station aber in Kleinbuchstaben angegeben werden. lower() habe ich nur für die Benutzeroberfläche verwendet!

In [186]:
searchPath("karlsplatz", "praterstern")
karlsplatz ist auf der selben Linie wie praterstern. Nehmen Sie die U2 und fahren Sie für 7 Stationen zu praterstern in Richtung seestadt
In [ ]: