Skip to main content

overpass_lib/query/
filter.rs

1use std::{
2    borrow::Cow,
3    collections::{hash_set::IntoIter, HashSet},
4    fmt::Write,
5};
6use crate::{
7    Bbox, Namer, OverpassQLError, OverpassQLNamed, OverpassQL, RecurseFilter, Set, TagFilter
8};
9#[cfg(doc)]
10use crate::{Node, Way, Relation};
11
12/// The type of element selected by a [FilterSet].
13#[derive(Debug, Clone, Copy, Default)]
14pub enum FilterType {
15    /// [Node]s
16    Node,
17    /// [Way]s
18    Way,
19    /// [Relation]s
20    Relation,
21    /// Any element type
22    #[default]
23    Any,
24    NodeOrWay,
25    NodeOrRelation,
26    WayOrRelation,
27    // Derived,
28    /// Ways or relations that the server determines represent a two-dimensional area and not just
29    /// a line. [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Areas)
30    Area,
31}
32
33impl OverpassQL for FilterType {
34    fn fmt_oql(&self, f: &mut impl Write) -> Result<(), OverpassQLError> {
35        match self {
36            Self::Node => write!(f, "node")?,
37            Self::Way => write!(f, "way")?,
38            Self::Relation => write!(f, "relation")?,
39            Self::Any => write!(f, "nwr")?,
40            Self::NodeOrWay => write!(f, "nw")?,
41            Self::NodeOrRelation => write!(f, "nr")?,
42            Self::WayOrRelation => write!(f, "wr")?,
43            // Self::Derived => write!(f, "derived")?,
44            Self::Area => write!(f, "area")?,
45        }
46        Ok(())
47    }
48}
49
50impl<'a> Into<Set<'a>> for FilterType {
51    fn into(self) -> Set<'a> {
52        Set::Filter(FilterSet { filter_type: self, ..Default::default() })
53    }
54}
55
56/// A subtype of [Set] that contains elements that satisfy the specified criteria.
57#[derive(Debug, Clone, Default)]
58pub struct FilterSet<'a> {
59    /// The type(s) of elements eligible to be in this set.
60    /// [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#The_Query_Statement)
61    pub filter_type: FilterType,
62
63    /// Only elements common to all of these sets are eligible to be in this set.
64    /// An empty collection means all elements are eligible.
65    /// [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#By_input_set_.28.setname.29)
66    pub inputs: HashSet<Cow<'a, Set<'a>>>,
67
68    /// Only elements with one of these identifiers are eligible to be in this set.
69    /// An empty collection means identifiers are not considered.
70    /// [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#By_element_id)
71    pub id_filters: HashSet<i64>,
72
73    /// Only elements whose tags satisfy all these filters are eligible to be in this set.
74    /// [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#By_tag_.28has-kv.29)
75    pub tag_filters: HashSet<TagFilter<'a>>,
76
77    /// Only elements that lie within these bounds are eligible to be in this set.
78    /// [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Bounding_box)
79    pub bbox_filter: Option<Bbox>,
80
81    /// Only elements with these relationships are eligible to be in this set.
82    /// [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Recurse_.28n.2C_w.2C_r.2C_bn.2C_bw.2C_br.29)
83    pub recurse_filters: HashSet<RecurseFilter<'a>>,
84}
85
86impl<'a> OverpassQLNamed<'a> for FilterSet<'a> {
87    fn fmt_oql_named<'b, 'c>(&'b self, f: &mut impl Write, namer: &mut Namer<'a, 'c>)
88    -> Result<(), OverpassQLError>
89    where 'b: 'c {
90        self.filter_type.fmt_oql(f)?;
91
92        for input in &self.inputs {
93            if let Some(name) = namer.get_or_assign(input) {
94                write!(f, ".{name}")?;
95            }
96        }
97
98        if self.id_filters.len() > 0 {
99            let mut iter = self.id_filters.iter();
100            write!(f, "(id:{}", iter.next().unwrap())?;
101            for i in iter {
102                write!(f, ",{i}")?;
103            }
104            write!(f, ")")?;
105        }
106
107        if let Some(bbox) = &self.bbox_filter {
108            write!(f, "(")?;
109            bbox.fmt_oql(f)?;
110            write!(f, ")")?;
111        }
112
113        for filter in &self.tag_filters {
114            filter.fmt_oql(f)?;
115        }
116
117        for filter in &self.recurse_filters {
118            filter.fmt_oql_named(f, namer)?;
119        }
120        
121        Ok(())
122    }
123}
124
125impl<'a> FilterSet<'a> {
126    /// The sets that must be defined before this set.
127    pub fn dependencies(&self) -> IntoIter<&Set<'a>> {
128        self.inputs.iter().map(|i| i.as_ref())
129            .chain(self.recurse_filters.iter().map(|r| r.input()))
130            .collect::<HashSet<_>>().into_iter()
131    }
132}