everymap_providers_google/domain/positioning/
mod.rs1pub mod types;
2
3use crate::client::GoogleClient;
4use async_trait::async_trait;
5use everymap_core::domains::positioning::{
6 NetworkPositioner, PositioningOptions, PositioningResponse as CorePositioningResponse,
7};
8use everymap_core::error::EveryMapResult;
9use everymap_core::types::Coordinate;
10use serde::Serialize;
11use std::sync::Arc;
12
13pub use types::*;
14
15const GEOLOCATION_BASE_URL: &str = "https://www.googleapis.com/geolocation/v1";
16
17#[derive(Debug, Serialize)]
19#[serde(rename_all = "camelCase")]
20struct GeolocationRequestBody {
21 #[serde(skip_serializing_if = "Option::is_none")]
22 consider_ip: Option<bool>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 wifi_access_points: Option<Vec<GoogleWifiAccessPoint>>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 cell_towers: Option<Vec<GoogleCellTower>>,
27}
28
29impl From<GooglePositioningOptions> for GeolocationRequestBody {
30 fn from(options: GooglePositioningOptions) -> Self {
31 Self {
32 consider_ip: options.consider_ip,
33 wifi_access_points: options.wifi_access_points,
34 cell_towers: options.cell_towers,
35 }
36 }
37}
38
39pub struct GooglePositioner {
41 pub(crate) client: Arc<GoogleClient>,
42 pub(crate) base_url: String,
43}
44
45impl GooglePositioner {
46 pub fn new(client: Arc<GoogleClient>) -> Self {
47 Self {
48 client,
49 base_url: GEOLOCATION_BASE_URL.to_string(),
50 }
51 }
52
53 pub fn with_base_url(client: Arc<GoogleClient>, base_url: String) -> Self {
54 Self { client, base_url }
55 }
56
57 pub async fn locate(
60 &self,
61 options: GooglePositioningOptions,
62 ) -> EveryMapResult<GoogleGeolocationResponse> {
63 let url = format!("{}/geolocate", self.base_url);
64 let body = GeolocationRequestBody::from(options);
65 let builder = self
66 .client
67 .build_request(reqwest::Method::POST, &url)
68 .json(&body);
69
70 let result: GoogleGeolocationResponse = self.client.request_json(builder).await?;
71 Ok(result)
72 }
73}
74
75fn positioning_options_from_core(options: &PositioningOptions) -> GooglePositioningOptions {
78 let mut google_opts = GooglePositioningOptions::default();
79
80 if let Some(extra) = &options.provider_extra {
81 if let Ok(parsed) = serde_json::from_value::<GooglePositioningOptions>(extra.clone()) {
82 google_opts = parsed;
83 }
84 }
85
86 google_opts
87}
88
89impl From<GoogleGeolocationResponse> for CorePositioningResponse {
90 fn from(response: GoogleGeolocationResponse) -> Self {
91 let coordinate = Coordinate::new(response.location.lat, response.location.lng)
92 .unwrap_or(Coordinate::ORIGIN);
93 Self {
94 coordinate,
95 accuracy: if response.accuracy > 0.0 {
96 Some(response.accuracy)
97 } else {
98 None
99 },
100 altitude: None,
101 altitude_accuracy: None,
102 raw: None,
103 }
104 }
105}
106
107#[async_trait]
108impl NetworkPositioner for GooglePositioner {
109 async fn get_position(
110 &self,
111 options: &PositioningOptions,
112 ) -> EveryMapResult<CorePositioningResponse> {
113 let google_opts = positioning_options_from_core(options);
114 let result = self.locate(google_opts).await?;
115 Ok(result.into())
116 }
117}