shapefile_gbk/record/
mod.rs

1//! Shape records
2use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
3use std::fmt;
4use std::io::{Read, Write};
5
6pub mod bbox;
7pub(crate) mod io;
8pub mod macros;
9pub mod multipatch;
10pub mod multipoint;
11pub mod point;
12pub mod polygon;
13pub mod polyline;
14pub mod traits;
15
16use super::{Error, ShapeType};
17pub use bbox::{BBoxZ, GenericBBox};
18pub use multipatch::{Multipatch, Patch};
19pub use multipoint::{Multipoint, MultipointM, MultipointZ};
20pub use point::{Point, PointM, PointZ};
21pub use polygon::{Polygon, PolygonM, PolygonRing, PolygonZ};
22pub use polyline::{Polyline, PolylineM, PolylineZ};
23use traits::HasXY;
24
25#[cfg(feature = "geo-types")]
26use geo_types;
27
28/// Value inferior to this are considered as NO_DATA
29pub const NO_DATA: f64 = -10e38;
30
31fn is_no_data(val: f64) -> bool {
32    val <= NO_DATA
33}
34
35/// Traits to be able to retrieve the ShapeType corresponding to the type
36pub trait HasShapeType {
37    /// Returns the ShapeType
38    fn shapetype() -> ShapeType;
39}
40
41/// Simple Trait to store the type of the shape
42pub trait ConcreteShape: Sized + HasShapeType {}
43
44pub trait ConcreteReadableShape: ConcreteShape {
45    /// Function that actually reads the `ActualShape` from the source
46    /// and returns it
47    fn read_shape_content<T: Read>(source: &mut T, record_size: i32) -> Result<Self, Error>;
48}
49
50/// Trait implemented by all the Shapes that can be read
51pub trait ReadableShape: Sized {
52    fn read_from<T: Read>(source: &mut T, record_size: i32) -> Result<Self, Error>;
53}
54
55impl<S: ConcreteReadableShape> ReadableShape for S {
56    fn read_from<T: Read>(mut source: &mut T, mut record_size: i32) -> Result<S, Error> {
57        let shapetype = ShapeType::read_from(&mut source)?;
58        record_size -= std::mem::size_of::<i32>() as i32;
59        if shapetype == Self::shapetype() {
60            S::read_shape_content(&mut source, record_size)
61        } else {
62            Err(Error::MismatchShapeType {
63                requested: Self::shapetype(),
64                actual: shapetype,
65            })
66        }
67    }
68}
69
70/// Trait implemented by all Shapes that can be written
71pub trait WritableShape {
72    /// Returns the size in bytes that the Shapes will take once written.
73    /// Does _not_ include the shapetype
74    fn size_in_bytes(&self) -> usize;
75
76    /// Writes the shape to the dest
77    fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), Error>;
78}
79
80pub trait EsriShape: HasShapeType + WritableShape {
81    fn x_range(&self) -> [f64; 2];
82    fn y_range(&self) -> [f64; 2];
83    /// Should return the Z range of this shape
84    fn z_range(&self) -> [f64; 2] {
85        [0.0, 0.0]
86    }
87    /// Should return the M range of this shape
88    fn m_range(&self) -> [f64; 2] {
89        [0.0, 0.0]
90    }
91}
92
93pub(crate) fn is_part_closed<PointType: PartialEq>(points: &[PointType]) -> bool {
94    if let (Some(first), Some(last)) = (points.first(), points.last()) {
95        first == last
96    } else {
97        false
98    }
99}
100
101pub(crate) fn close_points_if_not_already<PointType: PartialEq + Copy>(
102    points: &mut Vec<PointType>,
103) {
104    if !is_part_closed(points) {
105        if let Some(point) = points.first().copied() {
106            points.push(point)
107        }
108    }
109}
110
111#[derive(Eq, PartialEq, Debug)]
112pub(crate) enum RingType {
113    OuterRing,
114    InnerRing,
115}
116
117/// Given the points, check if they represent an outer ring of a polygon
118///
119/// As per ESRI's Shapefile 1998 whitepaper:
120/// `
121/// The order of vertices or orientation for a ring indicates which side of the ring
122/// is the interior of the polygon.
123/// The neighborhood to the right of an observer walking along
124/// the ring in vertex order is the neighborhood inside the polygon.
125/// Vertices of rings defining holes in polygons are in a counterclockwise direction.
126/// Vertices for a single, ringed polygon are, therefore, always in clockwise order.
127/// `
128///
129/// Inner Rings defines holes -> points are in counterclockwise order
130/// Outer Rings's points are un clockwise order
131///
132/// https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order/1180256#1180256
133pub(crate) fn ring_type_from_points_ordering<PointType: HasXY>(points: &[PointType]) -> RingType {
134    let area = points
135        .windows(2)
136        .map(|pts| (pts[1].x() - pts[0].x()) * (pts[1].y() + pts[0].y()))
137        .sum::<f64>()
138        / 2.0f64;
139
140    if area < 0.0 {
141        RingType::InnerRing
142    } else {
143        RingType::OuterRing
144    }
145}
146
147/// enum of Shapes that can be read or written to a shapefile
148///
149/// # geo-types
150///
151/// `shapefile::Shape` and `geo_types::Geometry<f64>` can be converted from one to another,
152/// however this conversion is not infallible so it is done using `TryFrom`
153///
154/// ```
155/// # #[cfg(feature = "geo-types")]
156/// # fn main() -> Result<(), shapefile::Error>{
157/// use std::convert::TryFrom;
158/// use shapefile::Shape;
159/// let mut shapes = shapefile::read_shapes("tests/data/line.shp")?;
160/// let last_shape = shapes.pop().unwrap();
161/// let geometry = geo_types::Geometry::<f64>::try_from(last_shape);
162///
163/// assert_eq!(geometry.is_ok(), true);
164/// assert_eq!(geo_types::Geometry::<f64>::try_from(Shape::NullShape).is_err(), true);
165/// # Ok(())
166/// # }
167/// # #[cfg(not(feature = "geo-types"))]
168/// # fn main() {}
169/// ```
170///
171pub enum Shape {
172    NullShape,
173    Point(Point),
174    PointM(PointM),
175    PointZ(PointZ),
176    Polyline(Polyline),
177    PolylineM(PolylineM),
178    PolylineZ(PolylineZ),
179    Polygon(Polygon),
180    PolygonM(PolygonM),
181    PolygonZ(PolygonZ),
182    Multipoint(Multipoint),
183    MultipointM(MultipointM),
184    MultipointZ(MultipointZ),
185    Multipatch(Multipatch),
186}
187
188impl HasShapeType for Shape {
189    fn shapetype() -> ShapeType {
190        ShapeType::Point
191    }
192}
193
194impl ReadableShape for Shape {
195    fn read_from<T: Read>(mut source: &mut T, mut record_size: i32) -> Result<Self, Error> {
196        let shapetype = ShapeType::read_from(&mut source)?;
197        record_size -= std::mem::size_of::<i32>() as i32;
198        let shape = match shapetype {
199            ShapeType::Polyline => {
200                Shape::Polyline(Polyline::read_shape_content(&mut source, record_size)?)
201            }
202            ShapeType::PolylineM => {
203                Shape::PolylineM(PolylineM::read_shape_content(&mut source, record_size)?)
204            }
205            ShapeType::PolylineZ => {
206                Shape::PolylineZ(PolylineZ::read_shape_content(&mut source, record_size)?)
207            }
208            ShapeType::Point => Shape::Point(Point::read_shape_content(&mut source, record_size)?),
209            ShapeType::PointM => {
210                Shape::PointM(PointM::read_shape_content(&mut source, record_size)?)
211            }
212            ShapeType::PointZ => {
213                Shape::PointZ(PointZ::read_shape_content(&mut source, record_size)?)
214            }
215            ShapeType::Polygon => {
216                Shape::Polygon(Polygon::read_shape_content(&mut source, record_size)?)
217            }
218            ShapeType::PolygonM => {
219                Shape::PolygonM(PolygonM::read_shape_content(&mut source, record_size)?)
220            }
221            ShapeType::PolygonZ => {
222                Shape::PolygonZ(PolygonZ::read_shape_content(&mut source, record_size)?)
223            }
224            ShapeType::Multipoint => {
225                Shape::Multipoint(Multipoint::read_shape_content(&mut source, record_size)?)
226            }
227            ShapeType::MultipointM => {
228                Shape::MultipointM(MultipointM::read_shape_content(&mut source, record_size)?)
229            }
230            ShapeType::MultipointZ => {
231                Shape::MultipointZ(MultipointZ::read_shape_content(&mut source, record_size)?)
232            }
233            ShapeType::Multipatch => {
234                Shape::Multipatch(Multipatch::read_shape_content(&mut source, record_size)?)
235            }
236            ShapeType::NullShape => Shape::NullShape,
237        };
238        Ok(shape)
239    }
240}
241
242impl Shape {
243    /// Returns the shapetype
244    pub fn shapetype(&self) -> ShapeType {
245        match self {
246            Shape::Polyline(_) => ShapeType::Polyline,
247            Shape::PolylineM(_) => ShapeType::PolylineM,
248            Shape::PolylineZ(_) => ShapeType::PolylineZ,
249            Shape::Point(_) => ShapeType::Point,
250            Shape::PointM(_) => ShapeType::PointM,
251            Shape::PointZ(_) => ShapeType::PointZ,
252            Shape::Polygon(_) => ShapeType::Polygon,
253            Shape::PolygonM(_) => ShapeType::PolygonM,
254            Shape::PolygonZ(_) => ShapeType::PolygonZ,
255            Shape::Multipoint(_) => ShapeType::Multipoint,
256            Shape::MultipointM(_) => ShapeType::Multipoint,
257            Shape::MultipointZ(_) => ShapeType::Multipoint,
258            Shape::Multipatch(_) => ShapeType::Multipatch,
259            Shape::NullShape => ShapeType::NullShape,
260        }
261    }
262}
263
264impl fmt::Display for Shape {
265    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266        write!(f, "Shape::")?;
267        match self {
268            Shape::Polyline(shp) => write!(f, "{}", shp),
269            Shape::PolylineM(shp) => write!(f, "{}", shp),
270            Shape::PolylineZ(shp) => write!(f, "{}", shp),
271            Shape::Point(shp) => write!(f, "{}", shp),
272            Shape::PointM(shp) => write!(f, "{}", shp),
273            Shape::PointZ(shp) => write!(f, "{}", shp),
274            Shape::Polygon(shp) => write!(f, "{}", shp),
275            Shape::PolygonM(shp) => write!(f, "{}", shp),
276            Shape::PolygonZ(shp) => write!(f, "{}", shp),
277            Shape::Multipoint(shp) => write!(f, "{}", shp),
278            Shape::MultipointM(shp) => write!(f, "{}", shp),
279            Shape::MultipointZ(shp) => write!(f, "{}", shp),
280            Shape::Multipatch(shp) => write!(f, "{}", shp),
281            Shape::NullShape => write!(f, "NullShape"),
282        }
283    }
284}
285
286/// Header of a shape record, present before any shape record
287#[derive(Debug, Copy, Clone)]
288pub(crate) struct RecordHeader {
289    pub record_number: i32,
290    pub record_size: i32,
291}
292
293impl RecordHeader {
294    pub(crate) const SIZE: usize = 2 * std::mem::size_of::<i32>();
295
296    pub fn read_from<T: Read>(source: &mut T) -> Result<RecordHeader, Error> {
297        let record_number = source.read_i32::<BigEndian>()?;
298        let record_size = source.read_i32::<BigEndian>()?;
299        Ok(RecordHeader {
300            record_number,
301            record_size,
302        })
303    }
304
305    pub fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), std::io::Error> {
306        dest.write_i32::<BigEndian>(self.record_number)?;
307        dest.write_i32::<BigEndian>(self.record_size)?;
308        Ok(())
309    }
310}
311
312/// Function that can converts a `Vec<Shape>` to a vector of any real struct
313/// (ie [Polyline](polyline/type.Polyline.html), [Multipatch](multipatch/struct.Multipatch.html), etc)
314/// if all the `Shapes` in the `Vec` are of the correct corresponding variant.
315///
316/// # Examples
317///
318/// ```
319/// use shapefile::{Polyline, Multipoint, Point, Shape};
320/// use shapefile::convert_shapes_to_vec_of;
321///
322/// // Build a Vec<Shape> with only polylines in it
323/// let points = vec![Point::default(), Point::default()];
324/// let shapes = vec![
325///     Shape::from(Polyline::new(points.clone())),
326///     Shape::from(Polyline::new(points)),
327/// ];
328///
329/// // try a conversion to the wrong type
330/// assert_eq!(convert_shapes_to_vec_of::<Multipoint>(shapes).is_ok(), false);
331/// ```
332///
333/// ```
334/// # fn main() -> Result<(), shapefile::Error> {
335/// use shapefile::{convert_shapes_to_vec_of, MultipointZ};
336/// let shapes = shapefile::read_shapes("tests/data/multipointz.shp")?;
337/// let multipoints = convert_shapes_to_vec_of::<MultipointZ>(shapes);
338/// assert_eq!(multipoints.is_ok(), true);
339/// # Ok(())
340/// # }
341/// ```
342pub fn convert_shapes_to_vec_of<S>(shapes: Vec<Shape>) -> Result<Vec<S>, Error>
343where
344    S: TryFrom<Shape>,
345    Error: From<<S as TryFrom<Shape>>::Error>,
346{
347    let mut concrete_shapes = Vec::<S>::with_capacity(shapes.len());
348    for shape in shapes {
349        let concrete = S::try_from(shape)?;
350        concrete_shapes.push(concrete);
351    }
352    Ok(concrete_shapes)
353}
354
355/// Macro to have less boiler plate code to write just to implement
356/// the ConcreteShape Trait
357macro_rules! impl_concrete_shape_for {
358    ($ConcreteType:ident) => {
359        impl ConcreteShape for $ConcreteType {}
360    };
361}
362
363/// macro that implements the From<T> Trait for the Shape enum
364/// where T is any of the ConcreteShape
365macro_rules! impl_from_concrete_shape {
366    ($ConcreteShape:ident=>Shape::$ShapeEnumVariant:ident) => {
367        impl From<$ConcreteShape> for Shape {
368            fn from(concrete: $ConcreteShape) -> Self {
369                Shape::$ShapeEnumVariant(concrete)
370            }
371        }
372    };
373}
374
375/// macro to implement the TryFrom<Shape> trait
376macro_rules! impl_try_from_shape {
377    (Shape::$ShapeEnumVariant:ident=>$ConcreteShape:ident) => {
378        impl TryFrom<Shape> for $ConcreteShape {
379            type Error = Error;
380            fn try_from(shape: Shape) -> Result<Self, Self::Error> {
381                match shape {
382                    Shape::$ShapeEnumVariant(shp) => Ok(shp),
383                    _ => Err(Error::MismatchShapeType {
384                        requested: Self::shapetype(),
385                        actual: shape.shapetype(),
386                    }),
387                }
388            }
389        }
390    };
391}
392
393macro_rules! impl_to_way_conversion {
394    (Shape::$ShapeEnumVariant:ident<=>$ConcreteShape:ident) => {
395        impl_try_from_shape!(Shape::$ShapeEnumVariant => $ConcreteShape);
396        impl_from_concrete_shape!($ConcreteShape => Shape::$ShapeEnumVariant);
397    };
398}
399
400impl_concrete_shape_for!(Point);
401impl_concrete_shape_for!(PointM);
402impl_concrete_shape_for!(PointZ);
403impl_concrete_shape_for!(Polyline);
404impl_concrete_shape_for!(PolylineM);
405impl_concrete_shape_for!(PolylineZ);
406impl_concrete_shape_for!(Polygon);
407impl_concrete_shape_for!(PolygonM);
408impl_concrete_shape_for!(PolygonZ);
409impl_concrete_shape_for!(Multipoint);
410impl_concrete_shape_for!(MultipointM);
411impl_concrete_shape_for!(MultipointZ);
412impl_concrete_shape_for!(Multipatch);
413
414impl_to_way_conversion!(Shape::Point <=> Point);
415impl_to_way_conversion!(Shape::PointM <=> PointM);
416impl_to_way_conversion!(Shape::PointZ <=> PointZ);
417impl_to_way_conversion!(Shape::Polyline <=> Polyline);
418impl_to_way_conversion!(Shape::PolylineM <=> PolylineM);
419impl_to_way_conversion!(Shape::PolylineZ <=> PolylineZ);
420impl_to_way_conversion!(Shape::Polygon <=> Polygon);
421impl_to_way_conversion!(Shape::PolygonM <=> PolygonM);
422impl_to_way_conversion!(Shape::PolygonZ <=> PolygonZ);
423impl_to_way_conversion!(Shape::Multipoint <=> Multipoint);
424impl_to_way_conversion!(Shape::MultipointM <=> MultipointM);
425impl_to_way_conversion!(Shape::MultipointZ <=> MultipointZ);
426impl_to_way_conversion!(Shape::Multipatch <=> Multipatch);
427
428/// Tries to convert a shapefile's Shape into a geo_types::Geometry
429///
430/// This conversion can fail because the conversion of shapefile's polygons & multipatch into
431/// their geo_types counter parts can fail. And the NullShape has no equivalent Geometry;
432#[cfg(feature = "geo-types")]
433impl TryFrom<Shape> for geo_types::Geometry<f64> {
434    type Error = &'static str;
435
436    fn try_from(shape: Shape) -> Result<Self, Self::Error> {
437        use geo_types::Geometry;
438        match shape {
439            Shape::NullShape => Err("Cannot convert NullShape into any geo_types Geometry"),
440            Shape::Point(point) => Ok(Geometry::Point(geo_types::Point::from(point))),
441            Shape::PointM(point) => Ok(Geometry::Point(geo_types::Point::from(point))),
442            Shape::PointZ(point) => Ok(Geometry::Point(geo_types::Point::from(point))),
443            Shape::Polyline(polyline) => Ok(Geometry::MultiLineString(
444                geo_types::MultiLineString::<f64>::from(polyline),
445            )),
446            Shape::PolylineM(polyline) => Ok(Geometry::MultiLineString(
447                geo_types::MultiLineString::<f64>::from(polyline),
448            )),
449            Shape::PolylineZ(polyline) => Ok(Geometry::MultiLineString(
450                geo_types::MultiLineString::<f64>::from(polyline),
451            )),
452            Shape::Polygon(polygon) => Ok(Geometry::MultiPolygon(
453                geo_types::MultiPolygon::<f64>::from(polygon),
454            )),
455            Shape::PolygonM(polygon) => Ok(Geometry::MultiPolygon(
456                geo_types::MultiPolygon::<f64>::from(polygon),
457            )),
458            Shape::PolygonZ(polygon) => Ok(Geometry::MultiPolygon(
459                geo_types::MultiPolygon::<f64>::from(polygon),
460            )),
461            Shape::Multipoint(multipoint) => Ok(Geometry::MultiPoint(
462                geo_types::MultiPoint::<f64>::from(multipoint),
463            )),
464            Shape::MultipointM(multipoint) => Ok(Geometry::MultiPoint(
465                geo_types::MultiPoint::<f64>::from(multipoint),
466            )),
467            Shape::MultipointZ(multipoint) => Ok(Geometry::MultiPoint(
468                geo_types::MultiPoint::<f64>::from(multipoint),
469            )),
470            Shape::Multipatch(multipatch) => {
471                geo_types::MultiPolygon::<f64>::try_from(multipatch).map(Geometry::MultiPolygon)
472            }
473        }
474    }
475}
476
477/// Converts a Geometry to a Shape
478///
479/// Since all Geometries are in 2D, the resulting shape will be 2D
480/// (Polygon, Polyline, etc and not PolylineM, PolylineZ, etc)
481///
482/// Fails if the geometry is a GeometryCollection, Rect, or Triangle
483#[cfg(feature = "geo-types")]
484impl TryFrom<geo_types::Geometry<f64>> for Shape {
485    type Error = &'static str;
486    fn try_from(geometry: geo_types::Geometry<f64>) -> Result<Self, Self::Error> {
487        match geometry {
488            geo_types::Geometry::Point(point) => Ok(Shape::Point(point.into())),
489            geo_types::Geometry::Line(line) => Ok(Shape::Polyline(line.into())),
490            geo_types::Geometry::LineString(polyline) => Ok(Shape::Polyline(polyline.into())),
491            geo_types::Geometry::Polygon(polygon) => Ok(Shape::Polygon(polygon.into())),
492            geo_types::Geometry::MultiPoint(multipoint) => Ok(Shape::Multipoint(multipoint.into())),
493            geo_types::Geometry::MultiLineString(multi_linestring) => {
494                Ok(Shape::Polyline(multi_linestring.into()))
495            }
496            geo_types::Geometry::MultiPolygon(multi_polygon) => {
497                Ok(Shape::Polygon(multi_polygon.into()))
498            }
499            geo_types::Geometry::GeometryCollection(_) => {
500                Err("Cannot convert geo_types::GeometryCollection into a Shape")
501            }
502            #[allow(unreachable_patterns)] // Unreachable before geo-types 0.6.0
503            _ => {
504                // New geometries Rect(_) and Triangle(_) added in 0.6.0
505                Err("Cannot convert unrecognized Geometry type into a Shape")
506            }
507        }
508    }
509}
510
511#[cfg(test)]
512mod tests {
513    use super::*;
514
515    #[test]
516    fn convert_to_vec_of_poly_err() {
517        let points = vec![Point::default(), Point::default()];
518        let shapes = vec![
519            Shape::Point(Point::default()),
520            Shape::Polyline(Polyline::new(points)),
521        ];
522        assert!(convert_shapes_to_vec_of::<Polyline>(shapes).is_err());
523    }
524
525    #[test]
526    fn convert_to_vec_of_point_err() {
527        let points = vec![Point::default(), Point::default()];
528        let shapes = vec![
529            Shape::Point(Point::default()),
530            Shape::Polyline(Polyline::new(points)),
531        ];
532        assert!(convert_shapes_to_vec_of::<Point>(shapes).is_err());
533    }
534
535    #[test]
536    fn convert_to_vec_of_poly_ok() {
537        let points = vec![Point::default(), Point::default()];
538
539        let shapes = vec![
540            Shape::from(Polyline::new(points.clone())),
541            Shape::from(Polyline::new(points)),
542        ];
543
544        assert!(convert_shapes_to_vec_of::<Polyline>(shapes).is_ok());
545    }
546
547    #[test]
548    fn convert_to_vec_of_point_ok() {
549        let shapes = vec![
550            Shape::Point(Point::default()),
551            Shape::Point(Point::default()),
552        ];
553        assert!(convert_shapes_to_vec_of::<Point>(shapes).is_ok());
554    }
555
556    #[test]
557    fn test_vertices_order() {
558        let mut points = vec![
559            Point::new(0.0, 0.0),
560            Point::new(1.0, 0.0),
561            Point::new(1.0, 1.0),
562            Point::new(0.0, 1.0),
563        ];
564
565        assert_eq!(ring_type_from_points_ordering(&points), RingType::InnerRing);
566        points.reverse();
567        assert_eq!(ring_type_from_points_ordering(&points), RingType::OuterRing);
568    }
569}