Skip to main content

shapefile/record/
multipoint.rs

1//! Module with the definition of Multipoint, MultipointM and MultipointZ
2//!
3//! All three variant of Multipoint Shape (Multipoint, MultipointM, MultipointZ)
4//! are specialization of the `GenericMultipoint`
5//!
6use std::fmt;
7use std::io::{Read, Seek, SeekFrom, Write};
8use std::mem::size_of;
9use std::ops::Index;
10use std::slice::SliceIndex;
11
12use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
13
14use super::io::*;
15use super::traits::{GrowablePoint, ShrinkablePoint};
16use super::EsriShape;
17use super::{ConcreteReadableShape, GenericBBox};
18use super::{Error, ShapeType};
19use super::{HasShapeType, WritableShape};
20use super::{Point, PointM, PointZ};
21
22#[cfg(feature = "geo-types")]
23use geo_types;
24
25/// Generic struct to create the Multipoint, MultipointM, MultipointZ types
26///
27/// Multipoints are a collection of... multiple points,
28/// they can be created from [`Vec`] of points using the [`From`] trait
29/// or using the [`new`] method.
30///
31/// `Multipoint` shapes only offers non-mutable access to the points data,
32/// to be able to mutate it you have to move the points data out of the struct.
33///
34/// ```
35/// use shapefile::{Multipoint, Point};
36/// let multipoint = Multipoint::from(vec![
37///     Point::new(1.0, 1.0),
38///     Point::new(2.0, 2.0),
39/// ]);
40///
41/// assert_eq!(multipoint[0], Point::new(1.0, 1.0));
42///
43/// let points: Vec<Point> = multipoint.into();
44/// assert_eq!(points.len(), 2);
45/// ```
46///
47/// # geo-types
48///
49/// Multipoints are convertible to the geo-types's Multipoint<f64>
50///
51/// ```
52/// # #[cfg(feature = "geo-types")]
53/// # fn main() -> Result<(), shapefile::Error> {
54/// let mut multipoints = shapefile::read_shapes_as::<_, shapefile::Multipoint>("tests/data/multipoint.shp")?;
55/// let geo_multipoint: geo_types::MultiPoint<f64> = multipoints.pop().unwrap().into();
56/// let multipoint = shapefile::Multipoint::from(geo_multipoint);
57/// # Ok(())
58/// # }
59/// # #[cfg(not(feature = "geo-types"))]
60/// # fn main() {}
61/// ```
62///
63/// [`new`]: #method.new
64#[derive(Debug, Clone, PartialEq)]
65pub struct GenericMultipoint<PointType> {
66    pub(crate) bbox: GenericBBox<PointType>,
67    pub(crate) points: Vec<PointType>,
68}
69
70impl<PointType: ShrinkablePoint + GrowablePoint + Copy> GenericMultipoint<PointType> {
71    /// Creates a new Multipoint shape
72    ///
73    /// # Examples
74    ///
75    /// Creating Multipoint
76    /// ```
77    /// use shapefile::{Multipoint, Point};
78    /// let points = vec![
79    ///     Point::new(1.0, 1.0),
80    ///     Point::new(2.0, 2.0),
81    /// ];
82    /// let multipoint = Multipoint::new(points);
83    /// ```
84    ///
85    /// Creating a MultipointM
86    /// ```
87    /// use shapefile::{MultipointM, PointM, NO_DATA};
88    /// let points = vec![
89    ///     PointM::new(1.0, 1.0, NO_DATA),
90    ///     PointM::new(2.0, 2.0, NO_DATA),
91    /// ];
92    /// let multipointm = MultipointM::new(points);
93    /// ```
94    ///
95    /// Creating a MultipointZ
96    /// ```
97    /// use shapefile::{MultipointZ, PointZ, NO_DATA};
98    /// let points = vec![
99    ///     PointZ::new(1.0, 1.0, 1.0, NO_DATA),
100    ///     PointZ::new(2.0, 2.0, 2.0, NO_DATA),
101    /// ];
102    /// let multipointz = MultipointZ::new(points);
103    /// ```
104    pub fn new(points: Vec<PointType>) -> Self {
105        let bbox = GenericBBox::<PointType>::from_points(&points);
106        Self { bbox, points }
107    }
108}
109
110impl<PointType> GenericMultipoint<PointType> {
111    /// Returns the bbox
112    ///
113    /// # Example
114    ///
115    /// ```
116    /// use shapefile::{MultipointZ, PointZ, NO_DATA};
117    /// let multipointz = MultipointZ::new(vec![
118    ///     PointZ::new(1.0, 4.0, 1.2, 4.2),
119    ///     PointZ::new(2.0, 6.0, 4.0, 13.37),
120    /// ]);
121    ///
122    /// let bbox = multipointz.bbox();
123    /// assert_eq!(bbox.min.x, 1.0);
124    /// assert_eq!(bbox.max.x, 2.0);
125    /// assert_eq!(bbox.m_range(), [4.2, 13.37])
126    /// ```
127    #[inline]
128    pub fn bbox(&self) -> &GenericBBox<PointType> {
129        &self.bbox
130    }
131
132    /// Returns a non-mutable slice of point
133    #[inline]
134    pub fn points(&self) -> &[PointType] {
135        &self.points
136    }
137
138    /// Returns a reference to a point
139    ///
140    /// # Example
141    ///
142    /// ```
143    /// use shapefile::{MultipointZ, PointZ};
144    /// let multipointz = MultipointZ::new(vec![
145    ///     PointZ::new(1.0, 4.0, 1.2, 4.2),
146    ///     PointZ::new(2.0, 6.0, 4.0, 13.37),
147    /// ]);
148    ///
149    /// assert_eq!(multipointz.point(0), Some(&PointZ::new(1.0, 4.0, 1.2, 4.2)));
150    /// assert_eq!(multipointz.point(2), None);
151    /// ```
152    #[inline]
153    pub fn point(&self, index: usize) -> Option<&PointType> {
154        self.points.get(index)
155    }
156
157    /// Consumes the shape, returning the points
158    #[inline]
159    pub fn into_inner(self) -> Vec<PointType> {
160        self.points
161    }
162}
163
164impl<PointType> From<Vec<PointType>> for GenericMultipoint<PointType>
165where
166    PointType: ShrinkablePoint + GrowablePoint + Copy,
167{
168    fn from(points: Vec<PointType>) -> Self {
169        Self::new(points)
170    }
171}
172
173impl<PointType, I: SliceIndex<[PointType]>> Index<I> for GenericMultipoint<PointType> {
174    type Output = I::Output;
175
176    #[inline]
177    fn index(&self, index: I) -> &Self::Output {
178        Index::index(&self.points, index)
179    }
180}
181
182// We do this because we can't use generics:
183// error[E0210]: type parameter `PointType` must be used as the type parameter for some local type
184// (e.g., `MyStruct<PointType>`)
185macro_rules! impl_from_multipoint_to_vec_for_point_type {
186    ($PointType:ty) => {
187        impl From<GenericMultipoint<$PointType>> for Vec<$PointType> {
188            fn from(multipoints: GenericMultipoint<$PointType>) -> Self {
189                multipoints.points
190            }
191        }
192    };
193}
194
195impl_from_multipoint_to_vec_for_point_type!(Point);
196impl_from_multipoint_to_vec_for_point_type!(PointM);
197impl_from_multipoint_to_vec_for_point_type!(PointZ);
198
199#[cfg(feature = "geo-types")]
200impl<PointType> From<GenericMultipoint<PointType>> for geo_types::MultiPoint<f64>
201where
202    geo_types::Point<f64>: From<PointType>,
203{
204    fn from(multi_points: GenericMultipoint<PointType>) -> Self {
205        multi_points
206            .points
207            .into_iter()
208            .map(geo_types::Point::from)
209            .collect::<Vec<geo_types::Point<f64>>>()
210            .into()
211    }
212}
213
214#[cfg(feature = "geo-types")]
215impl<PointType> From<geo_types::MultiPoint<f64>> for GenericMultipoint<PointType>
216where
217    PointType: From<geo_types::Point<f64>> + ShrinkablePoint + GrowablePoint + Copy,
218{
219    fn from(mp: geo_types::MultiPoint<f64>) -> Self {
220        let points = mp.into_iter().map(|p| p.into()).collect();
221        Self::new(points)
222    }
223}
224
225/*
226 * Multipoint
227 */
228
229/// Specialization of the `GenericMultipoint` struct to represent a `Multipoint` shape
230/// ( collection of [Point](../point/struct.Point.html))
231pub type Multipoint = GenericMultipoint<Point>;
232
233impl Multipoint {
234    pub(crate) fn size_of_record(num_points: i32) -> usize {
235        let mut size = 0usize;
236        size += 4 * size_of::<f64>(); // BBOX
237        size += size_of::<i32>(); // num points
238        size += size_of::<Point>() * num_points as usize;
239        size
240    }
241}
242
243impl fmt::Display for Multipoint {
244    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245        write!(f, "Multipoint({} points)", self.points.len())
246    }
247}
248
249impl HasShapeType for Multipoint {
250    fn shapetype() -> ShapeType {
251        ShapeType::Multipoint
252    }
253}
254
255impl ConcreteReadableShape for Multipoint {
256    fn read_shape_content<T: Read + Seek>(source: &mut T, record_size: i32) -> Result<Self, Error> {
257        let mut bbox = GenericBBox::<Point>::default();
258        bbox_read_xy_from(&mut bbox, source)?;
259
260        let num_points = source.read_i32::<LittleEndian>()?;
261        let expected = Self::size_of_record(num_points) as i32;
262        let diff = record_size - expected;
263
264        if diff < 0 {
265            return Err(Error::InvalidShapeRecordSize);
266        }
267
268        let points = read_xy_in_vec_of::<Point, T>(source, num_points)?;
269
270        if diff > 0 {
271            source.seek(SeekFrom::Current(i64::from(diff)))?;
272        }
273
274        Ok(Self { bbox, points })
275    }
276}
277
278impl WritableShape for Multipoint {
279    fn size_in_bytes(&self) -> usize {
280        let mut size = 0usize;
281        size += 4 * size_of::<f64>(); // BBOX
282        size += size_of::<i32>(); // num points
283        size += 2 * size_of::<f64>() * self.points.len();
284        size
285    }
286
287    fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), Error> {
288        bbox_write_xy_to(&self.bbox, dest)?;
289        dest.write_i32::<LittleEndian>(self.points.len() as i32)?;
290        for point in self.points.iter() {
291            dest.write_f64::<LittleEndian>(point.x)?;
292            dest.write_f64::<LittleEndian>(point.y)?;
293        }
294        Ok(())
295    }
296}
297
298impl EsriShape for Multipoint {
299    fn x_range(&self) -> [f64; 2] {
300        self.bbox.x_range()
301    }
302
303    fn y_range(&self) -> [f64; 2] {
304        self.bbox.y_range()
305    }
306}
307
308/*
309 * MultipointM
310 */
311
312/// Specialization of the `GenericMultipoint` struct to represent a `MultipointM` shape
313/// ( collection of [PointM](../point/struct.PointM.html))
314pub type MultipointM = GenericMultipoint<PointM>;
315
316impl MultipointM {
317    pub(crate) fn size_of_record(num_points: i32, is_m_used: bool) -> usize {
318        let mut size = Multipoint::size_of_record(num_points);
319        if is_m_used {
320            size += 2 * size_of::<f64>(); // M Range
321            size += size_of::<f64>() * num_points as usize; // M
322        }
323        size
324    }
325}
326
327impl fmt::Display for MultipointM {
328    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
329        write!(f, "MultipointM({} points)", self.points.len())
330    }
331}
332
333impl HasShapeType for MultipointM {
334    fn shapetype() -> ShapeType {
335        ShapeType::MultipointM
336    }
337}
338
339impl ConcreteReadableShape for MultipointM {
340    fn read_shape_content<T: Read + Seek>(source: &mut T, record_size: i32) -> Result<Self, Error> {
341        let mut bbox = GenericBBox::<PointM>::default();
342        bbox_read_xy_from(&mut bbox, source)?;
343
344        let num_points = source.read_i32::<LittleEndian>()?;
345
346        let size_with_m = Self::size_of_record(num_points, true) as i32;
347        let size_without_m = Self::size_of_record(num_points, false) as i32;
348
349        let size_read = if record_size >= size_with_m {
350            size_with_m
351        } else {
352            size_without_m
353        };
354
355        let diff = record_size - size_read;
356        if diff < 0 {
357            return Err(Error::InvalidShapeRecordSize);
358        }
359
360        let mut points = read_xy_in_vec_of::<PointM, T>(source, num_points)?;
361
362        if record_size >= size_with_m {
363            bbox_read_m_range_from(&mut bbox, source)?;
364            read_ms_into(source, &mut points)?;
365        }
366
367        if diff > 0 {
368            source.seek(SeekFrom::Current(i64::from(diff)))?;
369        }
370
371        Ok(Self { bbox, points })
372    }
373}
374
375impl WritableShape for MultipointM {
376    fn size_in_bytes(&self) -> usize {
377        let mut size = 0usize;
378        size += 4 * size_of::<f64>();
379        size += size_of::<i32>();
380        size += 3 * size_of::<f64>() * self.points.len();
381        size += 2 * size_of::<f64>();
382        size
383    }
384
385    fn write_to<T: Write>(&self, mut dest: &mut T) -> Result<(), Error> {
386        bbox_write_xy_to(&self.bbox, dest)?;
387        dest.write_i32::<LittleEndian>(self.points.len() as i32)?;
388
389        write_points(&mut dest, &self.points)?;
390
391        bbox_write_m_range_to(&self.bbox, dest)?;
392        write_ms(&mut dest, &self.points)?;
393        Ok(())
394    }
395}
396
397impl EsriShape for MultipointM {
398    fn x_range(&self) -> [f64; 2] {
399        self.bbox.x_range()
400    }
401
402    fn y_range(&self) -> [f64; 2] {
403        self.bbox.y_range()
404    }
405
406    fn m_range(&self) -> [f64; 2] {
407        self.bbox.m_range()
408    }
409}
410
411/*
412 * MultipointZ
413 */
414
415/// Specialization of the `GenericMultipoint` struct to represent a `MultipointZ` shape
416/// ( collection of [PointZ](../point/struct.PointZ.html))
417pub type MultipointZ = GenericMultipoint<PointZ>;
418
419impl fmt::Display for MultipointZ {
420    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
421        write!(f, "MultipointZ({} points)", self.points.len())
422    }
423}
424impl MultipointZ {
425    pub(crate) fn size_of_record(num_points: i32, is_m_used: bool) -> usize {
426        let mut size = Multipoint::size_of_record(num_points);
427        size += 2 * size_of::<f64>(); // Z Range
428        size += size_of::<f64>() * num_points as usize; // Z
429
430        if is_m_used {
431            size += 2 * size_of::<f64>(); // M Range
432            size += size_of::<f64>() * num_points as usize; // M
433        }
434
435        size
436    }
437}
438
439impl HasShapeType for MultipointZ {
440    fn shapetype() -> ShapeType {
441        ShapeType::MultipointZ
442    }
443}
444
445impl ConcreteReadableShape for MultipointZ {
446    fn read_shape_content<T: Read + Seek>(source: &mut T, record_size: i32) -> Result<Self, Error> {
447        let mut bbox = GenericBBox::<PointZ>::default();
448        bbox_read_xy_from(&mut bbox, source)?;
449        let num_points = source.read_i32::<LittleEndian>()?;
450
451        let size_with_m = Self::size_of_record(num_points, true) as i32;
452        let size_without_m = Self::size_of_record(num_points, false) as i32;
453
454        let size_read = if record_size >= size_with_m {
455            size_with_m
456        } else {
457            size_without_m
458        };
459
460        let diff = record_size - size_read;
461        if diff < 0 {
462            return Err(Error::InvalidShapeRecordSize);
463        }
464
465        let mut points = read_xy_in_vec_of::<PointZ, T>(source, num_points)?;
466
467        bbox_read_z_range_from(&mut bbox, source)?;
468        read_zs_into(source, &mut points)?;
469
470        if record_size >= size_with_m {
471            bbox_read_m_range_from(&mut bbox, source)?;
472            read_ms_into(source, &mut points)?;
473        }
474
475        if diff > 0 {
476            source.seek(SeekFrom::Current(i64::from(diff)))?;
477        }
478
479        Ok(Self { bbox, points })
480    }
481}
482
483impl WritableShape for MultipointZ {
484    fn size_in_bytes(&self) -> usize {
485        let mut size = 0usize;
486        size += 4 * size_of::<f64>();
487        size += size_of::<i32>();
488        size += 4 * size_of::<f64>() * self.points.len();
489        size += 2 * size_of::<f64>();
490        size += 2 * size_of::<f64>();
491        size
492    }
493
494    fn write_to<T: Write>(&self, mut dest: &mut T) -> Result<(), Error> {
495        bbox_write_xy_to(&self.bbox, dest)?;
496        dest.write_i32::<LittleEndian>(self.points.len() as i32)?;
497
498        write_points(&mut dest, &self.points)?;
499
500        bbox_write_z_range_to(&self.bbox, dest)?;
501        write_zs(&mut dest, &self.points)?;
502
503        bbox_write_m_range_to(&self.bbox, dest)?;
504        write_ms(&mut dest, &self.points)?;
505
506        Ok(())
507    }
508}
509
510impl EsriShape for MultipointZ {
511    fn x_range(&self) -> [f64; 2] {
512        self.bbox.x_range()
513    }
514
515    fn y_range(&self) -> [f64; 2] {
516        self.bbox.y_range()
517    }
518
519    fn z_range(&self) -> [f64; 2] {
520        self.bbox.z_range()
521    }
522
523    fn m_range(&self) -> [f64; 2] {
524        self.bbox.m_range()
525    }
526}
527
528#[cfg(test)]
529#[cfg(feature = "geo-types")]
530mod test_geo_types_conversions {
531    use super::*;
532    use crate::{geo_types, NO_DATA};
533    use geo_types::Coord;
534
535    #[test]
536    fn test_multipoint_to_geo_types_multipoint() {
537        let shapefile_points = vec![Point::new(1.0, 1.0), Point::new(2.0, 2.0)];
538        let geo_types_coords = shapefile_points
539            .iter()
540            .copied()
541            .map(Coord::<f64>::from)
542            .collect::<Vec<Coord<f64>>>();
543
544        let expected_shapefile_multipoint = Multipoint::new(shapefile_points);
545        let expected_geo_types_multipoint = geo_types::MultiPoint::from(geo_types_coords);
546
547        let geo_types_multipoint: geo_types::MultiPoint<f64> =
548            expected_shapefile_multipoint.clone().into();
549        let shapefile_multipoint: Multipoint = expected_geo_types_multipoint.clone().into();
550
551        assert_eq!(geo_types_multipoint, expected_geo_types_multipoint);
552        assert_eq!(shapefile_multipoint, expected_shapefile_multipoint);
553    }
554
555    #[test]
556    fn test_multipoint_m_to_geo_types_multipoint() {
557        let points = vec![
558            PointM::new(120.0, 56.0, 42.2),
559            PointM::new(6.0, 18.7, 462.54),
560        ];
561        let shapefile_multipoint = MultipointM::new(points);
562        let geo_types_multipoint = geo_types::MultiPoint::from(shapefile_multipoint);
563
564        let mut iter = geo_types_multipoint.into_iter();
565        let p1 = iter.next().unwrap();
566        let p2 = iter.next().unwrap();
567        assert_eq!(p1.x(), 120.0);
568        assert_eq!(p1.y(), 56.0);
569
570        assert_eq!(p2.x(), 6.0);
571        assert_eq!(p2.y(), 18.7);
572
573        let geo_types_multipoint: geo_types::MultiPoint<_> = vec![p1, p2].into();
574        let shapefile_multipoint = MultipointM::from(geo_types_multipoint);
575
576        assert_eq!(shapefile_multipoint.points[0].x, 120.0);
577        assert_eq!(shapefile_multipoint.points[0].y, 56.0);
578        assert_eq!(shapefile_multipoint.points[0].m, NO_DATA);
579
580        assert_eq!(shapefile_multipoint.points[1].x, 6.0);
581        assert_eq!(shapefile_multipoint.points[1].y, 18.7);
582        assert_eq!(shapefile_multipoint.points[0].m, NO_DATA);
583    }
584
585    #[test]
586    fn test_multipoint_z_to_geo_types_multipoint() {
587        let points = vec![
588            PointZ::new(1.0, 1.0, 17.0, 18.0),
589            PointZ::new(2.0, 2.0, 15.0, 16.0),
590        ];
591        let shapefile_multipoint = MultipointZ::new(points);
592        let geo_types_multipoint = geo_types::MultiPoint::from(shapefile_multipoint);
593
594        let mut iter = geo_types_multipoint.into_iter();
595        let p1 = iter.next().unwrap();
596        let p2 = iter.next().unwrap();
597        assert_eq!(p1.x(), 1.0);
598        assert_eq!(p1.y(), 1.0);
599
600        assert_eq!(p2.x(), 2.0);
601        assert_eq!(p2.y(), 2.0);
602
603        let geo_types_multipoint: geo_types::MultiPoint<_> = vec![p1, p2].into();
604        let shapefile_multipoint = MultipointZ::from(geo_types_multipoint);
605
606        assert_eq!(shapefile_multipoint.points[0].x, 1.0);
607        assert_eq!(shapefile_multipoint.points[0].y, 1.0);
608        assert_eq!(shapefile_multipoint.points[0].z, 0.0);
609        assert_eq!(shapefile_multipoint.points[0].m, NO_DATA);
610
611        assert_eq!(shapefile_multipoint.points[1].x, 2.0);
612        assert_eq!(shapefile_multipoint.points[1].y, 2.0);
613        assert_eq!(shapefile_multipoint.points[0].z, 0.0);
614        assert_eq!(shapefile_multipoint.points[0].m, NO_DATA);
615    }
616}
617
618#[cfg(test)]
619mod tests {
620    use super::{MultipointZ, PointZ};
621
622    #[test]
623    fn test_multipoint_index() {
624        let points = vec![
625            PointZ::new(1.0, 1.0, 17.0, 18.0),
626            PointZ::new(2.0, 2.0, 15.0, 16.0),
627        ];
628        let multipoint = MultipointZ::new(points.clone());
629
630        assert_eq!(multipoint[0], points[0]);
631        assert_eq!(multipoint[1], points[1]);
632
633        assert_eq!(multipoint[..1], points[..1]);
634    }
635}