Skip to main content

overpass_lib/query/
query.rs

1use std::{
2    collections::{HashMap, HashSet},
3    fmt::Write,
4};
5use chrono::{DateTime, Utc};
6use crate::{OverpassQL, OverpassQLError, Set, Bbox, OverpassQLNamed, Namer};
7#[cfg(doc)]
8use crate::{Element};
9
10/// The amount of detail to be included in [Query]-matched [Element]s.
11/// 
12/// [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Output_format_.28out%3A.29)
13#[derive(Debug, Clone, Copy, Default)]
14pub enum QueryVerbosity {
15    //Count,
16
17    /// Include the element type, ID, coordinates/members, and tags.
18    #[default]
19    Body,
20
21    /// Include the element type and ID only.
22    Ids,
23
24    /// Include the element type, ID, and coordinates/members only.
25    Skeleton,
26
27    /// Include the element type, ID, and tags only.
28    Tags,
29
30    //Meta,
31}
32
33impl OverpassQL for QueryVerbosity {
34    fn fmt_oql(&self, f: &mut impl Write) -> Result<(), OverpassQLError> {
35        match self {
36            //Self::Count => write!(f, "out count;"),
37            Self::Body => write!(f, "out;"),
38            Self::Ids => write!(f, "out ids;"),
39            Self::Tags => write!(f, "out tags;"),
40            Self::Skeleton => write!(f, "out skel;"),
41            //Self::Meta => write!(f, "out meta;"),
42        }?;
43        Ok(())
44    }
45}
46
47/// The root type of this API. It serializes into a complete Overpass QL query by calling [to_oql](OverpassQL::to_oql).
48#[derive(Debug, Default)]
49pub struct Query<'a> {
50    /// The length of time in seconds after which the server will abort the query.
51    /// [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#timeout%3A)
52    pub timeout_s: Option<u32>,
53
54    /// The maximum allowed memory for the query in bytes RAM on the server, beyond which the server will abort the query.
55    /// [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Element_limit_.28maxsize%3A.29)
56    pub max_size: Option<u32>,
57
58    /// Apply the query only to elements within the region defined by the bounding box.
59    /// [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Global_bounding_box_.28bbox.29)
60    pub search_bbox: Option<Bbox>,
61
62    /// Query the state of the map as of the given date/time.
63    /// [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Date)
64    pub as_of_date: Option<DateTime<Utc>>,
65
66    /// Query only elements created/modified between the first date/time, and the second date/time if supplied, or now if not.
67    /// [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Difference_between_two_dates_.28diff.29)
68    pub diff: Option<(DateTime<Utc>, Option<DateTime<Utc>>)>,
69
70    /// Adjust the amount of detail included in returned [Element](crate::Element)s.
71    /// [wiki](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Output_format_.28out%3A.29)
72    pub verbosity: QueryVerbosity,
73
74    /// The [Set] of [Element](crate::Element)s to be returned when this query is [evaluate](crate::Overpass::evaluate)d.
75    pub set: Set<'a>,
76}
77
78impl<'a> From<Set<'a>> for Query<'a> {
79    fn from(value: Set<'a>) -> Self {
80        Self {
81            set: value,
82            ..Default::default()
83        }
84    }
85}
86
87impl<'a> AsRef<Query<'a>> for Query<'a> {
88    fn as_ref(&self) -> &Query<'a> {
89        self
90    }
91}
92
93/// Determine the order in which the sets within this query must be defined.
94fn resolve_ordering<'a, 'b>(query_set: &'b Set<'a>)
95-> Result<Vec<&'b Set<'a>>, OverpassQLError>
96where 'a: 'b {
97    // for {k: [v]}, v must be defined before k
98    let mut refs = evaluate_refs(query_set, HashMap::new());
99
100    // for {k: [v]}, k must be defined before v
101    let mut back_refs = HashMap::new();
102    for (a, b) in refs.iter() {
103        for c in b {
104            back_refs.entry(*c).or_insert(HashSet::new()).insert(*a);
105        }
106    }
107
108    let mut output = vec![];
109
110    while refs.len() > 0 {
111        // find sets with no dependencies
112        let next_outputs = refs
113            .extract_if(|_, v| v.len() == 0)
114            .map(|(k, _)| k)
115            .collect::<Vec<_>>();
116
117        // fail if there aren't any
118        if next_outputs.len() == 0 {
119            return Err(OverpassQLError::CircularReference);
120        }
121
122        for next in next_outputs {
123            // output them first
124            output.push(next);
125
126            // take them out of any reference list that contains them
127            if let Some(next_refs) = back_refs.remove(next) {
128                for referent in next_refs.iter() {
129                    refs.get_mut(referent).unwrap().remove(next);
130                }
131            }
132            
133        }
134    }
135
136    Ok(output)
137}
138
139/// Generate a lookup table for the query's set dependencies.
140fn evaluate_refs<'a, 'b>(
141    set: &'b Set<'a>, 
142    mut refs: HashMap<&'b Set<'a>, HashSet<&'b Set<'a>>>,
143) -> HashMap<&'b Set<'a>, HashSet<&'b Set<'a>>>
144where 'a: 'b {
145    let deps = refs.entry(set).or_insert(HashSet::new());
146    let fresh = set.dependencies()
147        .filter(|s| deps.insert(s))
148        .collect::<Vec<_>>();
149
150    for i in fresh {
151        refs = evaluate_refs(i, refs);
152    }
153
154    refs
155}
156
157impl<'a> OverpassQL for Query<'a> {
158    fn fmt_oql(&self, f: &mut impl Write) -> Result<(), OverpassQLError> {
159        if let Some(d) = self.timeout_s {
160            write!(f, "[timeout:{}]", d)?;
161        }
162        if let Some(s) = self.max_size {
163            write!(f, "[maxsize:{s}]")?;
164        }
165        if let Some(bbox) = self.search_bbox {
166            write!(f, "[bbox:")?;
167            bbox.fmt_oql(f)?;
168            write!(f, "]")?;
169        }
170        if let Some(d) = self.as_of_date {
171            write!(f, r#"[date:"{d}"]"#)?;
172        }
173        if let Some((a, mayb)) = self.diff {
174            if let Some(b) = mayb {
175                write!(f, r#"[diff:"{a}","{b}"]"#)?;
176            } else {
177                write!(f, r#"[diff:"{a}"]"#)?;
178            }
179        }
180        write!(f, "[out:json];")?;
181
182        let mut namer = Namer::new(&self.set);
183        for set in resolve_ordering(&self.set)? {
184            set.fmt_oql_named(f, &mut namer)?;
185            write!(f, ";")?;
186        }
187
188        self.verbosity.fmt_oql(f)
189    }
190}
191
192#[cfg(test)]
193mod test {
194    use std::borrow::Cow;
195    use super::*;
196    use crate::{FilterSet, FilterType, TagFilter};
197
198    #[test]
199    fn resolve_ordering() {
200        let q1 = Set::Filter(FilterSet {
201            filter_type: FilterType::NodeOrWay,
202            tag_filters: HashSet::from([
203                TagFilter::equals("public_transport", "platform"),
204            ]),
205            ..Default::default()
206        });
207        let q2 = Set::Filter(FilterSet {
208            filter_type: FilterType::Node,
209            inputs: HashSet::from([Cow::Borrowed(&q1)]),
210            ..Default::default()
211        });
212
213        assert_eq!(super::resolve_ordering(&q2).unwrap(), vec![&q1, &q2]);
214    }
215
216    #[test]
217    fn fmt_oql() {
218        let q1 = Set::Filter(FilterSet {
219            filter_type: FilterType::NodeOrWay,
220            tag_filters: HashSet::from([
221                TagFilter::equals("public_transport", "platform"),
222            ]),
223            ..Default::default()
224        });
225        let q2 = Set::Filter(FilterSet {
226            filter_type: FilterType::Node,
227            inputs: HashSet::from([Cow::Borrowed(&q1)]),
228            ..Default::default()
229        });
230        let q = Query::from(q2);
231
232        assert_eq!(q.to_oql(), vec![
233            "[out:json];",
234            r#"nw["public_transport"="platform"]->.a;"#,
235            "node.a;",
236            "out;"
237        ].join(""));
238    }
239}