Introduction
Welcome to the OpenRailwayMap Exporter documentation! This Rust-based tool retrieves railway data from OpenRailwayMap using the Overpass API and exports it into a custom format. It offers a command-line interface, web app, 3D visualization, Python bindings, and more.
In this documentation, you'll learn how to install and use OpenRailwayMap Exporter, as well as how to integrate it with other tools and platforms. We will cover topics such as installation, usage, command-line options, web app, 3D visualization, Python bindings, Jupyter Notebook, profiling, benchmarking, and contributing to the project.
Let's get started!
Getting Started
This chapter covers the basics of using the OpenRailwayMap Exporter. It will guide you through the installation process and demonstrate how to run the tool to retrieve railway data. Additionally, you will learn about the available command-line options and how to use them effectively.
1.1 Installation
To install OpenRailwayMap Exporter, you need to have Rust and Cargo installed on your system. If you haven't installed them yet, visit the official Rust website and follow the installation instructions for your operating system.
Once you have Rust and Cargo installed, you can clone the OpenRailwayMap Exporter repository from GitHub:
git clone https://github.com/chriamue/openrailwaymap-exporter.git
Navigate to the repository folder and build the project:
cd openrailwaymap-exporter
cargo build --release
After the build process is complete, you will find the compiled binary in the target/release folder.
1.2 Basic Usage
Now that you have successfully built the OpenRailwayMap Exporter, you can start using it to download railway data.
To download railway data within a bounding box around a specific location, run the following command:
cargo run -- --bbox "latitude_min,longitude_min,latitude_max,longitude_max"
Alternatively, you can download railway data for a specific area by running:
cargo run -- --area "Area Name"
Refer to the main documentation for more examples and detailed usage instructions.
1.3 Command-Line Options
OpenRailwayMap Exporter offers several command-line options that allow you to customize the output format and destination, as well as enable additional features. For a comprehensive list of command-line options and their usage, refer to the main documentation.
Some of the available command-line options include:
-j: Save the downloaded elements in a JSON file.-d: Save the railway graph in Graphviz format.--svg: Save the railway graph as an SVG image.-o: Specify the output file name.
For more information on using these options and others, consult the main documentation.
In the next chapter, we will explore the web application.
Web App
In this chapter, we will cover how to use the OpenRailwayMap Exporter web app to visualize and interact with the downloaded railway data in a browser environment.
2.1 Compiling to WebAssembly (WASM)
To run the web app, you'll first need to compile the OpenRailwayMap Exporter to WebAssembly (WASM) using the following command:
wasm-pack build --target web
This command will generate a pkg folder containing the compiled WASM files, JavaScript bindings, and other necessary files.
2.2 Running the Web App
After compiling the project to WASM, you can run the web app using a local web server. In this example, we'll use Python's built-in HTTP server. If you don't have Python installed, you can download it from the official Python website.
Run the following command to start the HTTP server:
python3 -m http.server
This command will start the server on port 8000 by default. If you want to use a different port, simply add the desired port number at the end of the command, like this:
python3 -m http.server 8080
2.3 Accessing the Web App in Your Browser
Once the HTTP server is running, open your browser and navigate to http://localhost:8000 (or replace 8000 with the port number you used). You should see the OpenRailwayMap Exporter web app in your browser.
From the web app, you can download railway data, visualize it on a map, and interact with the railway elements. You can also customize the appearance of the map, such as the colors and line styles, to better visualize the railway data.
In the next chapter, we'll explore the 3D visualization capabilities of the OpenRailwayMap Exporter.
The Railway Graph Importer
In this chapter, we will discuss the Railway Graph Importer, a core component of the OpenRailwayMap Exporter that is responsible for importing railway graph data from different formats and sources.
3.1 Understanding the Railway Graph Importer
The Railway Graph Importer is a module that defines a trait called RailwayGraphImporter. This trait provides a unified interface for importing railway graph data from various sources and formats, such as JSON, XML, or other custom formats. By implementing this trait, developers can create custom importers that seamlessly integrate with the OpenRailwayMap Exporter.
The RailwayGraphImporter trait has a single method, import, which takes a reference to a serde_json::Value and returns a Result<RailwayGraph>. The method is responsible for converting the input data into a RailwayGraph object that can be used by the rest of the OpenRailwayMap Exporter.
3.2 Implementing a Custom Railway Graph Importer
To create a custom railway graph importer, you'll need to implement the RailwayGraphImporter trait for your importer struct. This involves providing a definition for the import method that takes a serde_json::Value as input and returns a Result<RailwayGraph>.
Here's a basic outline of how you can implement the RailwayGraphImporter trait:
#![allow(unused)] fn main() { use crate::railway_model::RailwayGraph; use crate::importer::RailwayGraphImporter; use serde_json::Value; use anyhow::Result; pub struct MyCustomImporter; impl RailwayGraphImporter for MyCustomImporter { fn import(input: &Value) -> Result<RailwayGraph> { // Your custom importer logic goes here. // 1. Parse the input data. // 2. Create a new RailwayGraph instance. // 3. Add nodes and edges to the RailwayGraph. // 4. Return the RailwayGraph wrapped in a Result. // Example: // let graph = RailwayGraph::new(); // Ok(graph) } } }
With your custom importer implementation, you can now use it in the OpenRailwayMap Exporter to import railway graph data from your custom format or source.
3.3 Using the Overpass Importer
The OpenRailwayMap Exporter comes with a built-in importer called OverpassImporter that fetches railway data from the OpenRailwayMap using the Overpass API. This importer is an implementation of the RailwayGraphImporter trait and serves as an example of how to create custom importers.
To use the OverpassImporter, you can simply create a new instance of the importer and call the import method with the desired input data:
#![allow(unused)] fn main() { use crate::importer::OverpassImporter; use serde_json::Value; // Create a new OverpassImporter instance. let importer = OverpassImporter::new(); // Deserialize the input data as a serde_json::Value. let input_data: Value = serde_json::from_str(&input_json)?; // Import the railway graph using the OverpassImporter. let railway_graph = importer.import(&input_data)?; }
In the next chapter, we will explore the 3D visualization capabilities of the OpenRailwayMap Exporter.
Downloading Railway Graphs from OpenStreetMap
In this chapter, we will discuss how the OpenRailwayMap Exporter can download railway graphs from OpenStreetMap using a client that fetches data through an API.
4.1 The Railway API Client
The OpenRailwayMap Exporter includes a module called the Railway API Client, which provides a trait and an implementation for fetching railway infrastructure data from an API. The RailwayApiClient trait offers a common asynchronous interface for retrieving data by area name or bounding box.
By implementing the RailwayApiClient trait, developers can create custom API clients that can fetch railway graph data from different APIs or sources. The OpenRailwayMap Exporter comes with a built-in API client called the OverpassApiClient, which fetches railway data from OpenStreetMap using the Overpass API.
4.2 Fetching Railway Data by Area Name or Bounding Box
The RailwayApiClient trait has two primary methods for fetching railway data: fetch_by_area_name and fetch_by_bbox. The fetch_by_area_name method allows users to download railway data by specifying the name of an area, while the fetch_by_bbox method enables users to provide a bounding box to define the area of interest.
These methods return a JSON Value containing the fetched railway data, which can then be passed to a RailwayGraphImporter to import the railway graph data into the OpenRailwayMap Exporter.
4.3 Using the Overpass API Client
To use the built-in Overpass API client, you can create a new instance of the client and connect it to the OpenRailwayMap API using the desired URL. Once connected, you can fetch railway data by area name or bounding box:
#![allow(unused)] fn main() { use crate::api_client::OverpassApiClient; // Create a new OverpassApiClient instance. let mut client = OverpassApiClient::new(); // Connect to the OpenRailwayMap API. client.connect("https://overpass-api.de/api/interpreter").await?; // Fetch railway data by area name or bounding box. let area_name = "Frankfurt am Main"; let bbox = "49.9,8.4,50.2,8.8"; let railway_data_by_area = client.fetch_by_area_name(area_name).await?; let railway_data_by_bbox = client.fetch_by_bbox(bbox).await?; }
In summary, the Railway API Client is a crucial component of the OpenRailwayMap Exporter, enabling users to fetch railway data from OpenStreetMap and other sources easily. By implementing the RailwayApiClient trait, developers can create custom clients that fetch railway data from different APIs or sources, providing flexibility and extensibility to the OpenRailwayMap Exporter.
Python Bindings for OpenRailwayMap Exporter
In this chapter, we will discuss how to use the Python bindings for the OpenRailwayMap Exporter, allowing users to interact with railway graphs and import them using Python.
5.1 Python Wrapper for OverpassImporter
The OpenRailwayMap Exporter provides a Python wrapper for the OverpassImporter struct called PyOverpassImporter. This wrapper enables users to import railway graph data from a JSON string in Python. To create a new PyOverpassImporter instance, simply call its constructor:
import openrailwaymap_exporter
# Create a new PyOverpassImporter instance.
overpass_importer = openrailwaymap_exporter.PyOverpassImporter()
To import a railway graph from a JSON string, use the import_graph method:
input_json = "..."
railway_graph = overpass_importer.import_graph(input_json)
5.2 Python Wrapper for RailwayGraph
The OpenRailwayMap Exporter also provides a Python wrapper for the RailwayGraph struct, called PyRailwayGraph. This wrapper allows users to interact with railway graphs in Python, providing access to the following methods:
node_count: Get the number of nodes in the railway graph.edge_count: Get the number of edges in the railway graph.get_node_by_id: Get a node by its ID from the railway graph.get_edge_by_id: Get an edge by its ID from the railway graph.
# Get the number of nodes and edges in the railway graph.
node_count = railway_graph.node_count()
edge_count = railway_graph.edge_count()
# Get a node and an edge by their IDs.
node = railway_graph.get_node_by_id(node_id)
edge = railway_graph.get_edge_by_id(edge_id)
5.3 Exporting Railway Graphs to SVG
The Python bindings also provide a function, export_svg, that generates an SVG representation of a given PyRailwayGraph:
import openrailwaymap_exporter
# Export the railway graph to an SVG string.
svg_string = openrailwaymap_exporter.export_svg(railway_graph)
5.4 Summary
In this chapter, we have seen how the Python bindings for the OpenRailwayMap Exporter make it easy for users to interact with railway graphs and import them using Python. By providing Python wrappers for the OverpassImporter and RailwayGraph structs, along with an SVG export function, the OpenRailwayMap Exporter can be used in various Python applications, opening up new possibilities for developers and users alike.
Railway Model
The Railway Model module is a core component of the OpenRailwayMap Exporter. It provides data structures and functions for working with railway infrastructure data. The main components of this module are the RailwayNode, RailwayEdge, and RailwayGraph structs, as well as a RailwayGraphBuilder for creating RailwayGraphs from raw data.
RailwayNode
RailwayNode represents a single node in the railway network. A node can represent a railway station, a junction, or any other point of interest within the railway infrastructure. Each node has a unique ID, latitude, and longitude.
RailwayEdge
RailwayEdge represents a railway segment connecting two nodes in the railway network. Each edge has a unique ID, a length, and a set of attributes such as the track type, maximum speed, or electrification.
RailwayGraph
RailwayGraph is the main data structure for representing railway networks. It is an undirected graph consisting of RailwayNode instances as nodes and RailwayEdge instances as edges. The graph also stores a HashMap that maps node IDs to their corresponding indices in the graph for easy retrieval.
RailwayGraph provides several methods for working with railway networks, such as:
- Retrieving a node or an edge by its ID
- Retrieving the edge connecting two nodes
- Retrieving all edges connected to a node
- Calculating the bounding box of the graph
- Calculating the total length of the railway network
- Finding the nearest node to a given position on an edge
RailwayGraphBuilder
The RailwayGraphBuilder is a helper struct for constructing RailwayGraph instances from raw data. It provides methods for adding nodes and edges to the graph and ensures that the graph remains consistent during construction.
Usage Example
Suppose you have imported railway infrastructure data from OpenStreetMap using the Overpass API. You can create a RailwayGraph instance from this data using the RailwayGraphBuilder, like this:
#![allow(unused)] fn main() { let mut builder = RailwayGraphBuilder::new(); // Add nodes and edges from the raw data for node in raw_nodes { builder.add_node(node.id, node.lat, node.lon); } for edge in raw_edges { builder.add_edge(edge.id, edge.start_node_id, edge.end_node_id, edge.length, edge.attributes); } // Build the RailwayGraph let railway_graph = builder.build(); }
Now you can use the methods provided by the RailwayGraph struct to interact with the railway network. For example, you can find the nearest node to a given position on an edge like this:
#![allow(unused)] fn main() { let edge_id = 12345; let position_on_edge = 0.75; let current_node_id = Some(67890); let nearest_node_id = railway_graph.nearest_node(edge_id, position_on_edge, current_node_id); println!("The nearest node has ID: {}", nearest_node_id.unwrap()); }
The Railway Simulation
In this chapter, we discuss the implementation of a railway simulation, which consists of a railway graph representing the infrastructure and a list of movable railway objects, such as trains, within the simulation. The simulation module provides a Simulation struct to manage the state of the simulation, decision agents for the objects, and metrics handlers to process events and gather metrics during the simulation run.
The Simulation struct holds a simulation environment, a list of agents, a list of metrics handlers, elapsed time of the simulation, a pause state, and a speedup factor. The environment contains a railway graph and a list of movable railway objects. Agents are used to make decisions for objects, and metrics handlers process events and gather metrics.
The core functionality of the simulation is implemented in several methods:
new: Creates a new simulation with the given railway graph.get_observable_environment: Returns a reference to the observable environment of the simulation, which allows external components to access the state of the simulation without being able to modify it.add_object: Adds a movable railway object to the simulation and associates a decision agent with it if provided.remove_object: Removes a movable railway object from the simulation.add_agent_for_object: Adds a decision agent for an object in the simulation.register_metrics_handler: Registers a metrics handler for the simulation.handle_event: Handles a simulation event by passing it to all registered metrics handlers.update: Updates the simulation state based on the given delta time and the speedup factor. This method is called periodically to advance the simulation.update_object: Updates the state of the object with the given id based on the given delta time. It is called internally by theupdatemethod.update_object_position: Updates the position of the object with the given id based on the given delta time. It is called internally by theupdate_objectmethod.update_train_target: Updates the target of the train with the given id. It is called internally by theupdate_objectmethod.
The simulation also includes additional modules such as agents, environment, commands, events, and metrics to provide more specialized functionality.
The agents module contains types related to decision-making agents for the movable railway objects. The environment module contains types related to the simulation environment and an ObservableEnvironment trait to provide read-only access to the environment. The commands module contains types related to commands that can be issued to the movable railway objects. The events module contains types related to events that occur during the simulation. Finally, the metrics module contains types related to metrics handlers that process events and gather metrics during the simulation run.
In conclusion, the railway simulation module provides a comprehensive framework for creating, managing, and updating a railway simulation. The simulation consists of a railway graph, movable railway objects, decision agents, and metrics handlers to process events and gather metrics. The modular structure allows for easy extension and customization, making it a suitable choice for a wide range of railway simulation applications.
AI-Driven Train Control
In this chapter, we will discuss the use of artificial intelligence to control trains in a railway network simulation. The AI module developed in this project uses reinforcement learning (RL) to manage the train's actions, optimizing the train's movement through the network while ensuring safety and efficiency.
8.1 Reinforcement Learning
Reinforcement learning is a type of machine learning where an agent learns to make decisions by interacting with its environment. The agent performs actions in the environment and receives feedback in the form of rewards or penalties. By exploring different actions and learning from the consequences, the agent gradually improves its decision-making abilities.
8.2 TrainAgentAI
The TrainAgentAI struct represents a train agent controlled by a reinforcement learning algorithm. This struct contains several fields, including:
railway_graph: The railway graph representing the train networkcurrent_node: The current node the train is attarget_node: The target node the train is heading toagent_rl: The reinforcement learning agent responsible for controlling the traintrainer: The trainer responsible for training the reinforcement learning agent
8.3 Training the AI Agent
The train method of the TrainAgentAI struct is used to train the reinforcement learning agent for a specified number of iterations. During training, the agent explores different actions and updates its knowledge based on the rewards it receives. The agent uses Q-learning, a widely-used RL algorithm, to learn the best actions for each state.
8.4 Making Decisions
Once the AI agent is trained, it can make decisions based on the current state of the train in the simulation. The best_action method returns the best action for the given state according to the trained reinforcement learning agent.
8.5 Observing the Environment
The observe method allows the train agent to update its internal state based on the current position, target node, and other relevant information. This method ensures that the AI agent has the most up-to-date information about the environment when making decisions.
8.6 Implementing the DecisionAgent Trait
By implementing the DecisionAgent trait for the TrainAgentAI struct, we enable the AI agent to interact with the simulation environment. The next_action and observe methods from the trait implementation allow the AI agent to make decisions and update its state based on the simulation environment.
8.7 Testing the AI Agent
The test case in the tests module demonstrates how to create and train a TrainAgentAI instance. The test uses a sample railway graph and trains the AI agent for a specified number of iterations. After training, the agent can make decisions based on its current state, as shown by the best_action method.
In summary, this chapter has introduced an AI-driven approach to train control in a railway network simulation. Using reinforcement learning, the AI agent learns to make decisions that optimize train movement while maintaining safety and efficiency.