Ver código fonte

search: Allow intersection of multiple queries

Johannes Hofmann 7 anos atrás
pai
commit
b2ad3b8d9f
5 arquivos alterados com 155 adições e 75 exclusões
  1. 3
    10
      src/args.rs
  2. 37
    20
      src/config.rs
  3. 9
    4
      src/main.rs
  4. 21
    12
      src/query.rs
  5. 85
    29
      src/search.rs

+ 3
- 10
src/args.rs Ver arquivo

@@ -37,16 +37,9 @@ pub fn parse<'a>() -> clap::ArgMatches<'a> {
37 37
         .arg(Arg::with_name("keyval")
38 38
             .short("k")
39 39
             .long("keyval")
40
-            .value_name("KEY:VALUE")
41
-            .validator(|s| {
42
-                let colons = (s.split(':').count() - 1).max(0);
43
-                if colons == 1 {
44
-                    Ok(())
45
-                } else {
46
-                    Err(format!("exactly one colon (':') is required to separate key and value, \
47
-                                found {}", colons))
48
-                }
49
-            })
40
+            .value_names(&["KEY", "VALUE"])
41
+            .number_of_values(2)
42
+            .multiple(true)
50 43
             .help("Search for places that are tagged with the given key and value")
51 44
             .takes_value(true))
52 45
         .arg(Arg::with_name("fps")

+ 37
- 20
src/config.rs Ver arquivo

@@ -29,7 +29,7 @@ pub struct Config {
29 29
     sources: Vec<(String, TileSource)>,
30 30
     pbf_path: Option<PathBuf>,
31 31
     search_pattern: Option<String>,
32
-    keyval: Option<(String, String)>,
32
+    keyval: Vec<(String, String)>,
33 33
     fps: f64,
34 34
     use_network: bool,
35 35
     async: bool,
@@ -69,15 +69,20 @@ impl Config {
69 69
     fn merge_arg_matches<'a>(&mut self, matches: &clap::ArgMatches<'a>) {
70 70
         self.search_pattern = matches.value_of("search").map(|s| s.to_string());
71 71
 
72
-        self.keyval = matches.value_of("keyval").and_then(|kv| {
73
-            let mut split = kv.split(':');
74
-
75
-            if let (Some(key), Some(value)) = (split.next(), split.next()) {
76
-                Some((key.to_string(), value.to_string()))
77
-            } else {
78
-                None
79
-            }
80
-        });
72
+        self.keyval = matches.values_of("keyval").map_or_else(
73
+            || vec![],
74
+            |mut kv| {
75
+                let mut vec = vec![];
76
+                loop {
77
+                    if let (Some(k), Some(v)) = (kv.next(), kv.next()) {
78
+                        vec.push((k.to_string(), v.to_string()));
79
+                    } else {
80
+                        break;
81
+                    }
82
+                }
83
+                vec
84
+            },
85
+        );
81 86
 
82 87
         if let Some(Ok(fps)) = matches.value_of("fps").map(|s| s.parse()) {
83 88
             self.fps = fps;
@@ -241,7 +246,7 @@ impl Config {
241 246
                         sources: vec![],
242 247
                         pbf_path,
243 248
                         search_pattern: None,
244
-                        keyval: None,
249
+                        keyval: vec![],
245 250
                         fps,
246 251
                         use_network,
247 252
                         async,
@@ -403,19 +408,31 @@ impl Config {
403 408
         self.search_pattern.as_ref().map(|s| s.as_str())
404 409
     }
405 410
 
406
-    pub fn keyval(&self) -> Option<(&str, &str)> {
407
-        self.keyval.as_ref().map(|kv| (kv.0.as_str(), kv.1.as_str()))
411
+    pub fn keyval(&self) -> &[(String, String)] {
412
+        self.keyval.as_slice()
408 413
     }
409 414
 
410 415
     pub fn query_args(&self) -> Option<QueryArgs> {
411
-        match (&self.search_pattern, &self.keyval) {
412
-            (&Some(ref pattern), &None) => Some(QueryArgs::ValuePattern(pattern.to_string())),
413
-            (&None, &Some(ref kv)) => Some(QueryArgs::KeyValue(kv.0.to_string(), kv.1.to_string())),
414
-            (&Some(_), &Some(_)) => {
415
-                //TODO implement
416
-                unimplemented!();
416
+        match (&self.search_pattern, self.keyval.len()) {
417
+            (&Some(ref pattern), 0) => Some(QueryArgs::ValuePattern(pattern.to_string())),
418
+            (&None, 1) => Some(
419
+                QueryArgs::KeyValue(self.keyval[0].0.to_string(), self.keyval[0].1.to_string())
420
+            ),
421
+            (&Some(ref pattern), _) => {
422
+                let mut vec = vec![QueryArgs::ValuePattern(pattern.to_string())];
423
+
424
+                for (k, v) in &self.keyval {
425
+                    vec.push(QueryArgs::KeyValue(k.to_string(), v.to_string()));
426
+                }
427
+                Some(QueryArgs::Intersection(vec))
428
+            },
429
+            (&None, x) if x > 0 => {
430
+                let mut vec = self.keyval.iter()
431
+                    .map(|&(ref k, ref v)| QueryArgs::KeyValue(k.to_string(), v.to_string()))
432
+                    .collect();
433
+                Some(QueryArgs::Intersection(vec))
417 434
             },
418
-            (&None, &None) => None,
435
+            (&None, _) => None,
419 436
         }
420 437
     }
421 438
 

+ 9
- 4
src/main.rs Ver arquivo

@@ -48,10 +48,12 @@ pub mod tile_source;
48 48
 pub mod url_template;
49 49
 pub mod vertex_attrib;
50 50
 
51
-use coord::{LatLonDeg, ScreenCoord};
51
+use coord::ScreenCoord;
52 52
 use glutin::dpi::{LogicalPosition, LogicalSize, PhysicalPosition};
53 53
 use glutin::{ControlFlow, ElementState, Event, GlContext, MouseButton, MouseScrollDelta, VirtualKeyCode, WindowEvent};
54 54
 use map_view_gl::MapViewGl;
55
+use search::MatchItem;
56
+use std::collections::hash_set::HashSet;
55 57
 use std::error::Error;
56 58
 use std::sync::mpsc;
57 59
 use std::time::{Duration, Instant};
@@ -91,13 +93,16 @@ fn handle_event(
91 93
     map: &mut MapViewGl,
92 94
     input_state: &mut InputState,
93 95
     sources: &mut TileSources,
94
-    marker_rx: &mpsc::Receiver<Vec<LatLonDeg>>,
96
+    marker_rx: &mpsc::Receiver<HashSet<MatchItem>>,
95 97
 ) -> Action {
96 98
     trace!("{:?}", event);
97 99
     match *event {
98 100
         Event::Awakened => {
99
-            for pos in marker_rx.try_iter().flat_map(|c| c.into_iter()) {
100
-                map.add_marker(pos.into());
101
+            for item in marker_rx.try_iter().flat_map(|c| c.into_iter()) {
102
+                match item {
103
+                    MatchItem::Node{pos, ..} => map.add_marker(pos.into()),
104
+                    MatchItem::Way{pos, ..} => map.add_marker(pos.into()),
105
+                }
101 106
             }
102 107
             Action::Redraw
103 108
         },

+ 21
- 12
src/query.rs Ver arquivo

@@ -1,6 +1,8 @@
1 1
 use coord::LatLonDeg;
2 2
 use osmpbf::{DenseNode, Node, PrimitiveBlock, Relation, Way};
3 3
 use regex::Regex;
4
+use search::MatchItem;
5
+use std::collections::hash_set::HashSet;
4 6
 
5 7
 
6 8
 pub trait Query {
@@ -16,14 +18,14 @@ pub trait Query {
16 18
 pub enum QueryArgs {
17 19
     ValuePattern(String),
18 20
     KeyValue(String, String),
19
-    Intersection(Box<(QueryArgs, QueryArgs)>),
21
+    Intersection(Vec<QueryArgs>),
20 22
 }
21 23
 
22 24
 #[derive(Debug)]
23 25
 pub enum QueryKind {
24 26
     ValuePattern(ValuePatternQuery),
25 27
     KeyValue(KeyValueQuery),
26
-    Intersection(Box<(QueryArgs, QueryArgs)>),
28
+    Intersection(Vec<QueryKind>),
27 29
 }
28 30
 
29 31
 impl QueryArgs {
@@ -35,9 +37,12 @@ impl QueryArgs {
35 37
             QueryArgs::KeyValue(k, v) => {
36 38
                 Ok(QueryKind::KeyValue(KeyValueQuery::new(k, v)))
37 39
             },
38
-            _ => {
39
-                //TODO implement
40
-                unimplemented!();
40
+            QueryArgs::Intersection(queries) => {
41
+                let mut subqueries = Vec::with_capacity(queries.len());
42
+                for q in queries {
43
+                    subqueries.push(q.compile()?);
44
+                }
45
+                Ok(QueryKind::Intersection(subqueries))
41 46
             },
42 47
         }
43 48
     }
@@ -189,27 +194,31 @@ impl Query for KeyValueQuery {
189 194
 pub fn find_query_matches<Q: Query>(
190 195
     block: &PrimitiveBlock,
191 196
     query: &Q,
192
-    matches: &mut Vec<LatLonDeg>,
193
-    way_node_ids: &mut Vec<i64>,
197
+    matches: &mut HashSet<MatchItem>,
198
+    way_node_ids: &mut HashSet<i64>,
194 199
 ) {
195 200
     if let Some(block_index) = query.create_block_index(block) {
196 201
         for node in block.groups().flat_map(|g| g.nodes()) {
197 202
             if query.node_matches(&block_index, &node) {
198
-                let pos = LatLonDeg::new(node.lat(), node.lon());
199
-                matches.push(pos);
203
+                matches.insert(MatchItem::Node{
204
+                    id: node.id(),
205
+                    pos: LatLonDeg::new(node.lat(), node.lon()),
206
+                });
200 207
             }
201 208
         }
202 209
 
203 210
         for node in block.groups().flat_map(|g| g.dense_nodes()) {
204 211
             if query.dense_node_matches(&block_index, &node) {
205
-                let pos = LatLonDeg::new(node.lat(), node.lon());
206
-                matches.push(pos);
212
+                matches.insert(MatchItem::Node{
213
+                    id: node.id,
214
+                    pos: LatLonDeg::new(node.lat(), node.lon()),
215
+                });
207 216
             }
208 217
         }
209 218
 
210 219
         for way in block.groups().flat_map(|g| g.ways()) {
211 220
             if query.way_matches(&block_index, &way) {
212
-                way_node_ids.push(way.refs_slice()[0]);
221
+                way_node_ids.insert(way.refs_slice()[0]);
213 222
             }
214 223
         }
215 224
     }

+ 85
- 29
src/search.rs Ver arquivo

@@ -3,6 +3,7 @@ use osmpbf::{Blob, BlobDecode, BlobReader, PrimitiveBlock};
3 3
 use query::{find_query_matches, QueryArgs, QueryKind};
4 4
 use scoped_threadpool::Pool;
5 5
 use std::collections::hash_set::HashSet;
6
+use std::hash::{Hash, Hasher};
6 7
 use std::path::{Path, PathBuf};
7 8
 use std::sync::mpsc::sync_channel;
8 9
 use std::thread;
@@ -29,6 +30,40 @@ enum WorkerMessage {
29 30
     DoBlob(Box<Blob>),
30 31
 }
31 32
 
33
+#[derive(Clone, Copy, Debug)]
34
+pub enum MatchItem {
35
+    Node{id: i64, pos: LatLonDeg},
36
+    Way{id: i64, pos: LatLonDeg},
37
+}
38
+
39
+impl Hash for MatchItem {
40
+    fn hash<H: Hasher>(&self, state: &mut H) {
41
+        match *self {
42
+            MatchItem::Node{id, pos: _} => {
43
+                1u64.hash(state);
44
+                id.hash(state);
45
+            },
46
+            MatchItem::Way{id, pos: _} => {
47
+                2u64.hash(state);
48
+                id.hash(state);
49
+            },
50
+        }
51
+    }
52
+}
53
+
54
+impl PartialEq for MatchItem {
55
+    fn eq(&self, other: &MatchItem) -> bool {
56
+        match (*self, *other) {
57
+            (MatchItem::Node{id: a, ..}, MatchItem::Node{id: b, ..}) => a == b,
58
+            (MatchItem::Way{id: a, ..}, MatchItem::Way{id: b, ..}) => a == b,
59
+            _ => false,
60
+        }
61
+    }
62
+}
63
+
64
+impl Eq for MatchItem {
65
+}
66
+
32 67
 pub fn par_search<P, F, G>(
33 68
     pbf_path: P,
34 69
     query_args: QueryArgs,
@@ -36,7 +71,7 @@ pub fn par_search<P, F, G>(
36 71
     finished_func: G,
37 72
 ) -> Result<thread::JoinHandle<()>, String>
38 73
 where P: AsRef<Path>,
39
-      F: Fn(Vec<LatLonDeg>) -> ControlFlow + Send + 'static,
74
+      F: Fn(HashSet<MatchItem>) -> ControlFlow + Send + 'static,
40 75
       G: Fn(Result<(), String>) + Send + 'static,
41 76
 {
42 77
     let pbf_path = PathBuf::from(pbf_path.as_ref());
@@ -48,45 +83,62 @@ where P: AsRef<Path>,
48 83
     Ok(handle)
49 84
 }
50 85
 
86
+fn first_query_pass(block: &PrimitiveBlock, query: &QueryKind)
87
+    -> (HashSet<MatchItem>, HashSet<i64>)
88
+{
89
+    let mut matches = HashSet::new();
90
+    let mut way_node_ids = HashSet::new();
91
+
92
+    match query {
93
+        &QueryKind::ValuePattern(ref query) => {
94
+            find_query_matches(block, query, &mut matches, &mut way_node_ids);
95
+        },
96
+        &QueryKind::KeyValue(ref query) => {
97
+            find_query_matches(block, query, &mut matches, &mut way_node_ids);
98
+        },
99
+        &QueryKind::Intersection(ref queries) => {
100
+            let mut q_iter = queries.iter();
101
+
102
+            let (mut sub_matches, mut sub_way_node_ids) = q_iter.next()
103
+                .map_or_else(
104
+                    || (HashSet::new(), HashSet::new()),
105
+                    |q| first_query_pass(block, q),
106
+                );
107
+
108
+            for q in q_iter {
109
+                let (m, w) = first_query_pass(block, q);
110
+                sub_matches = sub_matches.intersection(&m).cloned().collect();
111
+                sub_way_node_ids = sub_way_node_ids.intersection(&w).cloned().collect();
112
+            }
113
+            matches.extend(sub_matches);
114
+            way_node_ids.extend(sub_way_node_ids);
115
+        },
116
+    }
117
+
118
+    (matches, way_node_ids)
119
+}
120
+
51 121
 pub fn par_search_blocking<P, F>(
52 122
     pbf_path: P,
53 123
     query_args: QueryArgs,
54 124
     found_func: F,
55 125
 ) -> Result<(), String>
56 126
 where P: AsRef<Path>,
57
-      F: Fn(Vec<LatLonDeg>) -> ControlFlow + Send + 'static,
127
+      F: Fn(HashSet<MatchItem>) -> ControlFlow + Send + 'static,
58 128
 {
59 129
     let query = query_args.compile()?;
60 130
     let query = &query;
61 131
 
62
-    let first_pass = move |block: &PrimitiveBlock, _: &()| {
63
-        let mut matches = vec![];
64
-        let mut way_node_ids = vec![];
65
-
66
-        match query {
67
-            &QueryKind::ValuePattern(ref query) => {
68
-                find_query_matches(block, query, &mut matches, &mut way_node_ids);
69
-            },
70
-            &QueryKind::KeyValue(ref query) => {
71
-                find_query_matches(block, query, &mut matches, &mut way_node_ids);
72
-            },
73
-            _ => {
74
-                //TODO implement
75
-                unimplemented!();
76
-            },
77
-        }
78
-
79
-        (matches, way_node_ids)
80
-    };
81
-
82 132
     let mut way_node_ids: HashSet<i64> = HashSet::new();
83 133
 
84 134
     par_iter_blobs(
85 135
         &pbf_path,
86 136
         || {},
87
-        first_pass,
137
+        move |block: &PrimitiveBlock, _: &()| {
138
+            first_query_pass(block, query)
139
+        },
88 140
         |(matches, node_ids)| {
89
-            way_node_ids.extend(&node_ids);
141
+            way_node_ids.extend(node_ids);
90 142
             found_func(matches)
91 143
         },
92 144
     )?;
@@ -94,19 +146,23 @@ where P: AsRef<Path>,
94 146
     let way_node_ids = &way_node_ids;
95 147
 
96 148
     let second_pass = move |block: &PrimitiveBlock, _: &()| {
97
-        let mut matches = vec![];
149
+        let mut matches = HashSet::new();
98 150
 
99 151
         for node in block.groups().flat_map(|g| g.nodes()) {
100 152
             if way_node_ids.contains(&node.id()) {
101
-                let pos = LatLonDeg::new(node.lat(), node.lon());
102
-                matches.push(pos);
153
+                matches.insert(MatchItem::Way{
154
+                    id: node.id(),
155
+                    pos: LatLonDeg::new(node.lat(), node.lon()),
156
+                });
103 157
             }
104 158
         }
105 159
 
106 160
         for node in block.groups().flat_map(|g| g.dense_nodes()) {
107 161
             if way_node_ids.contains(&node.id) {
108
-                let pos = LatLonDeg::new(node.lat(), node.lon());
109
-                matches.push(pos);
162
+                matches.insert(MatchItem::Way{
163
+                    id: node.id,
164
+                    pos: LatLonDeg::new(node.lat(), node.lon()),
165
+                });
110 166
             }
111 167
         }
112 168