bachelor thesis submission

main
falk 2022-05-23 14:07:44 +02:00
commit 5b4edae20a
8 changed files with 911 additions and 0 deletions

96
.gitignore vendored Normal file
View File

@ -0,0 +1,96 @@
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Files generated by invoking Julia with --code-coverage
*.jl.cov
*.jl.*.cov
# Files generated by invoking Julia with --track-allocation
*.jl.mem
# System-specific files and directories generated by the BinaryProvider and BinDeps packages
# They contain absolute paths specific to the host computer, and so should not be committed
deps/deps.jl
deps/build.log
deps/downloads/
deps/usr/
deps/src/
# Build artifacts for creating documentation generated by the Documenter package
docs/build/
docs/site/
# File generated by Pkg, the package manager, based on a corresponding Project.toml
# It records a fixed state of all packages used by the project. As such, it should not be
# committed for packages, but should be committed for applications that require a static
# environment.
Manifest.toml
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
# the following file-extensions are created by the prototype. Therefore they will be ignored
*.osm
*.pdf
*.yaml

15
LICENSE Normal file
View File

@ -0,0 +1,15 @@
ISC License (ISC)
Copyright 2022 Falk Centner
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.

40
README.md Normal file
View File

@ -0,0 +1,40 @@
WARNING: This is a prototype which is still under development.
TODO: See source
# Required packages
- LightOSM.jl
- LightXML.jl
- Graphs.jl
- Cairo.jl
- MetaGraphs.jl
- HTTP.jl
- Plots.jl
- GraphPlot.jl
- GraphRecipes.jl
- Fontconfiq.jl
- Compose.jl
- YAML.jl
# Usage and Contributing
If there is an urgent usecase, here is a very short instruction:
Start the OSMTrainPath or DataGraph-class.
1.) Use the "addRelation(id)"/"addWay(id)"-function to download data from overpass.
2.) After you completed all downloads call the "createGraph()"-function.
3.) To see the data call "plotGraph()" and search for the "Buffer.pdf"-file in which the plot will be saved.
4.) Use "filterOnedirectional(startpoint-id, destinationpoint-id)"-function to get one specific directed graph.
5.) Plot again to see the filtered path.
6.) If there are maxspeeds "UNKNOWN" you must correct them with the "changeWaySpeed(way-id, newspeed)"-function.
7.) If there are more possible ways than one for your whished export, you must remove one way so there is no other possible way.
Therefore use the "removeWay(way-id)"-function.
8.) After you completed all maxspeeds and only one possibly way, you can export data to a YAML-file.
Therefore use the exportPathToYAML(description, filename, startpoint-id, destinationpoint-id).
Note that you have to name the filename like *name*.yaml.
# License
ISC-License
Copyright 2022 Falk Centner

239
src/DataGraph.jl Normal file
View File

@ -0,0 +1,239 @@
using Graphs, MetaGraphs, GraphPlot, Plots, GraphRecipes, Fontconfig, Cairo, Compose
include("./input.jl")
include("./output.jl")
nodeDict = Dict()
g = Graphs.SimpleDiGraph(1)
mdg = MetaDiGraph(g)
nodeXPositions = Float64[]
nodeYPositions = Float64[]
vecXCoordinates, vecYCoordinates = spring_layout(mdg) # This set the layout for the Graph-Packge, witch is used for plotting
nodeColorArray = []
nodeLabelArray = []
#
# The createGraph-function set a all data from the input-class in a MetaDiGraph.
# Therefore are some nested functions implemented.
#
# addDataToDiGraph creates the vertices and the edges in the MetaDiGraph. The data from the NamedTuples will be stored in these graph-objects.
#
# getNodeColorIndex returns an index-number which will be used later to color the nodes.
#
# getCartesianX and getCartesianY are used to transfer the lat&lon-coordinates in cartesian coordinates which are used for a plot of the graph (function->plotGraph()).
#
function createGraph()
g = Graphs.SimpleDiGraph(length(getFilteredNodeArray()))
global mdg=MetaDiGraph(g)
function addDatasToDiGraph(filteredNodeArray::Vector{Any},filteredWayArray::Vector{Any})
lonCorrection=0
for node in filteredNodeArray
lonCorrection=lonCorrection+node.lon
end
lonCorrection=lonCorrection/length(filteredNodeArray)
for node in filteredNodeArray
push!(nodeDict,node.nodeID=>(length(nodeDict)+1))
set_prop!(mdg,get(nodeDict,node.nodeID,"default value"),:id, node.nodeID)
push!(nodeColorArray,getNodeColorIndex(node.issignal,node.isswitch))
append!(nodeXPositions,getCartesianX(node.lon-lonCorrection,node.lat))
append!(nodeYPositions,getCartesianY(node.lon-lonCorrection,node.lat))
push!(nodeLabelArray,node.nodeID)
end
for way in filteredWayArray
bufferNode = first(way.containedNodeIDs)
for node in way.containedNodeIDs
if(node == first(way.containedNodeIDs))
else
Graphs.add_edge!(mdg,get(nodeDict,bufferNode.nodeID,"default value"),get(nodeDict,node.nodeID,"default value"))
set_prop!(mdg,Graphs.Edge(get(nodeDict,bufferNode.nodeID,"default value"),get(nodeDict,node.nodeID,"default value")), :maxspeed,way.vmax.forward)
set_prop!(mdg,Graphs.Edge(get(nodeDict,bufferNode.nodeID,"default value"),get(nodeDict,node.nodeID,"default value")), :wayID, way.wayID)
set_prop!(mdg,Graphs.Edge(get(nodeDict,bufferNode.nodeID,"default value"),get(nodeDict,node.nodeID,"default value")), :length, getEdgelength(node.lat,node.lon,bufferNode.lat,bufferNode.lon))
set_prop!(mdg,Graphs.Edge(get(nodeDict,bufferNode.nodeID,"default value"),get(nodeDict,node.nodeID,"default value")), :incline, way.incline.forward)
Graphs.add_edge!(mdg,get(nodeDict,node.nodeID,"default value"),get(nodeDict,bufferNode.nodeID,"default value"))
set_prop!(mdg,Graphs.Edge(get(nodeDict,node.nodeID,"default value"),get(nodeDict,bufferNode.nodeID,"default value")), :maxspeed,way.vmax.backward)
set_prop!(mdg,Graphs.Edge(get(nodeDict,node.nodeID,"default value"),get(nodeDict,bufferNode.nodeID,"default value")), :wayID, way.wayID)
set_prop!(mdg,Graphs.Edge(get(nodeDict,node.nodeID,"default value"),get(nodeDict,bufferNode.nodeID,"default value")), :length, getEdgelength(node.lat,node.lon,bufferNode.lat,bufferNode.lon))
set_prop!(mdg,Graphs.Edge(get(nodeDict,node.nodeID,"default value"),get(nodeDict,bufferNode.nodeID,"default value")), :incline, way.incline.backward)
bufferNode = node
end
end
end
end
function getNodeColorIndex(issignal::Bool,isswitch::Bool)
if(issignal==false&&isswitch==false)
return 1
elseif(issignal==true&&isswitch==false)
return 2
elseif(issignal==false&&isswitch==true)
return 3
else error("signal and switch in one node")
end
end
function getCartesianY(lon::Float64,lat::Float64)
return cos(deg2rad(lon))*cos(deg2rad(lat))*6371000
end
function getCartesianX(lon::Float64,lat::Float64)
return cos(deg2rad(lat))*sin(deg2rad(lon))*6371000
end
addDatasToDiGraph(getFilteredNodeArray(),getFilteredWayArray())
println("Graph created")
end
#
# The plotGraph-function is used by the user to plot the Graph in it's current data status.
# Therefore the allready filled Arrays with the Node-Coordinates will be converted to vectors (necessary for GraphPlot-Package).
# Afterwards the function starts to find the horizontal an vertical size for the plot to distort it to an satellite-like view.
# Then the function fills the wayLabelArray and the wayColorArray to set them in the graph. The data for the Nodes are allready setted in the nodeColorArray and the nodeLabelArray.
# Finaly the function starts the gplot-function from the GraphPlot-Package.
# You can change all plot-settings in the gplot-function as it's descripted in the GraphPlot-Package: https://github.com/JuliaGraphs/GraphPlot.jl
#
function plotGraph()
println("start building vectors")
vecXCoordinates = vec(nodeXPositions)
vecYCoordinates = vec(nodeYPositions)
horizontal = (maximum(vecXCoordinates)-minimum(vecXCoordinates))
vertical = (maximum(vecYCoordinates)-minimum(vecYCoordinates))
wayLabelArray = []
wayColorArray = []
graphEdges = collect(Graphs.edges(mdg))
# println(Graphs.ne(mdg)) # print's the actuell numbers of edges in the Graph
for graphEdge in graphEdges
push!(wayLabelArray,string(get_prop(mdg,graphEdge,:length))*" m, Way-ID="*string(get_prop(mdg,graphEdge,:wayID))*", maxspeed="*string(get_prop(mdg,graphEdge,:maxspeed))*", incline="*string(round(get_prop(mdg,graphEdge,:incline),digits=4)))
push!(wayColorArray, getEdgeColorIndex(get_prop(mdg,graphEdge,:maxspeed)))
end
nodeColor = [colorant"blue",colorant"red",colorant"orange"]
nodefillc = nodeColor[nodeColorArray]
edgestrokec = wayColorArray
println("start plotting")
draw(PDF("DataGraph-Plot.pdf",horizontal, vertical), gplot(mdg, vecXCoordinates, vecYCoordinates,nodelabel=nodeLabelArray, edgelabel=wayLabelArray, edgestrokec = edgestrokec, EDGELINEWIDTH = 1.0 / sqrt(Graphs.nv(g)),nodefillc = nodefillc ,arrowlengthfrac=0.0006 / sqrt(Graphs.nv(g)),NODESIZE = 0.0002 / sqrt(Graphs.nv(g))))
# draw(PDF("DataGraph-Plot.pdf"), gplot(mdg, vecXCoordinates, vecYCoordinates,nodelabel=nodeLabelArray, edgelabel=wayLabelArray, edgestrokec = edgestrokec, EDGELINEWIDTH = 1.0 / sqrt(Graphs.nv(g)),nodefillc = nodefillc ,arrowlengthfrac=0.0006 / sqrt(Graphs.nv(g)),NODESIZE = 0.0002 / sqrt(Graphs.nv(g))))
# draw(PDF("PlotSaveTestBS-UELZEN.pdf",horizontal, vertical), gplot(mdg, vecXN, vecYN, edgelabel=waylabel, edgestrokec = edgestrokec, EDGELINEWIDTH = 2.5 / sqrt(Graphs.nv(g)),nodefillc = nodefillc ,arrowlengthfrac=0.01 / sqrt(Graphs.nv(g)),NODESIZE = 0.005 / sqrt(Graphs.nv(g))))
println("Plot finished. Saved to DataGraph-Plot.pdf")
end
#
# The getYAMLExportArray-function is normaly used by the exportPathToYAML-function.
# It first finds a route and saves the points of the route in "pointsOfRoute".
# After that, the exportDataArray will be filled with the parameters for TrainRun.
# For the return this exportDataArray will be converted to a vector.
#
function getYAMLExportArray(startpoint::Int,destinationpoint::Int)
exportDataArray=[]
sumlength=0
pointsOfRoute = Graphs.enumerate_paths(Graphs.dijkstra_shortest_paths(mdg,get(nodeDict,string(startpoint),"default value")),get(nodeDict,string(destinationpoint),"default value"))
bufferNode = first(pointsOfRoute)
for node in pointsOfRoute
if(node==first(pointsOfRoute))
else
push!(exportDataArray,[round(get_prop(mdg,Graphs.Edge(bufferNode,node), :length)+sumlength,digits=2),parse(Int,get_prop(mdg,Graphs.Edge(bufferNode,node), :maxspeed)),round(get_prop(mdg,Graphs.Edge(bufferNode,node),:incline),digits=4)])
sumlength=sumlength+get_prop(mdg,Graphs.Edge(bufferNode,node), :length)
bufferNode=node
end
end
dataVector=vec(exportDataArray)
println("export-vector created")
return dataVector
end
#
# The exportPathToYAML-function calls an other function from the "output.jl"-class. Therefore it needs a description (saved in the yaml-file), a filename and the startpoint and the destinationpoint of the route.
# It doesn't matter if you've allready filtered the Path onedirectional or not.
# Note that it's possible to export only a part of the path of the plot.
#
function exportPathToYAML(description::String, filename::String, startpoint::Int,destinationpoint::Int)
createYAML(description, filename, getYAMLExportArray(startpoint,destinationpoint))
end
#
# The getEdgelength-function returns the length between two coordinate-positions.
# The length is calculated by a formula for spherical geometry, cause the standard coordinates (latitude & longitude) are for a globe and not a typical x-y-chart (cartesian).
# It returns a value in meter rounded to two positions after decimal point (cm).
#
function getEdgelength(lat1,lon1,lat2,lon2)
c=acos(sin(lat1*pi/180)*sin(lat2*pi/180)+cos(lat1*pi/180)*cos(lat2*pi/180)*cos(lon1*pi/180-lon2*pi/180))
length = round(c*6371000,digits=2)
if(length==0.00)
return 0.01 #This is necessary because TrainRun is unable to work with an edgelength below 0.01m
else return length
end
end
#
# The getEdgeColorIndex-function returns a color for a given String. It's normaly used by the plotGraph()-function.
#
function getEdgeColorIndex(maxspeed::String)
if(maxspeed=="UNKNOWN")
return colorant"orange"
elseif(maxspeed!="UNKNOWN")
return colorant"green"
end
end
#
# filterOnedirectional needs a startpoint and an endpoint to remove all unused edges.
# CAUTION!: This function is seraching for the shortest path in the graph.
# If there are multiple possiblitys to connect startpoint and endpoint, this function may not find the astimated path.
# Prepare the Graph with the "removeWayFromGraph()"-function so there is only one possible path in the graph.
#
function filterOnedirectional(startpoint::Int,destinationpoint::Int)
pointsOfRoute = Graphs.enumerate_paths(Graphs.dijkstra_shortest_paths(mdg,get(nodeDict,string(destinationpoint),"default value")),get(nodeDict,string(startpoint),"default value"))
remainingEdges = []
bufferNode = first(pointsOfRoute)
for node in pointsOfRoute
if(node==first(pointsOfRoute))
else
push!(remainingEdges,Graphs.Edge(node,bufferNode))
bufferNode=node
end
end
for edge in collect(MetaGraphs.edges(mdg))
if(in(edge,remainingEdges))
else MetaGraphs.rem_edge!(mdg,edge)
end
end
println("path filtered between "*string(startpoint)*" and "*string(destinationpoint))
end
#
# The changeWaySpeed-function changes the speed of a way. It doesn't matter what the previous value was.
# NOTE!: If you haven't allready filtered the path onedirectional, you change the speed for both directions.
# It ist highly recommended to filter onedirectional bevore using this function.
# To filter onedirectional you can use the filterOnedirectional-function.
#
function changeWaySpeed(wayID::Int,newSpeed::Int)
for edge in MetaGraphs.edges(mdg)
if(get_prop(mdg,edge, :wayID)==string(wayID))
set_prop!(mdg,edge, :maxspeed, string(newSpeed))
end
end
println("set new Wayspeed "*string(newSpeed)*" for Way "*string(wayID))
end
#
# The removeWayFromGraph-function removes a given way from the graph, if the given way (found by it's OSM-ID) is in the graph.
#
function removeWayFromGraph(wayID::Int)
for edge in collect(MetaGraphs.edges(mdg))
if(get_prop(mdg,edge, :wayID)==string(wayID))
clear_props!(mdg,edge)
Graphs.rem_edge!(mdg,edge)
end
end
println("Way "*string(wayID)*" was removed from the Graph")
end

12
src/OSMTrainPath.jl Normal file
View File

@ -0,0 +1,12 @@
# This is the main-module
# All relevant functions are callable from here
# This prototype can create path-files for the TrainRun-Tool (https://doi.org/10.5281/zenodo.6448563)
# Cause of some problems the typical structure with modules isn't yet setted
include("./input.jl")
include("./DataGraph.jl")
include("./output.jl")

53
src/download.jl Normal file
View File

@ -0,0 +1,53 @@
using LightOSM, HTTP, LightXML
#
# This function was created in the LightOSM-Package and is under the Copyright of this Package -> https://github.com/DeloitteDigitalAPAC/LightOSM.jl
# For the usage in this prototype the original LightOSM-function was addapted.
# The function checks if the overpass-server are available.
# Adaption of the original function: a print-information was removed.
#
function overpass_request(data::String)::String
LightOSM.check_overpass_server_availability()
return String(HTTP.post("http://overpass-api.de/api/interpreter",body=data).body)
end
#
# This function was created in the LightOSM-Package and is under the Copyright of this Package -> https://github.com/DeloitteDigitalAPAC/LightOSM.jl
# For the usage in this prototype the original LightOSM-function was addapted.
# It calls the overpass_request-function with the given string and a filepath for the datafile, which is also created with this function.
# The filetype is allways setted to "osm".
# Adaption of the original function: some given parameters were removed, additionally there usage in the function.
#
function download_osm_network(save_to_file_location,datas)::Union{XMLDocument,Dict{String,Any}}
data = overpass_request(datas)
#@info "Downloaded osm network data from $(["$k: $v" for (k, v) in download_kwargs]) in $download_format format"
if !(save_to_file_location isa Nothing)
save_to_file_location = LightOSM.validate_save_location(save_to_file_location, "osm")
write(save_to_file_location, data)
@info "Saved osm network data to disk: $save_to_file_location"
end
deserializer = LightOSM.string_deserializer(:osm)
return deserializer(data)
end
#
# This funtion is used to call the download_osm_network-function with a specific download-query-String, which is created by this function with the given OSM-ID.
#
function getOSMRelationXML(relationID::Int)
return download_osm_network("./Buffer.osm","[out:xml][timeout:25];relation("*string(relationID)*");(._;>>;);out;")
end
#
# This funtion is used to call the download_osm_network-function with a specific download-query-String, which is created by this function with the given OSM-ID.
#
function getOSMWayXML(wayID::Int)
return download_osm_network("./Buffer.osm","[out:xml][timeout:25];way("*string(wayID)*");(._;>>;);out;")
end

438
src/input.jl Normal file
View File

@ -0,0 +1,438 @@
using LightXML
include("./download.jl")
# `
# TODO: change variable names like Array(which are most time Any's).
# `
wayArray = []
nodeArray = []
filteredWayArray = []
filteredNodeArray = []
#
# This function returns the filteredWayArray (which is an Any).
#
function getFilteredWayArray()
return filteredWayArray
end
#
# This function returns the filteredNodeArray (which is an Any).
#
function getFilteredNodeArray()
return filteredNodeArray
end
#
# This function does a filtering of the given xroot and removes all members with a specific role (for example platform-data).
# To get the information which data are relevant, the function needs the relationID where the members are listet.
#
function filterAllWaysByDataFromRelation(relationID::Union{String, Int},xroot)
ID=""
if(typeof(relationID)==Int)
ID=string(relationID)
else
ID=relationID
end
xmlRelations=get_elements_by_tagname(xroot, "relation")
for relation in xmlRelations
if(attribute(relation,"id")==ID)
childnodesOfRelation = collect(child_nodes(relation))
for elements in childnodesOfRelation
if is_elementnode(elements)
if name(elements) == "member"
g = XMLElement(elements)
if(attribute(g,"role")=="")
addToFilteredWayArray(getWayByID(attribute(g,"ref")))
end
end
end
end
end
end
end
#
# This function checks the data-type of the given wayID and afterwards calls the addToFilteredWayArray-function with the ID.
#
function filterWayToFilteredWayArray(wayID::Union{String, Int})
ID=""
if(typeof(wayID)==Int)
ID=string(wayID)
else
ID=wayID
end
addToFilteredWayArray(getWayByID(ID))
end
#
# This function adds all nodes of all ways to the filteredNodeArray by calling the addToFilteredNodeArray-funtion.
#
function filterAllNodesByDataFromFilteredWayArray(filteredWayArray::Any)
for way in filteredWayArray
nodes = way.containedNodeIDs
for node in nodes
addToFilteredNodeArray(node)
end
end
end
#
# This function checks if a way is already saved in the filteredWayArray and returns the result with a Bool.
#
function checkIfAllreadyInFilteredWayArray(newway::NamedTuple)
for way in filteredWayArray
if(way==newway)
return true
end
end
return false
end
#
# This function checks if a node is already saved in the filteredNodeArray and returns the result with a Bool.
#
function checkIfAllreadyInFilteredNodeArray(newnode::NamedTuple)
for node in filteredNodeArray
if(node==newnode)
return true
end
end
return false
end
#
# This function adds a node to the filteredNodeArray if it's not already saved there.
#
function addToFilteredNodeArray(node::NamedTuple)
if(!checkIfAllreadyInFilteredNodeArray(node))
push!(filteredNodeArray,node)
end
end
#
# This function adds a way to the filteredWayArray if it's not already saved there.
#
function addToFilteredWayArray(way::NamedTuple)
if(!checkIfAllreadyInFilteredWayArray(way))
push!(filteredWayArray,way)
end
end
#
# This function returns a node-NamedTuple. Therefore the node-ID is required.
#
function getNodeByID(nodeID::Union{Int,String})
if(typeof(nodeID)==Int)
for node in nodeArray
if(node.nodeID==string(nodeID))
return node
end
end
else
for node in nodeArray
if(node.nodeID==nodeID)
return node
end
end
end
@debug "missed Node called. NodeID that is missed is: $nodeID"
end
#
# This function returns a way-NamedTuple from the wayArray by the wayID.
#
function getWayByID(wayID::Union{Int,String})
if(typeof(wayID)==Int)
for way in wayArray
if(way.wayID==string(wayID))
return way
end
end
else
for way in wayArray
if(way.wayID==wayID)
return way
end
end
end
@debug "missed Way called. WayID that is missed is: $wayID"
end
#
# This function returns a way in the filteredWayArray by the wayID.
#
function getWayFromFilteredWayArrayByID(wayID::Union{Int,String})
if(typeof(wayID)==Int)
for way in filteredWayArray
if(way.wayID==string(wayID))
return way
end
end
else
for way in filteredWayArray
if(way.wayID==wayID)
return way
end
end
end
@debug "missed Way called. WayID that is missed is: $wayID"
end
#
# The createNodes-function transfers the data from a given XMLElement to a NamedTuple and pushs this NamedTuple to the filteredNodeArray.
# In the transfer of the data the node-ID, the lat and lon coordinates and additional Bools for signal and switch information are stored in the NamedTuple.
#
function createNodes(xroot::XMLElement)
xmlNodes = get_elements_by_tagname(xroot, "node")
for node in xmlNodes
issignal = false
isswitch = false
if(has_children(node))
element = child_nodes(node)
for c in element
if(is_elementnode(c))
e= XMLElement(c)
if(attribute(e,"k")=="railway" && attribute(e,"v")=="signal")
issignal=true
end
if(attribute(e,"k")=="railway:switch"||(attribute(e,"k")=="railway"&&attribute(e,"v")=="switch"))
isswitch=true
end
end
end
end
nd = (nodeID = attribute(node,"id"), lat=parse(Float64,attribute(node,"lat")), lon=parse(Float64,attribute(node,"lon")), issignal=issignal, isswitch=isswitch)
push!(nodeArray,nd)
end
end
#
# The createWay-function transfers the data from a given XMLElement to a NamedTuple and pushs this NamedTuple to the filteredWayArray
# In the transfer of the data the way-ID, the containened Nodes (NamedTuples of the nodes), the maxspeed (NamedTuples because of direction-dependent maxspeeds) and the inclines (NamedTuples because of direction-dependent inclines) are stored in the NamedTuple.
#
function createWays(xroot::XMLElement)
xmlWays = get_elements_by_tagname(xroot, "way")
for way in xmlWays
w = (wayID = attribute(way,"id"), containedNodeIDs = getWayNodeIDs(way),vmax=getWayMaxspeed(way),incline=getWayIncline(way))
push!(wayArray,w)
end
end
#
# This function returns a NamedTuple with the direction-dependent inclines of a given way-XMLElement.
# The values in the OSM are percent while in german train-tracks the unit perthousand is common.
# To avoid an unit-change-mistake, the units are converted in this function.
#
function getWayIncline(xWay::XMLElement)
elements = child_nodes(xWay.node)
incline = 0.0
for e in elements
if(is_elementnode(e))
x = XMLElement(e)
if(attribute(x,"k")=="incline")
value = attribute(x,"v")
if(value=="down"||value=="up")
else incline = parse(Float64,replace(attribute(x,"v"),"%"=>""))
end
end
end
end
return (forward=incline*10,backward=incline*(-10)) # *10 necessary for change from % to per thousand -> (typically used for german-rail-incline)
end
#
# The getWayMaxspeed-function returns a NamedTuple with the OSM-maxspeed-data in which the maxspeed is listet with the direction for which the value is valid.
# If the OSM-data specify a direction with :forward and :backward, this will be safed in the NamedTuple.
# If there is no specification of the direction, both will be saved with the maxspeed-value.
# If there is no maxspeed in one or both directions, the value(s) will be saved with "UNKNOWN".
#
function getWayMaxspeed(xWay::XMLElement)
element = child_nodes(xWay.node)
maxspeedforward = "UNKNOWN"
maxspeedbackward = "UNKNOWN"
for c in element
if(is_elementnode(c))
e = XMLElement(c)
if(attribute(e,"k")=="maxspeed")
return maxspeed = (forward = attribute(e,"v"), backward = attribute(e,"v"))
elseif (attribute(e,"k")=="maxspeed:forward")
maxspeedforward = attribute(e,"v")
elseif (attribute(e,"k")=="maxspeed:backward")
maxspeedbackward = attribute(e,"v")
end
end
end
if(maxspeedforward=="UNKNOWN"&&maxspeedbackward=="UNKNOWN")
return maxspeed=(forward = "UNKNOWN", backward = "UNKNOWN")
else return maxspeed = (forward = maxspeedforward, backward = maxspeedbackward)
end
end
#
# This function returns an Any which contains the NamedTuples of the Nodes from the given way-XMLElement.
# To get the Nodes for the Any, the getNodeByID-funtion is used.
#
function getWayNodeIDs(xWay::XMLElement)
nodeIDs = []
element = child_nodes(xWay.node)
for c in element
if(is_elementnode(c))
e = XMLElement(c)
if(name(e)=="nd")
#push!(nodeIDs,(attribute(e,"ref")))
#or entweder nur die Nummer oder den ganzen Knoten
push!(nodeIDs,getNodeByID(attribute(e,"ref")))
end
end
end
# println(attribute(xWay, "id")," = ", length(nodeIDs))
return nodeIDs
end
#
# This function is used to add an OSM-Relation to the prototype memory.
# In contrast to the "addRelation"-funtion, this function reads the data from a given XML/OSM-file.
# Therefore the filepath and the OSM-Relation-ID is required.
# With these data the function calls the createWays/createNodes-functions and filters the NamedTuples with the filter***-functions.
#
function addRelationFromFile(id::Int,filepath::String)
xdoc = parse_file(filepath)
xroot = root(xdoc)
createNodes(xroot)
createWays(xroot)
println(length(nodeArray)," Nodes in nodeArray")
println(length(wayArray)," Ways in wayArray")
filterAllWaysByDataFromRelation(id,xroot)
filterAllNodesByDataFromFilteredWayArray(filteredWayArray)
println(length(filteredWayArray)," Ways in filteredWayArray")
println(length(filteredNodeArray)," Nodes in filteredNodeArray")
end
#
# This function is used to add an OSM-Way to the prototype memory.
# In contrast to the "addWay"-funtion this function reads the data from a given XML/OSM-file.
# Therefore the filepath and the OSM-Way-ID is required.
# With these data the function calls the createWays/createNodes-functions and filters the NamedTuples with the filter***-functions.
#
function addWayFromFile(id::Int,filepath::String)
xdoc = parse_file(filepath)
xroot = root(xdoc)
createNodes(xroot)
createWays(xroot)
println(length(nodeArray)," Nodes in nodeArray")
println(length(wayArray)," Ways in wayArray")
filterWayToFilteredWayArray(id)
filterAllNodesByDataFromFilteredWayArray(filteredWayArray)
println(length(filteredWayArray)," Ways in filteredWayArray")
println(length(filteredNodeArray)," Nodes in filteredNodeArray")
end
#
# This function is used to add an OSM-Relation to the prototype memory.
# It calls the Overpass-API via the download-class. Therefore the OSM-Relation-ID is required.
# After the download the function reads the Buffer.osm-file with the data and imports them.
# With these data the function calls the createWays/createNodes-functions and filters the NamedTuples with the filter***-functions.
#
function addRelation(id::Int)
getOSMRelationXML(id)
xdoc = parse_file("./Buffer.osm")
xroot = root(xdoc)
createNodes(xroot)
createWays(xroot)
println(length(nodeArray)," Nodes in nodeArray")
println(length(wayArray)," Ways in wayArray")
filterAllWaysByDataFromRelation(id,xroot)
filterAllNodesByDataFromFilteredWayArray(filteredWayArray)
println(length(filteredWayArray)," Ways in filteredWayArray")
println(length(filteredNodeArray)," Nodes in filteredNodeArray")
end
#
# This function is used to add an OSM-Way to the prototype memory.
# It calls the Overpass-API via the download-class. Therefore the OSM-Way-ID is required.
# After the download the function reads the Buffer.osm-file with the data and imports them.
# With these data the function calls the createWays/createNodes-functions and filters the NamedTuples with the filter***-functions.
#
function addWay(id::Int)
getOSMWayXML(id)
xdoc = parse_file("./Buffer.osm")
xroot = root(xdoc)
createNodes(xroot)
createWays(xroot)
println(length(nodeArray)," Nodes in nodeArray")
println(length(wayArray)," Ways in wayArray")
filterWayToFilteredWayArray(id)
filterAllNodesByDataFromFilteredWayArray(filteredWayArray)
println(length(filteredWayArray)," Ways in filteredWayArray")
println(length(filteredNodeArray)," Nodes in filteredNodeArray")
end
#
# This function is used to clear all collected data in any Arrays.
#
function clearAllArrays()
empty!(wayArray)
empty!(nodeArray)
empty!(filteredWayArray)
empty!(filteredNodeArray)
end
#
# This function prints the ways without maxspeed-information.
#
function checkWaySpeed()
waysWithoutSpeed=[]
for way in filteredWayArray
if(way.vmax.forward==""||way.vmax.backward=="")
push!(waysWithoutSpeed,way)
end
end
println("The following ways have none or one-directional only maxpeed")
for way in waysWithoutSpeed
println(way.wayID," maxspeed forward = ", way.vmax.forward, ", maxspeed backward = ",way.vmax.backward)
end
end
#
# This function can set the speed of a way. Therefore maxspeed-information for both direction and the OSM-ID of the Way is required.
#
function setWaySpeed(wayID::Union{Int,String}, maxspeedforward::Int, maxspeedbackward::Int)
w = getWayFromFilteredWayArrayByID(wayID)
newW = (wayID = w.wayID, containedNodeIDs = w.containedNodeIDs,vmax=(forward = string(maxspeedforward), backward = string(maxspeedbackward)))
replace!(filteredWayArray,w=>newW)
end
#
# This function can remove a way from the data. Therefore the OSM-ID of the Way is required.
#
function removeWay(id::Int)
println(indexin(getWayByID(id),filteredWayArray))
println("removed the way")
end

18
src/output.jl Normal file
View File

@ -0,0 +1,18 @@
using YAML
#
# The createYAML-function produce the YAML-output-file of this prototype.
# The file is named with the given filename which has to be in a "*name*.yaml" style (so with the .yaml).
# A given description is needed and will be setted in the file.
#
# In this version there is no information of the data-source in the file.
# TODO: add data-source information (OpenStreetMap).
#
function createYAML(description::String, filename::String, datas::Any)
dataVector=datas
d=Dict()
d2=Dict(:name => description, :sectionStarts => nothing, :sectionStarts_kmh => dataVector)
push!(d,:path => d2)
YAML.write_file(filename, d)
end