Skip to main content

shapefile/record/
multipatch.rs

1//! Module for the Multipatch shape
2use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
3
4use std::fmt;
5use std::io::{Read, Seek, SeekFrom, Write};
6use std::mem::size_of;
7
8use super::io::*;
9use super::ConcreteReadableShape;
10use super::{close_points_if_not_already, GenericBBox};
11use super::{Error, ShapeType};
12use super::{EsriShape, HasShapeType, Point, PointZ, WritableShape};
13
14#[cfg(feature = "geo-types")]
15use geo_types;
16#[cfg(feature = "geo-types")]
17use std::convert::TryFrom;
18
19#[derive(Debug, Copy, Clone, PartialEq)]
20enum PatchType {
21    TriangleStrip,
22    TriangleFan,
23    OuterRing,
24    InnerRing,
25    FirstRing,
26    Ring,
27}
28
29impl PatchType {
30    pub fn read_from<T: Read>(source: &mut T) -> Result<PatchType, Error> {
31        let code = source.read_i32::<LittleEndian>()?;
32        Self::from(code).ok_or(Error::InvalidPatchType(code))
33    }
34
35    pub fn from(code: i32) -> Option<PatchType> {
36        match code {
37            0 => Some(PatchType::TriangleStrip),
38            1 => Some(PatchType::TriangleFan),
39            2 => Some(PatchType::OuterRing),
40            3 => Some(PatchType::InnerRing),
41            4 => Some(PatchType::FirstRing),
42            5 => Some(PatchType::Ring),
43            _ => None,
44        }
45    }
46}
47
48#[derive(Debug, Clone, PartialEq)]
49pub enum Patch {
50    /// A linked strip of triangles, where every vertex
51    /// (after the first two)completes a new triangle.
52    ///
53    /// A new triangle is always formed by connecting
54    /// the new vertex with its two immediate predecessors
55    TriangleStrip(Vec<PointZ>),
56    /// A linked fan of triangles,
57    /// where every vertex (after the first two) completes a new triangle.
58    ///
59    ///  A new triangle is always formed by connecting
60    /// the new vertex with its immediate predecessor
61    /// and the first vertex of the part.
62    TriangleFan(Vec<PointZ>),
63    /// The outer ring of a polygon.
64    OuterRing(Vec<PointZ>),
65    /// A hole of a polygon
66    InnerRing(Vec<PointZ>),
67    /// The first ring of a polygon of an unspecified type
68    FirstRing(Vec<PointZ>),
69    /// A ring of a polygon of an unspecified type
70    Ring(Vec<PointZ>),
71}
72
73impl Patch {
74    /// Returns the slice of points contained within the patch
75    #[inline]
76    pub fn points(&self) -> &[PointZ] {
77        match self {
78            Patch::TriangleStrip(points) => points,
79            Patch::TriangleFan(points) => points,
80            Patch::OuterRing(points) => points,
81            Patch::InnerRing(points) => points,
82            Patch::FirstRing(points) => points,
83            Patch::Ring(points) => points,
84        }
85    }
86}
87
88impl AsRef<[PointZ]> for Patch {
89    fn as_ref(&self) -> &[PointZ] {
90        self.points()
91    }
92}
93
94// TODO all the checks described at page 24/34
95/// Shapefile's Multipatch shape (p 24/34)
96///
97/// The following things are important with Multipatch shape:
98/// 1) Ring types must be closed
99///    **(the various constructors will close the rings if you did not close them yourself)**
100/// 2) InnerRings must follow their OuterRings (**this is not checked**)
101/// 3) Parts must not intersects or penetrate each others (**this is not checked**)
102/// 4) The points organization of [`TriangleStrip`] and [`TriangleFan`] is **not checked**
103///
104/// [`TriangleStrip`]: enum.Patch.html#variant.TriangleStrip
105/// [`TriangleFan`]: enum.Patch.html#variant.TriangleFan
106#[derive(Debug, PartialEq, Clone)]
107pub struct Multipatch {
108    bbox: GenericBBox<PointZ>,
109    patches: Vec<Patch>,
110}
111
112impl Multipatch {
113    /// Creates a Multipatch with one patch
114    ///
115    /// The constructor closes rings patch
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// use shapefile::{PointZ, Multipatch, NO_DATA, Patch};
121    /// let points = vec![
122    ///     PointZ::new(0.0, 0.0, 0.0, NO_DATA),
123    ///     PointZ::new(0.0, 1.0, 0.0, NO_DATA),
124    ///     PointZ::new(1.0, 1.0, 0.0, NO_DATA),
125    ///     PointZ::new(1.0, 0.0, 0.0, NO_DATA),
126    /// ];
127    /// let multip = Multipatch::new(Patch::OuterRing(points));
128    /// ```
129    pub fn new(patch: Patch) -> Self {
130        Self::with_parts(vec![patch])
131    }
132
133    /// Creates a Multipatch with multiple patches
134    ///
135    /// Closes any patch part that is a ring
136    ///
137    /// # Example
138    ///
139    /// ```
140    /// use shapefile::{PointZ, Multipatch, NO_DATA, Patch};
141    /// let multipatch = Multipatch::with_parts(vec![
142    ///     Patch::OuterRing(vec![
143    ///         PointZ::new(0.0, 0.0, 0.0, NO_DATA),
144    ///         PointZ::new(0.0, 4.0, 0.0, NO_DATA),
145    ///         PointZ::new(4.0, 4.0, 0.0, NO_DATA),
146    ///         PointZ::new(4.0, 0.0, 0.0, NO_DATA),
147    ///     ]),
148    ///     Patch::InnerRing(vec![
149    ///         PointZ::new(0.0, 0.0, 0.0, NO_DATA),
150    ///         PointZ::new(0.0, 2.0, 0.0, NO_DATA),
151    ///         PointZ::new(2.0, 2.0, 0.0, NO_DATA),
152    ///         PointZ::new(2.0, 0.0, 0.0, NO_DATA),
153    ///     ])
154    /// ]);
155    /// ```
156    pub fn with_parts(mut patches: Vec<Patch>) -> Self {
157        for patch in patches.iter_mut() {
158            match patch {
159                Patch::TriangleStrip(_) => {}
160                Patch::TriangleFan(_) => {}
161                Patch::OuterRing(points) => close_points_if_not_already(points),
162                Patch::InnerRing(points) => close_points_if_not_already(points),
163                Patch::FirstRing(points) => close_points_if_not_already(points),
164                Patch::Ring(points) => close_points_if_not_already(points),
165            }
166        }
167        let mut bbox = GenericBBox::<PointZ>::from_points(patches[0].points());
168        for patch in &patches[1..] {
169            bbox.grow_from_points(patch.points());
170        }
171
172        Self { bbox, patches }
173    }
174
175    /// Returns the bounding box of the points contained in this multipatch
176    #[inline]
177    pub fn bbox(&self) -> &GenericBBox<PointZ> {
178        &self.bbox
179    }
180
181    /// Returns a reference to the patches of the Multipatch Shape
182    #[inline]
183    pub fn patches(&self) -> &Vec<Patch> {
184        &self.patches
185    }
186
187    /// Returns a reference to the patch at given index
188    #[inline]
189    pub fn patch(&self, index: usize) -> Option<&Patch> {
190        self.patches.get(index)
191    }
192
193    /// Consumes the shape and returns the patches
194    #[inline]
195    pub fn into_inner(self) -> Vec<Patch> {
196        self.patches
197    }
198
199    #[inline]
200    pub fn total_point_count(&self) -> usize {
201        self.patches.iter().map(|patch| patch.points().len()).sum()
202    }
203
204    pub(crate) fn size_of_record(num_points: i32, num_parts: i32, is_m_used: bool) -> usize {
205        let mut size = 0usize;
206        size += 4 * size_of::<f64>(); // BBOX
207        size += size_of::<i32>(); // num parts
208        size += size_of::<i32>(); // num points
209        size += size_of::<i32>() * num_parts as usize; // parts
210        size += size_of::<i32>() * num_parts as usize; // parts type
211        size += size_of::<Point>() * num_points as usize;
212        size += 2 * size_of::<f64>(); // mandatory Z Range
213        size += size_of::<f64>() * num_points as usize; // mandatory Z
214
215        if is_m_used {
216            size += 2 * size_of::<f64>(); // Optional M range
217            size += size_of::<f64>() * num_points as usize; // Optional M
218        }
219        size
220    }
221}
222
223impl fmt::Display for Multipatch {
224    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
225        write!(f, "Multipatch({} patches)", self.patches.len())
226    }
227}
228
229impl HasShapeType for Multipatch {
230    fn shapetype() -> ShapeType {
231        ShapeType::Multipatch
232    }
233}
234
235impl ConcreteReadableShape for Multipatch {
236    fn read_shape_content<T: Read + Seek>(source: &mut T, record_size: i32) -> Result<Self, Error> {
237        let reader = MultiPartShapeReader::<PointZ, T>::new(source)?;
238
239        let record_size_with_m =
240            Self::size_of_record(reader.num_points, reader.num_parts, true) as i32;
241        let record_size_without_m =
242            Self::size_of_record(reader.num_points, reader.num_parts, false) as i32;
243
244        let size_read = if record_size >= record_size_with_m {
245            record_size_with_m
246        } else {
247            record_size_without_m
248        };
249
250        let diff = record_size - size_read;
251        if diff < 0 {
252            return Err(Error::InvalidShapeRecordSize);
253        }
254
255        let mut patch_types = vec![PatchType::Ring; reader.num_parts as usize];
256        let mut patches = Vec::<Patch>::with_capacity(reader.num_parts as usize);
257        for i in 0..reader.num_parts {
258            patch_types[i as usize] = PatchType::read_from(reader.source)?;
259        }
260        let (bbox, patches_points) = reader
261            .read_xy()
262            .and_then(|rdr| rdr.read_zs())
263            .and_then(|rdr| rdr.read_ms_if(record_size >= record_size_with_m))
264            .map_err(Error::IoError)
265            .map(|rdr| (rdr.bbox, rdr.parts))?;
266
267        for (patch_type, points) in patch_types.iter().zip(patches_points) {
268            let patch = match patch_type {
269                PatchType::TriangleStrip => Patch::TriangleStrip(points),
270                PatchType::TriangleFan => Patch::TriangleFan(points),
271                PatchType::OuterRing => Patch::OuterRing(points),
272                PatchType::InnerRing => Patch::InnerRing(points),
273                PatchType::FirstRing => Patch::FirstRing(points),
274                PatchType::Ring => Patch::Ring(points),
275            };
276            patches.push(patch);
277        }
278
279        if diff > 0 {
280            source.seek(SeekFrom::Current(i64::from(diff)))?;
281        }
282        Ok(Self { bbox, patches })
283    }
284}
285
286impl WritableShape for Multipatch {
287    fn size_in_bytes(&self) -> usize {
288        let mut size = 0usize;
289        size += 4 * size_of::<f64>();
290        size += size_of::<i32>();
291        size += size_of::<i32>();
292        size += size_of::<i32>() * self.patches.len();
293        size += size_of::<i32>() * self.patches.len();
294        size += 4 * size_of::<f64>() * self.total_point_count();
295        size += 2 * size_of::<f64>();
296        size += 2 * size_of::<f64>();
297        size
298    }
299
300    fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), Error> {
301        let parts_iter = self.patches.iter().map(|patch| patch.points());
302        let writer = MultiPartShapeWriter::new(&self.bbox, parts_iter, dest);
303        writer
304            .write_bbox_xy()
305            .and_then(|wrt| wrt.write_num_parts())
306            .and_then(|wrt| wrt.write_num_points())
307            .and_then(|wrt| wrt.write_parts_array())
308            .and_then(|wrt| {
309                for patch in self.patches.iter() {
310                    match patch {
311                        Patch::TriangleStrip(_) => wrt.dst.write_i32::<LittleEndian>(0)?,
312                        Patch::TriangleFan(_) => wrt.dst.write_i32::<LittleEndian>(1)?,
313                        Patch::OuterRing(_) => wrt.dst.write_i32::<LittleEndian>(2)?,
314                        Patch::InnerRing(_) => wrt.dst.write_i32::<LittleEndian>(3)?,
315                        Patch::FirstRing(_) => wrt.dst.write_i32::<LittleEndian>(4)?,
316                        Patch::Ring(_) => wrt.dst.write_i32::<LittleEndian>(5)?,
317                    }
318                }
319                Ok(wrt)
320            })
321            .and_then(|wrt| wrt.write_xy())
322            .and_then(|wrt| wrt.write_bbox_z_range())
323            .and_then(|wrt| wrt.write_zs())
324            .and_then(|wrt| wrt.write_bbox_m_range())
325            .and_then(|wrt| wrt.write_ms())
326            .map_err(Error::IoError)
327            .map(|_wrt| {})
328    }
329}
330
331impl EsriShape for Multipatch {
332    fn x_range(&self) -> [f64; 2] {
333        self.bbox.x_range()
334    }
335
336    fn y_range(&self) -> [f64; 2] {
337        self.bbox.y_range()
338    }
339
340    fn z_range(&self) -> [f64; 2] {
341        self.bbox.z_range()
342    }
343
344    fn m_range(&self) -> [f64; 2] {
345        self.bbox.m_range()
346    }
347}
348/// Converts a Multipatch to Multipolygon
349///
350/// For simplicity,reasons, Triangle Fan & Triangle Strip are not considered
351/// to be valid polygons
352/// `
353/// When the individual types of rings in a collection of rings representing a polygonal patch with holes
354/// are unknown, the sequence must start with First Ring,
355/// followed by a number of Rings. A sequence of Rings not preceded by an First Ring
356/// is treated as a sequence of Outer Rings without holes.
357/// `
358#[cfg(feature = "geo-types")]
359impl TryFrom<Multipatch> for geo_types::MultiPolygon<f64> {
360    type Error = Error;
361
362    fn try_from(mp: Multipatch) -> Result<Self, Self::Error> {
363        use geo_types::{Coord, LineString};
364
365        let mut polygons = Vec::<geo_types::Polygon<f64>>::new();
366        let mut last_poly = None;
367        for patch in mp.patches {
368            match patch {
369                Patch::TriangleStrip(_) => return Err(Error::UnsupportedConversion),
370                Patch::TriangleFan(_) => return Err(Error::UnsupportedConversion),
371                Patch::OuterRing(points) | Patch::FirstRing(points) => {
372                    let exterior = points
373                        .into_iter()
374                        .map(Coord::<f64>::from)
375                        .collect::<Vec<Coord<f64>>>();
376
377                    if let Some(poly) = last_poly.take() {
378                        polygons.push(poly);
379                    }
380                    last_poly = Some(geo_types::Polygon::new(LineString::from(exterior), vec![]))
381                }
382                Patch::InnerRing(points) | Patch::Ring(points) => {
383                    let interior = points
384                        .into_iter()
385                        .map(Coord::<f64>::from)
386                        .collect::<Vec<Coord<f64>>>();
387
388                    if let Some(poly) = last_poly.as_mut() {
389                        poly.interiors_push(interior);
390                    } else {
391                        return Err(Error::OrphanedInnerRing);
392                    }
393                }
394            }
395        }
396
397        if let Some(poly) = last_poly {
398            polygons.push(poly);
399        }
400        Ok(polygons.into())
401    }
402}