diff --git a/README.md b/README.md index 39b29c1..49b79eb 100644 --- a/README.md +++ b/README.md @@ -136,20 +136,14 @@ position: #### Combined metric PAQI The PAQI (pollen/air quality index) metric is a special combined metric. -If selected, it not only merges items from the AQI and pollen metric into -`PAQI` by selecting the maximum value for each hour, but it also yields the -maximum forecast item for air quality index in `AQI_max` and for -pollen in `pollen_max` seperately (out the items that `PAQI` combined): +If selected, it merges items from the AQI and pollen metric into `PAQI` by +selecting the maximum value for each hour: ``` json { "lat": 52.0905169, "lon": 5.1109709, "time": 1652189065, - "AQI_max": { - "time": 1652191200, - "value": 6.09 - }, "PAQI": [ { "time": 1652187600, @@ -160,11 +154,7 @@ pollen in `pollen_max` seperately (out the items that `PAQI` combined): "value": 6.09 }, ... - ], - "pollen_max": { - "time": 1652209200, - "value": 6 - } + ] } ``` diff --git a/src/forecast.rs b/src/forecast.rs index e010b15..4090d8f 100644 --- a/src/forecast.rs +++ b/src/forecast.rs @@ -33,10 +33,6 @@ pub(crate) struct Forecast { #[serde(rename = "AQI", skip_serializing_if = "Option::is_none")] aqi: Option>, - /// The maximum air quality index value (when asked for PAQI). - #[serde(rename = "AQI_max", skip_serializing_if = "Option::is_none")] - aqi_max: Option, - /// The NO₂ concentration (when asked for). #[serde(rename = "NO2", skip_serializing_if = "Option::is_none")] no2: Option>, @@ -57,10 +53,6 @@ pub(crate) struct Forecast { #[serde(skip_serializing_if = "Option::is_none")] pollen: Option>, - /// The maximum pollen in the air (when asked for PAQI). - #[serde(skip_serializing_if = "Option::is_none")] - pollen_max: Option, - /// The precipitation (when asked for). #[serde(skip_serializing_if = "Option::is_none")] precipitation: Option>, @@ -144,13 +136,7 @@ pub(crate) async fn forecast( Metric::NO2 => forecast.no2 = providers::luchtmeetnet::get(position, metric).await, Metric::O3 => forecast.o3 = providers::luchtmeetnet::get(position, metric).await, Metric::PAQI => { - if let Some((paqi, pollen_max, aqi_max)) = - providers::combined::get(position, metric, maps_handle).await - { - forecast.paqi = Some(paqi); - forecast.aqi_max = Some(aqi_max); - forecast.pollen_max = Some(pollen_max); - } + forecast.paqi = providers::combined::get(position, metric, maps_handle).await } Metric::PM10 => forecast.pm10 = providers::luchtmeetnet::get(position, metric).await, Metric::Pollen => { diff --git a/src/lib.rs b/src/lib.rs index 499f01c..71ef288 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -154,13 +154,11 @@ mod tests { assert_f64_near!(json["lon"].as_f64().unwrap(), 5.478633); assert_matches!(json["time"], JsonValue::Number(_)); assert_matches!(json.get("AQI"), None); - assert_matches!(json.get("AQI_max"), None); assert_matches!(json.get("NO2"), None); assert_matches!(json.get("O3"), None); assert_matches!(json.get("PAQI"), None); assert_matches!(json.get("PM10"), None); assert_matches!(json.get("pollen"), None); - assert_matches!(json.get("pollen_max"), None); assert_matches!(json.get("precipitation"), None); assert_matches!(json.get("UVI"), None); @@ -174,13 +172,11 @@ mod tests { assert_f64_near!(json["lon"].as_f64().unwrap(), 5.478633); assert_matches!(json["time"], JsonValue::Number(_)); assert_matches!(json.get("AQI"), Some(JsonValue::Array(_))); - assert_matches!(json.get("AQI_max"), Some(JsonValue::Object(_))); assert_matches!(json.get("NO2"), Some(JsonValue::Array(_))); assert_matches!(json.get("O3"), Some(JsonValue::Array(_))); assert_matches!(json.get("PAQI"), Some(JsonValue::Array(_))); assert_matches!(json.get("PM10"), Some(JsonValue::Array(_))); assert_matches!(json.get("pollen"), Some(JsonValue::Array(_))); - assert_matches!(json.get("pollen_max"), Some(JsonValue::Object(_))); assert_matches!(json.get("precipitation"), Some(JsonValue::Array(_))); assert_matches!(json.get("UVI"), Some(JsonValue::Array(_))); } @@ -198,13 +194,11 @@ mod tests { assert_f64_near!(json["lon"].as_f64().unwrap(), 5.5); assert_matches!(json["time"], JsonValue::Number(_)); assert_matches!(json.get("AQI"), None); - assert_matches!(json.get("AQI_max"), None); assert_matches!(json.get("NO2"), None); assert_matches!(json.get("O3"), None); assert_matches!(json.get("PAQI"), None); assert_matches!(json.get("PM10"), None); assert_matches!(json.get("pollen"), None); - assert_matches!(json.get("pollen_max"), None); assert_matches!(json.get("precipitation"), None); assert_matches!(json.get("UVI"), None); @@ -218,13 +212,11 @@ mod tests { assert_f64_near!(json["lon"].as_f64().unwrap(), 5.5); assert_matches!(json["time"], JsonValue::Number(_)); assert_matches!(json.get("AQI"), Some(JsonValue::Array(_))); - assert_matches!(json.get("AQI_max"), Some(JsonValue::Object(_))); assert_matches!(json.get("NO2"), Some(JsonValue::Array(_))); assert_matches!(json.get("O3"), Some(JsonValue::Array(_))); assert_matches!(json.get("PAQI"), Some(JsonValue::Array(_))); assert_matches!(json.get("PM10"), Some(JsonValue::Array(_))); assert_matches!(json.get("pollen"), Some(JsonValue::Array(_))); - assert_matches!(json.get("pollen_max"), Some(JsonValue::Object(_))); assert_matches!(json.get("precipitation"), Some(JsonValue::Array(_))); assert_matches!(json.get("UVI"), Some(JsonValue::Array(_))); } diff --git a/src/providers/combined.rs b/src/providers/combined.rs index 0ecf2bc..f058b34 100644 --- a/src/providers/combined.rs +++ b/src/providers/combined.rs @@ -35,18 +35,15 @@ impl Item { /// Merges pollen samples and AQI items into combined items. /// /// The merging drops items from either the pollen samples or from the AQI items if they are not -/// stamped with half an hour of the first item of the latest starting series, thus lining them +/// stamped within an hour of the first item of the latest starting series, thus lining them /// before they are combined. /// -/// This function also finds the maximum pollen sample and AQI item. -/// /// Returns [`None`] if there are no pollen samples, if there are no AQI items, or if -/// lining them up fails. Returns [`None`] for the maximum pollen sample or maximum AQI item -/// if there are no samples or items. +/// lining them up fails. fn merge( pollen_samples: Vec, aqi_items: Vec, -) -> Option<(Vec, BuienradarSample, LuchtmeetnetItem)> { +) -> Option> { let mut pollen_samples = pollen_samples; let mut aqi_items = aqi_items; @@ -80,23 +77,6 @@ fn merge( aqi_items.drain(..idx); } - // Find the maximum sample/item of each series. - // Note 1: Unwrapping is possible because each series has at least an item otherwise `.first` - // would have failed above. - // Note 2: Ensure that the maximum sample/item is in scope of the time range covered by the - // combined items. - let zip_len = std::cmp::min(pollen_samples.len(), aqi_items.len()); - let pollen_max = pollen_samples[..zip_len] - .iter() - .max_by_key(|sample| sample.score) - .cloned() - .unwrap(); - let aqi_max = aqi_items[..zip_len] - .iter() - .max_by_key(|item| (item.value * 1_000.0) as u32) - .cloned() - .unwrap(); - // Combine the samples with items by taking the maximum of pollen sample score and AQI item // value. let items = pollen_samples @@ -110,21 +90,16 @@ fn merge( }) .collect(); - Some((items, pollen_max, aqi_max)) + Some(items) } /// Retrieves the combined forecasted items for the provided position and metric. /// -/// Besides the combined items, it also yields the maxium pollen sample and AQI item. -/// Note that the maximum values are calculated before combining them, so the time stamp -/// corresponds to the one in the original series, not to a timestamp of an item after merging. -/// /// It supports the following metric: /// * [`Metric::PAQI`] /// /// Returns [`None`] for the combined items if retrieving data from either the Buienradar or the -/// Luchtmeetnet provider fails or if they cannot be combined. Returns [`None`] for the maxiumum -/// pollen sample or AQI item if there are no samples or items. +/// Luchtmeetnet provider fails or if they cannot be combined. /// /// If the result is [`Some`], it will be cached for 30 minutes for the the given position and /// metric. @@ -138,7 +113,7 @@ pub(crate) async fn get( position: Position, metric: Metric, maps_handle: &MapsHandle, -) -> Option<(Vec, BuienradarSample, LuchtmeetnetItem)> { +) -> Option> { if metric != Metric::PAQI { return None; }; @@ -185,7 +160,7 @@ mod tests { // Perform a normal merge. let merged = super::merge(pollen_samples.clone(), aqi_items.clone()); assert!(merged.is_some()); - let (paqi, max_pollen, max_aqi) = merged.unwrap(); + let paqi = merged.unwrap(); assert_eq!( paqi, Vec::from([ @@ -194,8 +169,6 @@ mod tests { Item::new(t_2, 2.4), ]) ); - assert_eq!(max_pollen, BuienradarSample::new(t_1, 3)); - assert_eq!(max_aqi, LuchtmeetnetItem::new(t_1, 2.9)); // The pollen samples are shifted, i.e. one hour in the future. let shifted_pollen_samples = pollen_samples[2..] @@ -208,10 +181,8 @@ mod tests { .collect::>(); let merged = super::merge(shifted_pollen_samples, aqi_items.clone()); assert!(merged.is_some()); - let (paqi, max_pollen, max_aqi) = merged.unwrap(); + let paqi = merged.unwrap(); assert_eq!(paqi, Vec::from([Item::new(t_1, 2.9), Item::new(t_2, 3.0)])); - assert_eq!(max_pollen, BuienradarSample::new(t_2, 3)); - assert_eq!(max_aqi, LuchtmeetnetItem::new(t_1, 2.9)); // The AQI items are shifted, i.e. one hour in the future. let shifted_aqi_items = aqi_items[2..] @@ -224,25 +195,19 @@ mod tests { .collect::>(); let merged = super::merge(pollen_samples.clone(), shifted_aqi_items); assert!(merged.is_some()); - let (paqi, max_pollen, max_aqi) = merged.unwrap(); + let paqi = merged.unwrap(); assert_eq!(paqi, Vec::from([Item::new(t_1, 3.0), Item::new(t_2, 2.9)])); - assert_eq!(max_pollen, BuienradarSample::new(t_1, 3)); - assert_eq!(max_aqi, LuchtmeetnetItem::new(t_2, 2.9)); // The maximum sample/item should not be later then the interval the PAQI items cover. let merged = super::merge(pollen_samples[..3].to_vec(), aqi_items.clone()); assert!(merged.is_some()); - let (paqi, max_pollen, max_aqi) = merged.unwrap(); + let paqi = merged.unwrap(); assert_eq!(paqi, Vec::from([Item::new(t_0, 1.1)])); - assert_eq!(max_pollen, BuienradarSample::new(t_0, 1)); - assert_eq!(max_aqi, LuchtmeetnetItem::new(t_0, 1.1)); let merged = super::merge(pollen_samples.clone(), aqi_items[..3].to_vec()); assert!(merged.is_some()); - let (paqi, max_pollen, max_aqi) = merged.unwrap(); + let paqi = merged.unwrap(); assert_eq!(paqi, Vec::from([Item::new(t_0, 1.1)])); - assert_eq!(max_pollen, BuienradarSample::new(t_0, 1)); - assert_eq!(max_aqi, LuchtmeetnetItem::new(t_0, 1.1)); // Merging fails because the samples/items are too far (6 hours) apart. let shifted_aqi_items = aqi_items