shapefile/record/
multipatch.rs

1//! Module for the Multipatch shape
2use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
3
4use std::fmt;
5use std::io::{Read, 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>(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        if (record_size != record_size_with_m) & (record_size != record_size_without_m) {
245            Err(Error::InvalidShapeRecordSize)
246        } else {
247            let mut patch_types = vec![PatchType::Ring; reader.num_parts as usize];
248            let mut patches = Vec::<Patch>::with_capacity(reader.num_parts as usize);
249            for i in 0..reader.num_parts {
250                patch_types[i as usize] = PatchType::read_from(reader.source)?;
251            }
252            let (bbox, patches_points) = reader
253                .read_xy()
254                .and_then(|rdr| rdr.read_zs())
255                .and_then(|rdr| rdr.read_ms_if(record_size == record_size_with_m))
256                .map_err(Error::IoError)
257                .map(|rdr| (rdr.bbox, rdr.parts))?;
258
259            for (patch_type, points) in patch_types.iter().zip(patches_points) {
260                let patch = match patch_type {
261                    PatchType::TriangleStrip => Patch::TriangleStrip(points),
262                    PatchType::TriangleFan => Patch::TriangleFan(points),
263                    PatchType::OuterRing => Patch::OuterRing(points),
264                    PatchType::InnerRing => Patch::InnerRing(points),
265                    PatchType::FirstRing => Patch::FirstRing(points),
266                    PatchType::Ring => Patch::Ring(points),
267                };
268                patches.push(patch);
269            }
270            Ok(Self { bbox, patches })
271        }
272    }
273}
274
275impl WritableShape for Multipatch {
276    fn size_in_bytes(&self) -> usize {
277        let mut size = 0usize;
278        size += 4 * size_of::<f64>();
279        size += size_of::<i32>();
280        size += size_of::<i32>();
281        size += size_of::<i32>() * self.patches.len();
282        size += size_of::<i32>() * self.patches.len();
283        size += 4 * size_of::<f64>() * self.total_point_count();
284        size += 2 * size_of::<f64>();
285        size += 2 * size_of::<f64>();
286        size
287    }
288
289    fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), Error> {
290        let parts_iter = self.patches.iter().map(|patch| patch.points());
291        let writer = MultiPartShapeWriter::new(&self.bbox, parts_iter, dest);
292        writer
293            .write_bbox_xy()
294            .and_then(|wrt| wrt.write_num_parts())
295            .and_then(|wrt| wrt.write_num_points())
296            .and_then(|wrt| wrt.write_parts_array())
297            .and_then(|wrt| {
298                for patch in self.patches.iter() {
299                    match patch {
300                        Patch::TriangleStrip(_) => wrt.dst.write_i32::<LittleEndian>(0)?,
301                        Patch::TriangleFan(_) => wrt.dst.write_i32::<LittleEndian>(1)?,
302                        Patch::OuterRing(_) => wrt.dst.write_i32::<LittleEndian>(2)?,
303                        Patch::InnerRing(_) => wrt.dst.write_i32::<LittleEndian>(3)?,
304                        Patch::FirstRing(_) => wrt.dst.write_i32::<LittleEndian>(4)?,
305                        Patch::Ring(_) => wrt.dst.write_i32::<LittleEndian>(5)?,
306                    }
307                }
308                Ok(wrt)
309            })
310            .and_then(|wrt| wrt.write_xy())
311            .and_then(|wrt| wrt.write_bbox_z_range())
312            .and_then(|wrt| wrt.write_zs())
313            .and_then(|wrt| wrt.write_bbox_m_range())
314            .and_then(|wrt| wrt.write_ms())
315            .map_err(Error::IoError)
316            .map(|_wrt| {})
317    }
318}
319
320impl EsriShape for Multipatch {
321    fn x_range(&self) -> [f64; 2] {
322        self.bbox.x_range()
323    }
324
325    fn y_range(&self) -> [f64; 2] {
326        self.bbox.y_range()
327    }
328
329    fn z_range(&self) -> [f64; 2] {
330        self.bbox.z_range()
331    }
332
333    fn m_range(&self) -> [f64; 2] {
334        self.bbox.m_range()
335    }
336}
337/// Converts a Multipatch to Multipolygon
338///
339/// For simplicity,reasons, Triangle Fan & Triangle Strip are considered
340/// to be valid polygons
341/// `
342/// When the individual types of rings in a collection of rings representing a polygonal patch with holes
343/// are unknown, the sequence must start with First Ring,
344/// followed by a number of Rings. A sequence of Rings not preceded by an First Ring
345/// is treated as a sequence of Outer Rings without holes.
346/// `
347#[cfg(feature = "geo-types")]
348impl TryFrom<Multipatch> for geo_types::MultiPolygon<f64> {
349    type Error = &'static str;
350
351    fn try_from(mp: Multipatch) -> Result<Self, Self::Error> {
352        use geo_types::{Coord, LineString};
353
354        let mut polygons = Vec::<geo_types::Polygon<f64>>::new();
355        let mut last_poly = None;
356        for patch in mp.patches {
357            match patch {
358                Patch::TriangleStrip(_) => {
359                    return Err("Cannot convert Multipatch::TriangleStrip to Multipolygon")
360                }
361                Patch::TriangleFan(_) => {
362                    return Err("Cannot convert Multipatch::TriangleFan to Multipolygon")
363                }
364                Patch::OuterRing(points) | Patch::FirstRing(points) => {
365                    let exterior = points
366                        .into_iter()
367                        .map(Coord::<f64>::from)
368                        .collect::<Vec<Coord<f64>>>();
369
370                    if let Some(poly) = last_poly.take() {
371                        polygons.push(poly);
372                    }
373                    last_poly = Some(geo_types::Polygon::new(LineString::from(exterior), vec![]))
374                }
375                Patch::InnerRing(points) | Patch::Ring(points) => {
376                    let interior = points
377                        .into_iter()
378                        .map(Coord::<f64>::from)
379                        .collect::<Vec<Coord<f64>>>();
380
381                    if let Some(poly) = last_poly.as_mut() {
382                        poly.interiors_push(interior);
383                    } else {
384                        // This is the strange (?) case: inner ring without a previous outer ring
385                        polygons.push(geo_types::Polygon::<f64>::new(
386                            LineString::<f64>::from(Vec::<Coord<f64>>::new()),
387                            vec![LineString::from(interior)],
388                        ));
389                    }
390                }
391            }
392        }
393
394        if let Some(poly) = last_poly {
395            polygons.push(poly);
396        }
397        Ok(polygons.into())
398    }
399}