1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use std::{fs, path::Path};
use clap::{Parser, Subcommand};
use config::{Config, File};
use geo::MapCoords;
use geozero::{wkt::Wkt as WktReader, ToGeo};
use serde::{Deserialize, Serialize};
use crate::{
app::{cli_bbox::parse_bbox, network::NetworkEdgeListConfiguration, CliBoundingBox},
collection::OvertureMapsCollectionError,
graph::island_detection::IslandDetectionAlgorithm,
};
/// Command line tool for batch downloading and summarizing of OMF (Overture Maps Foundation) data
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
pub struct OmfApp {
#[command(subcommand)]
pub op: OmfOperation,
}
#[derive(Debug, Clone, Serialize, Deserialize, Subcommand)]
pub enum OmfOperation {
/// download all of the OMF transportation data
Network {
/// descriptive user-provided name for this import region.
#[arg(short, long)]
name: String,
/// configuration file defining how the network is imported and separated
/// into mode-specific edge lists.
#[arg(short, long)]
configuration_file: String,
/// location on disk to write output files. if not provided,
/// use the current working directory.
#[arg(short, long)]
output_directory: Option<String>,
/// use a stored raw data export from a previous run of OmfOperation::Network
/// which is a JSON file containing a TransportationCollection.
#[arg(short, long)]
local_source: Option<String>,
/// write the raw OMF dataset as a JSON blob to the output directory.
#[arg(short, long)]
store_raw: bool,
/// bounding box to filter data (format: xmin,xmax,ymin,ymax)
#[arg(short, long, value_parser = parse_bbox, allow_hyphen_values(true))]
bbox: Option<CliBoundingBox>,
/// write the list of segment and connector IDs for each edge created
#[arg(long)]
omf_ids: bool,
/// Optional WKT extent in json format. expects a json file with a single "extent" key
#[arg(short, long)]
extent_file: Option<String>,
},
}
impl OmfOperation {
pub fn run(&self) -> Result<(), OvertureMapsCollectionError> {
match self {
OmfOperation::Network {
name,
configuration_file,
output_directory,
local_source,
store_raw,
bbox,
omf_ids,
extent_file,
} => {
let filepath = Path::new(configuration_file);
let config = Config::builder()
.add_source(File::from(filepath))
.build()
.map_err(|e| {
let msg = format!("file '{configuration_file}' produced error: {e}");
OvertureMapsCollectionError::InvalidUserInput(msg)
})?;
let network_config = config
.get::<Vec<NetworkEdgeListConfiguration>>("edge_lists")
.map_err(|e| {
let msg = format!(
"error reading 'edge_lists' key in '{configuration_file}': {e}"
);
OvertureMapsCollectionError::InvalidUserInput(msg)
})?;
let island_algorithm_configuration = config
.get::<Option<IslandDetectionAlgorithm>>(
"island_algorithm_configuration",
)
.map_err(|e| {
let msg = format!(
"error reading 'island_algorithm_configuration' key in '{configuration_file}': {e}"
);
OvertureMapsCollectionError::InvalidUserInput(msg)
})?;
let outdir = match output_directory {
Some(out) => Path::new(out),
None => Path::new(""),
};
let local = local_source.as_ref().map(Path::new);
let extent = extent_file
.as_ref()
.map(|extent_path| {
let wkt_str = fs::read_to_string(extent_path).map_err(|e| {
OvertureMapsCollectionError::InvalidUserInput(format!(
"failed to load extent file {extent_path}: {e}"
))
})?;
let geometry_f64 = WktReader(wkt_str.trim()).to_geo().map_err(|e| {
OvertureMapsCollectionError::InvalidUserInput(format!(
"failed to parse string into WKT from {extent_path}: {e}"
))
})?;
let polygon = geometry_f64.map_coords(|geo::Coord { x, y }| geo::Coord {
x: x as f32,
y: y as f32,
});
Ok(polygon)
})
.transpose()?;
crate::app::network::run(
name,
bbox.as_ref(),
&network_config,
outdir,
local,
*store_raw,
island_algorithm_configuration,
*omf_ids,
extent,
)
}
}
}
}