valhalla_client/route.rs
1use crate::costing;
2pub use crate::shapes::ShapePoint;
3pub use crate::DateTime;
4use serde::{Deserialize, Serialize};
5
6#[derive(Deserialize, Debug, Clone)]
7pub(crate) struct Response {
8 pub(crate) trip: Trip,
9}
10
11#[derive(Deserialize, Debug, Clone)]
12pub struct Trip {
13 /// Status code
14 pub status: i32,
15 /// Status message
16 pub status_message: String,
17 /// The via [`Manifest::units`] specified units of length are returned.
18 ///
19 /// Either [`super::Units::Metric`] or [`super::Units::Imperial`].
20 pub units: super::Units,
21 /// The language of the narration instructions.
22 ///
23 /// If the user specified a language via [`Manifest::language`] in the directions options and the specified language was supported.
24 /// This returned value will be equal to the specified value.
25 /// Otherwise, this value will be the default (`en-US`) language.
26 pub language: String,
27 /// Location information is returned in the same form as it is entered.
28 ///
29 /// Additional fields are added to indicate the side of the street.
30 /// Output can be changed via via [`Manifest::locations`].
31 pub locations: Vec<Location>,
32 /// This array may contain warning objects informing about deprecated request parameters, clamped values etc.
33 pub warnings: Option<Vec<String>>,
34 /// Name of your route request.
35 ///
36 /// If an id is specified via [`Manifest::id`], the naming will be sent thru to the response.
37 pub id: Option<String>,
38 /// List of [`Leg`]s constituting a [`Trip`]
39 pub legs: Vec<Leg>,
40 /// Basic information about the entire [`Trip`]
41 pub summary: Summary,
42}
43#[cfg(feature = "gpx")]
44impl From<Trip> for gpx::Gpx {
45 fn from(trip: Trip) -> Self {
46 let mut gpx = Self {
47 version: gpx::GpxVersion::Gpx11,
48 creator: Some("valhalla".to_string()),
49 ..Default::default()
50 };
51 let track = gpx::Track {
52 name: Some("route".to_string()),
53 segments: trip.legs.iter().map(|leg| leg.into()).collect(),
54 ..Default::default()
55 };
56 gpx.tracks.push(track);
57
58 let ps = trip
59 .legs
60 .iter()
61 .flat_map(|leg| {
62 leg.maneuvers.iter().map(|m| {
63 let p = &leg.shape[m.begin_shape_index];
64
65 gpx::Waypoint::new(p.into())
66 })
67 })
68 .collect();
69 let route = gpx::Route {
70 name: Some("route".to_string()),
71 points: ps,
72 ..Default::default()
73 };
74 gpx.routes.push(route);
75 gpx
76 }
77}
78#[derive(Deserialize, Debug, Clone)]
79pub struct Summary {
80 /// Estimated elapsed time in seconds
81 pub time: f64,
82 /// Distance traveled
83 ///
84 /// Unit is either [`super::Units::Metric`] or [`super::Units::Imperial`] and specified in [`Trip`] for clarification.
85 /// See [`Manifest::units`] to change the units.
86 pub length: f64,
87 /// If the path uses one or more toll segments
88 pub has_toll: bool,
89 /// If the path uses one or more highway segments
90 pub has_highway: bool,
91 /// if the path uses one or more ferry segments
92 pub has_ferry: bool,
93 /// Minimum latitude of the sections bounding box
94 pub min_lat: f64,
95 /// Minimum longitude of the sections bounding box
96 pub min_lon: f64,
97 /// Maximum latitude of the sections bounding box
98 pub max_lat: f64,
99 /// Maximum longitude of the sections bounding box
100 pub max_lon: f64,
101}
102
103#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
104pub enum TravelMode {
105 #[serde(rename = "drive")]
106 Drive,
107 #[serde(rename = "pedestrian")]
108 Pedestrian,
109 #[serde(rename = "bicycle")]
110 Bicycle,
111 #[serde(rename = "transit")]
112 Transit,
113}
114
115#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
116#[serde(untagged)]
117pub enum TravelType {
118 Drive(DriveTravelType),
119 Pedestrian(costing::pedestrian::PedestrianType),
120 Bicycle(costing::bicycle::BicycleType),
121 Transit(TransitTravelType),
122}
123
124#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
125pub enum DriveTravelType {
126 #[serde(rename = "car")]
127 Car,
128 #[serde(rename = "motorcycle")]
129 Motorcycle,
130 #[serde(rename = "truck")]
131 Truck,
132 #[serde(rename = "motor_scooter")]
133 MotorScooter,
134}
135
136#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
137pub enum TransitTravelType {
138 #[serde(rename = "tram")]
139 Tram,
140 #[serde(rename = "metro")]
141 Metro,
142 #[serde(rename = "rail")]
143 Rail,
144 #[serde(rename = "bus")]
145 Bus,
146 #[serde(rename = "ferry")]
147 Ferry,
148 #[serde(rename = "cable_car")]
149 CableCar,
150 #[serde(rename = "gondola")]
151 Gondola,
152 #[serde(rename = "funicular")]
153 Funicular,
154}
155
156#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
157pub enum BssManeuverType {
158 #[serde(rename = "NoneAction")]
159 NoneAction,
160 #[serde(rename = "RentBikeAtBikeShare")]
161 RentBikeAtBikeShare,
162 #[serde(rename = "ReturnBikeAtBikeShare")]
163 ReturnBikeAtBikeShare,
164}
165
166#[derive(Deserialize, Debug, Clone)]
167pub struct Leg {
168 pub summary: Summary,
169
170 pub maneuvers: Vec<Maneuver>,
171
172 #[serde(deserialize_with = "crate::shapes::deserialize_shape")]
173 pub shape: Vec<ShapePoint>,
174}
175
176#[cfg(feature = "gpx")]
177impl From<&Leg> for gpx::TrackSegment {
178 fn from(leg: &Leg) -> Self {
179 Self {
180 points: leg.shape[leg.maneuvers[0].begin_shape_index
181 ..leg.maneuvers[leg.maneuvers.len() - 1].end_shape_index]
182 .iter()
183 .map(|location| gpx::Waypoint::new(location.into()))
184 .collect(),
185 }
186 }
187}
188
189#[derive(serde_repr::Deserialize_repr, Debug, Clone, Copy, PartialEq, Eq)]
190#[repr(i8)]
191pub enum ManeuverType {
192 None = 0,
193 Start,
194 StartRight,
195 StartLeft,
196 Destination,
197 DestinationRight,
198 DestinationLeft,
199 Becomes,
200 Continue,
201 SlightRight,
202 Right,
203 SharpRight,
204 UturnRight,
205 UturnLeft,
206 SharpLeft,
207 Left,
208 SlightLeft,
209 RampStraight,
210 RampRight,
211 RampLeft,
212 ExitRight,
213 ExitLeft,
214 StayStraight,
215 StayRight,
216 StayLeft,
217 Merge,
218 RoundaboutEnter,
219 RoundaboutExit,
220 FerryEnter,
221 FerryExit,
222 Transit,
223 TransitTransfer,
224 TransitRemainOn,
225 TransitConnectionStart,
226 TransitConnectionTransfer,
227 TransitConnectionDestination,
228 PostTransitConnectionDestination,
229 MergeRight,
230 MergeLeft,
231 ElevatorEnter,
232 StepsEnter,
233 EscalatorEnter,
234 BuildingEnter,
235 BuildingExit,
236}
237
238#[derive(Deserialize, Default, Clone, Debug)]
239#[serde(default)]
240pub struct Sign {
241 /// list of exit number elements.
242 ///
243 /// If an exit number element exists, it is typically just one value
244 ///
245 /// Example: `91B`
246 pub exit_number_elements: Vec<ManeuverSignElement>,
247 /// Exit branch elements.
248 ///
249 /// The exit branch element text is the subsequent road name or route number after the sign
250 ///
251 /// Example: `I 95 North`
252 pub exit_branch_elements: Vec<ManeuverSignElement>,
253 /// Exit toward elements.
254 ///
255 /// The exit toward element text is the location where the road ahead goes.
256 /// The location is typically a control city, but may also be a future road name or route number.
257 ///
258 /// Example: `New York`
259 pub exit_toward_elements: Vec<ManeuverSignElement>,
260 /// Exit name elements.
261 ///
262 /// The exit name element is the interchange identifier.
263 /// Typically not used in the US.
264 ///
265 /// Example: `Gettysburg Pike`
266 pub exit_name_elements: Vec<ManeuverSignElement>,
267}
268
269#[derive(Deserialize, Clone, Debug)]
270pub struct ManeuverSignElement {
271 /// Interchange sign text.
272 ///
273 /// Examples:
274 /// - exit number: `91B`
275 /// - exit branch: `I 95 North`
276 /// - exit toward: `New York`
277 /// - exit name: `Gettysburg Pike`
278 pub text: String,
279 /// The frequency of this sign element within a set a consecutive signs
280 pub consecutive_count: Option<usize>,
281}
282
283#[derive(Deserialize, Clone, Debug)]
284pub struct Maneuver {
285 /// Type of maneuver
286 #[serde(rename = "type")]
287 pub type_: ManeuverType,
288 /// Written maneuver instruction, describing the maneuver.
289 ///
290 /// Example: "Turn right onto Main Street".
291 pub instruction: String,
292
293 /// Text suitable for use as a verbal alert in a navigation application.
294 ///
295 /// The transition alert instruction will prepare the user for the forthcoming transition.
296 ///
297 /// Example: "Turn right onto North Prince Street"
298 pub verbal_transition_alert_instruction: Option<String>,
299
300 /// Text suitable for use as a verbal message immediately prior to the maneuver transition.
301 ///
302 /// Example: "Turn right onto North Prince Street, U.S. 2 22"
303 pub verbal_pre_transition_instruction: Option<String>,
304 /// Text suitable for use as a verbal message immediately after the maneuver transition.
305 ///
306 /// Example: "Continue on U.S. 2 22 for 3.9 miles"
307 pub verbal_post_transition_instruction: Option<String>,
308
309 /// List of street names that are consistent along the entire nonobvious maneuver
310 pub street_names: Option<Vec<String>>,
311
312 /// When present, these are the street names at the beginning (transition point) of the
313 /// nonobvious maneuver (if they are different than the names that are consistent along the
314 /// entire nonobvious maneuver).
315 pub begin_street_names: Option<Vec<String>>,
316 /// Estimated time along the maneuver in seconds.
317 pub time: f64,
318 /// Maneuver length in the [`super::Units`] specified via [`Manifest::units`]
319 pub length: f64,
320 /// Index into the list of shape points for the start of the maneuver.
321 pub begin_shape_index: usize,
322 /// Index into the list of shape points for the end of the maneuver.
323 pub end_shape_index: usize,
324 /// `true` if a toll booth is encountered on this maneuver.
325 pub toll: Option<bool>,
326 /// `true` if a highway is encountered on this maneuver.
327 pub highway: Option<bool>,
328 /// `true` if the maneuver is unpaved or rough pavement, or has any portions that have rough
329 /// pavement.
330 pub rough: Option<bool>,
331 /// `true` if a gate is encountered on this maneuver.
332 pub gate: Option<bool>,
333 /// `true` if a ferry is encountered on this maneuver.
334 pub ferry: Option<bool>,
335 /// Contains the interchange guide information at a road junction associated with this
336 /// maneuver.
337 ///
338 /// See [`Sign`] for details.
339 pub sign: Option<Sign>,
340 /// The spoke to exit roundabout after entering.
341 pub roundabout_exit_count: Option<i64>,
342 /// Written depart time instruction.
343 ///
344 /// Typically used with a transit maneuver, such as "Depart: 8:04 AM from 8 St - NYU".
345 pub depart_instruction: Option<String>,
346 /// Text suitable for use as a verbal depart time instruction.
347 ///
348 /// Typically used with a transit maneuver, such as "Depart at 8:04 AM from 8 St - NYU".
349 pub verbal_depart_instruction: Option<String>,
350 /// Written arrive time instruction.
351 ///
352 /// Typically used with a transit maneuver, such as "Arrive: 8:10 AM at 34 St - Herald Sq".
353 pub arrive_instruction: Option<String>,
354 /// Text suitable for use as a verbal arrive time instruction.
355 ///
356 /// Typically used with a transit maneuver, such as "Arrive at 8:10 AM at 34 St - Herald Sq".
357 pub verbal_arrive_instruction: Option<String>,
358 /// Contains the attributes that describe a specific transit route.
359 ///
360 /// See [`TransitInfo`] for details.
361 pub transit_info: Option<TransitInfo>,
362 /// `true` if [`Self::verbal_pre_transition_instruction`] has been appended with
363 /// the verbal instruction of the next maneuver and thus contains more than one instruction.
364 pub verbal_multi_cue: Option<bool>,
365 /// Travel mode
366 pub travel_mode: TravelMode,
367 /// Travel type
368 pub travel_type: TravelType,
369 /// Describes bike share maneuver.
370 ///
371 /// Used when travel_mode is [`TravelMode::Bicycle`].
372 ///
373 /// Default: [`BssManeuverType::NoneAction`]
374 pub bss_maneuver_type: Option<BssManeuverType>,
375}
376
377#[derive(Deserialize, Debug, Clone)]
378pub struct TransitInfo {
379 /// Global transit route identifier.
380 pub onestop_id: String,
381 /// Short name describing the transit route
382 ///
383 /// Example: "N"
384 pub short_name: String,
385 /// Long name describing the transit route
386 ///
387 /// Example: "Broadway Express"
388 pub long_name: String,
389 /// The sign on a public transport vehicle that identifies the route destination to passengers.
390 ///
391 /// Example: "ASTORIA - DITMARS BLVD"
392 pub headsign: String,
393 /// The numeric color value associated with a transit route.
394 ///
395 /// The value for yellow would be "16567306".
396 pub color: i32,
397 /// The numeric text color value associated with a transit route.
398 ///
399 /// The value for black would be "0".
400 pub text_color: String,
401 /// The description of the transit route
402 ///
403 /// Example: "Trains operate from Ditmars Boulevard, Queens, to Stillwell Avenue, Brooklyn, at all times
404 /// N trains in Manhattan operate along Broadway and across the Manhattan Bridge to and from Brooklyn.
405 /// Trains in Brooklyn operate along 4th Avenue, then through Borough Park to Gravesend.
406 /// Trains typically operate local in Queens, and either express or local in Manhattan and Brooklyn,
407 /// depending on the time. Late night trains operate via Whitehall Street, Manhattan.
408 /// Late night service is local"
409 pub description: String,
410 /// Global operator/agency identifier.
411 pub operator_onestop_id: String,
412 /// Operator/agency name
413 ///
414 /// Short name is used over long name.
415 ///
416 /// Example: "BART", "King County Marine Division", and so on.
417 pub operator_name: String,
418 /// Operator/agency URL
419 ///
420 /// Example: `http://web.mta.info/`.
421 pub operator_url: String,
422 /// A list of the stops/stations associated with a specific transit route.
423 ///
424 /// See [`TransitStop`] for details.
425 pub transit_stops: Vec<TransitStop>,
426}
427
428#[derive(serde_repr::Deserialize_repr, Debug, Clone, Copy, PartialEq, Eq)]
429#[repr(u8)]
430pub enum TransitStopType {
431 /// Simple stop.
432 Stop = 0,
433 /// Station.
434 Station,
435}
436
437#[derive(Deserialize, Debug, Clone)]
438pub struct TransitStop {
439 #[serde(rename = "type")]
440 pub type_: TransitStopType,
441 /// Name of the stop or station
442 ///
443 /// Example: "14 St - Union Sq"
444 pub name: String,
445 /// Arrival date and time
446 pub arrival_date_time: chrono::NaiveDateTime,
447 /// Departure date and time
448 pub departure_date_time: chrono::NaiveDateTime,
449 /// `true` if this stop is a marked as a parent stop.
450 pub is_parent_stop: bool,
451 /// `true` if the times are based on an assumed schedule because the actual schedule is not
452 /// known.
453 pub assumed_schedule: bool,
454 /// Latitude of the transit stop in degrees.
455 pub lat: f64,
456 /// Longitude of the transit stop in degrees.
457 pub lon: f64,
458}
459
460#[derive(Serialize, Default, Debug, Clone, Copy, PartialEq, Eq)]
461pub enum DirectionsType {
462 /// indicating no maneuvers or instructions should be returned.
463 #[serde(rename = "none")]
464 None,
465
466 /// indicating that only maneuvers be returned.
467 #[serde(rename = "maneuvers")]
468 Maneuvers,
469
470 /// indicating that maneuvers with instructions should be returned (this is the default if not
471 /// specified).
472 #[default]
473 #[serde(rename = "instructions")]
474 Instructions,
475}
476
477#[serde_with::skip_serializing_none]
478#[derive(Serialize, Default, Debug)]
479pub struct Manifest {
480 #[serde(flatten)]
481 costing: Option<costing::Costing>,
482 locations: Vec<Location>,
483 units: Option<super::Units>,
484 id: Option<String>,
485 language: Option<String>,
486 directions_type: Option<DirectionsType>,
487 alternates: Option<i32>,
488 exclude_locations: Option<Vec<Location>>,
489 exclude_polygons: Option<Vec<Vec<super::Coordinate>>>,
490 linear_references: Option<bool>,
491 prioritize_bidirectional: Option<bool>,
492 roundabout_exits: Option<bool>,
493 date_time: Option<DateTime>,
494}
495
496impl Manifest {
497 #[must_use]
498 pub fn builder() -> Self {
499 Self::default()
500 }
501 /// Configures the costing model
502 ///
503 /// Valhalla's routing service uses dynamic, run-time costing to generate the route path.
504 /// Can be configured with different settings depending on the costing model used.
505 ///
506 /// Default: [`costing::Costing::Auto`]
507 pub fn costing(mut self, costing: costing::Costing) -> Self {
508 self.costing = Some(costing);
509 self
510 }
511
512 /// Specify locations to visit as an ordered list
513 ///
514 /// Minimum number of locations: 2
515 ///
516 /// A location must include a latitude and longitude in decimal degrees.
517 /// The coordinates can come from many input sources, such as a GPS location, a point or a
518 /// click on a map, a geocoding service, and so on.
519 ///
520 /// **Note:** Valhalla cannot search for names or addresses or perform geocoding or reverse geocoding.
521 /// External search services, such as [Mapbox Geocoding](https://www.mapbox.com/api-documentation/#geocoding),
522 /// can be used to find places and geocode addresses, which must be converted to coordinates for input.
523 ///
524 /// To build a route, you need to specify two [`LocationType::Break`] locations.
525 /// In addition, you can include [`LocationType::Through`], [`LocationType::Via`] or
526 /// [`LocationType::BreakThrough`] locations to influence the route path.
527 /// See [`LocationType`] for further information.
528 pub fn locations(mut self, locations: impl IntoIterator<Item = Location>) -> Self {
529 self.locations = locations.into_iter().collect();
530 debug_assert!(self.locations.len() >= 2);
531 self
532 }
533
534 /// Sets the distance units for output.
535 ///
536 /// Possible unit types are
537 /// - miles via [`super::Units::Imperial`] and
538 /// - kilometers via [`super::Units::Metric`].
539 ///
540 /// Default: [`super::Units::Metric`]
541 pub fn units(mut self, units: super::Units) -> Self {
542 self.units = Some(units);
543 self
544 }
545
546 /// Name of the route request
547 ///
548 /// If id is specified, the naming will be sent through to the response.
549 pub fn id(mut self, id: impl ToString) -> Self {
550 self.id = Some(id.to_string());
551 self
552 }
553
554 /// The language of the narration instructions based on the
555 /// [IETF BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag) language tag string.
556 ///
557 /// If unsupported, the language `en-US` (United States-based English) is used
558 /// Currently supported language list can be found here:
559 /// <https://valhalla.github.io/valhalla/api/turn-by-turn/api-reference/#supported-language-tags>
560 ///
561 /// Default: `en-US` (United States-based English)
562 pub fn language(mut self, language: impl ToString) -> Self {
563 self.language = Some(language.to_string());
564 self
565 }
566 /// Sets the directions type
567 ///
568 /// [`DirectionsType`] is an enum with 3 values:
569 /// - [`DirectionsType::None`] indicates no maneuvers or instructions should be returned.
570 /// - [`DirectionsType::Maneuvers`] indicates that only maneuvers be returned.
571 /// - [`DirectionsType::Instructions`] indicates that maneuvers with instructions should be returned
572 ///
573 /// Default: [`DirectionsType::Instructions`]
574 pub fn directions_type(mut self, directions_type: DirectionsType) -> Self {
575 self.directions_type = Some(directions_type);
576 self
577 }
578
579 /// How many alternate routes should be provided
580 ///
581 /// There may be no alternates or fewer alternates than the user specifies.
582 ///
583 /// Alternates are not yet supported on
584 /// - multipoint routes (i.e. routes with more than 2 locations) and
585 /// - time dependent routes
586 pub fn alternates(mut self, alternates: i32) -> Self {
587 self.alternates = Some(alternates);
588 self
589 }
590
591 /// A set of [`Location`]s to exclude or avoid within a route
592 ///
593 /// They are mapped to the closest road or roads and these roads are excluded
594 /// from the route path computation.
595 pub fn exclude_locations(
596 mut self,
597 exclude_locations: impl IntoIterator<Item = Location>,
598 ) -> Self {
599 self.exclude_locations = Some(exclude_locations.into_iter().collect());
600 self
601 }
602
603 /// Sets at least one exterior rings of excluded polygons.
604 ///
605 /// **Note:** Contrary to [`Self::exclude_polygon`], this OVERRIDES previously set excluded polygons.
606 ///
607 /// Roads intersecting these rings will be avoided during path finding.
608 /// If you only need to avoid a few specific roads, it's much more efficient to use
609 /// [`Self::exclude_locations`].
610 /// Valhalla will close open rings (i.e. copy the first coordinate to the last position).
611 ///
612 /// # Example:
613 /// ```rust,no_run
614 /// use valhalla_client::blocking::Valhalla;
615 /// use valhalla_client::route::{Location, Manifest};
616 /// use valhalla_client::costing::{Costing};
617 ///
618 /// let polygon_around_midrecht_between_amsterdam_and_utrecht = vec![(4.9904022, 52.2528761), (4.8431168, 52.2392163), (4.8468933, 52.1799052), (4.9845657, 52.2102016), (4.9904022, 52.2528761)];
619 /// let polygon_around_leiden = vec![(4.5891266, 52.1979985),(4.4105987, 52.2560249),(4.3034820, 52.1592721),(4.5005493, 52.0935286),(4.5726471, 52.1373684),(4.5898132, 52.1984193),(4.5891266, 52.1979985)];
620 /// let amsterdam = Location::new(4.9041, 52.3676);
621 /// let utrecht = Location::new(5.1214, 52.0907);
622 ///
623 /// let manifest = Manifest::builder()
624 /// .locations([amsterdam, utrecht])
625 /// .exclude_polygons([polygon_around_leiden, polygon_around_midrecht_between_amsterdam_and_utrecht])
626 /// .costing(Costing::MotorScooter(Default::default()));
627 ///
628 /// let response = Valhalla::default()
629 /// .route(manifest)
630 /// .unwrap();
631 /// # assert!(!response.legs.is_empty());
632 /// ```
633 pub fn exclude_polygons(
634 mut self,
635 exclude_polygons: impl IntoIterator<Item = impl IntoIterator<Item = super::Coordinate>>,
636 ) -> Self {
637 let new_excluded_polygons = exclude_polygons
638 .into_iter()
639 .map(|e| e.into_iter().collect())
640 .collect();
641 self.exclude_polygons = Some(new_excluded_polygons);
642 self
643 }
644 /// Add one exterior rings as an excluded polygon.
645 ///
646 /// **Note:** Contrary to [`Self::exclude_polygons`], this APPENDS to the previously set excluded polygons.
647 ///
648 /// Roads intersecting these rings will be avoided during path finding.
649 /// If you only need to avoid a few specific roads, it's much more efficient to use
650 /// exclude_locations.
651 /// Valhalla will close open rings (i.e. copy the first coordinate to the last position).
652 ///
653 /// # Example:
654 /// ```rust,no_run
655 /// use valhalla_client::blocking::Valhalla;
656 /// use valhalla_client::route::{Location, Manifest};
657 /// use valhalla_client::costing::{Costing};
658 ///
659 /// let polygon_around_leiden = vec![(4.5891266, 52.1979985),(4.4105987, 52.2560249),(4.3034820, 52.1592721),(4.5005493, 52.0935286),(4.5726471, 52.1373684),(4.5898132, 52.1984193),(4.5891266, 52.1979985)];
660 /// let amsterdam = Location::new(4.9041, 52.3676);
661 /// let utrecht = Location::new(5.1214, 52.0907);
662 ///
663 /// let manifest = Manifest::builder()
664 /// .locations([amsterdam, utrecht])
665 /// .exclude_polygon(polygon_around_leiden)
666 /// .costing(Costing::Auto(Default::default()));
667 ///
668 /// let response = Valhalla::default()
669 /// .route(manifest)
670 /// .unwrap();
671 /// # assert!(!response.legs.is_empty());
672 /// ```
673 pub fn exclude_polygon(
674 mut self,
675 exclude_polygon: impl IntoIterator<Item = super::Coordinate>,
676 ) -> Self {
677 let new_excluded_polygon = exclude_polygon.into_iter().collect();
678 if let Some(ref mut polygons) = self.exclude_polygons {
679 polygons.push(new_excluded_polygon);
680 } else {
681 self.exclude_polygons = Some(vec![new_excluded_polygon]);
682 }
683 self
684 }
685
686 /// When present and true, the successful route response will include a key `linear_references`.
687 ///
688 /// Its value is an array of base64-encoded [OpenLR location references](https://en.wikipedia.org/wiki/OpenLR),
689 /// one for each graph edge of the road network matched by the input trace.
690 #[doc(hidden)] // TODO: need to implement the linear_references field
691 pub fn include_linear_references(mut self) -> Self {
692 self.linear_references = Some(true);
693 self
694 }
695
696 /// Prioritize bidirectional A* when `date_time.type = depart_at/current`.
697 ///
698 /// Currently, it does not update the time (and speeds) when searching for the route path, but
699 /// the ETA on that route is recalculated based on the time-dependent speeds
700 ///
701 /// Default: time_dependent_forward A* is used in these cases, but bidirectional A* is much faster
702 pub fn prioritize_bidirectional(mut self) -> Self {
703 self.prioritize_bidirectional = Some(true);
704 self
705 }
706
707 /// Don't include instructions at roundabouts to the output
708 ///
709 /// Default: `true`
710 pub fn roundabout_exits(mut self) -> Self {
711 self.roundabout_exits = Some(false);
712 self
713 }
714 /// Shortcut for configuring the arrival/departure date_time settings globally
715 /// instead of specifying it for each of the [locations](Location::date_time).
716 ///
717 /// See [`Location::date_time`] if you want a more granular API.
718 pub fn date_time(mut self, date_time: DateTime) -> Self {
719 self.date_time = Some(date_time);
720 self
721 }
722}
723
724#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq, Eq)]
725pub enum LocationType {
726 #[default]
727 #[serde(rename = "break")]
728 Break,
729
730 #[serde(rename = "through")]
731 Through,
732
733 #[serde(rename = "via")]
734 Via,
735
736 #[serde(rename = "break_through")]
737 BreakThrough,
738}
739
740#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq, Eq)]
741pub enum Side {
742 #[serde(rename = "same")]
743 Same,
744
745 #[serde(rename = "opposite")]
746 Opposite,
747
748 #[default]
749 #[serde(rename = "either")]
750 Either,
751}
752
753#[cfg(feature = "gpx")]
754impl From<&Location> for gpx::Waypoint {
755 fn from(location: &Location) -> Self {
756 let point =
757 geo_types::Point::new(f64::from(location.longitude), f64::from(location.latitude));
758 let mut p = Self::new(point);
759 p.name.clone_from(&location.name);
760 p
761 }
762}
763impl From<super::Coordinate> for Location {
764 fn from((latitude, longitude): super::Coordinate) -> Self {
765 Self {
766 latitude,
767 longitude,
768 ..Default::default()
769 }
770 }
771}
772
773impl Location {
774 /// Create a Location from latitude/longitude of the location in degrees.
775 ///
776 /// This is assumed to be both routing location and display location is equal.
777 /// See [`Self::display_coordinates`] to change the display location
778 pub fn new(longitude: f32, latitude: f32) -> Self {
779 Self {
780 latitude,
781 longitude,
782 ..Default::default()
783 }
784 }
785 /// Display Coordinate location in degrees.
786 ///
787 /// Will be used to determine the side of street.
788 /// Must be valid to achieve the desired effect.
789 pub fn display_coordinates(mut self, display_lat: f32, display_lon: f32) -> Self {
790 self.display_lat = Some(display_lat);
791 self.display_lon = Some(display_lon);
792 self
793 }
794
795 /// Sets the Street name.
796 ///
797 /// May be used to assist finding the correct routing location at the specified coordinate.
798 /// **This is not currently implemented.**
799 pub fn street_name(mut self, street: impl ToString) -> Self {
800 self.street = Some(street.to_string());
801 self
802 }
803
804 /// Sets the OpenStreetMap identification number for a polyline way.
805 ///
806 /// The way ID may be used to assist finding the correct routing location at the specified coordinate.
807 /// **This is not currently implemented.**
808 pub fn way_id(mut self, way_id: i64) -> Self {
809 self.way_id = Some(way_id);
810 self
811 }
812
813 /// Sets the Minimum number of nodes (intersections) reachable for a given edge (road between
814 /// intersections) to consider that edge as belonging to a connected region.
815 ///
816 /// When correlating this location to the route network, try to find candidates who are reachable
817 /// from this many or more nodes (intersections). If a given candidate edge reaches less than
818 /// this number of nodes it is considered to be a disconnected island, and we will search for more
819 /// candidates until we find at least one that isn't considered a disconnected island.
820 /// If this value is larger than the configured service limit it will be clamped to that limit.
821 ///
822 /// Default: `50` reachable nodes.
823 pub fn minimum_reachability(mut self, minimum_reachability: i32) -> Self {
824 self.minimum_reachability = Some(minimum_reachability);
825 self
826 }
827
828 /// The number of meters about this input location within which edges (roads between
829 /// intersections) will be considered as candidates for said location.
830 ///
831 /// When correlating this location to the route network, try to only return results within
832 /// this distance (meters) from this location. If there are no candidates within this distance
833 /// it will return the closest candidate within reason.
834 /// If this value is larger than the configured service limit it will be clamped to that limit.
835 ///
836 /// Default: `0` meters
837 pub fn radius(mut self, radius: i32) -> Self {
838 self.radius = Some(radius);
839 self
840 }
841
842 /// Whether or not to rank the edge candidates for this location.
843 ///
844 /// The ranking is used as a penalty within the routing algorithm so that some edges will be
845 /// penalized more heavily than others:
846 /// - If `true`, candidates will be ranked according to their distance from the input and
847 /// various other attributes.
848 /// - If `false` the candidates will all be treated as equal which should lead to routes that
849 /// are just the most optimal path with emphasis about which edges were selected.
850 pub fn rank_candidates(mut self, rank_candidates: bool) -> Self {
851 self.rank_candidates = Some(rank_candidates);
852 self
853 }
854 /// Which side of the road the location should be visited from.
855 ///
856 /// Whether the location should be visited from the [`Side::Same`], [`Side::Opposite`] or [`Side::Either`] side of
857 /// the road with respect to the side of the road the given locale drives on:
858 /// - In Germany (driving on the right side of the road), passing a value of same will only allow
859 /// you to leave from or arrive at a location such that the location will be on your right.
860 /// - In Australia (driving on the left side of the road), passing a value of same will force the location to be on
861 /// your left.
862 ///
863 /// A value of opposite will enforce arriving/departing from a location on the opposite side
864 /// of the road from that which you would be driving on while a value of either will make
865 /// no attempt limit the side of street that is available for the route.
866 ///
867 /// **Note:** If the location is not offset from the road centerline or is closest to an intersection
868 /// this option has no effect.
869 pub fn preferred_side(mut self, preferred_side: Side) -> Self {
870 self.preferred_side = Some(preferred_side);
871 self
872 }
873 /// Sets the type of the location
874 ///
875 /// Either [`LocationType::Break`], [`LocationType::Through`], [`LocationType::Via`] or [`LocationType::BreakThrough`].
876 /// The types of the first and last locations are ignored and are treated as [`LocationType::Break`].
877 /// Each type controls two characteristics:
878 /// - whether or not to allow an u-turn at the location and
879 /// - whether or not to generate guidance/legs at the location.
880 ///
881 /// Here is their behaviour:
882 /// - A [`LocationType::Break`] is a location at which we allows u-turns and generate legs and
883 /// arrival/departure maneuvers.
884 /// - A [`LocationType::Through`] location is a location at which we neither allow u-turns
885 /// nor generate legs or arrival/departure maneuvers.
886 /// - A [`LocationType::Via`] location is a location at which we allow u-turns,
887 /// but do not generate legs or arrival/departure maneuvers.
888 /// - A [`LocationType::BreakThrough`] location is a location at which we do not allow u-turns,
889 /// but do generate legs and arrival/departure maneuvers.
890 ///
891 /// Default: [`LocationType::Break`]
892 pub fn r#type(mut self, r#type: LocationType) -> Self {
893 self.r#type = Some(r#type);
894 self
895 }
896
897 /// Preferred direction of travel for the start from the location.
898 ///
899 /// This can be useful for mobile routing where a vehicle is traveling in a specific direction
900 /// along a road, and the route should start in that direction.
901 /// The heading is indicated in degrees from north in a clockwise direction:
902 /// - where north is `0°`,
903 /// - east is `90°`,
904 /// - south is `180°`, and
905 /// - west is `270°`.
906 pub fn heading(mut self, heading: u32) -> Self {
907 self.heading = Some(heading);
908 self
909 }
910 /// How close in degrees a given street's heading angle must be in order for it to be considered
911 /// as in the same direction of the heading parameter.
912 ///
913 /// The heading angle can be set via [`Self::heading`]
914 ///
915 /// Default: `60` degrees
916 pub fn heading_tolerance(mut self, heading_tolerance: u32) -> Self {
917 self.heading_tolerance = Some(heading_tolerance);
918 self
919 }
920 /// Location or business name.
921 ///
922 /// May be used in the route narration directions.
923 /// Example: `"You have arrived at <business name>"`
924 pub fn name(mut self, name: impl ToString) -> Self {
925 self.name = Some(name.to_string());
926 self
927 }
928 /// Cutoff at which we will assume the input is too far away from civilisation to be worth
929 /// correlating to the nearest graph elements.
930 ///
931 /// Default: `35 km`
932 pub fn search_cutoff(mut self, search_cutoff: f32) -> Self {
933 self.search_cutoff = Some(search_cutoff);
934 self
935 }
936 /// During edge correlation this is the tolerance used to determine whether or not to snap to
937 /// the intersection rather than along the street, if the snap location is within this distance
938 /// from the intersection is used instead.
939 ///
940 /// Default: `5 meters`
941 pub fn node_snap_tolerance(mut self, node_snap_tolerance: f32) -> Self {
942 self.node_snap_tolerance = Some(node_snap_tolerance);
943 self
944 }
945 /// Sets the tolerance for street side changes.
946 ///
947 /// The value means:
948 /// - If your input coordinate is less than this tolerance away from the edge centerline then we
949 /// set your side of street to none.
950 /// - Otherwise your side of street will be left or right depending on direction of travel.
951 ///
952 /// Default: `5 meters`
953 pub fn street_side_tolerance(mut self, street_side_tolerance: f32) -> Self {
954 self.street_side_tolerance = Some(street_side_tolerance);
955 self
956 }
957 /// The max distance in meters that the input coordinates or display ll can be from the edge
958 /// centerline for them to be used for determining the side of street.
959 ///
960 /// Beyond this distance the side of street is set to none.
961 ///
962 /// Default: `1000 meters`
963 pub fn street_side_max_distance(mut self, street_side_max_distance: f32) -> Self {
964 self.street_side_max_distance = Some(street_side_max_distance);
965 self
966 }
967
968 /// Allows configuring the preferred side selection.
969 ///
970 /// Disables the preferred side (set via [`Self::preferred_side`]) when set to [`Side::Same`]
971 /// or [`Side::Opposite`], if the edge has a road class less than that provided by this value.
972 ///
973 /// The road class must be one of the following strings:
974 /// - `motorway`,
975 /// - `trunk`,
976 /// - `primary`,
977 /// - `secondary`,
978 /// - `tertiary`,
979 /// - `unclassified`,
980 /// - `residential` or
981 /// - `service_other`.
982 ///
983 /// Default: `service_other` so that the preferred side will not be disabled for any edges
984 pub fn street_side_cutoff(mut self, street_side_cutoff: f32) -> Self {
985 self.street_side_cutoff = Some(street_side_cutoff);
986 self
987 }
988 /// Expected date/time for the user to be at the location in the local time zone of departure or arrival.
989 ///
990 /// Offers more granularity over setting time than the global [`Manifest::date_time`].
991 ///
992 /// If waiting was set on this location in the request, and it's an intermediate location,
993 /// the date_time will describe the departure time at this location.
994 pub fn date_time(mut self, date_time: chrono::NaiveDateTime) -> Self {
995 self.date_time = Some(date_time);
996 self
997 }
998 /// The waiting time at this location.
999 ///
1000 /// Only works if [`Manifest::r#type`] was set to
1001 /// - [`LocationType::Break`] or
1002 /// - [`LocationType::BreakThrough`]
1003 ///
1004 /// Example:
1005 /// A route describes a pizza delivery tour.
1006 /// Each location has a service time, which can be respected by setting waiting on the location.
1007 /// Then the departure will be delayed by this duration.
1008 pub fn waiting(mut self, waiting: chrono::Duration) -> Self {
1009 self.waiting = Some(waiting.num_seconds());
1010 self
1011 }
1012}
1013
1014#[serde_with::skip_serializing_none]
1015#[derive(Serialize, Deserialize, Default, Clone, Debug)]
1016pub struct Location {
1017 #[serde(rename = "lat")]
1018 latitude: f32,
1019 #[serde(rename = "lon")]
1020 longitude: f32,
1021 display_lat: Option<f32>,
1022 display_lon: Option<f32>,
1023 street: Option<String>,
1024 way_id: Option<i64>,
1025 minimum_reachability: Option<i32>,
1026 radius: Option<i32>,
1027 rank_candidates: Option<bool>,
1028 preferred_side: Option<Side>,
1029 #[serde(rename = "type")]
1030 r#type: Option<LocationType>,
1031 heading: Option<u32>,
1032 heading_tolerance: Option<u32>,
1033 name: Option<String>,
1034 search_cutoff: Option<f32>,
1035 node_snap_tolerance: Option<f32>,
1036 street_side_tolerance: Option<f32>,
1037 street_side_max_distance: Option<f32>,
1038 street_side_cutoff: Option<f32>,
1039 /// The waiting time in seconds at this location
1040 waiting: Option<i64>,
1041 /// Expected date/time for the user to be at the location.
1042 #[serde(serialize_with = "super::serialize_naive_date_time_opt")]
1043 date_time: Option<chrono::NaiveDateTime>,
1044}
1045
1046#[cfg(test)]
1047mod test {
1048 use super::*;
1049 #[test]
1050 fn serialisation() {
1051 assert_eq!(
1052 serde_json::to_value(Manifest::default()).unwrap(),
1053 serde_json::json!({"locations": []})
1054 );
1055 }
1056}