Skip to content

Commit

Permalink
Merge pull request #20 from NoahSchiro/v0.3.0-dev
Browse files Browse the repository at this point in the history
V0.3.0 dev
  • Loading branch information
NoahSchiro authored Sep 7, 2024
2 parents 44a7dcf + feee21d commit 7e421b7
Show file tree
Hide file tree
Showing 20 changed files with 364 additions and 464 deletions.
260 changes: 116 additions & 144 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "osmgraph"
authors = ["NoahSchiro"]
version = "0.2.0"
version = "0.3.0"
edition = "2021"

readme = "README.md"
Expand All @@ -23,14 +23,14 @@ name = "graph"
harness = false

[dev-dependencies]
tokio = { version = "1.39.2", features = ["macros"] } # Used for testing async functions
tokio = { version = "1.40", features = ["macros"] } # Used for testing async functions
plotters = "0.3.6" # This is used in parse_graph and astar examples
rand = "0.8.5" # This is used in astar example
criterion = "0.5.1" # For benchmarking

[dependencies]
petgraph = "0.6.5"
reqwest = { version = "0.12.5", features = ["json", "blocking"] }
tokio = { version = "1.39.2", features = ["rt-multi-thread", "fs"] }
serde = { version = "1.0.204", features = ["derive"] }
serde_json = "1.0.121"
reqwest = { version = "0.12.7", features = ["json", "blocking"] }
tokio = { version = "1.40", features = ["rt-multi-thread", "fs"] }
serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.128"
2 changes: 1 addition & 1 deletion assets/test.json

Large diffs are not rendered by default.

8 changes: 3 additions & 5 deletions benches/graph.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use osmgraph::{
overpass_api::OverpassResponse,
api::OverpassResponse,
graph::{
way::{OSMWay, get_osm_ways},
create_graph,
Expand All @@ -17,8 +17,7 @@ pub fn small_map_parsing(c: &mut Criterion) {
.expect("Was not able to load json!");

//Get the elements
let elements = json.elements().as_array()
.expect("Was not able to fetch elements from json!");
let elements = json.elements();

c.bench_function("node_parse", |b| b.iter(|| {
get_osm_nodes(elements).expect("Was not able to get nodes from json!")
Expand Down Expand Up @@ -49,8 +48,7 @@ pub fn large_map_parsing(c: &mut Criterion) {
.expect("Was not able to load json!");

//Get the elements
let elements = json.elements().as_array()
.expect("Was not able to fetch elements from json!");
let elements = json.elements();

group.bench_function("node_parse", |b| b.iter(|| {
get_osm_nodes(elements).expect("Was not able to get nodes from json!")
Expand Down
6 changes: 2 additions & 4 deletions examples/astar.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::collections::HashSet;
use std::error::Error;

use osmgraph::overpass_api::{QueryEngine, OverpassResponse};
use osmgraph::api::{QueryEngine, OverpassResponse, Element};
use osmgraph::graph::{OSMGraph, OSMNode, OSMEdge, create_graph};

use serde_json::Value;
use petgraph::graph::{Edge, NodeIndex};
use petgraph::algo::astar;

Expand Down Expand Up @@ -147,8 +146,7 @@ fn main() {
println!("Parsed the json!");

//Get the elements
let elements: &Vec<Value> = json.elements().as_array()
.expect("Was not able to get elements from json!");
let elements: &Vec<Element> = json.elements();
println!("{} elements in request", elements.len());

//Get the graph from the elements
Expand Down
6 changes: 2 additions & 4 deletions examples/parse_graph_from_place.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use std::error::Error;

use osmgraph::overpass_api::{QueryEngine, OverpassResponse};
use osmgraph::api::{QueryEngine, OverpassResponse, Element};
use osmgraph::graph::{OSMGraph, OSMNode, OSMEdge, create_graph};

use serde_json::Value;
use petgraph::stable_graph::DefaultIx;
use petgraph::graph::Edge;

Expand Down Expand Up @@ -117,8 +116,7 @@ fn main() {
println!("Parsed the json!");

//Get the elements
let elements: &Vec<Value> = json.elements().as_array()
.expect("Was not able to make request!");
let elements: &Vec<Element> = json.elements();
println!("{} elements in request", elements.len());

//Get the graph from the elements
Expand Down
6 changes: 2 additions & 4 deletions examples/parse_graph_from_polyline.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use std::error::Error;

use osmgraph::overpass_api::{QueryEngine, OverpassResponse};
use osmgraph::api::{QueryEngine, OverpassResponse, Element};
use osmgraph::graph::{OSMGraph, OSMNode, OSMEdge, create_graph};

use serde_json::Value;
use petgraph::stable_graph::DefaultIx;
use petgraph::graph::Edge;

Expand Down Expand Up @@ -125,8 +124,7 @@ fn main() {
println!("Parsed the json!");

//Get the elements
let elements: &Vec<Value> = json.elements().as_array()
.expect("Was not able to make request!");
let elements: &Vec<Element> = json.elements();
println!("{} elements in request", elements.len());

//Get the graph from the elements
Expand Down
7 changes: 2 additions & 5 deletions examples/parse_nodes.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use osmgraph::overpass_api::OverpassResponse;
use osmgraph::api::{OverpassResponse, Element};
use osmgraph::graph::{OSMNode, get_osm_nodes};

use serde_json::Value;

fn main() {

//Get json structure from disk
Expand All @@ -11,8 +9,7 @@ fn main() {
println!("Parsed the json!");

//Get the elements
let elements: &Vec<Value> = json.elements().as_array()
.expect("Was not able to fetch elements from json!");
let elements: &Vec<Element> = json.elements();
println!("{} elements in request", elements.len());

//Get the OSMNodes from the elements
Expand Down
6 changes: 2 additions & 4 deletions examples/parse_ways.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use osmgraph::overpass_api::OverpassResponse;
use osmgraph::api::{OverpassResponse, Element};
use osmgraph::graph::way::{OSMWay, get_osm_ways};

use serde_json::Value;

fn main() {

Expand All @@ -11,8 +10,7 @@ fn main() {
println!("Parsed the json...");

//Get the elements
let elements: &Vec<Value> = json.elements().as_array()
.expect("Was not able to parse elements from json!");
let elements: &Vec<Element> = json.elements();
println!("{} elements in request", elements.len());

//Get the OSMWay from the elements
Expand Down
13 changes: 5 additions & 8 deletions examples/query.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use osmgraph::overpass_api::{QueryEngine, OverpassResponse};

use serde_json::Value;
use osmgraph::api::{QueryEngine, OverpassResponse, Element};

fn main() {

//Create a query string in the format of the Overpass Query Language
let response = QueryEngine::new()
let response: String = QueryEngine::new()
.query_blocking(r#"
[out:json];
area[name="Manhattan"][admin_level=7]->.searchArea;
area[name="Selinsgrove"][admin_level=8]->.searchArea;
(
way["highway"~"motorway|trunk|primary|secondary|tertiary|unclassified|service|residential"](area.searchArea);
Expand All @@ -26,12 +24,11 @@ fn main() {
//Get json structure from the response string
let json: OverpassResponse = serde_json::from_str(&response)
.expect("Was not able to parse json!");

println!("Parsed the json!");

//Get the elements
let elements: &Vec<Value> = json.elements().as_array()
.expect("Was not able to fetch elements from json!");
let elements: &Vec<Element> = json.elements();

println!("{} elements in request", elements.len());
}
2 changes: 1 addition & 1 deletion examples/save_load.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use osmgraph::overpass_api::OverpassResponse;
use osmgraph::api::OverpassResponse;

fn main() {

Expand Down
23 changes: 23 additions & 0 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//! This module gives us all of the tools needed for interacting with the Overpass / OSM API
//! endpoint. This crate relies on [reqwest](https://docs.rs/reqwest/latest/reqwest/) to make this
//! possible but conviently hides the details of making HTTP requests to the endpoint.
//!
//! The API module provides to primary tools for the developer: the
//! [`crate::api::query_engine::QueryEngine`] and the
//! [`crate::api::overpass_response::OverpassResponse`].
//!
//! The query engine provides a simple interface for interacting with the API and the structure
//! provides some reasonable defaults of requests we might be interested in when we are using OSM
//! data for building graphs. Naturally, it's possible to modify these defaults to whatever your
//! end application demands.
//!
//! The overpass response exists because [serde_json](https://docs.rs/serde_json/latest/serde_json/)
//! can automatically parse json strings into structures, provided the structures have a shape that
//! matches the json. The response type from Overpass is very regular, so we can leverage this to
//! our advantage. If you are not using the Overpass API, you don't need this structure.
pub mod query_engine;
pub use query_engine::*;

pub mod overpass_response;
pub use overpass_response::*;
126 changes: 126 additions & 0 deletions src/api/overpass_response.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use std::io::Error;

use serde::{Serialize, Deserialize};
use serde_json::Value;

use tokio::{
fs::File,
io::{AsyncWriteExt, AsyncReadExt},
runtime::Runtime
};

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[serde(rename_all = "lowercase")]
#[serde(tag = "type")]
pub enum Element {
Node {
id: u64,
lat: f64,
lon: f64,
},
Way {
id: u64,
nodes: Vec<u64>,
tags: Option<Value>,
}
}

/// `OverpassResponse` is the basic structure that we expect the OSM to respond with.
/// Serde JSON helps us parse this string into the correct data structure.
///
/// Example:
/// ```rust
/// use osmgraph::api::{QueryEngine, OverpassResponse};
///
/// let engine = QueryEngine::new();
///
/// //Make the request
/// let response: String = engine.query_blocking(r#"
/// [out:json];
/// area[name="Selinsgrove"]->.searchArea;
/// (
/// way(area.searchArea);
/// node(area.searchArea);
/// );
/// out body;
/// >;
/// out skel qt;
/// "#.to_string()).expect("Was not able to request OSM!");
///
/// let json: OverpassResponse = serde_json::from_str(&response)
/// .expect("Was not able to parse json!");
/// ```
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
pub struct OverpassResponse {

//Graph data
elements: Vec<Element>,

//Metadata
generator: Value,
osm3s: Value,
version: Value
}

impl OverpassResponse {

/// Return the `elements` field from the response. This field is the most important as it
/// contains the actual graph information.
pub fn elements(&self) -> &Vec<Element> {
&self.elements
}
/// Return the `generator` field from the response.
pub fn generator(&self) -> &Value {
&self.generator
}
/// Return the `osm3s` field from the response.
pub fn osm3s(&self) -> &Value {
&self.osm3s
}
/// Return the `version` field from the response.
pub fn version(&self) -> &Value {
&self.version
}

/// Given a specified `filepath`, save the OverpassResponse to that location.
pub async fn save(&self, filepath: &str) -> Result<(), Error> {

let list_as_json = serde_json::to_string(self)?;
let mut file = File::create(filepath).await?;

file.write_all(list_as_json.as_bytes()).await?;
file.flush().await?;

Ok(())
}

/// Behaves the same as [`OverpassResponse::save`], but will wait for the function to finish before continuing.
pub fn save_blocking(&self, filepath: &str) -> Result<(), Error> {
Runtime::new()?
.block_on(self.save(filepath))
}

/// Given a specified `filepath`, load the OverpassResponse from that location. The file is
/// assumed to be a JSON and follow the structure of OverpassResponse.
pub async fn load(filepath: &str) -> Result<Self, Error> {

let mut file = File::open(filepath).await?;

let mut contents = Vec::new();

// Read the file's contents into the buffer
file.read_to_end(&mut contents).await?;

let contents_as_string: String = String::from_utf8_lossy(&contents).to_string();

let json: OverpassResponse = serde_json::from_str(&contents_as_string)?;

Ok(json)
}

/// Behaves the same as [`OverpassResponse::load`], but will wait for the function to finish before continuing.
pub fn load_blocking(filepath: &str) -> Result<Self, Error> {
Runtime::new()?
.block_on(Self::load(filepath))
}
}
Loading

0 comments on commit 7e421b7

Please sign in to comment.