1use std::io::Cursor;
2
3use crate::{
4 ewkb::{EwkbSerializable, GeometryType, BIG_ENDIAN},
5 types::*,
6};
7use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
8
9#[cfg(feature = "diesel")]
10use crate::ewkb::{read_ewkb_header, write_ewkb_header};
11use crate::sql_types::*;
12
13pub enum Dimension {
14 None = 0,
15 Z = 0x80000000,
16 M = 0x40000000,
17 ZM = 0x40000000 | 0x80000000,
18}
19
20impl EwkbSerializable for Point {
21 fn geometry_type(&self) -> u32 {
22 GeometryType::Point as u32
23 }
24}
25
26impl EwkbSerializable for PointZ {
27 fn geometry_type(&self) -> u32 {
28 GeometryType::Point as u32 | Dimension::Z as u32
29 }
30}
31
32impl EwkbSerializable for PointM {
33 fn geometry_type(&self) -> u32 {
34 GeometryType::Point as u32 | Dimension::M as u32
35 }
36}
37
38impl EwkbSerializable for PointZM {
39 fn geometry_type(&self) -> u32 {
40 GeometryType::Point as u32 | Dimension::ZM as u32
41 }
42}
43
44impl Point {
45 pub fn new(x: f64, y: f64, srid: Option<u32>) -> Self {
46 Point::new_point(x, y, srid, None, None).unwrap()
47 }
48}
49
50impl PointZ {
51 pub fn new(x: f64, y: f64, z: f64, srid: Option<u32>) -> Self {
52 PointZ::new_point(x, y, srid, Some(z), None).unwrap()
53 }
54}
55
56impl PointM {
57 pub fn new(x: f64, y: f64, m: f64, srid: Option<u32>) -> Self {
58 PointM::new_point(x, y, srid, None, Some(m)).unwrap()
59 }
60}
61
62impl PointZM {
63 pub fn new(x: f64, y: f64, z: f64, m: f64, srid: Option<u32>) -> Self {
64 PointZM::new_point(x, y, srid, Some(z), Some(m)).unwrap()
65 }
66}
67
68impl PointT for Point {
69 fn get_x(&self) -> f64 {
70 self.x
71 }
72
73 fn get_y(&self) -> f64 {
74 self.y
75 }
76
77 fn get_srid(&self) -> Option<u32> {
78 self.srid
79 }
80
81 fn get_z(&self) -> Option<f64> {
82 None
83 }
84
85 fn get_m(&self) -> Option<f64> {
86 None
87 }
88
89 fn dimension(&self) -> u32 {
90 0
91 }
92
93 fn new_point(
94 x: f64,
95 y: f64,
96 srid: Option<u32>,
97 z: Option<f64>,
98 m: Option<f64>,
99 ) -> Result<Self, PointConstructorError> {
100 if z.is_some() || m.is_some() {
101 return Err(PointConstructorError {
102 reason: format!("unexpectedly defined Z {:?} or M {:?} for Point", z, m)
103 .to_string(),
104 });
105 }
106 Ok(Point { x, y, srid })
107 }
108}
109
110impl PointT for PointZ {
111 fn get_x(&self) -> f64 {
112 self.x
113 }
114
115 fn get_y(&self) -> f64 {
116 self.y
117 }
118
119 fn get_srid(&self) -> Option<u32> {
120 self.srid
121 }
122
123 fn get_z(&self) -> Option<f64> {
124 Some(self.z)
125 }
126
127 fn get_m(&self) -> Option<f64> {
128 None
129 }
130
131 fn dimension(&self) -> u32 {
132 Dimension::Z as u32
133 }
134
135 fn new_point(
136 x: f64,
137 y: f64,
138 srid: Option<u32>,
139 z: Option<f64>,
140 m: Option<f64>,
141 ) -> Result<Self, PointConstructorError> {
142 if z.is_none() {
143 return Err(PointConstructorError {
144 reason: "Z is not defined, but mandatory for PointZ".to_string(),
145 });
146 }
147 if m.is_some() {
148 return Err(PointConstructorError {
149 reason: format!("unexpectedly defined M {:?} for PointZ", m).to_string(),
150 });
151 }
152 Ok(PointZ {
153 x,
154 y,
155 z: z.unwrap(),
156 srid,
157 })
158 }
159}
160
161impl PointT for PointM {
162 fn get_x(&self) -> f64 {
163 self.x
164 }
165
166 fn get_y(&self) -> f64 {
167 self.y
168 }
169
170 fn get_srid(&self) -> Option<u32> {
171 self.srid
172 }
173
174 fn get_z(&self) -> Option<f64> {
175 None
176 }
177
178 fn get_m(&self) -> Option<f64> {
179 Some(self.m)
180 }
181
182 fn dimension(&self) -> u32 {
183 Dimension::M as u32
184 }
185
186 fn new_point(
187 x: f64,
188 y: f64,
189 srid: Option<u32>,
190 z: Option<f64>,
191 m: Option<f64>,
192 ) -> Result<Self, PointConstructorError> {
193 if m.is_none() {
194 return Err(PointConstructorError {
195 reason: "M is not defined, but mandatory for PointM".to_string(),
196 });
197 }
198 if z.is_some() {
199 return Err(PointConstructorError {
200 reason: format!("unexpectedly defined Z {:?} for PointM", z).to_string(),
201 });
202 }
203 Ok(PointM {
204 x,
205 y,
206 m: m.unwrap(),
207 srid,
208 })
209 }
210}
211
212impl PointT for PointZM {
213 fn get_x(&self) -> f64 {
214 self.x
215 }
216
217 fn get_y(&self) -> f64 {
218 self.y
219 }
220
221 fn get_srid(&self) -> Option<u32> {
222 self.srid
223 }
224
225 fn get_z(&self) -> Option<f64> {
226 Some(self.z)
227 }
228
229 fn get_m(&self) -> Option<f64> {
230 Some(self.m)
231 }
232
233 fn dimension(&self) -> u32 {
234 Dimension::ZM as u32
235 }
236
237 fn new_point(
238 x: f64,
239 y: f64,
240 srid: Option<u32>,
241 z: Option<f64>,
242 m: Option<f64>,
243 ) -> Result<Self, PointConstructorError> {
244 if z.is_none() {
245 return Err(PointConstructorError {
246 reason: "Z is not defined, but mandatory for PointZM".to_string(),
247 });
248 }
249 if m.is_none() {
250 return Err(PointConstructorError {
251 reason: "M is not defined, but mandatory for PointZM".to_string(),
252 });
253 }
254 Ok(PointZM {
255 x,
256 y,
257 z: z.unwrap(),
258 m: m.unwrap(),
259 srid,
260 })
261 }
262}
263
264macro_rules! impl_point_from_to_sql {
265 ($g:ident, $p:ident) => {
266 #[cfg(feature = "diesel")]
267 impl diesel::deserialize::FromSql<$g, diesel::pg::Pg> for $p {
268 fn from_sql(bytes: diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
269 let mut r = Cursor::new(bytes.as_bytes());
270 let end = r.read_u8()?;
271 if end == BIG_ENDIAN {
272 read_point::<BigEndian, $p>(&mut r)
273 } else {
274 read_point::<LittleEndian, $p>(&mut r)
275 }
276 }
277 }
278
279 #[cfg(feature = "diesel")]
280 impl diesel::serialize::ToSql<$g, diesel::pg::Pg> for $p {
281 fn to_sql(
282 &self,
283 out: &mut diesel::serialize::Output<diesel::pg::Pg>,
284 ) -> diesel::serialize::Result {
285 write_point(self, self.get_srid(), out)?;
286 Ok(diesel::serialize::IsNull::No)
287 }
288 }
289 };
290}
291
292impl_point_from_to_sql!(Geometry, Point);
293impl_point_from_to_sql!(Geometry, PointZ);
294impl_point_from_to_sql!(Geometry, PointM);
295impl_point_from_to_sql!(Geometry, PointZM);
296
297impl_point_from_to_sql!(Geography, Point);
298impl_point_from_to_sql!(Geography, PointZ);
299impl_point_from_to_sql!(Geography, PointM);
300impl_point_from_to_sql!(Geography, PointZM);
301
302#[cfg(feature = "diesel")]
303pub fn write_point<T>(
304 point: &T,
305 srid: Option<u32>,
306 out: &mut diesel::serialize::Output<diesel::pg::Pg>,
307) -> diesel::serialize::Result
308where
309 T: PointT + EwkbSerializable,
310{
311 write_ewkb_header(point, srid, out)?;
312 write_point_coordinates(point, out)?;
313 Ok(diesel::serialize::IsNull::No)
314}
315
316#[cfg(feature = "diesel")]
317pub fn write_point_coordinates<T>(
318 point: &T,
319 out: &mut diesel::serialize::Output<diesel::pg::Pg>,
320) -> diesel::serialize::Result
321where
322 T: PointT,
323{
324 out.write_f64::<LittleEndian>(point.get_x())?;
325 out.write_f64::<LittleEndian>(point.get_y())?;
326 if point.get_z().is_some() {
327 out.write_f64::<LittleEndian>(point.get_z().unwrap())?;
328 }
329 if point.get_m().is_some() {
330 out.write_f64::<LittleEndian>(point.get_m().unwrap())?;
331 }
332 Ok(diesel::serialize::IsNull::No)
333}
334
335#[cfg(feature = "diesel")]
336fn read_point<T, P>(cursor: &mut Cursor<&[u8]>) -> diesel::deserialize::Result<P>
337where
338 T: byteorder::ByteOrder,
339 P: PointT,
340{
341 let g_header = read_ewkb_header::<T>(cursor)?.expect(GeometryType::Point)?;
342 read_point_coordinates::<T, P>(cursor, g_header.g_type, g_header.srid)
343}
344
345#[cfg(feature = "diesel")]
346pub fn read_point_coordinates<T, P>(
347 cursor: &mut Cursor<&[u8]>,
348 g_type: u32,
349 srid: Option<u32>,
350) -> diesel::deserialize::Result<P>
351where
352 T: byteorder::ByteOrder,
353 P: PointT,
354{
355 let x = cursor.read_f64::<T>()?;
356 let y = cursor.read_f64::<T>()?;
357 let mut z = None;
358 if g_type & Dimension::Z as u32 == Dimension::Z as u32 {
359 z = Some(cursor.read_f64::<T>()?);
360 }
361 let mut m = None;
362 if g_type & Dimension::M as u32 == Dimension::M as u32 {
363 m = Some(cursor.read_f64::<T>()?);
364 }
365 Ok(P::new_point(x, y, srid, z, m)?)
366}
367
368#[cfg(test)]
369mod tests {
370 use super::*;
372
373 #[test]
374 fn test_point_dimensions() {
375 assert_eq!(
376 Dimension::None as u32,
377 Point::new(0.0, 0.0, None).dimension()
378 );
379 assert_eq!(
380 Dimension::Z as u32,
381 PointZ::new(0.0, 0.0, 0.0, None).dimension()
382 );
383 assert_eq!(
384 Dimension::M as u32,
385 PointM::new(0.0, 0.0, 0.0, None).dimension()
386 );
387 assert_eq!(
388 Dimension::ZM as u32,
389 PointZM::new(0.0, 0.0, 0.0, 0.0, None).dimension()
390 );
391 }
392
393 #[test]
394 #[should_panic(expected = "unexpectedly defined Z Some(1.0) or M Some(1.0) for Point")]
395 fn test_new_point_err() {
396 Point::new_point(72.0, 64.0, None, Some(1.0), Some(1.0)).unwrap();
397 }
398
399 #[test]
400 #[should_panic(expected = "Z is not defined, but mandatory for PointZ")]
401 fn test_new_point_z_not_def_err() {
402 PointZ::new_point(72.0, 64.0, None, None, None).unwrap();
403 }
404
405 #[test]
406 #[should_panic(expected = "unexpectedly defined M Some(1.0) for PointZ")]
407 fn test_new_point_z_m_def_err() {
408 PointZ::new_point(72.0, 64.0, None, Some(1.0), Some(1.0)).unwrap();
409 }
410
411 #[test]
412 #[should_panic(expected = "M is not defined, but mandatory for PointM")]
413 fn test_new_point_m_not_def_err() {
414 PointM::new_point(72.0, 64.0, None, None, None).unwrap();
415 }
416
417 #[test]
418 #[should_panic(expected = "unexpectedly defined Z Some(1.0) for PointM")]
419 fn test_new_point_m_z_def_err() {
420 PointM::new_point(72.0, 64.0, None, Some(1.0), Some(1.0)).unwrap();
421 }
422
423 #[test]
424 #[should_panic(expected = "Z is not defined, but mandatory for PointZM")]
425 fn test_new_point_zm_z_not_def_err() {
426 PointZM::new_point(72.0, 64.0, None, None, Some(1.0)).unwrap();
427 }
428
429 #[test]
430 #[should_panic(expected = "M is not defined, but mandatory for PointZM")]
431 fn test_new_point_zm_m_not_def_err() {
432 PointZM::new_point(72.0, 64.0, None, Some(1.0), None).unwrap();
433 }
434}