everymap_providers_google/domain/matching/
mod.rs1pub mod types;
2
3use crate::client::GoogleClient;
4use async_trait::async_trait;
5use everymap_core::domains::matching::{
6 MatchedPoint, MatchingOptions, RouteMatcher, TraceResponse,
7};
8use everymap_core::error::EveryMapResult;
9use everymap_core::types::Coordinate;
10use std::sync::Arc;
11
12pub use types::*;
13
14const ROADS_BASE_URL: &str = "https://roads.googleapis.com/v1";
15
16pub struct GoogleRouteMatcher {
20 pub(crate) client: Arc<GoogleClient>,
21 pub(crate) base_url: String,
22}
23
24impl GoogleRouteMatcher {
25 pub fn new(client: Arc<GoogleClient>) -> Self {
26 Self {
27 client,
28 base_url: ROADS_BASE_URL.to_string(),
29 }
30 }
31
32 pub fn with_base_url(client: Arc<GoogleClient>, base_url: String) -> Self {
33 Self { client, base_url }
34 }
35}
36
37fn matching_options_from_core(options: &MatchingOptions) -> GoogleMatchOptions {
39 let mut google_opts = GoogleMatchOptions::default();
40
41 if options.transport_mode.is_some() {
42 log::warn!("Google Roads API snapToRoads does not support transport_mode; ignoring");
43 }
44 if options.heading.is_some() {
45 log::warn!("Google Roads API snapToRoads does not support heading; ignoring");
46 }
47 if options.departure_time.is_some() {
48 log::warn!("Google Roads API snapToRoads does not support departure_time; ignoring");
49 }
50 if !options.avoid.is_empty() {
51 log::warn!("Google Roads API snapToRoads does not support avoid restrictions; ignoring");
52 }
53
54 if let Some(extra) = &options.provider_extra {
55 if let Some(obj) = extra.as_object() {
56 if let Some(v) = obj.get("interpolate").and_then(|v| v.as_bool()) {
57 google_opts.interpolate = v;
58 }
59 if let Some(v) = obj.get("snapping").and_then(|v| v.as_str()) {
60 google_opts.snapping = Some(v.to_string());
61 }
62 }
63 }
64
65 google_opts
66}
67
68#[async_trait]
69impl RouteMatcher for GoogleRouteMatcher {
70 async fn match_route(
71 &self,
72 points: &[Coordinate],
73 options: &MatchingOptions,
74 ) -> EveryMapResult<TraceResponse> {
75 let google_opts = matching_options_from_core(options);
76
77 let path: String = points
79 .iter()
80 .map(|p| format!("{},{}", p.lat, p.lng))
81 .collect::<Vec<_>>()
82 .join("|");
83
84 let mut params: Vec<(&str, String)> = vec![("path", path)];
85
86 if google_opts.interpolate {
87 params.push(("interpolate", "true".to_string()));
88 }
89
90 let url = format!("{}/snapToRoads", self.base_url);
91 let builder = self
92 .client
93 .build_request(reqwest::Method::GET, &url)
94 .query(¶ms);
95
96 let response: GoogleSnapResponse = self.client.request_json(builder).await?;
97
98 let matched_points: Vec<MatchedPoint> = response
99 .snapped_points
100 .into_iter()
101 .map(|sp| {
102 MatchedPoint {
103 coordinate: Coordinate::new(sp.location.latitude, sp.location.longitude)
104 .unwrap_or(Coordinate::ORIGIN),
105 confidence: None, road_name: sp.place_id, }
108 })
109 .collect();
110
111 Ok(TraceResponse {
113 matched_points,
114 distance: 0.0,
115 duration: None,
116 raw: None,
117 })
118 }
119}