shapefile_gbk/record/
point.rs

1//! Module with the definition of Point, PointM and PointZ
2
3use std::io::{Read, Write};
4
5use super::EsriShape;
6use super::{ShapeType, NO_DATA};
7use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
8use std::mem::size_of;
9
10use super::ConcreteReadableShape;
11use super::Error;
12use super::{is_no_data, HasShapeType, WritableShape};
13use std::fmt;
14
15#[cfg(feature = "geo-types")]
16use geo_types;
17
18/// Point with only `x` and `y` coordinates
19#[derive(PartialEq, Debug, Default, Copy, Clone)]
20pub struct Point {
21    pub x: f64,
22    pub y: f64,
23}
24
25impl Point {
26    /// Creates a new point
27    ///
28    /// # Examples
29    ///
30    /// ```
31    /// use shapefile::Point;
32    /// let point = Point::new(1.0, 42.0);
33    /// assert_eq!(point.x, 1.0);
34    /// assert_eq!(point.y, 42.0);
35    /// ```
36    ///
37    /// ```
38    /// use shapefile::Point;
39    /// let point = Point::default();
40    /// assert_eq!(point.x, 0.0);
41    /// assert_eq!(point.y, 0.0);
42    /// ```
43    pub fn new(x: f64, y: f64) -> Self {
44        Self { x, y }
45    }
46}
47
48impl HasShapeType for Point {
49    fn shapetype() -> ShapeType {
50        ShapeType::Point
51    }
52}
53
54impl ConcreteReadableShape for Point {
55    fn read_shape_content<T: Read>(source: &mut T, record_size: i32) -> Result<Self, Error> {
56        if record_size == 2 * size_of::<f64>() as i32 {
57            let x = source.read_f64::<LittleEndian>()?;
58            let y = source.read_f64::<LittleEndian>()?;
59            Ok(Self { x, y })
60        } else {
61            Err(Error::InvalidShapeRecordSize)
62        }
63    }
64}
65
66impl WritableShape for Point {
67    fn size_in_bytes(&self) -> usize {
68        2 * size_of::<f64>()
69    }
70
71    fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), Error> {
72        dest.write_f64::<LittleEndian>(self.x)?;
73        dest.write_f64::<LittleEndian>(self.y)?;
74        Ok(())
75    }
76}
77
78impl EsriShape for Point {
79    fn x_range(&self) -> [f64; 2] {
80        [self.x, self.x]
81    }
82
83    fn y_range(&self) -> [f64; 2] {
84        [self.y, self.y]
85    }
86}
87
88impl fmt::Display for Point {
89    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90        write!(f, "Point(x: {}, y: {})", self.x, self.y)
91    }
92}
93
94#[cfg(feature = "geo-types")]
95impl From<Point> for geo_types::Point<f64> {
96    fn from(p: Point) -> Self {
97        geo_types::Point::new(p.x, p.y)
98    }
99}
100
101#[cfg(feature = "geo-types")]
102impl From<geo_types::Point<f64>> for Point {
103    fn from(p: geo_types::Point<f64>) -> Self {
104        Point::new(p.x(), p.y())
105    }
106}
107
108#[cfg(feature = "geo-types")]
109impl From<geo_types::Coordinate<f64>> for Point {
110    fn from(c: geo_types::Coordinate<f64>) -> Self {
111        Point::new(c.x, c.y)
112    }
113}
114
115#[cfg(feature = "geo-types")]
116impl From<Point> for geo_types::Coordinate<f64> {
117    fn from(p: Point) -> Self {
118        geo_types::Coordinate { x: p.x, y: p.y }
119    }
120}
121
122/*
123 * PointM
124 */
125
126/// Point with `x`, `y`, `m`
127#[derive(PartialEq, Debug, Copy, Clone)]
128pub struct PointM {
129    pub x: f64,
130    pub y: f64,
131    pub m: f64,
132}
133
134impl PointM {
135    /// Creates a new pointM
136    ///
137    /// # Examples
138    ///
139    /// ```
140    /// use shapefile::PointM;
141    /// let point = PointM::new(1.0, 42.0, 13.37);
142    /// assert_eq!(point.x, 1.0);
143    /// assert_eq!(point.y, 42.0);
144    /// assert_eq!(point.m, 13.37);
145    /// ```
146    ///
147    /// ```
148    /// use shapefile::{PointM, NO_DATA};
149    /// let point = PointM::default();
150    /// assert_eq!(point.x, 0.0);
151    /// assert_eq!(point.y, 0.0);
152    /// assert_eq!(point.m, NO_DATA);
153    /// ```
154    pub fn new(x: f64, y: f64, m: f64) -> Self {
155        Self { x, y, m }
156    }
157}
158
159impl HasShapeType for PointM {
160    fn shapetype() -> ShapeType {
161        ShapeType::PointM
162    }
163}
164
165impl ConcreteReadableShape for PointM {
166    fn read_shape_content<T: Read>(source: &mut T, record_size: i32) -> Result<Self, Error> {
167        if record_size == 3 * size_of::<f64>() as i32 {
168            let x = source.read_f64::<LittleEndian>()?;
169            let y = source.read_f64::<LittleEndian>()?;
170            let m = source.read_f64::<LittleEndian>()?;
171            Ok(Self { x, y, m })
172        } else {
173            Err(Error::InvalidShapeRecordSize)
174        }
175    }
176}
177
178impl WritableShape for PointM {
179    fn size_in_bytes(&self) -> usize {
180        3 * size_of::<f64>()
181    }
182
183    fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), Error> {
184        dest.write_f64::<LittleEndian>(self.x)?;
185        dest.write_f64::<LittleEndian>(self.y)?;
186        dest.write_f64::<LittleEndian>(self.m)?;
187        Ok(())
188    }
189}
190
191impl EsriShape for PointM {
192    fn x_range(&self) -> [f64; 2] {
193        [self.x, self.x]
194    }
195
196    fn y_range(&self) -> [f64; 2] {
197        [self.y, self.y]
198    }
199
200    fn m_range(&self) -> [f64; 2] {
201        if is_no_data(self.m) {
202            [0.0, 0.0]
203        } else {
204            [self.m, self.m]
205        }
206    }
207}
208
209impl fmt::Display for PointM {
210    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211        if is_no_data(self.m) {
212            write!(f, "Point(x: {}, y: {}, m: NO_DATA)", self.x, self.y)
213        } else {
214            write!(f, "Point(x: {}, y: {}, m: {})", self.x, self.y, self.m)
215        }
216    }
217}
218
219impl Default for PointM {
220    fn default() -> Self {
221        Self {
222            x: 0.0,
223            y: 0.0,
224            m: NO_DATA,
225        }
226    }
227}
228
229#[cfg(feature = "geo-types")]
230impl From<PointM> for geo_types::Point<f64> {
231    fn from(p: PointM) -> Self {
232        geo_types::Point::new(p.x, p.y)
233    }
234}
235
236#[cfg(feature = "geo-types")]
237impl From<geo_types::Point<f64>> for PointM {
238    fn from(p: geo_types::Point<f64>) -> Self {
239        PointM {
240            x: p.x(),
241            y: p.y(),
242            ..Default::default()
243        }
244    }
245}
246
247#[cfg(feature = "geo-types")]
248impl From<geo_types::Coordinate<f64>> for PointM {
249    fn from(c: geo_types::Coordinate<f64>) -> Self {
250        PointM::new(c.x, c.y, NO_DATA)
251    }
252}
253
254#[cfg(feature = "geo-types")]
255impl From<PointM> for geo_types::Coordinate<f64> {
256    fn from(p: PointM) -> Self {
257        geo_types::Coordinate { x: p.x, y: p.y }
258    }
259}
260
261/*
262 * PointZ
263 */
264
265/// Point with `x`, `y`, `m`, `z`
266#[derive(PartialEq, Debug, Copy, Clone)]
267pub struct PointZ {
268    pub x: f64,
269    pub y: f64,
270    pub z: f64,
271    pub m: f64,
272}
273
274impl PointZ {
275    /// Creates a new pointZ
276    ///
277    /// # Examples
278    ///
279    /// ```
280    /// use shapefile::{PointZ, NO_DATA};
281    /// let point = PointZ::new(1.0, 42.0, 13.37, NO_DATA);
282    /// assert_eq!(point.x, 1.0);
283    /// assert_eq!(point.y, 42.0);
284    /// assert_eq!(point.z, 13.37);
285    /// assert_eq!(point.m, NO_DATA);
286    /// ```
287    pub fn new(x: f64, y: f64, z: f64, m: f64) -> Self {
288        Self { x, y, z, m }
289    }
290
291    fn read_xyz<R: Read>(source: &mut R) -> std::io::Result<Self> {
292        let x = source.read_f64::<LittleEndian>()?;
293        let y = source.read_f64::<LittleEndian>()?;
294        let z = source.read_f64::<LittleEndian>()?;
295        Ok(Self {
296            x,
297            y,
298            z,
299            m: NO_DATA,
300        })
301    }
302}
303
304impl HasShapeType for PointZ {
305    fn shapetype() -> ShapeType {
306        ShapeType::PointZ
307    }
308}
309
310impl ConcreteReadableShape for PointZ {
311    fn read_shape_content<T: Read>(source: &mut T, record_size: i32) -> Result<Self, Error> {
312        if record_size == 3 * size_of::<f64>() as i32 {
313            let point = Self::read_xyz(source)?;
314            Ok(point)
315        } else if record_size == 4 * size_of::<f64>() as i32 {
316            let mut point = Self::read_xyz(source)?;
317            point.m = source.read_f64::<LittleEndian>()?;
318            Ok(point)
319        } else {
320            Err(Error::InvalidShapeRecordSize)
321        }
322    }
323}
324
325impl WritableShape for PointZ {
326    fn size_in_bytes(&self) -> usize {
327        4 * size_of::<f64>()
328    }
329
330    fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), Error> {
331        dest.write_f64::<LittleEndian>(self.x)?;
332        dest.write_f64::<LittleEndian>(self.y)?;
333        dest.write_f64::<LittleEndian>(self.z)?;
334        dest.write_f64::<LittleEndian>(self.m)?;
335        Ok(())
336    }
337}
338
339impl EsriShape for PointZ {
340    fn x_range(&self) -> [f64; 2] {
341        [self.x, self.x]
342    }
343
344    fn y_range(&self) -> [f64; 2] {
345        [self.y, self.y]
346    }
347
348    fn z_range(&self) -> [f64; 2] {
349        [self.z, self.z]
350    }
351
352    fn m_range(&self) -> [f64; 2] {
353        if is_no_data(self.m) {
354            [0.0, 0.0]
355        } else {
356            [self.m, self.m]
357        }
358    }
359}
360
361impl Default for PointZ {
362    fn default() -> Self {
363        Self {
364            x: 0.0,
365            y: 0.0,
366            z: 0.0,
367            m: NO_DATA,
368        }
369    }
370}
371
372impl fmt::Display for PointZ {
373    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
374        if is_no_data(self.m) {
375            write!(
376                f,
377                "Point(x: {}, y: {}, z: {}, m: NO_DATA)",
378                self.x, self.y, self.z
379            )
380        } else {
381            write!(
382                f,
383                "Point(x: {}, y: {}, z: {}, m: {})",
384                self.x, self.y, self.z, self.m
385            )
386        }
387    }
388}
389
390#[cfg(feature = "geo-types")]
391impl From<PointZ> for geo_types::Point<f64> {
392    fn from(p: PointZ) -> Self {
393        geo_types::Point::new(p.x, p.y)
394    }
395}
396
397#[cfg(feature = "geo-types")]
398impl From<geo_types::Point<f64>> for PointZ {
399    fn from(p: geo_types::Point<f64>) -> Self {
400        PointZ {
401            x: p.x(),
402            y: p.y(),
403            ..Default::default()
404        }
405    }
406}
407
408#[cfg(feature = "geo-types")]
409impl From<geo_types::Coordinate<f64>> for PointZ {
410    fn from(c: geo_types::Coordinate<f64>) -> Self {
411        PointZ::new(c.x, c.y, 0.0, NO_DATA)
412    }
413}
414
415#[cfg(feature = "geo-types")]
416impl From<PointZ> for geo_types::Coordinate<f64> {
417    fn from(p: PointZ) -> Self {
418        geo_types::Coordinate { x: p.x, y: p.y }
419    }
420}
421
422#[cfg(test)]
423#[cfg(feature = "geo-types")]
424mod test_geo_types {
425    use super::*;
426    #[test]
427    fn geo_types_point_conversion() {
428        let p = Point::new(14.0, 42.65);
429        let gp: geo_types::Point<f64> = p.into();
430
431        assert_eq!(gp.x(), 14.0);
432        assert_eq!(gp.y(), 42.65);
433
434        let p: Point = gp.into();
435        assert_eq!(p.x, 14.0);
436        assert_eq!(p.y, 42.65);
437    }
438
439    #[test]
440    fn geo_types_point_m_conversion() {
441        let p = PointM::new(14.0, 42.65, 652.3);
442        let gp: geo_types::Point<f64> = p.into();
443
444        assert_eq!(gp.x(), 14.0);
445        assert_eq!(gp.y(), 42.65);
446
447        let p: PointM = gp.into();
448        assert_eq!(p.x, 14.0);
449        assert_eq!(p.y, 42.65);
450        assert_eq!(p.m, NO_DATA);
451    }
452
453    #[test]
454    fn geo_types_point_z_conversion() {
455        let p = PointZ::new(14.0, 42.65, 111.0, 652.3);
456        let gp: geo_types::Point<f64> = p.into();
457
458        assert_eq!(gp.x(), 14.0);
459        assert_eq!(gp.y(), 42.65);
460
461        let p: PointZ = gp.into();
462        assert_eq!(p.x, 14.0);
463        assert_eq!(p.y, 42.65);
464        assert_eq!(p.z, 0.0);
465        assert_eq!(p.m, NO_DATA);
466    }
467}