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}