gistools/readers/osm/
filter.rs

1use super::{
2    OSMReader,
3    node::IntermediateNode,
4    primitive::PrimitiveBlock,
5    relation::IntermediateRelation,
6    way::{IntermediateWay, WayNodes},
7};
8use crate::{data_store::KVStore, parsers::Reader};
9use alloc::{collections::BTreeMap, string::String};
10use s2json::{MValue, VectorPoint};
11
12/// OSM Filterable is a trait to ensure that an object can be filtered
13pub trait OSMFilterable {
14    /// Check if the object is filterable
15    fn is_filterable<
16        T: Reader,
17        _N: KVStore<u64, VectorPoint<MValue>>,
18        N: KVStore<u64, IntermediateNode>,
19        _W: KVStore<u64, WayNodes>,
20        W: KVStore<u64, IntermediateWay>,
21        R: KVStore<u64, IntermediateRelation>,
22    >(
23        &self,
24        pb: &PrimitiveBlock,
25        reader: &mut OSMReader<T, _N, N, _W, W, R>,
26    ) -> bool;
27}
28
29/// Types of objects that can be filtered
30#[derive(Debug, Copy, Clone, PartialEq)]
31pub enum OSMTagFilterType {
32    /// Apply filter to all object types
33    All,
34    /// Apply filter to nodes
35    Node,
36    /// Apply filter to ways
37    Way,
38    /// Apply filter to relations
39    Relation,
40}
41
42/// Filter map. Used internally by TagFilter
43type FilterMap = BTreeMap<String, Option<String>>;
44
45/// # Tag Filter
46///
47/// ## Description
48/// Builds a filter for the tags when parsing data.
49/// Can parse tags from nodes, ways and relations.
50/// Also allows the ability to add tags that apply to all object types.
51/// Can filter by key, but also both key and value.
52///
53/// ## Usage
54///
55/// Note that if you don't add a filter for a specific type, then that type will pass all
56/// key-value pairs through.
57///
58/// ```rs
59/// use gistools::readers::{OSMTagFilter, OSMTagFilterType};
60///
61/// const filter = OSMTagFilter::default();
62/// // add a node filter
63/// filter.add_filter(OSMTagFilterType::Node, "foo".into(), Some("bar".into()));
64/// // add a way filter
65/// filter.add_filter(OSMTagFilterType::Way, "foo".into(), Some("bar".into()));
66/// // add a relation filter
67/// filter.add_filter(OSMTagFilterType::Relation, "foo".into(), Some("bar".into()));
68/// // add a filter that effects all types
69/// filter.add_filter(OSMTagFilterType::All, "foo".into(), Some("bar".into()));
70/// ```
71#[derive(Debug, Clone, Default)]
72pub struct OSMTagFilter {
73    all_filters: FilterMap,
74    node_filters: FilterMap,
75    way_filters: FilterMap,
76    relation_filters: FilterMap,
77}
78impl OSMTagFilter {
79    /// Add a filter
80    pub fn add_filter(
81        &mut self,
82        filter_type: OSMTagFilterType,
83        key: String,
84        value: Option<String>,
85    ) {
86        let filter = self.get_filter(filter_type);
87        filter.insert(key, value);
88    }
89
90    /// Check if a filter has been found
91    ///
92    /// ## Parameters
93    /// - `filter_type`: The filter type
94    /// - `key`: The key
95    /// - `value`: The value (optional)
96    ///
97    /// ## Returns
98    /// True if the filter has been found
99    pub fn match_found(&mut self, filter_type: OSMTagFilterType, key: &str, value: &str) -> bool {
100        // check all filters first
101        if self.check_filter(OSMTagFilterType::All, key, value) {
102            return true;
103        }
104        // check type-specific filters
105        if filter_type != OSMTagFilterType::All && self.check_filter(filter_type, key, value) {
106            return true;
107        }
108
109        false
110    }
111
112    /// Internal method to get the correct filter map
113    fn get_filter(&mut self, filter_type: OSMTagFilterType) -> &mut FilterMap {
114        match filter_type {
115            OSMTagFilterType::All => &mut self.all_filters,
116            OSMTagFilterType::Node => &mut self.node_filters,
117            OSMTagFilterType::Way => &mut self.way_filters,
118            OSMTagFilterType::Relation => &mut self.relation_filters,
119        }
120    }
121
122    /// Internal method to check if a filter contains a match with a value
123    fn check_filter(&mut self, filter_type: OSMTagFilterType, key: &str, value: &str) -> bool {
124        let filter = self.get_filter(filter_type);
125        match (filter.get(key), value) {
126            (Some(Some(filter_value)), v) => filter_value == v,
127            (Some(None), _) => true,
128            _ => false,
129        }
130    }
131}