gistools/readers/wkt/
mod.rs1use crate::parsers::{FeatureReader, clean_string};
2use alloc::{string::String, vec, vec::Vec};
3use s2json::{
4 BBox3D, MValue, Properties, VectorFeature, VectorGeometry, VectorLineString,
5 VectorMultiLineString, VectorMultiPolygon, VectorPoint,
6};
7
8#[derive(Debug, Clone, PartialEq)]
10pub enum WKTAValue {
11 Point(VectorPoint),
13 Array(Vec<WKTAValue>),
15}
16impl WKTAValue {
17 pub fn get_point(&mut self) -> Option<&mut VectorPoint> {
19 match self {
20 WKTAValue::Point(point) => Some(point),
21 WKTAValue::Array(arr) => arr.first_mut().and_then(|v| v.get_point()),
22 }
23 }
24 pub fn get_linestring(&mut self) -> Option<VectorLineString> {
26 match self {
27 WKTAValue::Point(point) => Some(vec![point.clone()]),
28 WKTAValue::Array(arr) => {
29 arr.iter_mut().map(|v| v.get_point().map(core::mem::take)).collect()
30 }
31 }
32 }
33 pub fn get_multilinestring(&mut self) -> Option<VectorMultiLineString> {
35 match self {
36 WKTAValue::Point(point) => Some(vec![vec![point.clone()]]),
37 WKTAValue::Array(arr) => arr.iter_mut().map(|v| v.get_linestring()).collect(),
38 }
39 }
40}
41
42pub type WKTArray = Vec<WKTAValue>;
44
45#[derive(Debug, Clone)]
73pub struct WKTGeometryReader {
74 pub features: Vec<VectorFeature>,
76}
77impl WKTGeometryReader {
78 pub fn new(data: String) -> Self {
80 let mut features = vec![];
81 let wkt_strings = split_wkt_geometry(data);
82 for wkt_string in wkt_strings {
83 let geometry = parse_wkt_geometry(wkt_string);
84 if let Some(geometry) = geometry {
85 features.push(VectorFeature { geometry, ..Default::default() });
86 }
87 }
88 WKTGeometryReader { features }
89 }
90
91 pub fn len(&self) -> usize {
93 self.features.len()
94 }
95
96 pub fn is_empty(&self) -> bool {
98 self.features.is_empty()
99 }
100}
101#[derive(Debug)]
103pub struct WKTIterator<'a> {
104 reader: &'a WKTGeometryReader,
105 index: usize,
106 len: usize,
107}
108impl Iterator for WKTIterator<'_> {
109 type Item = VectorFeature;
110
111 fn next(&mut self) -> Option<Self::Item> {
112 if self.index >= self.len {
113 return None;
114 }
115 self.index += 1;
116 self.reader.features.get(self.index - 1).cloned()
117 }
118}
119impl FeatureReader<(), Properties, MValue> for WKTGeometryReader {
121 type FeatureIterator<'a> = WKTIterator<'a>;
122
123 fn iter(&self) -> Self::FeatureIterator<'_> {
124 WKTIterator { reader: self, index: 0, len: self.features.len() }
125 }
126
127 fn par_iter(&self, pool_size: usize, thread_id: usize) -> Self::FeatureIterator<'_> {
128 let start = self.len() * thread_id / pool_size;
129 let end = self.len() * (thread_id + 1) / pool_size;
130 WKTIterator { reader: self, index: start, len: end }
131 }
132}
133
134pub fn parse_wkt_geometry(wkt_str: String) -> Option<VectorGeometry> {
162 if wkt_str.starts_with("POINT") {
163 parse_wkt_point(wkt_str)
164 } else if wkt_str.starts_with("MULTIPOINT") {
165 parse_wkt_line(wkt_str, LineParseType::MultiPoint)
166 } else if wkt_str.starts_with("LINESTRING") {
167 parse_wkt_line(wkt_str, LineParseType::LineString)
168 } else if wkt_str.starts_with("MULTILINESTRING") {
169 parse_wkt_multi_line(wkt_str, MultiLineParseType::MultiLineString)
170 } else if wkt_str.starts_with("POLYGON") {
171 parse_wkt_multi_line(wkt_str, MultiLineParseType::Polygon)
172 } else if wkt_str.starts_with("MULTIPOLYGON") {
173 parse_wkt_multi_polygon(wkt_str)
174 } else {
175 None
176 }
177}
178
179pub fn split_wkt_geometry(mut input: String) -> Vec<String> {
190 let mut words: Vec<&str> = input.split_whitespace().collect();
192 let mut i = 0;
193 while i < words.len() {
194 if words[i].contains("EMPTY") && i > 0 {
195 words.drain(i - 1..=i);
196 i = i.saturating_sub(1);
197 } else {
198 i += 1;
199 }
200 }
201 input = words.join(" ");
202
203 let mut geometries = Vec::new();
204 let mut start = 0;
205 let mut found = false;
206 let mut depth = 0;
207 let input_chars: Vec<char> = input.chars().collect();
208
209 for i in 0..input_chars.len() {
210 match input_chars[i] {
211 '(' => {
212 depth += 1;
213 found = true;
214 }
215 ')' => {
216 depth -= 1;
217 if found && depth == 0 {
218 let end = i + 1;
219 let segment: String =
220 input_chars[start..end].iter().collect::<String>().trim().into();
221 geometries.push(segment);
222 start = end;
223 found = false;
224 }
225 }
226 _ => {}
227 }
228 }
229
230 let mut i = 0;
231 while i < geometries.len() {
232 if geometries[i].starts_with("GEOMETRYCOLLECTION") {
233 let g = geometries.remove(i);
234 let inner = g[g.find('(').unwrap() + 1..g.len() - 1].into();
235 let nested = split_wkt_geometry(inner);
236 geometries.splice(i..i, nested);
237 } else {
238 if geometries[i].starts_with(',') {
239 geometries[i] = geometries[i].trim_start_matches(',').trim().into();
240 }
241 i += 1;
242 }
243 }
244
245 geometries.into_iter().filter(|g| !g.is_empty()).collect()
246}
247
248fn parse_wkt_point(wkt_str: String) -> Option<VectorGeometry> {
256 if let Some(WKTAValue::Point(point)) = parse_wkt_array(wkt_str).get_mut(0) {
257 let bbox = BBox3D::from_point(point);
258 Some(VectorGeometry::new_point(core::mem::take(point), Some(bbox)))
259 } else {
260 None
261 }
262}
263
264enum LineParseType {
265 MultiPoint,
266 LineString,
267}
268
269fn parse_wkt_line(wkt_str: String, r#type: LineParseType) -> Option<VectorGeometry> {
279 let mut line = parse_wkt_array(wkt_str);
280 let points: VectorLineString =
281 line.iter_mut().map(|e| e.get_point().map(core::mem::take).unwrap_or_default()).collect();
282 let bbox = BBox3D::from_linestring(&points);
283 match r#type {
284 LineParseType::MultiPoint => Some(VectorGeometry::new_multipoint(points, Some(bbox))),
285 LineParseType::LineString => Some(VectorGeometry::new_linestring(points, Some(bbox))),
286 }
287}
288
289enum MultiLineParseType {
290 MultiLineString,
291 Polygon,
292}
293
294fn parse_wkt_multi_line(wkt_str: String, r#type: MultiLineParseType) -> Option<VectorGeometry> {
304 let mut multiline = parse_wkt_array(wkt_str);
305 let lines: VectorMultiLineString =
306 multiline.iter_mut().map(|e| e.get_linestring().unwrap_or_default()).collect();
307 let bbox = BBox3D::from_multi_linestring(&lines);
308 match r#type {
309 MultiLineParseType::MultiLineString => {
310 Some(VectorGeometry::new_multilinestring(lines, Some(bbox)))
311 }
312 MultiLineParseType::Polygon => Some(VectorGeometry::new_polygon(lines, Some(bbox))),
313 }
314}
315
316fn parse_wkt_multi_polygon(wkt_str: String) -> Option<VectorGeometry> {
324 let mut multipolygon = parse_wkt_array(wkt_str);
325 let polygons: VectorMultiPolygon =
326 multipolygon.iter_mut().map(|e| e.get_multilinestring().unwrap_or_default()).collect();
327 let bbox = BBox3D::from_multi_polygon(&polygons);
328 Some(VectorGeometry::new_multipolygon(polygons, Some(bbox)))
329}
330
331pub fn parse_wkt_array(wkt_str: String) -> WKTArray {
339 let mut res = Vec::new();
340 let _ = _parse_wkt_array(wkt_str, &mut res);
341 if let Some(WKTAValue::Array(inner)) = res.first() { inner.clone() } else { res }
342}
343
344fn _parse_wkt_array(mut wkt_str: String, res: &mut WKTArray) -> String {
354 while !wkt_str.is_empty() {
355 let comma_index = wkt_str.find(',').unwrap_or(usize::MAX);
356 let start_bracket_index = wkt_str.find('(').unwrap_or(usize::MAX);
357 let end_bracket_index = wkt_str.find(')').unwrap_or(usize::MAX);
358
359 if comma_index < start_bracket_index.min(end_bracket_index) {
360 let key = &wkt_str[..comma_index].trim();
361 if !key.is_empty() {
362 res.push(WKTAValue::Point(build_point(key)));
363 }
364 wkt_str = wkt_str[comma_index + 1..].into();
365 } else if start_bracket_index < end_bracket_index {
366 let mut inner = Vec::new();
367 let inner_str = &wkt_str[start_bracket_index + 1..];
368 wkt_str = _parse_wkt_array(inner_str.into(), &mut inner);
369 res.push(WKTAValue::Array(inner));
370 } else {
371 if end_bracket_index > 0 {
372 let key = &wkt_str[..end_bracket_index].trim();
373 if !key.is_empty() {
374 res.push(WKTAValue::Point(build_point(key)));
375 }
376 wkt_str = wkt_str[end_bracket_index + 1..].into();
377 } else {
378 wkt_str = wkt_str[1..].into();
379 }
380 return wkt_str;
381 }
382 }
383 wkt_str
384}
385
386fn build_point(input: &str) -> VectorPoint {
394 let binding = clean_string(input);
395 let parts: Vec<&str> = binding.split_whitespace().collect();
396
397 let x = parts.first().and_then(|v| v.parse::<f64>().ok()).unwrap_or(0.0);
398 let y = parts.get(1).and_then(|v| v.parse::<f64>().ok()).unwrap_or(0.0);
399 let z = parts.get(2).and_then(|v| v.parse::<f64>().ok());
400
401 VectorPoint::new(x, y, z, None)
402}