Skip to content

Commit 4f2ddea

Browse files
committed
Add bbox option to filter converted gml spatially
1 parent c9d7f2d commit 4f2ddea

File tree

2 files changed

+65
-9
lines changed

2 files changed

+65
-9
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ publish = false
1010
quick-xml = "0.22"
1111
yaml-rust = "0.4"
1212
regex = "1"
13+
lazy_static = "1"

src/main.rs

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use quick_xml::events::Event;
99
use yaml_rust::YamlLoader;
1010
use yaml_rust::yaml::Yaml;
1111
use regex::Regex;
12+
use lazy_static::lazy_static;
1213

1314
struct Table<'a> {
1415
path: String,
@@ -56,7 +57,8 @@ struct Column<'a> {
5657
find: Option<&'a str>,
5758
replace: Option<&'a str>,
5859
consol: Option<&'a str>,
59-
subtable: Option<Table<'a>>
60+
subtable: Option<Table<'a>>,
61+
bbox: Option<BBox>
6062
}
6163
impl std::fmt::Debug for Column<'_> {
6264
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -80,7 +82,24 @@ impl Geometry {
8082
}
8183
}
8284

83-
fn gml_to_ewkb(cell: &RefCell<String>, coll: &[Geometry]) {
85+
struct BBox {
86+
minx: f64,
87+
miny: f64,
88+
maxx: f64,
89+
maxy: f64
90+
}
91+
impl BBox {
92+
fn from(str: &str) -> Option<BBox> {
93+
lazy_static! {
94+
static ref RE: Regex = Regex::new(r"^([0-9.]+),([0-9.]+) ([0-9.]+),([0-9.]+)$").unwrap();
95+
}
96+
RE.captures(str).map(|caps|
97+
BBox { minx: caps[1].parse().unwrap(), miny: caps[2].parse().unwrap(), maxx: caps[3].parse().unwrap(), maxy: caps[4].parse().unwrap() }
98+
)
99+
}
100+
}
101+
102+
fn gml_to_ewkb(cell: &RefCell<String>, coll: &[Geometry], bbox: Option<&BBox>) -> bool {
84103
let mut ewkb: Vec<u8> = vec![];
85104

86105
if coll.len() > 1 {
@@ -100,11 +119,39 @@ fn gml_to_ewkb(cell: &RefCell<String>, coll: &[Geometry]) {
100119
};
101120
ewkb.extend_from_slice(&[1, geom.gtype, 0, 0, code]);
102121
ewkb.extend_from_slice(&geom.srid.to_le_bytes());
103-
if geom.gtype == 3 { ewkb.extend_from_slice(&(geom.rings.len() as u32).to_le_bytes()); } // Only polygons have multiple rings
104-
for ring in geom.rings.iter() {
105-
if geom.gtype != 1 { ewkb.extend_from_slice(&((ring.len() as u32)/geom.dims as u32).to_le_bytes()); } // Points don't have multiple vertices
106-
for pos in ring.iter() {
107-
ewkb.extend_from_slice(&pos.to_le_bytes());
122+
if geom.gtype == 3 { ewkb.extend_from_slice(&(geom.rings.len() as u32).to_le_bytes()); } // Only polygons can have multiple rings
123+
if let Some(bbox) = bbox {
124+
let mut overlap = false;
125+
let mut overlapx = false;
126+
for ring in geom.rings.iter() {
127+
if geom.gtype != 1 { ewkb.extend_from_slice(&((ring.len() as u32)/geom.dims as u32).to_le_bytes()); } // Points don't have multiple vertices
128+
for (i, pos) in ring.iter().enumerate() {
129+
if overlap == true { }
130+
else if geom.dims == 2 {
131+
if i%2 == 0 {
132+
overlapx = false;
133+
if *pos >= bbox.minx && *pos <= bbox.maxx { overlapx = true; }
134+
}
135+
else if overlapx && *pos < bbox.miny && *pos > bbox.maxy { overlap = true; }
136+
}
137+
else { // geom.dims == 3
138+
if i%3 == 0 {
139+
overlapx = false;
140+
if *pos >= bbox.minx && *pos <= bbox.maxx { overlapx = true; }
141+
}
142+
else if overlapx && i%3 == 1 && (*pos >= bbox.miny && *pos <= bbox.maxy) { overlap = true; }
143+
}
144+
ewkb.extend_from_slice(&pos.to_le_bytes());
145+
}
146+
}
147+
if overlap == false { return false; }
148+
}
149+
else {
150+
for ring in geom.rings.iter() {
151+
if geom.gtype != 1 { ewkb.extend_from_slice(&((ring.len() as u32)/geom.dims as u32).to_le_bytes()); } // Points don't have multiple vertices
152+
for pos in ring.iter() {
153+
ewkb.extend_from_slice(&pos.to_le_bytes());
154+
}
108155
}
109156
}
110157
}
@@ -113,6 +160,7 @@ fn gml_to_ewkb(cell: &RefCell<String>, coll: &[Geometry]) {
113160
for byte in ewkb.iter() {
114161
value.push_str(&format!("{:02X}", byte));
115162
}
163+
true
116164
}
117165

118166
fn add_table<'a>(rowpath: &str, outfile: Option<&str>, filemode: &str, skip: Option<&'a str>, colspec: &'a [Yaml]) -> Table<'a> {
@@ -137,6 +185,7 @@ fn add_table<'a>(rowpath: &str, outfile: Option<&str>, filemode: &str, skip: Opt
137185
let find = col["find"].as_str();
138186
let replace = col["repl"].as_str();
139187
let consol = col["cons"].as_str();
188+
let bbox = col["bbox"].as_str().and_then(|str| BBox::from(str));
140189

141190
if convert.is_some() && !vec!("xml-to-text", "gml-to-ewkb").contains(&convert.unwrap()) {
142191
panic!("Option 'convert' contains invalid value {}", convert.unwrap());
@@ -152,8 +201,11 @@ fn add_table<'a>(rowpath: &str, outfile: Option<&str>, filemode: &str, skip: Opt
152201
eprintln!("Notice: when using filtering (incl/excl) and consolidation on a single column, the filter is checked at each phase of consolidation separately");
153202
}
154203
}
204+
if bbox.is_some() && (convert.is_none() || convert.unwrap() != "gml-to-ewkb") {
205+
eprintln!("Warning: the bbox option has no function without conversion type 'gml-to-ekwb'");
206+
}
155207

156-
let column = Column { name: name.to_string(), path, value: RefCell::new(String::new()), attr, hide, include, exclude, convert, find, replace, consol, subtable };
208+
let column = Column { name: name.to_string(), path, value: RefCell::new(String::new()), attr, hide, include, exclude, convert, find, replace, consol, subtable, bbox };
157209
table.columns.push(column);
158210
}
159211
table
@@ -447,7 +499,10 @@ fn main() -> std::io::Result<()> {
447499
for i in 0..table.columns.len() {
448500
if path == table.columns[i].path {
449501
gmltoewkb = false;
450-
gml_to_ewkb(&table.columns[i].value, &gmlcoll);
502+
if !gml_to_ewkb(&table.columns[i].value, &gmlcoll, table.columns[i].bbox.as_ref()) {
503+
filtered = true;
504+
table.clear_columns();
505+
}
451506
gmlcoll.clear();
452507
break;
453508
}

0 commit comments

Comments
 (0)