gistools/readers/gbfs/schema_v3/
mod.rs1mod gbfs;
2mod gbfs_versions;
3mod geofencing_zones;
4mod manifest;
5mod station_information;
6mod station_status;
7mod system_alerts;
8mod system_information;
9mod system_pricing_plans;
10mod system_regions;
11mod vehicle_status;
12mod vehicle_types;
13
14use crate::{
15 geometry::{ConvertFeature, convert_geometry_to_vector},
16 parsers::FeatureReader,
17 util::fetch_url,
18};
19use alloc::{format, string::String, vec, vec::Vec};
20pub use gbfs::*;
21pub use gbfs_versions::*;
22pub use geofencing_zones::*;
23pub use manifest::*;
24use s2json::{Features, MValue, Properties, VectorFeature, VectorGeometry, VectorPoint};
25use serde::{Deserialize, Serialize};
26pub use station_information::*;
27pub use station_status::*;
28pub use system_alerts::*;
29pub use system_information::*;
30pub use system_pricing_plans::*;
31pub use system_regions::*;
32pub use vehicle_status::*;
33pub use vehicle_types::*;
34
35pub type GBFSGeofencingFeatureV3 = VectorFeature<(), GBFSGeofencingZonesV3Properties, MValue>;
37
38#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, MValue)]
40pub struct GBFSStationV3FeaturesProperties {
41 pub station_id: String,
43 pub name: String,
45 pub short_name: Option<String>,
47 pub address: Option<String>,
49 pub cross_street: Option<String>,
51 pub region_id: Option<String>,
53 pub post_code: Option<String>,
55 pub station_opening_hours: 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 GBFSStationV3FeaturesProperties {
73 pub fn new(station: &GBFSStationV3, locale: &str) -> Self {
75 let name = &station
77 .name
78 .iter()
79 .find(|n| n.language == locale)
80 .or_else(|| station.name.first())
81 .map_or(String::new(), |n| n.text.clone());
82 let short_name = station.short_name.as_ref().and_then(|s| {
83 s.iter().find(|name| name.language == locale).map(|name| name.text.clone())
84 });
85 GBFSStationV3FeaturesProperties {
86 station_id: station.station_id.clone(),
87 name: name.clone(),
88 short_name,
89 address: station.address.clone(),
90 cross_street: station.cross_street.clone(),
91 region_id: station.region_id.clone(),
92 post_code: station.post_code.clone(),
93 station_opening_hours: station.station_opening_hours.clone(),
94 is_virtual_station: station.is_virtual_station,
95 parking_type: station.parking_type.clone().map(|s| serde_json::to_string(&s).unwrap()),
96 parking_hoop: station.parking_hoop,
97 contact_phone: station.contact_phone.clone(),
98 capacity: station.capacity,
99 is_valet_station: station.is_valet_station,
100 is_charging_station: station.is_charging_station,
101 }
102 }
103}
104
105pub type GBFSStationFeatureV3 = VectorFeature<(), GBFSStationV3FeaturesProperties, MValue>;
107
108pub type GBFSVehicleFeatureV3 = VectorFeature<(), GBFSVehicleV3, MValue>;
110
111#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
113pub struct GBFSReaderV3 {
114 pub locale: String,
116 pub gbfs: GBFSV3,
118 pub gbfs_versions: Option<GBFSVersionsV3>,
120 pub system_information: GBFSSystemInformationV3,
122 pub station_information: Option<GBFSStationInformationV3>,
124 pub station_status: Option<GBFSStationStatusV3>,
126 pub vehicle_status: Option<GBFSVehicleStatusV3>,
128 pub vehicle_types: Option<GBFSVehicleTypesV3>,
130 pub system_alerts: Option<GBFSSystemAlertsV3>,
132 pub system_regions: Option<GBFSSystemRegionsV3>,
134 pub system_pricing_plans: Option<GBFSSystemPricingPlansV3>,
136 pub geofencing_zones: Option<GBFSGeofencingZonesV3>,
138 pub manifest: Option<GBFSManifestV3>,
140}
141impl GBFSReaderV3 {
142 pub fn features(&self) -> Vec<VectorFeature> {
144 let mut res = vec![];
145
146 res.extend(self.station_features().iter().map(|f| f.to_m_vector_feature(|_| None)));
147 res.extend(self.geofencing_features().iter().map(|f| f.to_m_vector_feature(|_| None)));
148 res.extend(self.vehicle_features().iter().map(|f| f.to_m_vector_feature(|_| None)));
149
150 res
151 }
152
153 pub fn station_features(&self) -> Vec<GBFSStationFeatureV3> {
155 let mut res = vec![];
156
157 if let Some(station_information) = &self.station_information {
158 for station in &station_information.data.stations {
159 let properties = GBFSStationV3FeaturesProperties::new(station, &self.locale);
160 res.push(VectorFeature::new_wm(
161 None,
162 properties.clone(),
163 VectorGeometry::new_point(VectorPoint::from_xy(station.lon, station.lat), None),
164 None,
165 ));
166 if let Some(station_area) = &station.station_area {
167 res.push(VectorFeature::new_wm(
168 None,
169 properties,
170 convert_geometry_to_vector(station_area, true),
171 None,
172 ));
173 }
174 }
175 }
176
177 res
178 }
179
180 pub fn geofencing_features(&self) -> Vec<GBFSGeofencingFeatureV3> {
182 let mut res = vec![];
183
184 if let Some(geofencing_zones) = &self.geofencing_zones {
185 for feature in &geofencing_zones.data.geofencing_zones.features {
186 match feature {
187 Features::Feature(f) => {
188 res.push(f.to_vector(Some(true)));
189 }
190 Features::VectorFeature(vf) => res.push(vf.clone()),
191 }
192 }
193 }
194
195 res
196 }
197
198 pub fn vehicle_features(&self) -> Vec<GBFSVehicleFeatureV3> {
200 let mut res = vec![];
201
202 if let Some(vehicle_status) = &self.vehicle_status {
203 for vehicle in &vehicle_status.data.vehicles {
204 if let (Some(lat), Some(lon)) = (vehicle.lat, vehicle.lon) {
205 res.push(VectorFeature::new_wm(
206 None,
207 vehicle.clone(),
208 VectorGeometry::new_point(VectorPoint::from_xy(lon, lat), None),
209 None,
210 ));
211 }
212 }
213 }
214
215 res
216 }
217}
218
219#[derive(Debug)]
221pub struct GBFSIteratorV3 {
222 features: Vec<VectorFeature>,
223 index: usize,
224 len: usize,
225}
226impl Iterator for GBFSIteratorV3 {
227 type Item = VectorFeature;
228
229 fn next(&mut self) -> Option<Self::Item> {
230 if self.index >= self.len {
231 return None;
232 }
233 self.index += 1;
234 self.features.get(self.index - 1).cloned()
235 }
236}
237impl FeatureReader<(), Properties, MValue> for GBFSReaderV3 {
239 type FeatureIterator<'a> = GBFSIteratorV3;
240
241 fn iter(&self) -> Self::FeatureIterator<'_> {
242 let features = self.features();
243 let len = features.len();
244 GBFSIteratorV3 { features, index: 0, len }
245 }
246
247 fn par_iter(&self, pool_size: usize, thread_id: usize) -> Self::FeatureIterator<'_> {
248 let features = self.features();
249 let start = features.len() * thread_id / pool_size;
250 let end = features.len() * (thread_id + 1) / pool_size;
251 GBFSIteratorV3 { features, index: start, len: end }
252 }
253}
254
255pub async fn build_gbfs_reader_v3(
265 gbfs: &GBFSV3,
266 locale: Option<String>,
267 path: Option<String>,
268) -> GBFSReaderV3 {
269 let feeds = gbfs.data.feeds.clone();
270
271 let mut reader = GBFSReaderV3 {
272 locale: locale.unwrap_or("en".into()),
273 gbfs: gbfs.clone(),
274 ..Default::default()
275 };
276
277 for feed in feeds {
278 let name = serde_json::to_string(&feed.name).unwrap();
279 if &name == "gbfs" {
280 continue;
281 }
282 let url = if let Some(ref path) = path {
283 format!("{}/{}.json", path, name.trim_matches('"'))
284 } else {
285 feed.url
286 };
287
288 if let Ok(url_data) = fetch_url::<()>(&url, &[], None, None).await {
289 match feed.name {
290 GBFSV30FeedsName::Gbfs => {}
291 GBFSV30FeedsName::GbfsVersions => {
292 reader.gbfs_versions = Some(serde_json::from_slice(&url_data).unwrap());
293 }
294 GBFSV30FeedsName::StationInformation => {
295 reader.station_information = Some(serde_json::from_slice(&url_data).unwrap());
296 }
297 GBFSV30FeedsName::StationStatus => {
298 reader.station_status = Some(serde_json::from_slice(&url_data).unwrap());
299 }
300 GBFSV30FeedsName::SystemAlerts => {
301 reader.system_alerts = Some(serde_json::from_slice(&url_data).unwrap());
302 }
303 GBFSV30FeedsName::SystemInformation => {
304 reader.system_information = serde_json::from_slice(&url_data).unwrap();
305 }
306 GBFSV30FeedsName::SystemPricingPlans => {
307 reader.system_pricing_plans = Some(serde_json::from_slice(&url_data).unwrap());
308 }
309 GBFSV30FeedsName::SystemRegions => {
310 reader.system_regions = Some(serde_json::from_slice(&url_data).unwrap());
311 }
312 GBFSV30FeedsName::VehicleStatus => {
313 reader.vehicle_status = Some(serde_json::from_slice(&url_data).unwrap());
314 }
315 GBFSV30FeedsName::VehicleTypes => {
316 reader.vehicle_types = Some(serde_json::from_slice(&url_data).unwrap());
317 }
318 GBFSV30FeedsName::GeofencingZones => {
319 reader.geofencing_zones = Some(serde_json::from_slice(&url_data).unwrap());
320 }
321 }
322 }
323 }
324
325 if let Some(manifest_url) = reader.system_information.data.manifest_url.clone() {
326 let manifest_url = if let Some(ref path) = path {
327 format!("{}/manifest.json", path)
328 } else {
329 manifest_url
330 };
331 let manifest_data = fetch_url::<()>(&manifest_url, &[], None, None).await.unwrap();
332 reader.manifest = serde_json::from_slice(&manifest_data).unwrap_or(None);
333 }
334
335 reader
336}