geoarrow_array/geozero/import/
multilinestring.rs

1use geoarrow_schema::MultiLineStringType;
2use geozero::{GeomProcessor, GeozeroGeometry};
3
4use crate::array::MultiLineStringArray;
5use crate::builder::MultiLineStringBuilder;
6use crate::capacity::MultiLineStringCapacity;
7use crate::geozero::import::util::{from_xy, from_xyzm};
8
9/// GeoZero trait to convert to GeoArrow MultiLineStringArray.
10pub trait ToMultiLineStringArray {
11    /// Convert to GeoArrow MultiLineStringArray
12    fn to_multi_line_string_array(
13        &self,
14        typ: MultiLineStringType,
15    ) -> geozero::error::Result<MultiLineStringArray> {
16        Ok(self.to_multi_line_string_builder(typ)?.finish())
17    }
18
19    /// Convert to a GeoArrow MultiLineStringBuilder
20    fn to_multi_line_string_builder(
21        &self,
22        typ: MultiLineStringType,
23    ) -> geozero::error::Result<MultiLineStringBuilder>;
24}
25
26impl<T: GeozeroGeometry> ToMultiLineStringArray for T {
27    fn to_multi_line_string_builder(
28        &self,
29        typ: MultiLineStringType,
30    ) -> geozero::error::Result<MultiLineStringBuilder> {
31        let mut mutable_array = MultiLineStringBuilder::new(typ);
32        self.process_geom(&mut mutable_array)?;
33        Ok(mutable_array)
34    }
35}
36
37#[allow(unused_variables)]
38impl GeomProcessor for MultiLineStringBuilder {
39    fn geometrycollection_begin(&mut self, size: usize, idx: usize) -> geozero::error::Result<()> {
40        // reserve `size` geometries
41        let capacity = MultiLineStringCapacity::new(0, 0, size);
42        self.reserve(capacity);
43        Ok(())
44    }
45
46    fn geometrycollection_end(&mut self, idx: usize) -> geozero::error::Result<()> {
47        // self.shrink_to_fit()
48        Ok(())
49    }
50
51    fn xy(&mut self, x: f64, y: f64, idx: usize) -> geozero::error::Result<()> {
52        // # Safety:
53        // This upholds invariants because we call try_push_length in multipoint_begin to ensure
54        // offset arrays are correct.
55        self.push_coord(&from_xy(x, y).expect("valid coord"))
56            .unwrap();
57        Ok(())
58    }
59
60    fn coordinate(
61        &mut self,
62        x: f64,
63        y: f64,
64        z: Option<f64>,
65        m: Option<f64>,
66        t: Option<f64>,
67        tm: Option<u64>,
68        idx: usize,
69    ) -> geozero::error::Result<()> {
70        // # Safety:
71        // This upholds invariants because we call try_push_length in multipoint_begin to ensure
72        // offset arrays are correct.
73        self.push_coord(&from_xyzm(x, y, z, m).expect("valid coord"))
74            .unwrap();
75        Ok(())
76    }
77
78    // Here, size is the number of LineStrings in the MultiLineString
79    fn multilinestring_begin(&mut self, size: usize, idx: usize) -> geozero::error::Result<()> {
80        // reserve `size` line strings
81        let capacity = MultiLineStringCapacity::new(0, size, 0);
82        self.reserve(capacity);
83
84        // # Safety:
85        // This upholds invariants because we separately update the ring offsets in
86        // linestring_begin
87        self.try_push_geom_offset(size).unwrap();
88        Ok(())
89    }
90
91    fn linestring_begin(
92        &mut self,
93        tagged: bool,
94        size: usize,
95        idx: usize,
96    ) -> geozero::error::Result<()> {
97        // > An untagged LineString is either a Polygon ring or part of a MultiLineString
98        // So if tagged, we need to update the geometry offsets array.
99        if tagged {
100            // reserve 1 line strings
101            let capacity = MultiLineStringCapacity::new(0, 1, 0);
102            self.reserve(capacity);
103
104            // # Safety:
105            // This upholds invariants because we separately update the ring offsets in
106            // linestring_begin
107            self.try_push_geom_offset(1).unwrap();
108        }
109
110        // reserve `size` coordinates
111        let capacity = MultiLineStringCapacity::new(size, 0, 0);
112        self.reserve(capacity);
113
114        // # Safety:
115        // This upholds invariants because we separately update the geometry offsets in
116        // polygon_begin
117        self.try_push_ring_offset(size).unwrap();
118        Ok(())
119    }
120}
121
122#[cfg(test)]
123mod test {
124    use geo_types::Geometry;
125    use geoarrow_schema::Dimension;
126    use geozero::error::Result;
127
128    use super::*;
129    use crate::test::multilinestring::{ml0, ml1};
130
131    #[test]
132    fn from_geozero() -> Result<()> {
133        let geo_geoms = vec![ml0(), ml1()];
134
135        let geo = Geometry::GeometryCollection(
136            geo_geoms
137                .clone()
138                .into_iter()
139                .map(Geometry::MultiLineString)
140                .collect(),
141        );
142        let typ = MultiLineStringType::new(Dimension::XY, Default::default());
143        let geo_arr = geo.to_multi_line_string_array(typ.clone()).unwrap();
144
145        let geo_arr2 = MultiLineStringBuilder::from_multi_line_strings(&geo_geoms, typ).finish();
146
147        // These are constructed with two different code paths
148        assert_eq!(geo_arr, geo_arr2);
149        Ok(())
150    }
151}