gistools/readers/gbfs/schema_v1/
mod.rs

1mod free_bike_status;
2mod gbfs;
3mod gbfs_versions;
4mod station_information;
5mod station_status;
6mod system_alerts;
7mod system_calendar;
8mod system_hours;
9mod system_information;
10mod system_pricing_plans;
11mod system_regions;
12
13use crate::{parsers::FeatureReader, util::fetch_url};
14use alloc::{format, string::String, vec, vec::Vec};
15pub use free_bike_status::*;
16pub use gbfs::*;
17pub use gbfs_versions::*;
18use s2json::{MValue, Properties, VectorFeature, VectorGeometry, VectorPoint};
19use serde::{Deserialize, Serialize};
20pub use station_information::*;
21pub use station_status::*;
22pub use system_alerts::*;
23pub use system_calendar::*;
24pub use system_hours::*;
25pub use system_information::*;
26pub use system_pricing_plans::*;
27pub use system_regions::*;
28
29/// Station Information feature properties
30#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, MValue)]
31pub struct GBFSStationV1FeaturesProperties {
32    /// ID
33    pub station_id: String,
34    /// Name
35    pub name: String,
36    /// Short Name
37    pub short_name: Option<String>,
38    /// Address
39    pub address: Option<String>,
40    /// Cross Street
41    pub cross_street: Option<String>,
42    /// Region ID
43    pub region_id: Option<String>,
44    /// Post Code
45    pub post_code: Option<String>,
46    /// Capacity
47    pub capacity: Option<u64>,
48}
49impl From<&GBFSStationInformationV11Station> for GBFSStationV1FeaturesProperties {
50    fn from(station: &GBFSStationInformationV11Station) -> Self {
51        GBFSStationV1FeaturesProperties {
52            station_id: station.station_id.clone(),
53            name: station.name.clone(),
54            short_name: station.short_name.clone(),
55            address: station.address.clone(),
56            cross_street: station.cross_street.clone(),
57            region_id: station.region_id.clone(),
58            post_code: station.post_code.clone(),
59            capacity: station.capacity,
60        }
61    }
62}
63
64/// Station Information Point Feature
65pub type GBFSStationFeatureV1 = VectorFeature<(), GBFSStationV1FeaturesProperties, MValue>;
66
67/// Bike Information Point Feature
68pub type GBFSBikeFeatureV1 = VectorFeature<(), GBFSFreeBikeV11, MValue>;
69
70/// GBFS Version 1 Reader
71#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
72pub struct GBFSReaderV1 {
73    /// Free Bike Status
74    pub free_bike_status: Option<GBFSFreeBikeStatusV1>,
75    /// GBFS information
76    pub gbfs: GBFSV1,
77    /// GBFS versions
78    pub gbfs_versions: Option<GBFSVersionsV1>,
79    /// System Information
80    pub station_information: Option<GBFSStationInformationV1>,
81    /// Station Status
82    pub station_status: Option<GBFSStationStatusV1>,
83    /// System Alerts
84    pub system_alerts: Option<GBFSSystemAlertsV1>,
85    /// System Calendar
86    pub system_calendar: Option<GBFSSystemCalendarV1>,
87    /// System Hours
88    pub system_hours: Option<GBFSSystemHoursV1>,
89    /// System Information
90    pub system_information: GBFSSystemInformationV1,
91    /// System Pricing Plans
92    pub system_pricing_plans: Option<GBFSSystemPricingPlansV1>,
93    /// System Regions
94    pub system_regions: Option<GBFSSystemRegionsV1>,
95}
96impl GBFSReaderV1 {
97    /// Get all features from the GBFS V1 data
98    pub fn features(&self) -> Vec<VectorFeature> {
99        let mut res = vec![];
100
101        res.extend(self.station_features().iter().map(|f| f.to_m_vector_feature(|_| None)));
102        res.extend(self.bike_features().iter().map(|f| f.to_m_vector_feature(|_| None)));
103
104        res
105    }
106
107    /// Get all station features from the GBFS V1 data
108    pub fn station_features(&self) -> Vec<GBFSStationFeatureV1> {
109        let mut res = vec![];
110
111        if let Some(station_information) = &self.station_information {
112            for station in &station_information.data.stations {
113                res.push(VectorFeature::new_wm(
114                    None,
115                    GBFSStationV1FeaturesProperties::from(station),
116                    VectorGeometry::new_point(VectorPoint::from_xy(station.lon, station.lat), None),
117                    None,
118                ));
119            }
120        }
121
122        res
123    }
124
125    /// Get all bike features from the GBFS V1 data
126    pub fn bike_features(&self) -> Vec<GBFSBikeFeatureV1> {
127        let mut res = vec![];
128
129        if let Some(free_bike_status) = &self.free_bike_status {
130            for bike in &free_bike_status.data.bikes {
131                res.push(VectorFeature::new_wm(
132                    None,
133                    bike.clone(),
134                    VectorGeometry::new_point(VectorPoint::from_xy(bike.lon, bike.lat), None),
135                    None,
136                ));
137            }
138        }
139
140        res
141    }
142}
143
144/// The GBFS V1 Iterator tool
145#[derive(Debug)]
146pub struct GBFSIteratorV1 {
147    features: Vec<VectorFeature>,
148    index: usize,
149    len: usize,
150}
151impl Iterator for GBFSIteratorV1 {
152    type Item = VectorFeature;
153
154    fn next(&mut self) -> Option<Self::Item> {
155        if self.index >= self.len {
156            return None;
157        }
158        self.index += 1;
159        self.features.get(self.index - 1).cloned()
160    }
161}
162/// A feature reader trait with a callback-based approach
163impl FeatureReader<(), Properties, MValue> for GBFSReaderV1 {
164    type FeatureIterator<'a> = GBFSIteratorV1;
165
166    fn iter(&self) -> Self::FeatureIterator<'_> {
167        let features: Vec<VectorFeature> =
168            self.features().iter().map(|f| f.to_m_vector_feature(|_| None)).collect();
169        let len = features.len();
170        GBFSIteratorV1 { features, index: 0, len }
171    }
172
173    fn par_iter(&self, pool_size: usize, thread_id: usize) -> Self::FeatureIterator<'_> {
174        let features: Vec<VectorFeature> =
175            self.features().iter().map(|f| f.to_m_vector_feature(|_| None)).collect();
176        let start = features.len() * thread_id / pool_size;
177        let end = features.len() * (thread_id + 1) / pool_size;
178        GBFSIteratorV1 { features, index: start, len: end }
179    }
180}
181
182/// Parse a GBFSV1 schema and build a V1 GBFS reader
183///
184/// ## Parameters
185/// - `gbfs`: the GBFS schema to parse
186/// - `locale`: the locale to use if provided, otherwise default to en
187/// - `path`: if provided, will use this path instead of the url (for testing)
188///
189/// ## Returns
190/// The GBFS reader
191pub async fn build_gbfs_reader_v1(
192    gbfs: &GBFSV1,
193    locale: Option<String>,
194    path: Option<String>,
195) -> GBFSReaderV1 {
196    let locale = locale.unwrap_or("en".into());
197    let data = &gbfs.data;
198    let first_locale = data.keys().next().unwrap();
199    let feeds = data.get(&locale).unwrap_or(data.get(first_locale).unwrap());
200    let feeds = feeds.feeds.clone();
201
202    let mut reader = GBFSReaderV1 { gbfs: gbfs.clone(), ..Default::default() };
203
204    for feed in feeds {
205        let name = serde_json::to_string(&feed.name).unwrap();
206        if &name == "gbfs" {
207            continue;
208        }
209        let url = if let Some(ref path) = path {
210            format!("{}/{}.json", path, name.trim_matches('"'))
211        } else {
212            feed.url
213        };
214
215        if let Ok(url_data) = fetch_url::<()>(&url, &[], None, None).await {
216            match feed.name {
217                GBFSV11FeedsName::FreeBikeStatus => {
218                    reader.free_bike_status = Some(serde_json::from_slice(&url_data).unwrap());
219                }
220                GBFSV11FeedsName::Gbfs => {}
221                GBFSV11FeedsName::GbfsVersions => {
222                    reader.gbfs_versions = Some(serde_json::from_slice(&url_data).unwrap());
223                }
224                GBFSV11FeedsName::StationInformation => {
225                    reader.station_information = Some(serde_json::from_slice(&url_data).unwrap());
226                }
227                GBFSV11FeedsName::StationStatus => {
228                    reader.station_status = Some(serde_json::from_slice(&url_data).unwrap());
229                }
230                GBFSV11FeedsName::SystemAlerts => {
231                    reader.system_alerts = Some(serde_json::from_slice(&url_data).unwrap());
232                }
233                GBFSV11FeedsName::SystemCalendar => {
234                    reader.system_calendar = Some(serde_json::from_slice(&url_data).unwrap());
235                }
236                GBFSV11FeedsName::SystemHours => {
237                    reader.system_hours = Some(serde_json::from_slice(&url_data).unwrap());
238                }
239                GBFSV11FeedsName::SystemInformation => {
240                    reader.system_information = serde_json::from_slice(&url_data).unwrap();
241                }
242                GBFSV11FeedsName::SystemPricingPlans => {
243                    reader.system_pricing_plans = Some(serde_json::from_slice(&url_data).unwrap());
244                }
245                GBFSV11FeedsName::SystemRegions => {
246                    reader.system_regions = Some(serde_json::from_slice(&url_data).unwrap());
247                }
248            }
249        }
250    }
251
252    reader
253}