postgis_diesel/
multiline.rs1use 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 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 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}