use crate::{
Feature as _, GeometryType,
osm::OsmFeature,
styling::{Checker, RenderingState},
};
use super::{TagValue, YamlFilter, YamlGeometry};
pub(super) fn compile_filter(filter: &YamlFilter) -> Checker<OsmFeature>
{
let geometry = filter.geometry;
let min_zoom = filter.min_zoom;
let max_zoom = filter.max_zoom;
let tag_filters: Vec<(String, Vec<String>)> = filter
.tag
.as_ref()
.map(|tags| {
tags
.iter()
.map(|(key, value)| {
let values = match value
{
TagValue::String(v) => vec![v.clone()],
TagValue::Sequence(vs) => vs.clone(),
};
(key.clone(), values)
})
.collect()
})
.unwrap_or_default();
Box::new(
move |rendering_state: &RenderingState, feature: &OsmFeature| {
if let Some(geometry) = geometry
{
let expected = match geometry
{
YamlGeometry::Point => GeometryType::Point,
YamlGeometry::Linestring => GeometryType::LineString,
YamlGeometry::Polygon => GeometryType::Polygon,
YamlGeometry::Multipolygon => GeometryType::Collection,
};
if feature.geometry_type() != expected
&& (feature.geometry_type() != GeometryType::Collection
|| feature.element_geometry_type() != expected)
{
return false;
}
}
if let Some(min_zoom) = min_zoom
&& rendering_state.zoom_level < min_zoom
{
return false;
}
if let Some(max_zoom) = max_zoom
&& rendering_state.zoom_level > max_zoom
{
return false;
}
tag_filters.iter().all(|(key, expected_values)| {
feature
.tag(key)
.is_some_and(|actual_value| expected_values.iter().any(|v| v == actual_value))
})
},
)
}
#[cfg(test)]
mod tests
{
use std::collections::HashMap;
use super::*;
fn make_feature(tags: Vec<(&str, &str)>, geometry: geo::Geometry) -> OsmFeature
{
OsmFeature {
id: 1,
geometry,
tags: tags
.into_iter()
.map(|(k, v)| (k.to_owned(), v.to_owned()))
.collect(),
}
}
fn point() -> geo::Geometry
{
geo::Geometry::Point(geo::Point::new(0.0, 0.0))
}
fn linestring() -> geo::Geometry
{
geo::Geometry::LineString(geo::LineString::from(vec![(0.0, 0.0), (1.0, 1.0)]))
}
fn polygon() -> geo::Geometry
{
geo::Geometry::Polygon(geo::Polygon::new(
geo::LineString::from(vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]),
vec![],
))
}
fn rs(zoom_level: f64) -> RenderingState
{
RenderingState { zoom_level }
}
#[test]
fn geometry_only_filter()
{
let filter = YamlFilter {
geometry: Some(YamlGeometry::Polygon),
tag: None,
min_zoom: None,
max_zoom: None,
};
let checker = compile_filter(&filter);
assert!(checker(&rs(10.0), &make_feature(vec![], polygon())));
assert!(!checker(&rs(10.0), &make_feature(vec![], linestring())));
}
#[test]
fn single_value_tag_filter()
{
let filter = YamlFilter {
geometry: None,
tag: Some(HashMap::from([(
"natural".to_string(),
TagValue::String("water".to_string()),
)])),
min_zoom: None,
max_zoom: None,
};
let checker = compile_filter(&filter);
assert!(checker(
&rs(10.0),
&make_feature(vec![("natural", "water")], polygon())
));
assert!(!checker(
&rs(10.0),
&make_feature(vec![("natural", "wood")], polygon())
));
}
#[test]
fn list_value_tag_filter()
{
let filter = YamlFilter {
geometry: None,
tag: Some(HashMap::from([(
"highway".to_string(),
TagValue::Sequence(vec!["primary".to_string(), "trunk".to_string()]),
)])),
min_zoom: None,
max_zoom: None,
};
let checker = compile_filter(&filter);
assert!(checker(
&rs(10.0),
&make_feature(vec![("highway", "primary")], linestring())
));
assert!(!checker(
&rs(10.0),
&make_feature(vec![("highway", "service")], linestring())
));
}
#[test]
fn zoom_range_filter()
{
let filter = YamlFilter {
geometry: None,
tag: None,
min_zoom: Some(8.0),
max_zoom: Some(12.0),
};
let checker = compile_filter(&filter);
let feature = make_feature(vec![], point());
assert!(checker(&rs(10.0), &feature));
assert!(!checker(&rs(7.0), &feature));
assert!(!checker(&rs(13.0), &feature));
}
#[test]
fn combined_filter()
{
let filter = YamlFilter {
geometry: Some(YamlGeometry::Polygon),
tag: Some(HashMap::from([(
"natural".to_string(),
TagValue::String("water".to_string()),
)])),
min_zoom: Some(8.0),
max_zoom: Some(12.0),
};
let checker = compile_filter(&filter);
assert!(checker(
&rs(10.0),
&make_feature(vec![("natural", "water")], polygon())
));
assert!(!checker(
&rs(10.0),
&make_feature(vec![("natural", "water")], linestring())
));
assert!(!checker(
&rs(10.0),
&make_feature(vec![("natural", "wood")], polygon())
));
}
}