postgis_diesel/
multiline.rs

1use std::fmt::Debug;
2use std::io::Cursor;
3
4use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
5
6#[cfg(feature = "diesel")]
7use crate::{
8    ewkb::{read_ewkb_header, write_ewkb_header},
9    linestring::write_linestring,
10    points::read_point_coordinates,
11};
12use crate::{
13    ewkb::{EwkbSerializable, GeometryType, BIG_ENDIAN},
14    points::Dimension,
15    types::{LineString, MultiLineString, PointT},
16};
17
18use crate::sql_types::*;
19
20impl<T> MultiLineString<T>
21where
22    T: PointT + Clone,
23{
24    pub fn new(srid: Option<u32>) -> Self {
25        Self::with_capacity(srid, 0)
26    }
27
28    pub fn with_capacity(srid: Option<u32>, cap: usize) -> Self {
29        MultiLineString {
30            lines: Vec::with_capacity(cap),
31            srid,
32        }
33    }
34
35    pub fn add_line(&mut self) -> &mut Self {
36        self.add_line_with_cap(0)
37    }
38
39    pub fn add_line_with_cap(&mut self, cap: usize) -> &mut Self {
40        self.lines.push(LineString::with_capacity(self.srid, cap));
41        self
42    }
43
44    pub fn add_point(&mut self, point: T) -> &mut Self {
45        if self.lines.last().is_none() {
46            self.add_line();
47        }
48        self.lines.last_mut().unwrap().add_point(point);
49        self
50    }
51
52    pub fn add_points(&mut self, points: impl IntoIterator<Item = T>) -> &mut Self {
53        if self.lines.last().is_none() {
54            self.add_line();
55        }
56        let last = self.lines.last_mut().unwrap();
57        for point in points {
58            last.points.push(point);
59        }
60        self
61    }
62
63    pub fn dimension(&self) -> u32 {
64        let mut dimension = Dimension::None as u32;
65        if let Some(line) = self.lines.first() {
66            dimension |= line.dimension();
67        }
68        dimension
69    }
70}
71
72impl<T> EwkbSerializable for MultiLineString<T>
73where
74    T: PointT,
75{
76    fn geometry_type(&self) -> u32 {
77        let mut g_type = GeometryType::MultiLineString as u32;
78        if let Some(line) = self.lines.first() {
79            g_type |= line.dimension();
80        }
81        g_type
82    }
83}
84
85#[cfg(feature = "diesel")]
86impl<T> diesel::serialize::ToSql<Geometry, diesel::pg::Pg> for MultiLineString<T>
87where
88    T: PointT + Debug + PartialEq + EwkbSerializable + Clone,
89{
90    fn to_sql(
91        &self,
92        out: &mut diesel::serialize::Output<diesel::pg::Pg>,
93    ) -> diesel::serialize::Result {
94        write_multiline(self, self.srid, out)
95    }
96}
97
98#[cfg(feature = "diesel")]
99impl<T> diesel::serialize::ToSql<Geography, diesel::pg::Pg> for MultiLineString<T>
100where
101    T: PointT + Debug + PartialEq + EwkbSerializable + Clone,
102{
103    fn to_sql(
104        &self,
105        out: &mut diesel::serialize::Output<diesel::pg::Pg>,
106    ) -> diesel::serialize::Result {
107        write_multiline(self, self.srid, out)
108    }
109}
110
111#[cfg(feature = "diesel")]
112impl<T> diesel::deserialize::FromSql<Geometry, diesel::pg::Pg> for MultiLineString<T>
113where
114    T: PointT + Debug + Clone,
115{
116    fn from_sql(bytes: diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
117        let mut r = Cursor::new(bytes.as_bytes());
118        let end = r.read_u8()?;
119        if end == BIG_ENDIAN {
120            read_multiline::<BigEndian, T>(&mut r)
121        } else {
122            read_multiline::<LittleEndian, T>(&mut r)
123        }
124    }
125}
126
127#[cfg(feature = "diesel")]
128impl<T> diesel::deserialize::FromSql<Geography, diesel::pg::Pg> for MultiLineString<T>
129where
130    T: PointT + Debug + Clone,
131{
132    fn from_sql(bytes: diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
133        diesel::deserialize::FromSql::<Geometry, diesel::pg::Pg>::from_sql(bytes)
134    }
135}
136
137#[cfg(feature = "diesel")]
138pub fn write_multiline<T>(
139    multiline: &MultiLineString<T>,
140    srid: Option<u32>,
141    out: &mut diesel::serialize::Output<diesel::pg::Pg>,
142) -> diesel::serialize::Result
143where
144    T: PointT + EwkbSerializable + Clone,
145{
146    write_ewkb_header(multiline, srid, out)?;
147    // number of lines
148    out.write_u32::<LittleEndian>(multiline.lines.len() as u32)?;
149    for line in multiline.lines.iter() {
150        write_linestring(line, None, out)?;
151    }
152    Ok(diesel::serialize::IsNull::No)
153}
154
155#[cfg(feature = "diesel")]
156fn read_multiline<T, P>(
157    cursor: &mut Cursor<&[u8]>,
158) -> diesel::deserialize::Result<MultiLineString<P>>
159where
160    T: byteorder::ByteOrder,
161    P: PointT + Clone,
162{
163    let g_header = read_ewkb_header::<T>(cursor)?.expect(GeometryType::MultiLineString)?;
164    read_multiline_body::<T, P>(g_header.g_type, g_header.srid, cursor)
165}
166
167#[cfg(feature = "diesel")]
168pub fn read_multiline_body<T, P>(
169    g_type: u32,
170    srid: Option<u32>,
171    cursor: &mut Cursor<&[u8]>,
172) -> diesel::deserialize::Result<MultiLineString<P>>
173where
174    T: byteorder::ByteOrder,
175    P: PointT + Clone,
176{
177    let lines_n = cursor.read_u32::<T>()?;
178    let mut multiline = MultiLineString::with_capacity(srid, lines_n as usize);
179    for _i in 0..lines_n {
180        // skip 1 byte for byte order and 4 bytes for point type
181        cursor.read_u8()?;
182        cursor.read_u32::<T>()?;
183        let points_n = cursor.read_u32::<T>()?;
184        multiline.add_line_with_cap(points_n as usize);
185        for _p in 0..points_n {
186            multiline.add_point(read_point_coordinates::<T, P>(cursor, g_type, srid)?);
187        }
188    }
189    Ok(multiline)
190}