gistools/readers/gbfs/schema_v2/
mod.rs1mod free_bike_status;
2mod gbfs;
3mod gbfs_versions;
4mod geofencing_zones;
5mod station_information;
6mod station_status;
7mod system_alerts;
8mod system_calendar;
9mod system_hours;
10mod system_information;
11mod system_pricing_plans;
12mod system_regions;
13mod vehicle_types;
14
15use crate::{
16 geometry::{ConvertFeature, convert_geometry_to_vector},
17 parsers::FeatureReader,
18 util::fetch_url,
19};
20use alloc::{format, string::String, vec, vec::Vec};
21pub use free_bike_status::*;
22pub use gbfs::*;
23pub use gbfs_versions::*;
24pub use geofencing_zones::*;
25use s2json::{Features, MValue, Properties, VectorFeature, VectorGeometry, VectorPoint};
26use serde::{Deserialize, Serialize};
27pub use station_information::*;
28pub use station_status::*;
29pub use system_alerts::*;
30pub use system_calendar::*;
31pub use system_hours::*;
32pub use system_information::*;
33pub use system_pricing_plans::*;
34pub use system_regions::*;
35pub use vehicle_types::*;
36
37pub type GBFSGeofencingFeatureV2 = VectorFeature<(), GBFSGeofencingZonesV2Properties, MValue>;
39
40#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, MValue)]
42pub struct GBFSStationV2FeaturesProperties {
43 pub station_id: String,
45 pub name: String,
47 pub short_name: Option<String>,
49 pub address: Option<String>,
51 pub cross_street: Option<String>,
53 pub region_id: Option<String>,
55 pub post_code: Option<String>,
57 pub is_virtual_station: Option<bool>,
59 pub parking_type: Option<String>,
61 pub parking_hoop: Option<bool>,
63 pub contact_phone: Option<String>,
65 pub capacity: Option<u64>,
67 pub is_valet_station: Option<bool>,
69 pub is_charging_station: Option<bool>,
71}
72impl From<&GBFSStationInformationStationV23> for GBFSStationV2FeaturesProperties {
73 fn from(station: &GBFSStationInformationStationV23) -> Self {
74 GBFSStationV2FeaturesProperties {
75 station_id: station.station_id.clone(),
76 name: station.name.clone(),
77 short_name: station.short_name.clone(),
78 address: station.address.clone(),
79 cross_street: station.cross_street.clone(),
80 region_id: station.region_id.clone(),
81 post_code: station.post_code.clone(),
82 is_virtual_station: station.is_virtual_station,
83 parking_type: station.parking_type.clone().map(|s| serde_json::to_string(&s).unwrap()),
84 parking_hoop: station.parking_hoop,
85 contact_phone: station.contact_phone.clone(),
86 capacity: station.capacity,
87 is_valet_station: station.is_valet_station,
88 is_charging_station: station.is_charging_station,
89 }
90 }
91}
92
93pub type GBFSStationFeatureV2 = VectorFeature<(), GBFSStationV2FeaturesProperties, MValue>;
95
96pub type GBFSBikeFeatureV2 = VectorFeature<(), GBFSFreeBikeStatusBikeV23, MValue>;
98
99#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
101pub struct GBFSReaderV2 {
102 pub free_bike_status: Option<GBFSFreeBikeStatusV2>,
104 pub gbfs: GBFSV2,
106 pub gbfs_versions: Option<GBFSVersionsV2>,
108 pub geofencing_zones: Option<GBFSGeofencingZonesV2>,
110 pub station_information: Option<GBFSStationInformationV2>,
112 pub station_status: Option<GBFSStationStatusV2>,
114 pub system_alerts: Option<GBFSSystemAlertsV2>,
116 pub system_calendar: Option<GBFSSystemCalendarV2>,
118 pub system_hours: Option<GBFSSystemHoursV2>,
120 pub system_information: GBFSSystemInformationV2,
122 pub system_pricing_plans: Option<GBFSSystemPricingPlansV2>,
124 pub system_regions: Option<GBFSSystemRegionsV2>,
126 pub vehicle_types: Option<GBFSVehicleTypesV2>,
128}
129impl GBFSReaderV2 {
130 pub fn features(&self) -> Vec<VectorFeature> {
132 let mut res = vec![];
133
134 res.extend(self.station_features().iter().map(|f| f.to_m_vector_feature(|_| None)));
135 res.extend(self.bike_features().iter().map(|f| f.to_m_vector_feature(|_| None)));
136 res.extend(self.geofencing_features().iter().map(|f| f.to_m_vector_feature(|_| None)));
137
138 res
139 }
140
141 pub fn station_features(&self) -> Vec<GBFSStationFeatureV2> {
143 let mut res = vec![];
144
145 if let Some(station_information) = &self.station_information {
146 for station in &station_information.data.stations {
147 let properties = GBFSStationV2FeaturesProperties::from(station);
148 res.push(VectorFeature::new_wm(
149 None,
150 properties.clone(),
151 VectorGeometry::new_point(VectorPoint::from_xy(station.lon, station.lat), None),
152 None,
153 ));
154 if let Some(station_area) = &station.station_area {
155 res.push(VectorFeature::new_wm(
156 None,
157 properties,
158 convert_geometry_to_vector(station_area, true),
159 None,
160 ));
161 }
162 }
163 }
164
165 res
166 }
167
168 pub fn geofencing_features(&self) -> Vec<GBFSGeofencingFeatureV2> {
170 let mut res = vec![];
171
172 if let Some(geofencing_zones) = &self.geofencing_zones {
173 for feature in &geofencing_zones.data.geofencing_zones.features {
174 match feature {
175 Features::Feature(f) => {
176 res.push(f.to_vector(Some(true)));
177 }
178 Features::VectorFeature(vf) => res.push(vf.clone()),
179 }
180 }
181 }
182
183 res
184 }
185
186 pub fn bike_features(&self) -> Vec<GBFSBikeFeatureV2> {
188 let mut res = vec![];
189
190 if let Some(free_bike_status) = &self.free_bike_status {
191 for bike in &free_bike_status.data.bikes {
192 if let (Some(lon), Some(lat)) = (bike.lon, bike.lat) {
193 res.push(VectorFeature::new_wm(
194 None,
195 bike.clone(),
196 VectorGeometry::new_point(VectorPoint::from_xy(lon, lat), None),
197 None,
198 ));
199 }
200 }
201 }
202
203 res
204 }
205}
206
207#[derive(Debug)]
209pub struct GBFSIteratorV2 {
210 features: Vec<VectorFeature>,
211 index: usize,
212 len: usize,
213}
214impl Iterator for GBFSIteratorV2 {
215 type Item = VectorFeature;
216
217 fn next(&mut self) -> Option<Self::Item> {
218 if self.index >= self.len {
219 return None;
220 }
221 self.index += 1;
222 self.features.get(self.index - 1).cloned()
223 }
224}
225impl FeatureReader<(), Properties, MValue> for GBFSReaderV2 {
227 type FeatureIterator<'a> = GBFSIteratorV2;
228
229 fn iter(&self) -> Self::FeatureIterator<'_> {
230 let features = self.features();
231 let len = features.len();
232 GBFSIteratorV2 { features, index: 0, len }
233 }
234
235 fn par_iter(&self, pool_size: usize, thread_id: usize) -> Self::FeatureIterator<'_> {
236 let features = self.features();
237 let start = features.len() * thread_id / pool_size;
238 let end = features.len() * (thread_id + 1) / pool_size;
239 GBFSIteratorV2 { features, index: start, len: end }
240 }
241}
242
243pub async fn build_gbfs_reader_v2(
253 gbfs: &GBFSV2,
254 locale: Option<String>,
255 path: Option<String>,
256) -> GBFSReaderV2 {
257 let locale = locale.unwrap_or("en".into());
258 let data = &gbfs.data;
259 let first_locale = data.keys().next().unwrap();
260 let feeds = data.get(&locale).unwrap_or(data.get(first_locale).unwrap());
261 let feeds = feeds.feeds.clone();
262
263 let mut reader = GBFSReaderV2 { gbfs: gbfs.clone(), ..Default::default() };
264
265 for feed in feeds {
266 let name = serde_json::to_string(&feed.name).unwrap();
267 if &name == "gbfs" {
268 continue;
269 }
270 let url = if let Some(ref path) = path {
271 format!("{}/{}.json", path, name.trim_matches('"'))
272 } else {
273 feed.url
274 };
275
276 if let Ok(url_data) = fetch_url::<()>(&url, &[], None, None).await {
277 match feed.name {
278 GBFSV21FeedsName::FreeBikeStatus => {
279 reader.free_bike_status = Some(serde_json::from_slice(&url_data).unwrap());
280 }
281 GBFSV21FeedsName::Gbfs => {}
282 GBFSV21FeedsName::GbfsVersions => {
283 reader.gbfs_versions = Some(serde_json::from_slice(&url_data).unwrap());
284 }
285 GBFSV21FeedsName::StationInformation => {
286 reader.station_information = Some(serde_json::from_slice(&url_data).unwrap());
287 }
288 GBFSV21FeedsName::StationStatus => {
289 reader.station_status = Some(serde_json::from_slice(&url_data).unwrap());
290 }
291 GBFSV21FeedsName::SystemAlerts => {
292 reader.system_alerts = Some(serde_json::from_slice(&url_data).unwrap());
293 }
294 GBFSV21FeedsName::SystemCalendar => {
295 reader.system_calendar = Some(serde_json::from_slice(&url_data).unwrap());
296 }
297 GBFSV21FeedsName::SystemHours => {
298 reader.system_hours = Some(serde_json::from_slice(&url_data).unwrap());
299 }
300 GBFSV21FeedsName::SystemInformation => {
301 reader.system_information = serde_json::from_slice(&url_data).unwrap();
302 }
303 GBFSV21FeedsName::SystemPricingPlans => {
304 reader.system_pricing_plans = Some(serde_json::from_slice(&url_data).unwrap());
305 }
306 GBFSV21FeedsName::SystemRegions => {
307 reader.system_regions = Some(serde_json::from_slice(&url_data).unwrap());
308 }
309 GBFSV21FeedsName::VehicleTypes => {
310 reader.vehicle_types = Some(serde_json::from_slice(&url_data).unwrap());
311 }
312 GBFSV21FeedsName::GeofencingZones => {
313 reader.geofencing_zones = Some(serde_json::from_slice(&url_data).unwrap());
314 }
315 }
316 }
317 }
318
319 reader
320}