Ver código fonte

Add key-value queries

* Add --keyval command line option to search for elements with a
  specific tag
* Add query module and Query trait to abstract over different types of
  queries
Johannes Hofmann 7 anos atrás
pai
commit
f974ea1701
5 arquivos alterados com 254 adições e 38 exclusões
  1. 15
    0
      src/args.rs
  2. 29
    0
      src/config.rs
  3. 4
    2
      src/main.rs
  4. 189
    0
      src/query.rs
  5. 17
    36
      src/search.rs

+ 15
- 0
src/args.rs Ver arquivo

@@ -34,6 +34,21 @@ pub fn parse<'a>() -> clap::ArgMatches<'a> {
34 34
             .value_name("PATTERN")
35 35
             .help("Search for places which match the given pattern")
36 36
             .takes_value(true))
37
+        .arg(Arg::with_name("keyval")
38
+            .short("k")
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
+            })
50
+            .help("Search for places that are tagged with the given key and value")
51
+            .takes_value(true))
37 52
         .arg(Arg::with_name("fps")
38 53
             .long("fps")
39 54
             .value_name("FPS")

+ 29
- 0
src/config.rs Ver arquivo

@@ -1,5 +1,6 @@
1 1
 use clap;
2 2
 use directories::ProjectDirs;
3
+use query::QueryArgs;
3 4
 use session::Session;
4 5
 use std::fmt::Debug;
5 6
 use std::fs::File;
@@ -28,6 +29,7 @@ pub struct Config {
28 29
     sources: Vec<(String, TileSource)>,
29 30
     pbf_path: Option<PathBuf>,
30 31
     search_pattern: Option<String>,
32
+    keyval: Option<(String, String)>,
31 33
     fps: f64,
32 34
     use_network: bool,
33 35
     async: bool,
@@ -67,6 +69,16 @@ impl Config {
67 69
     fn merge_arg_matches<'a>(&mut self, matches: &clap::ArgMatches<'a>) {
68 70
         self.search_pattern = matches.value_of("search").map(|s| s.to_string());
69 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
+        });
81
+
70 82
         if let Some(Ok(fps)) = matches.value_of("fps").map(|s| s.parse()) {
71 83
             self.fps = fps;
72 84
         }
@@ -229,6 +241,7 @@ impl Config {
229 241
                         sources: vec![],
230 242
                         pbf_path,
231 243
                         search_pattern: None,
244
+                        keyval: None,
232 245
                         fps,
233 246
                         use_network,
234 247
                         async,
@@ -390,6 +403,22 @@ impl Config {
390 403
         self.search_pattern.as_ref().map(|s| s.as_str())
391 404
     }
392 405
 
406
+    pub fn keyval(&self) -> Option<(&str, &str)> {
407
+        self.keyval.as_ref().map(|kv| (kv.0.as_str(), kv.1.as_str()))
408
+    }
409
+
410
+    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!();
417
+            },
418
+            (&None, &None) => None,
419
+        }
420
+    }
421
+
393 422
     pub fn fps(&self) -> f64 {
394 423
         self.fps
395 424
     }

+ 4
- 2
src/main.rs Ver arquivo

@@ -36,6 +36,7 @@ pub mod ortho_tile_layer;
36 36
 pub mod orthografic_view;
37 37
 pub mod program;
38 38
 pub mod projection;
39
+pub mod query;
39 40
 pub mod search;
40 41
 pub mod session;
41 42
 pub mod texture;
@@ -314,12 +315,13 @@ fn run() -> Result<(), Box<Error>> {
314 315
     }
315 316
 
316 317
     let (marker_tx, marker_rx) = mpsc::channel();
317
-    if let (Some(path), Some(pattern)) = (config.pbf_path(), config.search_pattern()) {
318
+
319
+    if let (Some(path), Some(query_args)) = (config.pbf_path(), config.query_args()) {
318 320
         let proxy = events_loop.create_proxy();
319 321
 
320 322
         search::par_search(
321 323
             path,
322
-            pattern,
324
+            query_args,
323 325
             move |coords| {
324 326
                 if coords.is_empty() {
325 327
                     search::ControlFlow::Continue

+ 189
- 0
src/query.rs Ver arquivo

@@ -0,0 +1,189 @@
1
+use coord::LatLonDeg;
2
+use osmpbf::{DenseNode, Node, PrimitiveBlock, Relation, Way};
3
+use regex::Regex;
4
+
5
+
6
+pub trait Query {
7
+    type BI;
8
+    fn create_block_index(&self, &PrimitiveBlock) -> Self::BI;
9
+    fn node_matches(&self, &Self::BI, node: &Node) -> bool;
10
+    fn dense_node_matches(&self, &Self::BI, dnode: &DenseNode) -> bool;
11
+    fn way_matches(&self, &Self::BI, way: &Way) -> bool;
12
+    fn relation_matches(&self, &Self::BI, relation: &Relation) -> bool;
13
+}
14
+
15
+#[derive(Debug, Eq, PartialEq)]
16
+pub enum QueryArgs {
17
+    ValuePattern(String),
18
+    KeyValue(String, String),
19
+    Intersection(Box<(QueryArgs, QueryArgs)>),
20
+}
21
+
22
+#[derive(Debug)]
23
+pub enum QueryKind {
24
+    ValuePattern(ValuePatternQuery),
25
+    KeyValue(KeyValueQuery),
26
+    Intersection(Box<(QueryArgs, QueryArgs)>),
27
+}
28
+
29
+impl QueryArgs {
30
+    pub fn compile(self) -> Result<QueryKind, String> {
31
+        match self {
32
+            QueryArgs::ValuePattern(pattern) => {
33
+                Ok(QueryKind::ValuePattern(ValuePatternQuery::new(&pattern)?))
34
+            },
35
+            QueryArgs::KeyValue(k, v) => {
36
+                Ok(QueryKind::KeyValue(KeyValueQuery::new(k, v)))
37
+            },
38
+            _ => {
39
+                //TODO implement
40
+                unimplemented!();
41
+            },
42
+        }
43
+    }
44
+}
45
+
46
+#[derive(Debug)]
47
+pub struct ValuePatternQuery {
48
+    re: Regex,
49
+}
50
+
51
+impl ValuePatternQuery {
52
+    pub fn new(pattern: &str) -> Result<Self, String> {
53
+        let re = Regex::new(&pattern)
54
+            .map_err(|e| format!("{}", e))?;
55
+        Ok(ValuePatternQuery { re })
56
+    }
57
+}
58
+
59
+impl Query for ValuePatternQuery {
60
+    type BI = ();
61
+
62
+    fn create_block_index(&self, _block: &PrimitiveBlock) -> () {
63
+        ()
64
+    }
65
+
66
+    fn node_matches(&self, _: &(), node: &Node) -> bool {
67
+        for (_key, val) in node.tags() {
68
+            if self.re.is_match(val) {
69
+                return true;
70
+            }
71
+        }
72
+        return false;
73
+    }
74
+
75
+    fn dense_node_matches(&self, _: &(), dnode: &DenseNode) -> bool {
76
+        for (_key, val) in dnode.tags() {
77
+            if self.re.is_match(val) {
78
+                return true;
79
+            }
80
+        }
81
+        return false;
82
+    }
83
+
84
+    fn way_matches(&self, _: &(), way: &Way) -> bool {
85
+        for (_key, val) in way.tags() {
86
+            if self.re.is_match(val) {
87
+                return true;
88
+            }
89
+        }
90
+        return false;
91
+    }
92
+
93
+    fn relation_matches(&self, _: &(), relation: &Relation) -> bool {
94
+        for (_key, val) in relation.tags() {
95
+            if self.re.is_match(val) {
96
+                return true;
97
+            }
98
+        }
99
+        return false;
100
+    }
101
+}
102
+
103
+#[derive(Debug)]
104
+pub struct KeyValueQuery {
105
+    key: String,
106
+    value: String,
107
+}
108
+
109
+impl KeyValueQuery {
110
+    pub fn new<S: Into<String>>(key: S, value: S) -> Self {
111
+        KeyValueQuery {
112
+            key: key.into(),
113
+            value: value.into(),
114
+        }
115
+    }
116
+}
117
+
118
+impl Query for KeyValueQuery {
119
+    type BI = ();
120
+
121
+    fn create_block_index(&self, _block: &PrimitiveBlock) -> () {
122
+        ()
123
+    }
124
+
125
+    fn node_matches(&self, _: &(), node: &Node) -> bool {
126
+        for (key, val) in node.tags() {
127
+            if key == self.key && val == self.value {
128
+                return true;
129
+            }
130
+        }
131
+        return false;
132
+    }
133
+
134
+    fn dense_node_matches(&self, _: &(), dnode: &DenseNode) -> bool {
135
+        for (key, val) in dnode.tags() {
136
+            if key == self.key && val == self.value {
137
+                return true;
138
+            }
139
+        }
140
+        return false;
141
+    }
142
+
143
+    fn way_matches(&self, _: &(), way: &Way) -> bool {
144
+        for (key, val) in way.tags() {
145
+            if key == self.key && val == self.value {
146
+                return true;
147
+            }
148
+        }
149
+        return false;
150
+    }
151
+
152
+    fn relation_matches(&self, _: &(), relation: &Relation) -> bool {
153
+        for (key, val) in relation.tags() {
154
+            if key == self.key && val == self.value {
155
+                return true;
156
+            }
157
+        }
158
+        return false;
159
+    }
160
+}
161
+
162
+pub fn find_query_matches<Q: Query>(
163
+    block: &PrimitiveBlock,
164
+    query: &Q,
165
+    matches: &mut Vec<LatLonDeg>,
166
+    way_node_ids: &mut Vec<i64>,
167
+) {
168
+    let block_index = query.create_block_index(block);
169
+
170
+    for node in block.groups().flat_map(|g| g.nodes()) {
171
+        if query.node_matches(&block_index, &node) {
172
+            let pos = LatLonDeg::new(node.lat(), node.lon());
173
+            matches.push(pos);
174
+        }
175
+    }
176
+
177
+    for node in block.groups().flat_map(|g| g.dense_nodes()) {
178
+        if query.dense_node_matches(&block_index, &node) {
179
+            let pos = LatLonDeg::new(node.lat(), node.lon());
180
+            matches.push(pos);
181
+        }
182
+    }
183
+
184
+    for way in block.groups().flat_map(|g| g.ways()) {
185
+        if query.way_matches(&block_index, &way) {
186
+            way_node_ids.push(way.refs_slice()[0]);
187
+        }
188
+    }
189
+}

+ 17
- 36
src/search.rs Ver arquivo

@@ -1,6 +1,6 @@
1 1
 use coord::LatLonDeg;
2 2
 use osmpbf::{Blob, BlobDecode, BlobReader, PrimitiveBlock};
3
-use regex::Regex;
3
+use query::{find_query_matches, QueryArgs, QueryKind};
4 4
 use scoped_threadpool::Pool;
5 5
 use std::collections::hash_set::HashSet;
6 6
 use std::path::{Path, PathBuf};
@@ -31,7 +31,7 @@ enum WorkerMessage {
31 31
 
32 32
 pub fn par_search<P, F, G>(
33 33
     pbf_path: P,
34
-    search_pattern: &str,
34
+    query_args: QueryArgs,
35 35
     found_func: F,
36 36
     finished_func: G,
37 37
 ) -> Result<thread::JoinHandle<()>, String>
@@ -40,9 +40,8 @@ where P: AsRef<Path>,
40 40
       G: Fn(Result<(), String>) + Send + 'static,
41 41
 {
42 42
     let pbf_path = PathBuf::from(pbf_path.as_ref());
43
-    let search_pattern = search_pattern.to_string();
44 43
     let handle = thread::spawn(move|| {
45
-        let res = par_search_blocking(pbf_path, &search_pattern, found_func);
44
+        let res = par_search_blocking(pbf_path, query_args, found_func);
46 45
         finished_func(res);
47 46
     });
48 47
 
@@ -51,48 +50,30 @@ where P: AsRef<Path>,
51 50
 
52 51
 pub fn par_search_blocking<P, F>(
53 52
     pbf_path: P,
54
-    search_pattern: &str,
53
+    query_args: QueryArgs,
55 54
     found_func: F,
56 55
 ) -> Result<(), String>
57 56
 where P: AsRef<Path>,
58 57
       F: Fn(Vec<LatLonDeg>) -> ControlFlow + Send + 'static,
59 58
 {
60
-    let re = Regex::new(search_pattern)
61
-        .map_err(|e| format!("{}", e))?;
62
-    let re = &re;
59
+    let query = query_args.compile()?;
60
+    let query = &query;
63 61
 
64 62
     let first_pass = move |block: &PrimitiveBlock, _: &()| {
65 63
         let mut matches = vec![];
66 64
         let mut way_node_ids = vec![];
67 65
 
68
-        for node in block.groups().flat_map(|g| g.nodes()) {
69
-            for (_key, val) in node.tags() {
70
-                if re.is_match(val) {
71
-                    let pos = LatLonDeg::new(node.lat(), node.lon());
72
-                    matches.push(pos);
73
-                    break;
74
-                }
75
-            }
76
-        }
77
-
78
-        for node in block.groups().flat_map(|g| g.dense_nodes()) {
79
-            for (_key, val) in node.tags() {
80
-                if re.is_match(val) {
81
-                    let pos = LatLonDeg::new(node.lat(), node.lon());
82
-                    matches.push(pos);
83
-                    break;
84
-                }
85
-            }
86
-        }
87
-
88
-        for way in block.groups().flat_map(|g| g.ways()) {
89
-            for (_key, val) in way.tags() {
90
-                if re.is_match(val) && !way.refs_slice().is_empty() {
91
-                    //TODO take middle node, not first one
92
-                    way_node_ids.push(way.refs_slice()[0]);
93
-                    break;
94
-                }
95
-            }
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
+            },
96 77
         }
97 78
 
98 79
         (matches, way_node_ids)