use crate::ArcGISGeometry;
use derive_getters::Getters;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Getters)]
#[serde(rename_all = "camelCase")]
pub struct NALocation {
geometry: ArcGISGeometry,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
curb_approach: Option<CurbApproach>,
#[serde(skip_serializing_if = "Option::is_none")]
bearing: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
bearing_tolerance: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
nav_latency: Option<f64>,
}
impl NALocation {
pub fn new(geometry: ArcGISGeometry) -> Self {
Self {
geometry,
name: None,
curb_approach: None,
bearing: None,
bearing_tolerance: None,
nav_latency: None,
}
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn with_curb_approach(mut self, approach: CurbApproach) -> Self {
self.curb_approach = Some(approach);
self
}
pub(crate) fn from_feature(feature: &crate::Feature) -> Self {
tracing::debug!("Converting FeatureSet feature to NALocation");
let attrs = feature.attributes();
let name = attrs
.get("Name")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let curb_approach =
attrs
.get("CurbApproach")
.and_then(|v| v.as_i64())
.and_then(|i| match i {
0 => Some(CurbApproach::EitherSide),
1 => Some(CurbApproach::RightSide),
2 => Some(CurbApproach::LeftSide),
3 => Some(CurbApproach::NoUTurn),
_ => None,
});
let geometry = feature
.geometry()
.as_ref()
.and_then(|old_geom| {
serde_json::to_value(old_geom)
.ok()
.and_then(|v| serde_json::from_value(v).ok())
})
.unwrap_or(ArcGISGeometry::Point(crate::ArcGISPoint::new(0.0, 0.0)));
Self {
geometry,
name,
curb_approach,
bearing: None,
bearing_tolerance: None,
nav_latency: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum CurbApproach {
#[serde(rename = "esriNAEitherSideOfVehicle")]
EitherSide = 0,
#[serde(rename = "esriNARightSideOfVehicle")]
RightSide = 1,
#[serde(rename = "esriNALeftSideOfVehicle")]
LeftSide = 2,
#[serde(rename = "esriNANoUTurn")]
NoUTurn = 3,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TravelMode {
DrivingTime,
DrivingDistance,
TruckingTime,
TruckingDistance,
WalkingTime,
WalkingDistance,
RuralDrivingTime,
RuralDrivingDistance,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ImpedanceAttribute {
#[serde(rename = "TravelTime")]
TravelTime,
#[serde(rename = "Miles")]
Miles,
#[serde(rename = "Kilometers")]
Kilometers,
#[serde(rename = "TimeAt1KPH")]
TimeAt1KPH,
#[serde(rename = "WalkTime")]
WalkTime,
#[serde(rename = "TruckTravelTime")]
TruckTravelTime,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum RestrictionAttribute {
#[serde(rename = "Avoid Toll Roads")]
AvoidTollRoads,
#[serde(rename = "Avoid Limited Access Roads")]
AvoidHighways,
#[serde(rename = "Avoid Unpaved Roads")]
AvoidUnpavedRoads,
#[serde(rename = "Avoid Ferries")]
AvoidFerries,
#[serde(rename = "Avoid Gates")]
AvoidGates,
#[serde(rename = "Oneway")]
Oneway,
#[serde(rename = "Height Restriction")]
HeightRestriction,
#[serde(rename = "Weight Restriction")]
WeightRestriction,
#[serde(rename = "Weight per Axle Restriction")]
WeightPerAxleRestriction,
#[serde(rename = "Length Restriction")]
LengthRestriction,
#[serde(rename = "Width Restriction")]
WidthRestriction,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum UTurnPolicy {
#[serde(rename = "esriNFSBAllowBacktrack")]
AllowBacktrack,
#[serde(rename = "esriNFSBAtDeadEndsOnly")]
AtDeadEndsOnly,
#[serde(rename = "esriNFSBAtDeadEndsAndIntersections")]
AtDeadEndsAndIntersections,
#[serde(rename = "esriNFSBNoBacktrack")]
NoBacktrack,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum OutputLine {
#[serde(rename = "esriNAOutputLineNone")]
None,
#[serde(rename = "esriNAOutputLineStraight")]
Straight,
#[serde(rename = "esriNAOutputLineTrueShapeWithMeasure")]
TrueShapeWithMeasure,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum TravelDirection {
#[serde(rename = "esriNATravelDirectionFromFacility")]
FromFacility,
#[serde(rename = "esriNATravelDirectionToFacility")]
ToFacility,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum BarrierType {
Point,
Line,
Polygon,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum DirectionsLength {
#[serde(rename = "esriNAMiles")]
Miles,
#[serde(rename = "esriNAKilometers")]
Kilometers,
#[serde(rename = "esriNAMeters")]
Meters,
#[serde(rename = "esriNAFeet")]
Feet,
#[serde(rename = "esriNAYards")]
Yards,
#[serde(rename = "esriNANauticalMiles")]
NauticalMiles,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum DirectionsTimeAttribute {
#[serde(rename = "TravelTime")]
TravelTime,
#[serde(rename = "WalkTime")]
WalkTime,
#[serde(rename = "TruckTravelTime")]
TruckTravelTime,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum DirectionsStyle {
#[serde(rename = "esriDMTStandard")]
Standard,
#[serde(rename = "esriDMTPrint")]
Print,
#[serde(rename = "esriDMTDesktop")]
Desktop,
#[serde(rename = "esriDMTNavigation")]
Navigation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum RouteShape {
#[serde(rename = "none")]
None,
#[serde(rename = "straight")]
Straight,
#[serde(rename = "true")]
True,
#[serde(rename = "trueShapeWithMeasures")]
TrueShapeWithMeasures,
}
#[derive(Debug, Clone, Serialize, derive_builder::Builder, Getters)]
#[builder(setter(into, strip_option))]
#[serde(rename_all = "camelCase")]
pub struct RouteParameters {
stops: Vec<NALocation>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
barriers: Option<Vec<NALocation>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
polyline_barriers: Option<Vec<ArcGISGeometry>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
polygon_barriers: Option<Vec<ArcGISGeometry>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
return_directions: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
return_routes: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
return_stops: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
return_barriers: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
out_sr: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
impedance_attribute: Option<ImpedanceAttribute>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
restriction_attribute_names: Option<Vec<RestrictionAttribute>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
attribute_parameter_values: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
use_hierarchy: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
start_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
uturn_policy: Option<UTurnPolicy>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
directions_length_units: Option<DirectionsLength>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
directions_time_attribute: Option<DirectionsTimeAttribute>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
directions_style: Option<DirectionsStyle>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
directions_language: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
preserve_first_stop: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
preserve_last_stop: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
find_best_sequence: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
return_to_start: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
use_time_windows: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
accumulate_attribute_names: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
output_lines: Option<OutputLine>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
travel_mode: Option<TravelMode>,
}
impl RouteParameters {
pub fn builder() -> RouteParametersBuilder {
RouteParametersBuilder::default()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Getters)]
#[serde(rename_all = "camelCase")]
pub struct RouteResult {
#[serde(default)]
routes: Vec<Route>,
#[serde(default)]
stops: Vec<Stop>,
#[serde(default)]
barriers: Vec<NALocation>,
#[serde(default)]
messages: Vec<NAMessage>,
}
impl<'de> serde::Deserialize<'de> for RouteResult {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{MapAccess, Visitor};
use std::fmt;
struct RouteResultVisitor;
impl<'de> Visitor<'de> for RouteResultVisitor {
type Value = RouteResult;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a RouteResult with FeatureSet routes and stops")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut routes_fs: Option<crate::FeatureSet> = None;
let mut stops_fs: Option<crate::FeatureSet> = None;
let mut barriers: Option<Vec<NALocation>> = None;
let mut messages: Option<Vec<NAMessage>> = None;
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"routes" => {
routes_fs = Some(map.next_value()?);
}
"stops" => {
stops_fs = Some(map.next_value()?);
}
"barriers" => {
barriers = Some(map.next_value()?);
}
"messages" => {
messages = Some(map.next_value()?);
}
_ => {
let _: serde::de::IgnoredAny = map.next_value()?;
}
}
}
let routes_fs = routes_fs.unwrap_or_default();
let stops_fs = stops_fs.unwrap_or_default();
let barriers = barriers.unwrap_or_default();
let messages = messages.unwrap_or_default();
tracing::debug!(
route_feature_count = routes_fs.features().len(),
stop_feature_count = stops_fs.features().len(),
"Deserializing RouteResult from FeatureSets"
);
let routes: Vec<Route> = routes_fs
.features()
.iter()
.map(Route::from_feature)
.collect();
let stops: Vec<Stop> = stops_fs.features().iter().map(Stop::from_feature).collect();
tracing::debug!(
route_count = routes.len(),
stop_count = stops.len(),
"Successfully deserialized RouteResult"
);
Ok(RouteResult {
routes,
stops,
barriers,
messages,
})
}
}
deserializer.deserialize_map(RouteResultVisitor)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
#[serde(rename_all = "camelCase")]
pub struct Route {
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "Total_Miles")]
#[serde(alias = "total_length")] total_length: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "Total_TravelTime")]
#[serde(alias = "total_time")] total_time: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "Total_DriveTime")]
#[serde(alias = "total_drive_time")] total_drive_time: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "Total_WaitTime")]
#[serde(alias = "total_wait_time")] total_wait_time: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
geometry: Option<ArcGISGeometry>,
#[serde(default)]
directions: Vec<Direction>,
#[serde(skip_serializing_if = "Option::is_none")]
start_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
end_time: Option<i64>,
}
impl Route {
fn from_feature(feature: &crate::Feature) -> Self {
tracing::debug!("Converting FeatureSet feature to Route");
let attrs = feature.attributes();
let name = attrs
.get("Name")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let total_length = attrs.get("Total_Miles").and_then(|v| v.as_f64());
let total_time = attrs.get("Total_TravelTime").and_then(|v| v.as_f64());
let total_drive_time = attrs.get("Total_DriveTime").and_then(|v| v.as_f64());
let total_wait_time = attrs.get("Total_WaitTime").and_then(|v| v.as_f64());
let geometry = feature
.geometry()
.as_ref()
.and_then(|old_geom| {
serde_json::to_value(old_geom)
.ok()
.and_then(|v| serde_json::from_value(v).ok())
})
.unwrap_or_else(|| {
ArcGISGeometry::Point(crate::ArcGISPoint::new(0.0, 0.0))
});
tracing::debug!(
name = ?name,
total_miles = ?total_length,
total_time_minutes = ?total_time,
"Extracted route data from feature"
);
Route {
name,
total_length,
total_time,
total_drive_time,
total_wait_time,
geometry: Some(geometry),
directions: Vec::new(), start_time: None, end_time: None, }
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
#[serde(rename_all = "camelCase")]
pub struct Stop {
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
geometry: Option<ArcGISGeometry>,
#[serde(skip_serializing_if = "Option::is_none")]
arrival_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
departure_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
wait_time: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
cumulative_length: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
sequence: Option<i32>,
}
impl Stop {
fn from_feature(feature: &crate::Feature) -> Self {
tracing::debug!("Converting FeatureSet feature to Stop");
let attrs = feature.attributes();
let name = attrs
.get("Name")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let geometry = feature
.geometry()
.as_ref()
.and_then(|old_geom| {
serde_json::to_value(old_geom)
.ok()
.and_then(|v| serde_json::from_value(v).ok())
})
.unwrap_or_else(|| {
ArcGISGeometry::Point(crate::ArcGISPoint::new(0.0, 0.0))
});
let arrival_time = None;
let departure_time = None;
let wait_time = None;
let cumulative_length = attrs.get("Cumul_Miles").and_then(|v| v.as_f64());
let sequence = attrs
.get("Sequence")
.and_then(|v| v.as_i64())
.map(|i| i as i32);
tracing::debug!(
name = ?name,
sequence = ?sequence,
cumul_miles = ?cumulative_length,
"Extracted stop data from feature"
);
Stop {
name,
geometry: Some(geometry),
arrival_time,
departure_time,
wait_time,
cumulative_length,
sequence,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
#[serde(rename_all = "camelCase")]
pub struct Direction {
#[serde(skip_serializing_if = "Option::is_none")]
text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
length: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
time: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
geometry: Option<ArcGISGeometry>,
#[serde(skip_serializing_if = "Option::is_none")]
maneuver_type: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Getters)]
#[serde(rename_all = "camelCase")]
pub struct NAMessage {
#[serde(rename = "type")]
message_type: i32,
description: String,
}
#[derive(Debug, Clone, Serialize, derive_builder::Builder, Getters)]
#[builder(setter(into, strip_option))]
#[serde(rename_all = "camelCase")]
pub struct ServiceAreaParameters {
facilities: Vec<NALocation>,
default_breaks: Vec<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
barriers: Option<Vec<NALocation>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
polyline_barriers: Option<Vec<ArcGISGeometry>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
polygon_barriers: Option<Vec<ArcGISGeometry>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
travel_direction: Option<TravelDirection>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
out_sr: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
impedance_attribute: Option<ImpedanceAttribute>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
merge_similar_polygon_ranges: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
split_polygons_at_breaks: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
trim_outer_polygon: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
trim_polygon_distance: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
time_of_day: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
use_hierarchy: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
uturn_policy: Option<UTurnPolicy>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
restriction_attribute_names: Option<Vec<RestrictionAttribute>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
travel_mode: Option<TravelMode>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
return_facilities: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
return_barriers: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
return_polygons: Option<bool>,
}
impl ServiceAreaParameters {
pub fn builder() -> ServiceAreaParametersBuilder {
ServiceAreaParametersBuilder::default()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(bound(deserialize = "T: serde::Deserialize<'de>"))]
struct ServiceAreaFeatureSet<T> {
#[serde(default)]
features: Vec<T>,
}
impl<T> Default for ServiceAreaFeatureSet<T> {
fn default() -> Self {
Self {
features: Vec::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ServiceAreaResult {
#[serde(default, rename = "saPolygons")]
sapolygons_raw: ServiceAreaFeatureSet<ServiceAreaPolygon>,
#[serde(default, rename = "saPolylines")]
sapolylines_raw: ServiceAreaFeatureSet<ServiceAreaPolyline>,
#[serde(default)]
facilities: Vec<NALocation>,
#[serde(default)]
messages: Vec<NAMessage>,
}
impl ServiceAreaResult {
pub fn service_area_polygons(&self) -> &[ServiceAreaPolygon] {
&self.sapolygons_raw.features
}
pub fn service_area_polylines(&self) -> &[ServiceAreaPolyline] {
&self.sapolylines_raw.features
}
pub fn facilities(&self) -> &[NALocation] {
&self.facilities
}
pub fn messages(&self) -> &[NAMessage] {
&self.messages
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
#[serde(rename_all = "camelCase")]
pub struct ServiceAreaPolygon {
#[serde(skip_serializing_if = "Option::is_none")]
facility_id: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
from_break: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
to_break: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
geometry: Option<ArcGISGeometry>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
#[serde(rename_all = "camelCase")]
pub struct ServiceAreaPolyline {
#[serde(skip_serializing_if = "Option::is_none")]
facility_id: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
from_break: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
to_break: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
geometry: Option<ArcGISGeometry>,
}
#[derive(Debug, Clone, Serialize, derive_builder::Builder, Getters)]
#[builder(setter(into, strip_option))]
#[serde(rename_all = "camelCase")]
pub struct ClosestFacilityParameters {
incidents: Vec<NALocation>,
facilities: Vec<NALocation>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
default_target_facility_count: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
barriers: Option<Vec<NALocation>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
polyline_barriers: Option<Vec<ArcGISGeometry>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
polygon_barriers: Option<Vec<ArcGISGeometry>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
travel_direction: Option<TravelDirection>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
out_sr: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
impedance_attribute: Option<ImpedanceAttribute>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
accumulate_attribute_names: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
return_directions: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
return_routes: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
return_facilities: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
return_incidents: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
return_barriers: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
output_lines: Option<OutputLine>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
time_of_day: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
use_hierarchy: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
uturn_policy: Option<UTurnPolicy>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
restriction_attribute_names: Option<Vec<RestrictionAttribute>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
travel_mode: Option<TravelMode>,
}
impl ClosestFacilityParameters {
pub fn builder() -> ClosestFacilityParametersBuilder {
ClosestFacilityParametersBuilder::default()
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct ClosestFacilityResult {
routes: Vec<Route>,
facilities: Vec<NALocation>,
incidents: Vec<NALocation>,
messages: Vec<NAMessage>,
}
impl ClosestFacilityResult {
pub fn routes(&self) -> &[Route] {
&self.routes
}
pub fn facilities(&self) -> &[NALocation] {
&self.facilities
}
pub fn incidents(&self) -> &[NALocation] {
&self.incidents
}
pub fn messages(&self) -> &[NAMessage] {
&self.messages
}
}
impl<'de> serde::Deserialize<'de> for ClosestFacilityResult {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{MapAccess, Visitor};
use std::fmt;
struct ClosestFacilityResultVisitor;
impl<'de> Visitor<'de> for ClosestFacilityResultVisitor {
type Value = ClosestFacilityResult;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(
"a ClosestFacilityResult with FeatureSet routes, facilities, and incidents",
)
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut routes_fs: Option<crate::FeatureSet> = None;
let mut facilities_fs: Option<crate::FeatureSet> = None;
let mut incidents_fs: Option<crate::FeatureSet> = None;
let mut messages: Option<Vec<NAMessage>> = None;
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"routes" => {
routes_fs = Some(map.next_value()?);
}
"facilities" => {
facilities_fs = Some(map.next_value()?);
}
"incidents" => {
incidents_fs = Some(map.next_value()?);
}
"messages" => {
messages = Some(map.next_value()?);
}
_ => {
let _: serde::de::IgnoredAny = map.next_value()?;
}
}
}
let routes_fs = routes_fs.unwrap_or_default();
let facilities_fs = facilities_fs.unwrap_or_default();
let incidents_fs = incidents_fs.unwrap_or_default();
let messages = messages.unwrap_or_default();
tracing::debug!(
route_feature_count = routes_fs.features().len(),
facility_feature_count = facilities_fs.features().len(),
incident_feature_count = incidents_fs.features().len(),
"Deserializing ClosestFacilityResult from FeatureSets"
);
let routes: Vec<Route> = routes_fs
.features()
.iter()
.map(Route::from_feature)
.collect();
let facilities: Vec<NALocation> = facilities_fs
.features()
.iter()
.map(NALocation::from_feature)
.collect();
let incidents: Vec<NALocation> = incidents_fs
.features()
.iter()
.map(NALocation::from_feature)
.collect();
tracing::debug!(
route_count = routes.len(),
facility_count = facilities.len(),
incident_count = incidents.len(),
"Successfully deserialized ClosestFacilityResult"
);
Ok(ClosestFacilityResult {
routes,
facilities,
incidents,
messages,
})
}
}
deserializer.deserialize_map(ClosestFacilityResultVisitor)
}
}
#[derive(Debug, Clone, Serialize, derive_builder::Builder, Getters)]
#[builder(setter(into, strip_option))]
#[serde(rename_all = "camelCase")]
pub struct ODCostMatrixParameters {
origins: Vec<NALocation>,
destinations: Vec<NALocation>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
barriers: Option<Vec<NALocation>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
polyline_barriers: Option<Vec<ArcGISGeometry>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
polygon_barriers: Option<Vec<ArcGISGeometry>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
travel_direction: Option<TravelDirection>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
out_sr: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
impedance_attribute: Option<ImpedanceAttribute>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
accumulate_attribute_names: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
time_of_day: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
use_hierarchy: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
uturn_policy: Option<UTurnPolicy>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
restriction_attribute_names: Option<Vec<RestrictionAttribute>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
travel_mode: Option<TravelMode>,
}
impl ODCostMatrixParameters {
pub fn builder() -> ODCostMatrixParametersBuilder {
ODCostMatrixParametersBuilder::default()
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ODCostMatrixResponse {
#[serde(default, rename = "odCostMatrix")]
od_cost_matrix: ODCostMatrixData,
#[serde(default)]
messages: Vec<NAMessage>,
}
#[derive(Debug, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
struct ODCostMatrixData {
#[serde(default)]
cost_attribute_names: Vec<String>,
#[serde(flatten)]
matrix: std::collections::HashMap<String, std::collections::HashMap<String, Vec<f64>>>,
}
#[derive(Debug, Clone, PartialEq, Getters)]
pub struct ODCostMatrixResult {
od_lines: Vec<ODLine>,
origins: Vec<NALocation>,
destinations: Vec<NALocation>,
messages: Vec<NAMessage>,
}
impl<'de> serde::Deserialize<'de> for ODCostMatrixResult {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let response = ODCostMatrixResponse::deserialize(deserializer)?;
let mut od_lines = Vec::new();
for (origin_id_str, destinations) in response.od_cost_matrix.matrix {
if origin_id_str == "costAttributeNames" {
continue;
}
let origin_id = origin_id_str.parse::<i32>().ok();
for (dest_id_str, costs) in destinations {
let destination_id = dest_id_str.parse::<i32>().ok();
let mut total_time = None;
let mut total_distance = None;
for (i, name) in response
.od_cost_matrix
.cost_attribute_names
.iter()
.enumerate()
{
if i < costs.len() {
match name.as_str() {
"TravelTime" | "Minutes" => total_time = Some(costs[i]),
"Miles" | "Kilometers" => total_distance = Some(costs[i]),
_ => {}
}
}
}
od_lines.push(ODLine {
origin_id,
destination_id,
total_time,
total_distance,
origin_name: None,
destination_name: None,
});
}
}
tracing::debug!(
od_line_count = od_lines.len(),
"Deserialized OD cost matrix result"
);
Ok(ODCostMatrixResult {
od_lines,
origins: Vec::new(),
destinations: Vec::new(),
messages: response.messages,
})
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
#[serde(rename_all = "camelCase")]
pub struct ODLine {
#[serde(skip_serializing_if = "Option::is_none")]
origin_id: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
destination_id: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
total_time: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
total_distance: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
origin_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
destination_name: Option<String>,
}