initial commit

This commit is contained in:
Marco Thomas
2022-07-27 12:12:05 +02:00
commit 0b29bdab3d
6 changed files with 275 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
__pycache__

85
classes.py Normal file
View File

@@ -0,0 +1,85 @@
from enum import Enum
import math
class ParsingMode(Enum):
LOCATIONS = 1
FLIGHTSCHEDULE = 2
FINDCONNECTION = 3
class LocationType(Enum):
LOCATION = 1
HALTESTELLE = 2
FLUGHAFEN = 3
class TransportData():
def __init__(self, co2: float, speed: float, distance_penalty: float):
self.co2 = co2
self.speed = speed
self.distance_penalty = distance_penalty
class TransportMethod(Enum):
"""
(co2 emission, speed, distance_penalty)
"""
WALK = TransportData(0, 4, 0.2)
CITY = TransportData(0.189, 30, 0.2)
AUTOBAHN = TransportData(0.189, 100, 0.2)
OEPNV = TransportData(0.055, 30, 0.1)
ICE = TransportData(0.055, 100, 0.1)
AIRPLANE = TransportData(0.2113, 900, 0.02)
class Coordinate:
def __init__(self, lat: float, long: float):
self.lat = lat
self.long = long
def __str__(self):
return f"Lat: {self.lat}, Long: {self.long}"
def __repr__(self):
return self.__str__()
class Location:
def __init__(self, coord: Coordinate, continent: int, name: str, type: LocationType):
self.coord = coord
self.continent = continent
self.name = name
self.type = type
def __str__(self):
return f"{{Location: {self.coord}, continent: {self.continent}, name: {self.name}, type: {self.type}}}"
def __repr__(self):
return self.__str__()
def distance(self, loc2) -> float:
"""
Calculate the distance im km between two locations with the help of the
Seitenkosinussatz
"""
rErde = 6378.388
lat1 = float(self.coord.lat)
long1 = float(self.coord.long)
lat2 = float(loc2.coord.lat)
long2 = float(loc2.coord.long)
inner = math.sin(lat1) * math.sin(lat2) + math.cos(lat1) * math.cos(lat2) * math.cos(long2 - long1)
return rErde * math.acos(inner)
class DataSet:
def __init__(self, locations: dict, flights: dict, connection: tuple):
self.locations = locations
self.flights = flights
self.connection = connection
def __str__(self):
return f"locations: {self.locations}, flights: {self.flights}, connection: {self.connection}"
def __repr__(self):
return self.__str__()

77
dijkstra.py Normal file
View File

@@ -0,0 +1,77 @@
"""
Module, which holds the logic for the solving algorithms
"""
from classes import DataSet, LocationType, Location, TransportMethod
def calc_co2(distance: float, method: TransportMethod) -> float:
"""
Return co2 emission in kg
"""
return (distance * method.value.distance_penalty) * method.value.co2
def calc_time(distance: float, method: TransportMethod) -> float:
"""
Return time taking in h
"""
return (distance * method.value.distance_penalty) / method.value.speed
def add_node(graph: dict, frm: str, to: str, locs: dict, method: TransportMethod):
"""
Add a node into the graph
"""
distance = locs[frm].distance(locs[to])
co2 = calc_co2(distance, method)
time = calc_time(distance, method)
new_connection: dict = {to: {"type": method, "co2": co2, "time": time}}
graph[frm].append(new_connection)
print(f"Added node from {frm} to {to} with type {method}.")
def create_graph(dataset: DataSet) -> dict:
"""
Creates the initial graph, with all edges
"""
locations = dataset.locations
graph: dict = {}
# add nodes with no edges
for start in locations:
graph.update({start: []})
# add edges
for start in locations:
for dest in locations:
# skip, if we wouldnt go anywhere
if start == dest:
continue
print(f"Searching for nodes from {start} to {dest}...")
distance = locations[start].distance(locations[dest])
# Individualtransport
if distance <= 1:
add_node(graph, start, dest, locations, TransportMethod.WALK)
if distance <= 10 + 1:
add_node(graph, start, dest, locations, TransportMethod.CITY)
if distance <= 2000 + 10 + 1:
add_node(graph, start, dest, locations, TransportMethod.AUTOBAHN)
# Train
if locations[start].type == LocationType.HALTESTELLE:
dest_type = locations[dest].type
# there are only trains between haltestellen or airports
if dest_type == LocationType.HALTESTELLE or dest_type == LocationType.FLUGHAFEN:
if distance <= 25:
add_node(graph, start, dest, locations, TransportMethod.OEPNV)
else:
add_node(graph, start, dest, locations, TransportMethod.ICE)
# Flying
flights = dataset.flights
for flight in flights:
add_node(graph, start, dest, locations, TransportMethod.AIRPLANE)
return graph

13
file.txt Normal file
View File

@@ -0,0 +1,13 @@
Locations:
csu_zen ; Location; 48.176971; 11.5895754; 1; CSU-Zentrale
mun_hbf ; PublicTransportStop; 48.140235; 11.559417; 1; Muenchen Hbf.
mun_flugh; Airport; 48.140235; 11.770723; 1; Muenchen Flughafen
reichstag; Location; 52.518191; 13.3751725; 1; Reichstags Gebaeude
ber_flugh; Airport; 52.553625; 13.2901544; 1; Berlin Flughafen Tegel
ber_hbf ; PublicTransportStop; 52.524195; 13.3693013; 1; Berlin Hbf
FlightSchedule:
mun_flugh; ber_flugh; 0; True
FindBestConnections:
csu_zen; muc

21
main.py Normal file
View File

@@ -0,0 +1,21 @@
"""
Run program from here.
Requires: python > 3.10.x
"""
from parser import parse
from dijkstra import create_graph
INPUTFILE = "file.txt"
def main():
dataset: dict = parse(INPUTFILE)
print(dataset)
graph: dict = create_graph(dataset)
print(graph)
if __name__ == "__main__":
main()

78
parser.py Normal file
View File

@@ -0,0 +1,78 @@
"""
Parse the input file
"""
from classes import ParsingMode, Coordinate, LocationType, Location, DataSet
def parse(filename: str) -> DataSet:
"""
Parse a given file with given format and return a DataSet containing the
parsed locations, flightschedules and wanted connection
"""
locations: dict = {}
flights: dict = {}
connection: tuple = ()
with open(filename, "r") as file:
for line in file.readlines():
line = line.replace("\n", "") # strip newline
line = line.replace(" ", "") # strip whitespaces
# skip empty lines
if line == "":
continue
# meta parsing
match line:
case "Locations:":
print("Parsing `Locations`...")
current_parsing = ParsingMode.LOCATIONS
continue
case "FlightSchedule:":
print("Parsing `FlightSchedule`...")
current_parsing = ParsingMode.FLIGHTSCHEDULE
continue
case "FindBestConnections:":
print("Parsing `FindBestConnections`...")
current_parsing = ParsingMode.FINDCONNECTION
continue
match current_parsing:
case ParsingMode.LOCATIONS:
print("Parsing location...")
splitted = line.split(";")
assert(len(splitted) == 6) # make sure we have a location
id = splitted[0]
type = splitted[1]
match type:
case "Location":
type = LocationType.LOCATION
case "PublicTransportStop":
type = LocationType.HALTESTELLE
case "Airport":
type = LocationType.FLUGHAFEN
lat = splitted[2]
long = splitted[3]
continent = splitted[4]
name = splitted[5]
coord = Coordinate(lat, long)
location = Location(coord, continent, name, type)
locations.update({id: location})
case ParsingMode.FLIGHTSCHEDULE:
print("Parsing flight schedule...")
splitted = line.split(";")
assert(len(splitted) >= 2)
id1 = splitted[0]
id2 = splitted[1]
if len(splitted) == 3:
stops = splitted[2]
else:
stops = 0
flights.update({id1: {"to": id2, "stops": stops}})
continue
case ParsingMode.FINDCONNECTION:
print("Parsing connection...")
splitted = line.split(";")
assert(len(splitted) == 2)
connection = (splitted[0], splitted[1])
return DataSet(locations, flights, connection)