Skip to main content

geoarrow_geojson/encoder/
rect.rs

1use std::io::Write;
2
3use arrow_json::Encoder;
4use geo_traits::{CoordTrait, RectTrait};
5use geoarrow_array::GeoArrowArrayAccessor;
6use geoarrow_array::array::RectArray;
7
8// An [Encoder] for [RectArray].
9pub struct RectEncoder(RectArray);
10
11impl RectEncoder {
12    pub fn new(array: RectArray) -> Self {
13        Self(array)
14    }
15}
16
17impl Encoder for RectEncoder {
18    fn encode(&mut self, idx: usize, out: &mut Vec<u8>) {
19        let rect = self.0.value(idx).unwrap();
20        encode_rect(&rect, out);
21    }
22}
23
24pub(crate) fn encode_rect(rect: &impl RectTrait<T = f64>, out: &mut Vec<u8>) {
25    out.extend(br#"{"type":"Polygon","coordinates":["#);
26
27    // Get the min and max coordinates
28    let min_coord = rect.min();
29    let max_coord = rect.max();
30
31    // Create exterior ring coordinates: [min_x, min_y], [min_x, max_y], [max_x, max_y], [max_x, min_y], [min_x, min_y]
32    out.push(b'[');
33
34    // Bottom-left: [min_x, min_y]
35    out.push(b'[');
36    write!(out, "{}", min_coord.x()).unwrap();
37    out.push(b',');
38    write!(out, "{}", min_coord.y()).unwrap();
39    out.push(b']');
40    out.push(b',');
41
42    // Top-left: [min_x, max_y]
43    out.push(b'[');
44    write!(out, "{}", min_coord.x()).unwrap();
45    out.push(b',');
46    write!(out, "{}", max_coord.y()).unwrap();
47    out.push(b']');
48    out.push(b',');
49
50    // Top-right: [max_x, max_y]
51    out.push(b'[');
52    write!(out, "{}", max_coord.x()).unwrap();
53    out.push(b',');
54    write!(out, "{}", max_coord.y()).unwrap();
55    out.push(b']');
56    out.push(b',');
57
58    // Bottom-right: [max_x, min_y]
59    out.push(b'[');
60    write!(out, "{}", max_coord.x()).unwrap();
61    out.push(b',');
62    write!(out, "{}", min_coord.y()).unwrap();
63    out.push(b']');
64    out.push(b',');
65
66    // Close the ring: [min_x, min_y]
67    out.push(b'[');
68    write!(out, "{}", min_coord.x()).unwrap();
69    out.push(b',');
70    write!(out, "{}", min_coord.y()).unwrap();
71    out.push(b']');
72
73    out.push(b']'); // Close exterior ring
74    out.push(b']'); // Close coordinates array
75    out.push(b'}'); // Close geometry object
76}
77
78#[cfg(test)]
79mod test {
80    use std::str::FromStr;
81
82    use geo_types::{Rect, coord};
83    use geoarrow_array::builder::RectBuilder;
84    use geoarrow_schema::{BoxType, Dimension};
85
86    use super::*;
87
88    #[test]
89    fn encode_rect() {
90        // Create a simple rect array for testing
91        let geoms = [Rect::new(
92            coord! { x: 10., y: 20. },
93            coord! { x: 30., y: 10. },
94        )];
95        let typ = BoxType::new(Dimension::XY, Default::default());
96        let array = RectBuilder::from_rects(geoms.iter(), typ).finish();
97
98        let mut encoder = RectEncoder::new(array);
99
100        let mut out = vec![];
101        encoder.encode(0, &mut out);
102        let s = String::from_utf8(out).unwrap();
103        // The rect bounds should create a polygon: min_x=10, min_y=10, max_x=30, max_y=20
104        let expected =
105            r#"{"type":"Polygon","coordinates":[[[10,10],[10,20],[30,20],[30,10],[10,10]]]}"#;
106        assert_eq!(s, expected);
107
108        geojson::Geometry::from_str(expected).expect("Should be valid GeoJSON");
109    }
110}