Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions configuration/test_denver.toml
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,17 @@ tree = "geo_json"
[[plugin.output_plugins]]
type = "bambam"
format = "aggregate"
binning = { type = "time", feature = "trip_time", values = [10,20,30,40], unit = "minutes" }
geometry_model = { type = "destination_point" }
isochrone_algorithm = { type = "concave_hull", concavity = 2.0, simplify_epsilon_latlon = 0.00135 }
isochrone_format = "wkb"

binning.type = "global"
binning.value = { type = "time", feature = "trip_time", values = [10,20,30,40], unit = "minutes" }

geometry_model.type = "global"
geometry_model.value = { type = "destination_point" }

isochrone_algorithm.type = "global"
isochrone_algorithm.value = { type = "concave_hull", concavity = 2.0, simplify_epsilon_latlon = 0.00135 }

[[plugin.output_plugins]]
type = "isochrone"

Expand Down
12 changes: 9 additions & 3 deletions configuration/test_gtfs_config_boulder.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,17 @@ tree = "geo_json"
[[plugin.output_plugins]]
type = "bambam"
format = "aggregate"
binning = { type = "time", feature = "trip_time", values = [10,20,30,40], unit = "minutes" }
geometry_model = { type = "destination_point" }
isochrone_algorithm = { type = "k_nearest_concave_hull", k = 3 }
isochrone_format = "wkb"

binning.type = "global"
binning.value = { type = "time", feature = "trip_time", values = [10,20,30,40], unit = "minutes" }

geometry_model.type = "global"
geometry_model.value = { type = "destination_point" }

isochrone_algorithm.type = "global"
isochrone_algorithm.value = { type = "k_nearest_concave_hull", k = 3 }

[[plugin.output_plugins]]
type = "isochrone"

Expand Down
12 changes: 9 additions & 3 deletions configuration/test_gtfs_config_denver_rtd.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,17 @@ tree = "geo_json"
[[plugin.output_plugins]]
type = "bambam"
format = "aggregate"
binning = { type = "time", feature = "trip_time", values = [10,20,30,40], unit = "minutes" }
geometry_model = { type = "destination_point" }
isochrone_algorithm = { type = "k_nearest_concave_hull", k = 3 }
isochrone_format = "wkb"

binning.type = "global"
binning.value = { type = "time", feature = "trip_time", values = [10,20,30,40], unit = "minutes" }

geometry_model.type = "global"
geometry_model.value = { type = "destination_point" }

isochrone_algorithm.type = "global"
isochrone_algorithm.value = { type = "k_nearest_concave_hull", k = 3 }

[[plugin.output_plugins]]
type = "isochrone"

Expand Down
4 changes: 2 additions & 2 deletions rust/bambam-core/src/model/bambam_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use serde_json::{json, Value};
pub const TIME_BINS: &str = "bin";
pub const TIME_BIN: &str = "time_bin";
pub const INFO: &str = "info";
pub const MODE: &str = "mode";
pub const TRIP_MODE: &str = "trip_mode";
pub const OUTPUT_CONFIG: &str = "output_config";
pub const ISOCHRONE: &str = "isochrone";
pub const ISOCHRONE_ALGORITHM: &str = "isochrone_algorithm";
Expand Down Expand Up @@ -54,7 +54,7 @@ pub mod get {
};

pub fn mode(value: &Value) -> Result<String, OutputPluginError> {
let path = ["request", super::MODE];
let path = ["request", super::TRIP_MODE];
super::get_nested(value, &path).map_err(|e| {
let dot_path = path.join(".");
OutputPluginError::OutputPluginFailed(format!(
Expand Down
6 changes: 3 additions & 3 deletions rust/bambam-core/src/model/bambam_typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ pub struct RequestSection<'a>(&'a Value);

impl<'a> RequestSection<'a> {
/// Returns the transport `mode` string (e.g. `"car"`, `"transit"`).
pub fn get_mode(&self) -> Result<String, OutputPluginError> {
get_field(self.0, bambam_field::MODE)
pub fn get_trip_mode(&self) -> Result<String, OutputPluginError> {
get_field(self.0, bambam_field::TRIP_MODE)
}
}

Expand Down Expand Up @@ -576,7 +576,7 @@ mod tests {
fn request_section_get_mode() {
let mut value = json!({"request": {"mode": "transit"}});
let row = BambamOutputRow::new(&mut value);
assert_eq!(row.request().unwrap().get_mode().unwrap(), "transit");
assert_eq!(row.request().unwrap().get_trip_mode().unwrap(), "transit");
}

/// Request section: missing request key returns error.
Expand Down
64 changes: 64 additions & 0 deletions rust/bambam-core/src/model/output_plugin/global_or_modal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::collections::HashMap;

use routee_compass::plugin::output::OutputPluginError;
use serde::{Deserialize, Serialize};

/// a configuration that can either be defined for all modes or defined
/// to vary by mode.
///
/// Note: if modal is selected, then all modes must be represented in the `values`,
/// unless a fallback is provided.
///
/// # Global Example
///
/// All modes using the same time binning configuration:
///
/// ```toml
/// binning.type = "global"
/// binning.value = { type = "time", feature = "trip_time", values = [10,20,30,40], unit = "minutes" }
/// ```
///
/// # Modal Example
///
/// Geometry Model linestring sampling density varying by mode:
///
/// ```toml
/// [plugin.output_plugins.geometry_model]
/// type = "modal"
/// values.walk = { type = "linestring_stride", stride = 20.0, distance_unit = "meters" }
/// values.bike = { type = "linestring_stride", stride = 50.0, distance_unit = "meters" }
/// values.transit = { type = "linestring_stride", stride = 50.0, distance_unit = "meters" }
/// values.drive = { type = "linestring_stride", stride = 150.0, distance_unit = "meters" }
/// ```
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum GlobalOrModal<T> {
Global {
value: T,
},
Modal {
values: HashMap<String, T>,
fallback: Option<T>,
},
}

impl<T> GlobalOrModal<T> {
/// gets the configuration value. if the user has provided a global value, we are done.
/// if the user has provided modal values, we first check if this mode has a matching config.
/// if not, we attempt to draw from the fallback value.
pub fn get_value(&self, mode: &str) -> Result<&T, OutputPluginError> {
match self {
GlobalOrModal::Global { value } => Ok(value),
GlobalOrModal::Modal { values, fallback } => {
if let Some(value) = values.get(mode) {
return Ok(value);
}
fallback.as_ref().ok_or_else(|| {
let name = std::any::type_name::<T>();
let msg = format!("user specified modal configuration for {name} but mode '{mode}' not found and no fallback specified.");
OutputPluginError::OutputPluginFailed(msg)
})
}
}
}
}
2 changes: 2 additions & 0 deletions rust/bambam-core/src/model/output_plugin/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod global_or_modal;
pub mod isochrone;
pub mod opportunity;
mod output_config;

pub use global_or_modal::GlobalOrModal;
pub use output_config::BambamOutputConfig;
9 changes: 5 additions & 4 deletions rust/bambam-core/src/model/output_plugin/output_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::model::{
output_plugin::{
isochrone::{GeometryModelConfig, IsochroneAlgorithm, IsochroneOutputFormat},
opportunity::OpportunityOrientation,
GlobalOrModal,
},
};
use serde::{Deserialize, Serialize};
Expand All @@ -14,15 +15,15 @@ use serde::{Deserialize, Serialize};
pub enum BambamOutputConfig {
Aggregate {
/// the method for binning the output. required when opportunity_format == Aggregate.
binning: BinningConfig,
binning: GlobalOrModal<BinningConfig>,
/// any additional filters to apply when selecting destinations. optional for both
/// opportunity_formats.
destination_filter: Option<Vec<DestinationPredicate>>,
destination_filter: Option<GlobalOrModal<Vec<DestinationPredicate>>>,
/// algorithm for assigning physical destination locations from a search tree branch.
/// used in the isochrone drawing procedure.
geometry_model: GeometryModelConfig,
geometry_model: GlobalOrModal<GeometryModelConfig>,
/// algorithm used to draw isochrones from the destination points.
isochrone_algorithm: IsochroneAlgorithm,
isochrone_algorithm: GlobalOrModal<IsochroneAlgorithm>,
/// geometry format to use when writing isochrones.
isochrone_format: IsochroneOutputFormat,
/// location along a roadway where the opportunity is map matched. by default,
Expand Down
12 changes: 9 additions & 3 deletions rust/bambam-omf/src/util/bambam-config-omf.toml
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,17 @@ type = "traversal"
[[plugin.output_plugins]]
type = "bambam"
format = "aggregate"
binning = { type = "time", feature = "trip_time", values = [10,20,30,40], unit = "minutes" }
geometry_model = { type = "destination_point" }
isochrone_algorithm = { type = "k_nearest_concave_hull", k = 3 }
isochrone_format = "wkb"

binning.type = "global"
binning.value = { type = "time", feature = "trip_time", values = [10,20,30,40], unit = "minutes" }

geometry_model.type = "global"
geometry_model.value = { type = "destination_point" }

isochrone_algorithm.type = "global"
isochrone_algorithm.value = { type = "k_nearest_concave_hull", k = 3 }


[[plugin.output_plugins]]
type = "isochrone"
Expand Down
15 changes: 10 additions & 5 deletions rust/bambam/src/model/output_plugin/bambam/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ impl OutputPlugin for BambamOutputPlugin {
_result: &Result<(SearchAppResult, SearchInstance), CompassAppError>,
) -> Result<(), OutputPluginError> {
let mut row = bambam_typed::BambamOutputRow::new(output);
let req = row.request()?;
let mode = req.get_trip_mode()?;
let mut info = row.info_mut()?;

match &self.0 {
Expand All @@ -30,15 +32,18 @@ impl OutputPlugin for BambamOutputPlugin {
isochrone_format,
opportunity_orientation,
} => {
// set values provided by configuration
info.set_opportunity_format(OpportunityFormat::Aggregate)?;
info.set_opportunity_orientation(*opportunity_orientation)?;
info.set_bin_range(binning)?;
info.set_isochrone_format(isochrone_format)?;

// set values that may vary based on the travel mode.
info.set_bin_range(binning.get_value(&mode)?)?;
if let Some(f) = destination_filter {
info.set_destination_filter(f)?;
info.set_destination_filter(f.get_value(&mode)?)?;
}
info.set_geometry_model(geometry_model)?;
info.set_isochrone_algorithm(isochrone_algorithm)?;
info.set_isochrone_format(isochrone_format)?;
info.set_geometry_model(geometry_model.get_value(&mode)?)?;
info.set_isochrone_algorithm(isochrone_algorithm.get_value(&mode)?)?;
}
BambamOutputConfig::Disaggregate {
destination_filter,
Expand Down
Loading