1use core::ffi::CStr;
4
5use crate::error::{Error, Result};
6use crate::{Geom, IndexKind, Point, Rect};
7
8enum ParseFormat {
9 Wkt,
10 Wkb,
11 GeoJson,
12 Hex,
13 GeoBin,
14 Auto,
15}
16
17fn parse_error_from_c(ptr: *const libc::c_char, format: ParseFormat) -> Error {
18 let msg = if ptr.is_null() {
19 "Unknown error".to_owned()
20 } else {
21 let c_str = unsafe { CStr::from_ptr(ptr) };
22 c_str.to_string_lossy().into_owned()
23 };
24
25 if msg == "no memory" {
26 return Error::OutOfMemory;
27 }
28
29 match format {
30 ParseFormat::Wkt => Error::WktParse(msg),
31 ParseFormat::Wkb => Error::WkbParse(msg),
32 ParseFormat::GeoJson => Error::GeoJsonParse(msg),
33 ParseFormat::Hex => Error::HexParse(msg),
34 ParseFormat::GeoBin => Error::GeoBinParse(msg),
35 ParseFormat::Auto => Error::Parse(msg),
36 }
37}
38
39impl Geom {
40 pub fn from_geojson(geojson: &str) -> Result<Self> {
41 Self::from_geojson_ix(geojson, IndexKind::Default)
42 }
43
44 pub fn from_geojson_ix(geojson: &str, ix: IndexKind) -> Result<Self> {
45 let ptr = unsafe {
46 tg_geom_sys::tg_parse_geojsonn_ix(
47 geojson.as_ptr() as *const libc::c_char,
48 geojson.len(),
49 ix.into(),
50 )
51 };
52 Self::from_parsed_ptr(ptr, ParseFormat::GeoJson)
53 }
54
55 pub fn from_wkt(wkt: &str) -> Result<Self> {
56 Self::from_wkt_ix(wkt, IndexKind::Default)
57 }
58
59 pub fn from_wkt_ix(wkt: &str, ix: IndexKind) -> Result<Self> {
60 let ptr = unsafe {
61 tg_geom_sys::tg_parse_wktn_ix(wkt.as_ptr() as *const libc::c_char, wkt.len(), ix.into())
62 };
63 Self::from_parsed_ptr(ptr, ParseFormat::Wkt)
64 }
65
66 pub fn from_wkb(wkb: &[u8]) -> Result<Self> {
67 Self::from_wkb_ix(wkb, IndexKind::Default)
68 }
69
70 pub fn from_wkb_ix(wkb: &[u8], ix: IndexKind) -> Result<Self> {
71 let ptr = unsafe { tg_geom_sys::tg_parse_wkb_ix(wkb.as_ptr(), wkb.len(), ix.into()) };
72 Self::from_parsed_ptr(ptr, ParseFormat::Wkb)
73 }
74
75 pub fn from_hex(hex: &str) -> Result<Self> {
76 Self::from_hex_ix(hex, IndexKind::Default)
77 }
78
79 pub fn from_hex_ix(hex: &str, ix: IndexKind) -> Result<Self> {
80 let ptr = unsafe {
81 tg_geom_sys::tg_parse_hexn_ix(hex.as_ptr() as *const libc::c_char, hex.len(), ix.into())
82 };
83 Self::from_parsed_ptr(ptr, ParseFormat::Hex)
84 }
85
86 pub fn from_geobin(geobin: &[u8]) -> Result<Self> {
87 Self::from_geobin_ix(geobin, IndexKind::Default)
88 }
89
90 pub fn from_geobin_ix(geobin: &[u8], ix: IndexKind) -> Result<Self> {
91 let ptr =
92 unsafe { tg_geom_sys::tg_parse_geobin_ix(geobin.as_ptr(), geobin.len(), ix.into()) };
93 Self::from_parsed_ptr(ptr, ParseFormat::GeoBin)
94 }
95
96 pub fn parse(data: &[u8]) -> Result<Self> {
98 Self::parse_ix(data, IndexKind::Default)
99 }
100
101 pub fn parse_ix(data: &[u8], ix: IndexKind) -> Result<Self> {
102 let ptr = unsafe {
103 tg_geom_sys::tg_parse_ix(data.as_ptr() as *const libc::c_void, data.len(), ix.into())
104 };
105 Self::from_parsed_ptr(ptr, ParseFormat::Auto)
106 }
107
108 fn from_parsed_ptr(ptr: *mut tg_geom_sys::tg_geom, format: ParseFormat) -> Result<Self> {
109 if ptr.is_null() {
110 return Err(Error::OutOfMemory);
111 }
112
113 let err_ptr = unsafe { tg_geom_sys::tg_geom_error(ptr) };
114 if !err_ptr.is_null() {
115 let err = parse_error_from_c(err_ptr, format);
116 unsafe { tg_geom_sys::tg_geom_free(ptr) };
117 return Err(err);
118 }
119
120 unsafe { Self::from_raw(ptr) }.ok_or(Error::OutOfMemory)
121 }
122
123 pub fn to_geojson(&self) -> String {
124 let needed =
125 unsafe { tg_geom_sys::tg_geom_geojson(self.as_ptr(), core::ptr::null_mut(), 0) };
126 if needed == 0 {
127 return String::new();
128 }
129 let mut buf = vec![0u8; needed + 1];
130 let written = unsafe {
131 tg_geom_sys::tg_geom_geojson(
132 self.as_ptr(),
133 buf.as_mut_ptr() as *mut libc::c_char,
134 buf.len(),
135 )
136 };
137 buf.truncate(written);
138 String::from_utf8_lossy(&buf).into_owned()
139 }
140
141 pub fn to_wkt(&self) -> String {
142 let needed = unsafe { tg_geom_sys::tg_geom_wkt(self.as_ptr(), core::ptr::null_mut(), 0) };
143 if needed == 0 {
144 return String::new();
145 }
146 let mut buf = vec![0u8; needed + 1];
147 let written = unsafe {
148 tg_geom_sys::tg_geom_wkt(
149 self.as_ptr(),
150 buf.as_mut_ptr() as *mut libc::c_char,
151 buf.len(),
152 )
153 };
154 buf.truncate(written);
155 String::from_utf8_lossy(&buf).into_owned()
156 }
157
158 pub fn to_wkb(&self) -> Vec<u8> {
159 let len = unsafe { tg_geom_sys::tg_geom_wkb(self.as_ptr(), core::ptr::null_mut(), 0) };
160 let mut buf = vec![0u8; len];
161 unsafe {
162 tg_geom_sys::tg_geom_wkb(self.as_ptr(), buf.as_mut_ptr(), len);
163 }
164 buf
165 }
166
167 pub fn to_hex(&self) -> String {
168 let needed = unsafe { tg_geom_sys::tg_geom_hex(self.as_ptr(), core::ptr::null_mut(), 0) };
169 if needed == 0 {
170 return String::new();
171 }
172 let mut buf = vec![0u8; needed + 1];
173 let written = unsafe {
174 tg_geom_sys::tg_geom_hex(
175 self.as_ptr(),
176 buf.as_mut_ptr() as *mut libc::c_char,
177 buf.len(),
178 )
179 };
180 buf.truncate(written);
181 String::from_utf8_lossy(&buf).into_owned()
182 }
183
184 pub fn to_geobin(&self) -> Vec<u8> {
185 let len = unsafe { tg_geom_sys::tg_geom_geobin(self.as_ptr(), core::ptr::null_mut(), 0) };
186 let mut buf = vec![0u8; len];
187 unsafe {
188 tg_geom_sys::tg_geom_geobin(self.as_ptr(), buf.as_mut_ptr(), len);
189 }
190 buf
191 }
192}
193
194pub fn geobin_rect(geobin: &[u8]) -> Rect {
196 let r = unsafe { tg_geom_sys::tg_geobin_rect(geobin.as_ptr(), geobin.len()) };
197 r.into()
198}
199
200pub fn geobin_point(geobin: &[u8]) -> Point {
202 let p = unsafe { tg_geom_sys::tg_geobin_point(geobin.as_ptr(), geobin.len()) };
203 p.into()
204}
205
206pub fn geobin_fullrect(geobin: &[u8]) -> (i32, [f64; 4], [f64; 4]) {
208 let mut min = [0.0f64; 4];
209 let mut max = [0.0f64; 4];
210 let dims = unsafe {
211 tg_geom_sys::tg_geobin_fullrect(
212 geobin.as_ptr(),
213 geobin.len(),
214 min.as_mut_ptr(),
215 max.as_mut_ptr(),
216 )
217 };
218 (dims, min, max)
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224 use crate::GeomType;
225
226 fn p(x: f64, y: f64) -> Point {
227 Point::new(x, y)
228 }
229
230 fn r(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Rect {
231 Rect::from_coords(min_x, min_y, max_x, max_y)
232 }
233
234 #[test]
235 fn test_wkt_point() {
236 let geom = Geom::from_wkt("POINT(1 2)").unwrap();
237 assert_eq!(geom.geom_type(), GeomType::Point);
238 assert_eq!(geom.point(), p(1.0, 2.0));
239 }
240
241 #[test]
242 fn test_wkt_point_z() {
243 let geom = Geom::from_wkt("POINT Z(1 2 3)").unwrap();
244 assert_eq!(geom.geom_type(), GeomType::Point);
245 assert!(geom.has_z());
246 assert_eq!(geom.z(), 3.0);
247 }
248
249 #[test]
250 fn test_wkt_point_empty() {
251 let geom = Geom::from_wkt("POINT EMPTY").unwrap();
252 assert_eq!(geom.geom_type(), GeomType::Point);
253 assert!(geom.is_empty());
254 }
255
256 #[test]
257 fn test_wkt_linestring() {
258 let geom = Geom::from_wkt("LINESTRING(0 0, 1 1, 2 2)").unwrap();
259 assert_eq!(geom.geom_type(), GeomType::LineString);
260 assert!(!geom.is_empty());
261 }
262
263 #[test]
264 fn test_wkt_linestring_empty() {
265 let geom = Geom::from_wkt("LINESTRING EMPTY").unwrap();
266 assert_eq!(geom.geom_type(), GeomType::LineString);
267 assert!(geom.is_empty());
268 }
269
270 #[test]
271 fn test_wkt_polygon() {
272 let geom = Geom::from_wkt("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))").unwrap();
273 assert_eq!(geom.geom_type(), GeomType::Polygon);
274 assert!(!geom.is_empty());
275 }
276
277 #[test]
278 fn test_wkt_polygon_with_hole() {
279 let geom =
280 Geom::from_wkt("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 8 2, 8 8, 2 8, 2 2))")
281 .unwrap();
282 assert_eq!(geom.geom_type(), GeomType::Polygon);
283 let poly = geom.poly().unwrap();
284 assert_eq!(poly.num_holes(), 1);
285 }
286
287 #[test]
288 fn test_wkt_polygon_empty() {
289 let geom = Geom::from_wkt("POLYGON EMPTY").unwrap();
290 assert_eq!(geom.geom_type(), GeomType::Polygon);
291 assert!(geom.is_empty());
292 }
293
294 #[test]
295 fn test_wkt_multipoint() {
296 let geom = Geom::from_wkt("MULTIPOINT((1 2), (3 4))").unwrap();
297 assert_eq!(geom.geom_type(), GeomType::MultiPoint);
298 assert_eq!(geom.num_points(), 2);
299 }
300
301 #[test]
302 fn test_wkt_multilinestring() {
303 let geom = Geom::from_wkt("MULTILINESTRING((0 0, 1 1), (2 2, 3 3))").unwrap();
304 assert_eq!(geom.geom_type(), GeomType::MultiLineString);
305 assert_eq!(geom.num_lines(), 2);
306 }
307
308 #[test]
309 fn test_wkt_multipolygon() {
310 let geom = Geom::from_wkt(
311 "MULTIPOLYGON(((0 0, 1 0, 1 1, 0 1, 0 0)), ((2 2, 3 2, 3 3, 2 3, 2 2)))",
312 )
313 .unwrap();
314 assert_eq!(geom.geom_type(), GeomType::MultiPolygon);
315 assert_eq!(geom.num_polys(), 2);
316 }
317
318 #[test]
319 fn test_wkt_geometrycollection() {
320 let geom = Geom::from_wkt("GEOMETRYCOLLECTION(POINT(1 2), LINESTRING(0 0, 1 1))").unwrap();
321 assert_eq!(geom.geom_type(), GeomType::GeometryCollection);
322 assert_eq!(geom.num_geometries(), 2);
323 }
324
325 #[test]
326 fn test_wkt_parse_error() {
327 let result = Geom::from_wkt("INVALID WKT");
328 assert!(result.is_err());
329 match result {
330 Err(Error::WktParse(_)) => {}
331 _ => panic!("Expected WktParse error"),
332 }
333
334 assert!(Geom::from_wkt(" POINT ").is_err());
335 assert!(Geom::from_wkt("POINT").is_err());
336 assert!(Geom::from_wkt("POINT EMPTY").is_ok());
337 assert!(Geom::from_wkt("POINT ZM ()").is_err());
338 assert!(Geom::from_wkt("POINT Z ()").is_err());
339 assert!(Geom::from_wkt("POINT ()").is_err());
340 assert!(Geom::from_wkt("POINT ZM () asdf").is_err());
341 }
342
343 #[test]
344 fn test_wkb_roundtrip_point() {
345 let original = Geom::from_wkt("POINT(1 2)").unwrap();
346 let wkb = original.to_wkb();
347 let parsed = Geom::from_wkb(&wkb).unwrap();
348 assert_eq!(parsed.geom_type(), GeomType::Point);
349 assert_eq!(parsed.point(), p(1.0, 2.0));
350 }
351
352 #[test]
353 fn test_wkb_roundtrip_linestring() {
354 let original = Geom::from_wkt("LINESTRING(0 0, 1 1, 2 2)").unwrap();
355 let wkb = original.to_wkb();
356 let parsed = Geom::from_wkb(&wkb).unwrap();
357 assert_eq!(parsed.geom_type(), GeomType::LineString);
358 }
359
360 #[test]
361 fn test_wkb_roundtrip_polygon() {
362 let original = Geom::from_wkt("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))").unwrap();
363 let wkb = original.to_wkb();
364 let parsed = Geom::from_wkb(&wkb).unwrap();
365 assert_eq!(parsed.geom_type(), GeomType::Polygon);
366 }
367
368 #[test]
369 fn test_wkb_invalid() {
370 let result = Geom::from_wkb(&[0x00, 0x01, 0x02, 0x03]);
371 assert!(result.is_err());
372 }
373
374 #[test]
375 fn test_hex_roundtrip() {
376 let original = Geom::from_wkt("POINT(1 2)").unwrap();
377 let hex = original.to_hex();
378 let parsed = Geom::from_hex(&hex).unwrap();
379 assert_eq!(parsed.geom_type(), GeomType::Point);
380 assert_eq!(parsed.point(), p(1.0, 2.0));
381 }
382
383 #[test]
384 fn test_hex_format() {
385 let geom = Geom::from_wkt("POINT(1 2)").unwrap();
386 let hex = geom.to_hex();
387 assert!(hex.chars().all(|c| c.is_ascii_hexdigit()));
389 }
390
391 #[test]
392 fn test_geojson_point() {
393 let geojson = r#"{"type":"Point","coordinates":[1,2]}"#;
394 let geom = Geom::from_geojson(geojson).unwrap();
395 assert_eq!(geom.geom_type(), GeomType::Point);
396 assert_eq!(geom.point(), p(1.0, 2.0));
397 }
398
399 #[test]
400 fn test_geojson_linestring() {
401 let geojson = r#"{"type":"LineString","coordinates":[[0,0],[1,1],[2,2]]}"#;
402 let geom = Geom::from_geojson(geojson).unwrap();
403 assert_eq!(geom.geom_type(), GeomType::LineString);
404 }
405
406 #[test]
407 fn test_geojson_polygon() {
408 let geojson = r#"{"type":"Polygon","coordinates":[[[0,0],[10,0],[10,10],[0,10],[0,0]]]}"#;
409 let geom = Geom::from_geojson(geojson).unwrap();
410 assert_eq!(geom.geom_type(), GeomType::Polygon);
411 }
412
413 #[test]
414 fn test_geojson_feature() {
415 let geojson = r#"{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2]},"properties":{"name":"test"}}"#;
416 let geom = Geom::from_geojson(geojson).unwrap();
417 assert!(geom.is_feature());
418 assert_eq!(geom.geom_type(), GeomType::Point);
419 }
420
421 #[test]
422 fn test_geojson_roundtrip() {
423 let original = Geom::from_wkt("POINT(1 2)").unwrap();
424 let geojson = original.to_geojson();
425 let parsed = Geom::from_geojson(&geojson).unwrap();
426 assert_eq!(parsed.geom_type(), GeomType::Point);
427 assert_eq!(parsed.point(), p(1.0, 2.0));
428 }
429
430 #[test]
431 fn test_geojson_invalid() {
432 let result = Geom::from_geojson("{ invalid json }");
433 assert!(result.is_err());
434 }
435
436 #[test]
437 fn test_geobin_roundtrip_point() {
438 let original = Geom::from_wkt("POINT(1 2)").unwrap();
439 let geobin = original.to_geobin();
440 let parsed = Geom::from_geobin(&geobin).unwrap();
441 assert_eq!(parsed.geom_type(), GeomType::Point);
442 assert_eq!(parsed.point(), p(1.0, 2.0));
443 }
444
445 #[test]
446 fn test_geobin_roundtrip_polygon() {
447 let original = Geom::from_wkt("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))").unwrap();
448 let geobin = original.to_geobin();
449 let parsed = Geom::from_geobin(&geobin).unwrap();
450 assert_eq!(parsed.geom_type(), GeomType::Polygon);
451 }
452
453 #[test]
454 fn test_geobin_rect() {
455 let geom = Geom::from_wkt("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))").unwrap();
456 let geobin = geom.to_geobin();
457 let rect = geobin_rect(&geobin);
458 assert_eq!(rect, r(0.0, 0.0, 10.0, 10.0));
459 }
460
461 #[test]
462 fn test_geobin_point_extraction() {
463 let geom = Geom::from_wkt("POINT(5 6)").unwrap();
464 let geobin = geom.to_geobin();
465 let point = geobin_point(&geobin);
466 assert_eq!(point, p(5.0, 6.0));
467 }
468
469 #[test]
470 fn test_geobin_fullrect() {
471 let geom = Geom::from_wkt("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))").unwrap();
472 let geobin = geom.to_geobin();
473 let (dims, min, max) = geobin_fullrect(&geobin);
474 assert!(dims >= 2);
475 assert_eq!(min[0], 0.0);
476 assert_eq!(min[1], 0.0);
477 assert_eq!(max[0], 10.0);
478 assert_eq!(max[1], 10.0);
479 }
480
481 #[test]
482 fn test_parse_auto_wkt() {
483 let data = b"POINT(1 2)";
484 let geom = Geom::parse(data).unwrap();
485 assert_eq!(geom.geom_type(), GeomType::Point);
486 }
487
488 #[test]
489 fn test_parse_auto_geojson() {
490 let data = br#"{"type":"Point","coordinates":[1,2]}"#;
491 let geom = Geom::parse(data).unwrap();
492 assert_eq!(geom.geom_type(), GeomType::Point);
493 }
494
495 #[test]
496 fn test_parse_with_index_kind() {
497 let geom_default = Geom::from_wkt_ix(
498 "POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))",
499 IndexKind::Default,
500 )
501 .unwrap();
502 let geom_natural = Geom::from_wkt_ix(
503 "POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))",
504 IndexKind::Natural,
505 )
506 .unwrap();
507 let geom_none = Geom::from_wkt_ix(
508 "POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))",
509 IndexKind::None,
510 )
511 .unwrap();
512
513 assert_eq!(geom_default.geom_type(), GeomType::Polygon);
515 assert_eq!(geom_natural.geom_type(), GeomType::Polygon);
516 assert_eq!(geom_none.geom_type(), GeomType::Polygon);
517 }
518
519 #[test]
520 fn test_to_wkt_point() {
521 let geom = Geom::new_point(p(1.0, 2.0)).unwrap();
522 let wkt = geom.to_wkt();
523 assert!(wkt.contains("POINT"));
524 assert!(wkt.contains("1"));
525 assert!(wkt.contains("2"));
526 }
527
528 #[test]
529 fn test_to_wkt_polygon() {
530 let geom = Geom::from_wkt("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))").unwrap();
531 let wkt = geom.to_wkt();
532 assert!(wkt.contains("POLYGON"));
533 }
534
535 #[test]
536 fn test_empty_wkt() {
537 let geom = Geom::from_wkt("POINT EMPTY").unwrap();
538 let wkt = geom.to_wkt();
539 assert!(wkt.contains("EMPTY"));
540 }
541
542 #[test]
543 fn test_multipoint_wkt() {
544 let geom = Geom::from_wkt("MULTIPOINT((0 0), (1 1), (2 2))").unwrap();
545 let wkt = geom.to_wkt();
546 assert!(wkt.contains("MULTIPOINT"));
547 }
548
549 #[test]
550 fn test_geometrycollection_wkt() {
551 let geom = Geom::from_wkt("GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(0 0, 1 1))").unwrap();
552 let wkt = geom.to_wkt();
553 assert!(wkt.contains("GEOMETRYCOLLECTION"));
554 }
555}