Skip to main content

arcgis/services/routing/
types.rs

1//! Types for routing and network analysis operations.
2
3use crate::ArcGISGeometry;
4use derive_getters::Getters;
5use serde::{Deserialize, Serialize};
6
7/// A location for network analysis operations.
8///
9/// Can represent a stop, facility, incident, or barrier.
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Getters)]
11#[serde(rename_all = "camelCase")]
12pub struct NALocation {
13    /// Geometry of the location (typically a point).
14    geometry: ArcGISGeometry,
15
16    /// Optional name for the location.
17    #[serde(skip_serializing_if = "Option::is_none")]
18    name: Option<String>,
19
20    /// Optional curb approach.
21    #[serde(skip_serializing_if = "Option::is_none")]
22    curb_approach: Option<CurbApproach>,
23
24    /// Optional bearing angle (0-360 degrees).
25    #[serde(skip_serializing_if = "Option::is_none")]
26    bearing: Option<f64>,
27
28    /// Optional bearing tolerance (0-180 degrees).
29    #[serde(skip_serializing_if = "Option::is_none")]
30    bearing_tolerance: Option<f64>,
31
32    /// Optional navigation latency (seconds).
33    #[serde(skip_serializing_if = "Option::is_none")]
34    nav_latency: Option<f64>,
35}
36
37impl NALocation {
38    /// Creates a new NALocation from a geometry.
39    pub fn new(geometry: ArcGISGeometry) -> Self {
40        Self {
41            geometry,
42            name: None,
43            curb_approach: None,
44            bearing: None,
45            bearing_tolerance: None,
46            nav_latency: None,
47        }
48    }
49
50    /// Sets the name of the location.
51    pub fn with_name(mut self, name: impl Into<String>) -> Self {
52        self.name = Some(name.into());
53        self
54    }
55
56    /// Sets the curb approach.
57    pub fn with_curb_approach(mut self, approach: CurbApproach) -> Self {
58        self.curb_approach = Some(approach);
59        self
60    }
61}
62
63/// Curb approach for navigating to a location.
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
65pub enum CurbApproach {
66    /// Either side of the vehicle.
67    #[serde(rename = "esriNAEitherSideOfVehicle")]
68    EitherSide = 0,
69    /// Right side of the vehicle.
70    #[serde(rename = "esriNARightSideOfVehicle")]
71    RightSide = 1,
72    /// Left side of the vehicle.
73    #[serde(rename = "esriNALeftSideOfVehicle")]
74    LeftSide = 2,
75    /// No U-turn.
76    #[serde(rename = "esriNANoUTurn")]
77    NoUTurn = 3,
78}
79
80/// Travel mode for routing.
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
82#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
83pub enum TravelMode {
84    /// Driving time.
85    DrivingTime,
86    /// Driving distance.
87    DrivingDistance,
88    /// Trucking time.
89    TruckingTime,
90    /// Trucking distance.
91    TruckingDistance,
92    /// Walking time.
93    WalkingTime,
94    /// Walking distance.
95    WalkingDistance,
96    /// Rural driving time.
97    RuralDrivingTime,
98    /// Rural driving distance.
99    RuralDrivingDistance,
100}
101
102/// Impedance attribute for cost calculation.
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
104pub enum ImpedanceAttribute {
105    /// Travel time (minutes).
106    #[serde(rename = "TravelTime")]
107    TravelTime,
108    /// Miles.
109    #[serde(rename = "Miles")]
110    Miles,
111    /// Kilometers.
112    #[serde(rename = "Kilometers")]
113    Kilometers,
114    /// Time at one kilometer per hour.
115    #[serde(rename = "TimeAt1KPH")]
116    TimeAt1KPH,
117    /// Walk time.
118    #[serde(rename = "WalkTime")]
119    WalkTime,
120    /// Truck travel time.
121    #[serde(rename = "TruckTravelTime")]
122    TruckTravelTime,
123}
124
125/// Restriction attribute for routing.
126#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
127pub enum RestrictionAttribute {
128    /// Avoid toll roads.
129    #[serde(rename = "Avoid Toll Roads")]
130    AvoidTollRoads,
131    /// Avoid highways.
132    #[serde(rename = "Avoid Limited Access Roads")]
133    AvoidHighways,
134    /// Avoid unpaved roads.
135    #[serde(rename = "Avoid Unpaved Roads")]
136    AvoidUnpavedRoads,
137    /// Avoid ferries.
138    #[serde(rename = "Avoid Ferries")]
139    AvoidFerries,
140    /// Avoid gates.
141    #[serde(rename = "Avoid Gates")]
142    AvoidGates,
143    /// One way restriction.
144    #[serde(rename = "Oneway")]
145    Oneway,
146    /// Height restriction.
147    #[serde(rename = "Height Restriction")]
148    HeightRestriction,
149    /// Weight restriction.
150    #[serde(rename = "Weight Restriction")]
151    WeightRestriction,
152    /// Weight per axle restriction.
153    #[serde(rename = "Weight per Axle Restriction")]
154    WeightPerAxleRestriction,
155    /// Length restriction.
156    #[serde(rename = "Length Restriction")]
157    LengthRestriction,
158    /// Width restriction.
159    #[serde(rename = "Width Restriction")]
160    WidthRestriction,
161}
162
163/// U-turn policy at junctions.
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
165pub enum UTurnPolicy {
166    /// Allow U-turns anywhere.
167    #[serde(rename = "esriNFSBAllowBacktrack")]
168    AllowBacktrack,
169    /// Allow U-turns at dead ends only.
170    #[serde(rename = "esriNFSBAtDeadEndsOnly")]
171    AtDeadEndsOnly,
172    /// Allow U-turns at dead ends and intersections.
173    #[serde(rename = "esriNFSBAtDeadEndsAndIntersections")]
174    AtDeadEndsAndIntersections,
175    /// No U-turns.
176    #[serde(rename = "esriNFSBNoBacktrack")]
177    NoBacktrack,
178}
179
180/// Output line type for routes.
181#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
182pub enum OutputLine {
183    /// No line output (coordinates only).
184    #[serde(rename = "esriNAOutputLineNone")]
185    None,
186    /// Straight lines between stops.
187    #[serde(rename = "esriNAOutputLineStraight")]
188    Straight,
189    /// True shape with measures.
190    #[serde(rename = "esriNAOutputLineTrueShapeWithMeasure")]
191    TrueShapeWithMeasure,
192}
193
194/// Travel direction for analysis.
195#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
196pub enum TravelDirection {
197    /// From facility/origin.
198    #[serde(rename = "esriNATravelDirectionFromFacility")]
199    FromFacility,
200    /// To facility/destination.
201    #[serde(rename = "esriNATravelDirectionToFacility")]
202    ToFacility,
203}
204
205/// Type of barrier.
206#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
207pub enum BarrierType {
208    /// Point barrier.
209    Point,
210    /// Line barrier.
211    Line,
212    /// Polygon barrier.
213    Polygon,
214}
215
216/// Directions length units.
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
218pub enum DirectionsLength {
219    /// Miles.
220    #[serde(rename = "esriNAMiles")]
221    Miles,
222    /// Kilometers.
223    #[serde(rename = "esriNAKilometers")]
224    Kilometers,
225    /// Meters.
226    #[serde(rename = "esriNAMeters")]
227    Meters,
228    /// Feet.
229    #[serde(rename = "esriNAFeet")]
230    Feet,
231    /// Yards.
232    #[serde(rename = "esriNAYards")]
233    Yards,
234    /// Nautical miles.
235    #[serde(rename = "esriNANauticalMiles")]
236    NauticalMiles,
237}
238
239/// Directions time attribute.
240#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
241pub enum DirectionsTimeAttribute {
242    /// Travel time.
243    #[serde(rename = "TravelTime")]
244    TravelTime,
245    /// Walk time.
246    #[serde(rename = "WalkTime")]
247    WalkTime,
248    /// Truck travel time.
249    #[serde(rename = "TruckTravelTime")]
250    TruckTravelTime,
251}
252
253/// Directions style.
254#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
255pub enum DirectionsStyle {
256    /// Complete directions.
257    #[serde(rename = "esriDMTStandard")]
258    Standard,
259    /// Directions suitable for printing.
260    #[serde(rename = "esriDMTPrint")]
261    Print,
262    /// Directions for desktop applications.
263    #[serde(rename = "esriDMTDesktop")]
264    Desktop,
265    /// Directions for navigation devices.
266    #[serde(rename = "esriDMTNavigation")]
267    Navigation,
268}
269
270/// Shape type for route geometry.
271#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
272pub enum RouteShape {
273    /// No shape.
274    #[serde(rename = "none")]
275    None,
276    /// Straight line.
277    #[serde(rename = "straight")]
278    Straight,
279    /// True route shape.
280    #[serde(rename = "true")]
281    True,
282    /// True shape with measures.
283    #[serde(rename = "trueShapeWithMeasures")]
284    TrueShapeWithMeasures,
285}
286
287/// Parameters for route calculation.
288///
289/// Use [`RouteParameters::builder()`] to construct instances.
290#[derive(Debug, Clone, Serialize, derive_builder::Builder, Getters)]
291#[builder(setter(into, strip_option))]
292#[serde(rename_all = "camelCase")]
293pub struct RouteParameters {
294    /// Stops for the route (REQUIRED).
295    /// Minimum 2 stops required.
296    stops: Vec<NALocation>,
297
298    /// Point barriers to avoid.
299    #[serde(skip_serializing_if = "Option::is_none")]
300    #[builder(default)]
301    barriers: Option<Vec<NALocation>>,
302
303    /// Polyline barriers to avoid.
304    #[serde(skip_serializing_if = "Option::is_none")]
305    #[builder(default)]
306    polyline_barriers: Option<Vec<ArcGISGeometry>>,
307
308    /// Polygon barriers to avoid.
309    #[serde(skip_serializing_if = "Option::is_none")]
310    #[builder(default)]
311    polygon_barriers: Option<Vec<ArcGISGeometry>>,
312
313    /// Whether to return directions.
314    #[serde(skip_serializing_if = "Option::is_none")]
315    #[builder(default)]
316    return_directions: Option<bool>,
317
318    /// Whether to return routes.
319    #[serde(skip_serializing_if = "Option::is_none")]
320    #[builder(default)]
321    return_routes: Option<bool>,
322
323    /// Whether to return stops.
324    #[serde(skip_serializing_if = "Option::is_none")]
325    #[builder(default)]
326    return_stops: Option<bool>,
327
328    /// Whether to return barriers.
329    #[serde(skip_serializing_if = "Option::is_none")]
330    #[builder(default)]
331    return_barriers: Option<bool>,
332
333    /// Output spatial reference WKID.
334    #[serde(skip_serializing_if = "Option::is_none")]
335    #[builder(default)]
336    out_sr: Option<i32>,
337
338    /// Impedance attribute for cost.
339    #[serde(skip_serializing_if = "Option::is_none")]
340    #[builder(default)]
341    impedance_attribute: Option<ImpedanceAttribute>,
342
343    /// Restriction attributes.
344    #[serde(skip_serializing_if = "Option::is_none")]
345    #[builder(default)]
346    restriction_attribute_names: Option<Vec<RestrictionAttribute>>,
347
348    /// Attribute parameter values (JSON object).
349    #[serde(skip_serializing_if = "Option::is_none")]
350    #[builder(default)]
351    attribute_parameter_values: Option<serde_json::Value>,
352
353    /// Whether to use hierarchy in solving.
354    #[serde(skip_serializing_if = "Option::is_none")]
355    #[builder(default)]
356    use_hierarchy: Option<bool>,
357
358    /// Time of day for traffic-aware routing (epoch milliseconds).
359    #[serde(skip_serializing_if = "Option::is_none")]
360    #[builder(default)]
361    start_time: Option<i64>,
362
363    /// U-turn policy.
364    #[serde(skip_serializing_if = "Option::is_none")]
365    #[builder(default)]
366    uturn_policy: Option<UTurnPolicy>,
367
368    /// Directions length units.
369    #[serde(skip_serializing_if = "Option::is_none")]
370    #[builder(default)]
371    directions_length_units: Option<DirectionsLength>,
372
373    /// Directions time attribute.
374    #[serde(skip_serializing_if = "Option::is_none")]
375    #[builder(default)]
376    directions_time_attribute: Option<DirectionsTimeAttribute>,
377
378    /// Directions style.
379    #[serde(skip_serializing_if = "Option::is_none")]
380    #[builder(default)]
381    directions_style: Option<DirectionsStyle>,
382
383    /// Directions language (e.g., "en", "es", "fr").
384    #[serde(skip_serializing_if = "Option::is_none")]
385    #[builder(default)]
386    directions_language: Option<String>,
387
388    /// Whether to preserve first stop.
389    #[serde(skip_serializing_if = "Option::is_none")]
390    #[builder(default)]
391    preserve_first_stop: Option<bool>,
392
393    /// Whether to preserve last stop.
394    #[serde(skip_serializing_if = "Option::is_none")]
395    #[builder(default)]
396    preserve_last_stop: Option<bool>,
397
398    /// Whether to find best sequence.
399    #[serde(skip_serializing_if = "Option::is_none")]
400    #[builder(default)]
401    find_best_sequence: Option<bool>,
402
403    /// Whether to return to start.
404    #[serde(skip_serializing_if = "Option::is_none")]
405    #[builder(default)]
406    return_to_start: Option<bool>,
407
408    /// Whether to use time windows.
409    #[serde(skip_serializing_if = "Option::is_none")]
410    #[builder(default)]
411    use_time_windows: Option<bool>,
412
413    /// Accumulate attributes.
414    #[serde(skip_serializing_if = "Option::is_none")]
415    #[builder(default)]
416    accumulate_attribute_names: Option<Vec<String>>,
417
418    /// Output line type.
419    #[serde(skip_serializing_if = "Option::is_none")]
420    #[builder(default)]
421    output_lines: Option<OutputLine>,
422
423    /// Travel mode.
424    #[serde(skip_serializing_if = "Option::is_none")]
425    #[builder(default)]
426    travel_mode: Option<TravelMode>,
427}
428
429impl RouteParameters {
430    /// Creates a builder for RouteParameters.
431    pub fn builder() -> RouteParametersBuilder {
432        RouteParametersBuilder::default()
433    }
434}
435
436/// Result from route calculation.
437#[derive(Debug, Clone, PartialEq, Serialize, Getters)]
438#[serde(rename_all = "camelCase")]
439pub struct RouteResult {
440    /// Calculated routes.
441    #[serde(default)]
442    routes: Vec<Route>,
443
444    /// Stops with arrival/departure times.
445    #[serde(default)]
446    stops: Vec<Stop>,
447
448    /// Barriers that were used.
449    #[serde(default)]
450    barriers: Vec<NALocation>,
451
452    /// Messages from the solve operation.
453    #[serde(default)]
454    messages: Vec<NAMessage>,
455}
456
457impl<'de> serde::Deserialize<'de> for RouteResult {
458    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
459    where
460        D: serde::Deserializer<'de>,
461    {
462        use serde::de::{MapAccess, Visitor};
463        use std::fmt;
464
465        struct RouteResultVisitor;
466
467        impl<'de> Visitor<'de> for RouteResultVisitor {
468            type Value = RouteResult;
469
470            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
471                formatter.write_str("a RouteResult with FeatureSet routes and stops")
472            }
473
474            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
475            where
476                A: MapAccess<'de>,
477            {
478                let mut routes_fs: Option<crate::FeatureSet> = None;
479                let mut stops_fs: Option<crate::FeatureSet> = None;
480                let mut barriers: Option<Vec<NALocation>> = None;
481                let mut messages: Option<Vec<NAMessage>> = None;
482
483                while let Some(key) = map.next_key::<String>()? {
484                    match key.as_str() {
485                        "routes" => {
486                            routes_fs = Some(map.next_value()?);
487                        }
488                        "stops" => {
489                            stops_fs = Some(map.next_value()?);
490                        }
491                        "barriers" => {
492                            barriers = Some(map.next_value()?);
493                        }
494                        "messages" => {
495                            messages = Some(map.next_value()?);
496                        }
497                        _ => {
498                            // Skip unknown fields
499                            let _: serde::de::IgnoredAny = map.next_value()?;
500                        }
501                    }
502                }
503
504                let routes_fs = routes_fs.unwrap_or_default();
505                let stops_fs = stops_fs.unwrap_or_default();
506                let barriers = barriers.unwrap_or_default();
507                let messages = messages.unwrap_or_default();
508
509                tracing::debug!(
510                    route_feature_count = routes_fs.features().len(),
511                    stop_feature_count = stops_fs.features().len(),
512                    "Deserializing RouteResult from FeatureSets"
513                );
514
515                // Convert FeatureSet features to Route objects (infallible)
516                let routes: Vec<Route> = routes_fs
517                    .features()
518                    .iter()
519                    .map(Route::from_feature)
520                    .collect();
521
522                // Convert FeatureSet features to Stop objects (infallible)
523                let stops: Vec<Stop> = stops_fs.features().iter().map(Stop::from_feature).collect();
524
525                tracing::debug!(
526                    route_count = routes.len(),
527                    stop_count = stops.len(),
528                    "Successfully deserialized RouteResult"
529                );
530
531                Ok(RouteResult {
532                    routes,
533                    stops,
534                    barriers,
535                    messages,
536                })
537            }
538        }
539
540        deserializer.deserialize_map(RouteResultVisitor)
541    }
542}
543
544/// A calculated route.
545#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
546#[serde(rename_all = "camelCase")]
547pub struct Route {
548    /// Route name.
549    #[serde(skip_serializing_if = "Option::is_none")]
550    name: Option<String>,
551
552    /// Total length.
553    #[serde(skip_serializing_if = "Option::is_none")]
554    total_length: Option<f64>,
555
556    /// Total time (minutes).
557    #[serde(skip_serializing_if = "Option::is_none")]
558    total_time: Option<f64>,
559
560    /// Total drive time (minutes).
561    #[serde(skip_serializing_if = "Option::is_none")]
562    total_drive_time: Option<f64>,
563
564    /// Total wait time (minutes).
565    #[serde(skip_serializing_if = "Option::is_none")]
566    total_wait_time: Option<f64>,
567
568    /// Route geometry (polyline).
569    #[serde(skip_serializing_if = "Option::is_none")]
570    geometry: Option<ArcGISGeometry>,
571
572    /// Turn-by-turn directions.
573    #[serde(default)]
574    directions: Vec<Direction>,
575
576    /// Start time (epoch milliseconds).
577    #[serde(skip_serializing_if = "Option::is_none")]
578    start_time: Option<i64>,
579
580    /// End time (epoch milliseconds).
581    #[serde(skip_serializing_if = "Option::is_none")]
582    end_time: Option<i64>,
583}
584
585impl Route {
586    /// Extracts a Route from a FeatureSet Feature.
587    ///
588    /// Infallible - missing fields are represented as None.
589    fn from_feature(feature: &crate::Feature) -> Self {
590        tracing::debug!("Converting FeatureSet feature to Route");
591
592        let attrs = feature.attributes();
593
594        let name = attrs
595            .get("Name")
596            .and_then(|v| v.as_str())
597            .map(|s| s.to_string());
598
599        let total_length = attrs.get("Total_Miles").and_then(|v| v.as_f64());
600
601        let total_time = attrs.get("Total_TravelTime").and_then(|v| v.as_f64());
602
603        let total_drive_time = attrs.get("Total_DriveTime").and_then(|v| v.as_f64());
604
605        let total_wait_time = attrs.get("Total_WaitTime").and_then(|v| v.as_f64());
606
607        let geometry = feature.geometry().clone();
608
609        tracing::debug!(
610            name = ?name,
611            total_miles = ?total_length,
612            total_time_minutes = ?total_time,
613            "Extracted route data from feature"
614        );
615
616        Route {
617            name,
618            total_length,
619            total_time,
620            total_drive_time,
621            total_wait_time,
622            geometry,
623            directions: Vec::new(), // Directions come from separate array in response
624            start_time: None,       // Not in route attributes
625            end_time: None,         // Not in route attributes
626        }
627    }
628}
629
630/// A stop on a route.
631#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
632#[serde(rename_all = "camelCase")]
633pub struct Stop {
634    /// Stop name.
635    #[serde(skip_serializing_if = "Option::is_none")]
636    name: Option<String>,
637
638    /// Stop geometry.
639    #[serde(skip_serializing_if = "Option::is_none")]
640    geometry: Option<ArcGISGeometry>,
641
642    /// Arrival time (epoch milliseconds).
643    #[serde(skip_serializing_if = "Option::is_none")]
644    arrival_time: Option<i64>,
645
646    /// Departure time (epoch milliseconds).
647    #[serde(skip_serializing_if = "Option::is_none")]
648    departure_time: Option<i64>,
649
650    /// Wait time (minutes).
651    #[serde(skip_serializing_if = "Option::is_none")]
652    wait_time: Option<f64>,
653
654    /// Cumulative length to this stop.
655    #[serde(skip_serializing_if = "Option::is_none")]
656    cumulative_length: Option<f64>,
657
658    /// Sequence number in optimized route.
659    #[serde(skip_serializing_if = "Option::is_none")]
660    sequence: Option<i32>,
661}
662
663impl Stop {
664    /// Extracts a Stop from a FeatureSet Feature.
665    ///
666    /// Infallible - missing fields are represented as None.
667    fn from_feature(feature: &crate::Feature) -> Self {
668        tracing::debug!("Converting FeatureSet feature to Stop");
669
670        let attrs = feature.attributes();
671
672        let name = attrs
673            .get("Name")
674            .and_then(|v| v.as_str())
675            .map(|s| s.to_string());
676
677        let geometry = feature.geometry().clone();
678
679        let arrival_time = None; // Not directly in attributes
680
681        let departure_time = None; // Not directly in attributes
682
683        let wait_time = None; // Not directly in attributes
684
685        let cumulative_length = attrs.get("Cumul_Miles").and_then(|v| v.as_f64());
686
687        let sequence = attrs
688            .get("Sequence")
689            .and_then(|v| v.as_i64())
690            .map(|i| i as i32);
691
692        tracing::debug!(
693            name = ?name,
694            sequence = ?sequence,
695            cumul_miles = ?cumulative_length,
696            "Extracted stop data from feature"
697        );
698
699        Stop {
700            name,
701            geometry,
702            arrival_time,
703            departure_time,
704            wait_time,
705            cumulative_length,
706            sequence,
707        }
708    }
709}
710
711/// A direction instruction.
712#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
713#[serde(rename_all = "camelCase")]
714pub struct Direction {
715    /// Instruction text.
716    #[serde(skip_serializing_if = "Option::is_none")]
717    text: Option<String>,
718
719    /// Length of this segment.
720    #[serde(skip_serializing_if = "Option::is_none")]
721    length: Option<f64>,
722
723    /// Time for this segment (minutes).
724    #[serde(skip_serializing_if = "Option::is_none")]
725    time: Option<f64>,
726
727    /// Direction geometry.
728    #[serde(skip_serializing_if = "Option::is_none")]
729    geometry: Option<ArcGISGeometry>,
730
731    /// Maneuver type.
732    #[serde(skip_serializing_if = "Option::is_none")]
733    maneuver_type: Option<String>,
734}
735
736/// Network Analyst message.
737#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Getters)]
738#[serde(rename_all = "camelCase")]
739pub struct NAMessage {
740    /// Message type (0=informative, 1=warning, 2=error).
741    #[serde(rename = "type")]
742    message_type: i32,
743
744    /// Message description.
745    description: String,
746}
747
748/// Parameters for service area calculation.
749///
750/// Use [`ServiceAreaParameters::builder()`] to construct instances.
751#[derive(Debug, Clone, Serialize, derive_builder::Builder, Getters)]
752#[builder(setter(into, strip_option))]
753#[serde(rename_all = "camelCase")]
754pub struct ServiceAreaParameters {
755    /// Facilities (starting points) for service areas (REQUIRED).
756    facilities: Vec<NALocation>,
757
758    /// Break values (time or distance) for service areas (REQUIRED).
759    /// For example: [5, 10, 15] for 5, 10, and 15 minute service areas.
760    default_breaks: Vec<f64>,
761
762    /// Point barriers to avoid.
763    #[serde(skip_serializing_if = "Option::is_none")]
764    #[builder(default)]
765    barriers: Option<Vec<NALocation>>,
766
767    /// Polyline barriers to avoid.
768    #[serde(skip_serializing_if = "Option::is_none")]
769    #[builder(default)]
770    polyline_barriers: Option<Vec<ArcGISGeometry>>,
771
772    /// Polygon barriers to avoid.
773    #[serde(skip_serializing_if = "Option::is_none")]
774    #[builder(default)]
775    polygon_barriers: Option<Vec<ArcGISGeometry>>,
776
777    /// Travel direction (from or to facilities).
778    #[serde(skip_serializing_if = "Option::is_none")]
779    #[builder(default)]
780    travel_direction: Option<TravelDirection>,
781
782    /// Output spatial reference WKID.
783    #[serde(skip_serializing_if = "Option::is_none")]
784    #[builder(default)]
785    out_sr: Option<i32>,
786
787    /// Impedance attribute for cost.
788    #[serde(skip_serializing_if = "Option::is_none")]
789    #[builder(default)]
790    impedance_attribute: Option<ImpedanceAttribute>,
791
792    /// Whether to merge similar polygons.
793    #[serde(skip_serializing_if = "Option::is_none")]
794    #[builder(default)]
795    merge_similar_polygon_ranges: Option<bool>,
796
797    /// Whether to split polygons at breaks.
798    #[serde(skip_serializing_if = "Option::is_none")]
799    #[builder(default)]
800    split_polygons_at_breaks: Option<bool>,
801
802    /// Whether to trim outer polygon.
803    #[serde(skip_serializing_if = "Option::is_none")]
804    #[builder(default)]
805    trim_outer_polygon: Option<bool>,
806
807    /// Trim distance (in units matching impedance).
808    #[serde(skip_serializing_if = "Option::is_none")]
809    #[builder(default)]
810    trim_polygon_distance: Option<f64>,
811
812    /// Time of day for traffic-aware analysis (epoch milliseconds).
813    #[serde(skip_serializing_if = "Option::is_none")]
814    #[builder(default)]
815    time_of_day: Option<i64>,
816
817    /// Whether to use hierarchy.
818    #[serde(skip_serializing_if = "Option::is_none")]
819    #[builder(default)]
820    use_hierarchy: Option<bool>,
821
822    /// U-turn policy.
823    #[serde(skip_serializing_if = "Option::is_none")]
824    #[builder(default)]
825    uturn_policy: Option<UTurnPolicy>,
826
827    /// Restriction attributes.
828    #[serde(skip_serializing_if = "Option::is_none")]
829    #[builder(default)]
830    restriction_attribute_names: Option<Vec<RestrictionAttribute>>,
831
832    /// Travel mode.
833    #[serde(skip_serializing_if = "Option::is_none")]
834    #[builder(default)]
835    travel_mode: Option<TravelMode>,
836
837    /// Whether to return facilities.
838    #[serde(skip_serializing_if = "Option::is_none")]
839    #[builder(default)]
840    return_facilities: Option<bool>,
841
842    /// Whether to return barriers.
843    #[serde(skip_serializing_if = "Option::is_none")]
844    #[builder(default)]
845    return_barriers: Option<bool>,
846
847    /// Whether to return service area polygons.
848    #[serde(skip_serializing_if = "Option::is_none")]
849    #[builder(default)]
850    return_polygons: Option<bool>,
851}
852
853impl ServiceAreaParameters {
854    /// Creates a builder for ServiceAreaParameters.
855    pub fn builder() -> ServiceAreaParametersBuilder {
856        ServiceAreaParametersBuilder::default()
857    }
858}
859
860/// Helper to deserialize service area feature sets.
861#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
862#[serde(bound(deserialize = "T: serde::Deserialize<'de>"))]
863struct ServiceAreaFeatureSet<T> {
864    #[serde(default)]
865    features: Vec<T>,
866}
867
868impl<T> Default for ServiceAreaFeatureSet<T> {
869    fn default() -> Self {
870        Self {
871            features: Vec::new(),
872        }
873    }
874}
875
876/// Result from service area calculation.
877#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
878#[serde(rename_all = "camelCase")]
879pub struct ServiceAreaResult {
880    /// Service area polygons.
881    #[serde(default, rename = "saPolygons")]
882    sapolygons_raw: ServiceAreaFeatureSet<ServiceAreaPolygon>,
883
884    /// Service area polylines (network edges).
885    #[serde(default, rename = "saPolylines")]
886    sapolylines_raw: ServiceAreaFeatureSet<ServiceAreaPolyline>,
887
888    /// Facilities that were used.
889    #[serde(default)]
890    facilities: Vec<NALocation>,
891
892    /// Messages from the solve operation.
893    #[serde(default)]
894    messages: Vec<NAMessage>,
895}
896
897impl ServiceAreaResult {
898    /// Gets the service area polygons.
899    pub fn service_area_polygons(&self) -> &[ServiceAreaPolygon] {
900        &self.sapolygons_raw.features
901    }
902
903    /// Gets the service area polylines.
904    pub fn service_area_polylines(&self) -> &[ServiceAreaPolyline] {
905        &self.sapolylines_raw.features
906    }
907
908    /// Gets the facilities.
909    pub fn facilities(&self) -> &[NALocation] {
910        &self.facilities
911    }
912
913    /// Gets the messages.
914    pub fn messages(&self) -> &[NAMessage] {
915        &self.messages
916    }
917}
918
919/// A service area polygon.
920#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
921#[serde(rename_all = "camelCase")]
922pub struct ServiceAreaPolygon {
923    /// Facility ID this area belongs to.
924    #[serde(skip_serializing_if = "Option::is_none")]
925    facility_id: Option<i32>,
926
927    /// Break value (time or distance).
928    #[serde(skip_serializing_if = "Option::is_none")]
929    from_break: Option<f64>,
930
931    /// End break value.
932    #[serde(skip_serializing_if = "Option::is_none")]
933    to_break: Option<f64>,
934
935    /// Polygon geometry.
936    #[serde(skip_serializing_if = "Option::is_none")]
937    geometry: Option<ArcGISGeometry>,
938}
939
940/// A service area polyline.
941#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
942#[serde(rename_all = "camelCase")]
943pub struct ServiceAreaPolyline {
944    /// Facility ID this line belongs to.
945    #[serde(skip_serializing_if = "Option::is_none")]
946    facility_id: Option<i32>,
947
948    /// Break value (time or distance).
949    #[serde(skip_serializing_if = "Option::is_none")]
950    from_break: Option<f64>,
951
952    /// End break value.
953    #[serde(skip_serializing_if = "Option::is_none")]
954    to_break: Option<f64>,
955
956    /// Polyline geometry.
957    #[serde(skip_serializing_if = "Option::is_none")]
958    geometry: Option<ArcGISGeometry>,
959}
960
961/// Parameters for closest facility calculation.
962///
963/// Use [`ClosestFacilityParameters::builder()`] to construct instances.
964#[derive(Debug, Clone, Serialize, derive_builder::Builder, Getters)]
965#[builder(setter(into, strip_option))]
966#[serde(rename_all = "camelCase")]
967pub struct ClosestFacilityParameters {
968    /// Incidents (demand points) to analyze (REQUIRED).
969    incidents: Vec<NALocation>,
970
971    /// Facilities (supply points) to analyze (REQUIRED).
972    facilities: Vec<NALocation>,
973
974    /// Number of closest facilities to find per incident.
975    #[serde(skip_serializing_if = "Option::is_none")]
976    #[builder(default)]
977    default_target_facility_count: Option<i32>,
978
979    /// Point barriers to avoid.
980    #[serde(skip_serializing_if = "Option::is_none")]
981    #[builder(default)]
982    barriers: Option<Vec<NALocation>>,
983
984    /// Polyline barriers to avoid.
985    #[serde(skip_serializing_if = "Option::is_none")]
986    #[builder(default)]
987    polyline_barriers: Option<Vec<ArcGISGeometry>>,
988
989    /// Polygon barriers to avoid.
990    #[serde(skip_serializing_if = "Option::is_none")]
991    #[builder(default)]
992    polygon_barriers: Option<Vec<ArcGISGeometry>>,
993
994    /// Travel direction (from incidents or to facilities).
995    #[serde(skip_serializing_if = "Option::is_none")]
996    #[builder(default)]
997    travel_direction: Option<TravelDirection>,
998
999    /// Output spatial reference WKID.
1000    #[serde(skip_serializing_if = "Option::is_none")]
1001    #[builder(default)]
1002    out_sr: Option<i32>,
1003
1004    /// Impedance attribute for cost.
1005    #[serde(skip_serializing_if = "Option::is_none")]
1006    #[builder(default)]
1007    impedance_attribute: Option<ImpedanceAttribute>,
1008
1009    /// Accumulate attributes.
1010    #[serde(skip_serializing_if = "Option::is_none")]
1011    #[builder(default)]
1012    accumulate_attribute_names: Option<Vec<String>>,
1013
1014    /// Whether to return directions.
1015    #[serde(skip_serializing_if = "Option::is_none")]
1016    #[builder(default)]
1017    return_directions: Option<bool>,
1018
1019    /// Whether to return routes.
1020    #[serde(skip_serializing_if = "Option::is_none")]
1021    #[builder(default)]
1022    return_routes: Option<bool>,
1023
1024    /// Whether to return facilities.
1025    #[serde(skip_serializing_if = "Option::is_none")]
1026    #[builder(default)]
1027    return_facilities: Option<bool>,
1028
1029    /// Whether to return incidents.
1030    #[serde(skip_serializing_if = "Option::is_none")]
1031    #[builder(default)]
1032    return_incidents: Option<bool>,
1033
1034    /// Whether to return barriers.
1035    #[serde(skip_serializing_if = "Option::is_none")]
1036    #[builder(default)]
1037    return_barriers: Option<bool>,
1038
1039    /// Output line type.
1040    #[serde(skip_serializing_if = "Option::is_none")]
1041    #[builder(default)]
1042    output_lines: Option<OutputLine>,
1043
1044    /// Time of day for traffic-aware routing (epoch milliseconds).
1045    #[serde(skip_serializing_if = "Option::is_none")]
1046    #[builder(default)]
1047    time_of_day: Option<i64>,
1048
1049    /// Whether to use hierarchy.
1050    #[serde(skip_serializing_if = "Option::is_none")]
1051    #[builder(default)]
1052    use_hierarchy: Option<bool>,
1053
1054    /// U-turn policy.
1055    #[serde(skip_serializing_if = "Option::is_none")]
1056    #[builder(default)]
1057    uturn_policy: Option<UTurnPolicy>,
1058
1059    /// Restriction attributes.
1060    #[serde(skip_serializing_if = "Option::is_none")]
1061    #[builder(default)]
1062    restriction_attribute_names: Option<Vec<RestrictionAttribute>>,
1063
1064    /// Travel mode.
1065    #[serde(skip_serializing_if = "Option::is_none")]
1066    #[builder(default)]
1067    travel_mode: Option<TravelMode>,
1068}
1069
1070impl ClosestFacilityParameters {
1071    /// Creates a builder for ClosestFacilityParameters.
1072    pub fn builder() -> ClosestFacilityParametersBuilder {
1073        ClosestFacilityParametersBuilder::default()
1074    }
1075}
1076
1077/// Helper to deserialize closest facility feature sets.
1078#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1079#[serde(bound(deserialize = "T: serde::Deserialize<'de>"))]
1080struct ClosestFacilityFeatureSet<T> {
1081    #[serde(default)]
1082    features: Vec<T>,
1083}
1084
1085impl<T> Default for ClosestFacilityFeatureSet<T> {
1086    fn default() -> Self {
1087        Self {
1088            features: Vec::new(),
1089        }
1090    }
1091}
1092
1093/// Result from closest facility calculation.
1094#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1095#[serde(rename_all = "camelCase")]
1096pub struct ClosestFacilityResult {
1097    /// Routes from incidents to facilities.
1098    #[serde(default)]
1099    routes: ClosestFacilityFeatureSet<Route>,
1100
1101    /// Facilities that were analyzed.
1102    #[serde(default)]
1103    facilities: Vec<NALocation>,
1104
1105    /// Incidents that were analyzed.
1106    #[serde(default)]
1107    incidents: Vec<NALocation>,
1108
1109    /// Messages from the solve operation.
1110    #[serde(default)]
1111    messages: Vec<NAMessage>,
1112}
1113
1114impl ClosestFacilityResult {
1115    /// Gets the routes.
1116    pub fn routes(&self) -> &[Route] {
1117        &self.routes.features
1118    }
1119
1120    /// Gets the facilities.
1121    pub fn facilities(&self) -> &[NALocation] {
1122        &self.facilities
1123    }
1124
1125    /// Gets the incidents.
1126    pub fn incidents(&self) -> &[NALocation] {
1127        &self.incidents
1128    }
1129
1130    /// Gets the messages.
1131    pub fn messages(&self) -> &[NAMessage] {
1132        &self.messages
1133    }
1134}
1135
1136/// Parameters for origin-destination cost matrix calculation.
1137///
1138/// Use [`ODCostMatrixParameters::builder()`] to construct instances.
1139#[derive(Debug, Clone, Serialize, derive_builder::Builder, Getters)]
1140#[builder(setter(into, strip_option))]
1141#[serde(rename_all = "camelCase")]
1142pub struct ODCostMatrixParameters {
1143    /// Origins for the matrix (REQUIRED).
1144    origins: Vec<NALocation>,
1145
1146    /// Destinations for the matrix (REQUIRED).
1147    destinations: Vec<NALocation>,
1148
1149    /// Point barriers to avoid.
1150    #[serde(skip_serializing_if = "Option::is_none")]
1151    #[builder(default)]
1152    barriers: Option<Vec<NALocation>>,
1153
1154    /// Polyline barriers to avoid.
1155    #[serde(skip_serializing_if = "Option::is_none")]
1156    #[builder(default)]
1157    polyline_barriers: Option<Vec<ArcGISGeometry>>,
1158
1159    /// Polygon barriers to avoid.
1160    #[serde(skip_serializing_if = "Option::is_none")]
1161    #[builder(default)]
1162    polygon_barriers: Option<Vec<ArcGISGeometry>>,
1163
1164    /// Travel direction (origins to destinations or reverse).
1165    #[serde(skip_serializing_if = "Option::is_none")]
1166    #[builder(default)]
1167    travel_direction: Option<TravelDirection>,
1168
1169    /// Output spatial reference WKID.
1170    #[serde(skip_serializing_if = "Option::is_none")]
1171    #[builder(default)]
1172    out_sr: Option<i32>,
1173
1174    /// Impedance attribute for cost.
1175    #[serde(skip_serializing_if = "Option::is_none")]
1176    #[builder(default)]
1177    impedance_attribute: Option<ImpedanceAttribute>,
1178
1179    /// Accumulate attributes.
1180    #[serde(skip_serializing_if = "Option::is_none")]
1181    #[builder(default)]
1182    accumulate_attribute_names: Option<Vec<String>>,
1183
1184    /// Time of day for traffic-aware analysis (epoch milliseconds).
1185    #[serde(skip_serializing_if = "Option::is_none")]
1186    #[builder(default)]
1187    time_of_day: Option<i64>,
1188
1189    /// Whether to use hierarchy.
1190    #[serde(skip_serializing_if = "Option::is_none")]
1191    #[builder(default)]
1192    use_hierarchy: Option<bool>,
1193
1194    /// U-turn policy.
1195    #[serde(skip_serializing_if = "Option::is_none")]
1196    #[builder(default)]
1197    uturn_policy: Option<UTurnPolicy>,
1198
1199    /// Restriction attributes.
1200    #[serde(skip_serializing_if = "Option::is_none")]
1201    #[builder(default)]
1202    restriction_attribute_names: Option<Vec<RestrictionAttribute>>,
1203
1204    /// Travel mode.
1205    #[serde(skip_serializing_if = "Option::is_none")]
1206    #[builder(default)]
1207    travel_mode: Option<TravelMode>,
1208}
1209
1210impl ODCostMatrixParameters {
1211    /// Creates a builder for ODCostMatrixParameters.
1212    pub fn builder() -> ODCostMatrixParametersBuilder {
1213        ODCostMatrixParametersBuilder::default()
1214    }
1215}
1216
1217/// Result from origin-destination cost matrix calculation.
1218#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
1219#[serde(rename_all = "camelCase")]
1220pub struct ODCostMatrixResult {
1221    /// Origin-destination cost matrix lines.
1222    #[serde(default, rename = "odLines")]
1223    od_lines: Vec<ODLine>,
1224
1225    /// Origins that were analyzed.
1226    #[serde(default)]
1227    origins: Vec<NALocation>,
1228
1229    /// Destinations that were analyzed.
1230    #[serde(default)]
1231    destinations: Vec<NALocation>,
1232
1233    /// Messages from the solve operation.
1234    #[serde(default)]
1235    messages: Vec<NAMessage>,
1236}
1237
1238/// An origin-destination cost matrix line.
1239#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
1240#[serde(rename_all = "camelCase")]
1241pub struct ODLine {
1242    /// Origin ID.
1243    #[serde(skip_serializing_if = "Option::is_none")]
1244    origin_id: Option<i32>,
1245
1246    /// Destination ID.
1247    #[serde(skip_serializing_if = "Option::is_none")]
1248    destination_id: Option<i32>,
1249
1250    /// Total travel time (minutes).
1251    #[serde(skip_serializing_if = "Option::is_none")]
1252    total_time: Option<f64>,
1253
1254    /// Total distance.
1255    #[serde(skip_serializing_if = "Option::is_none")]
1256    total_distance: Option<f64>,
1257
1258    /// Origin name.
1259    #[serde(skip_serializing_if = "Option::is_none")]
1260    origin_name: Option<String>,
1261
1262    /// Destination name.
1263    #[serde(skip_serializing_if = "Option::is_none")]
1264    destination_name: Option<String>,
1265}