shapefile_gbk/
lib.rs

1//! Read & Write [Shapefile](http://downloads.esri.com/support/whitepapers/mo_/shapefile.pdf) in Rust
2//!
3//! A _shapefile_ is in reality a collection of 3 mandatory files:
4//!  -  .shp (feature geometry aka shapes)
5//!  -  .shx (index of feature geometry)
6//!  -  .dbf (attribute information, aka records)
7//!
8//! As different shapefiles can store different type of shapes
9//! (but one shapefile can only store the same type of shapes)
10//! This library provide two ways of reading the shapes:
11//!
12//! 1) Reading as [Shape](record/enum.Shape.html) and then do a `match` to handle the different shapes
13//! 2) Reading directly as concrete shapes (ie Polyline, PolylineZ, Point, etc) this of course only
14//! works if the file actually contains shapes that matches the requested type
15//!
16//! # Shapefiles shapes
17//!
18//! The [`Point`], [`PointM`] and [`PointZ`] are the base data types of shapefiles,
19//! the other shapes (`Polyline, Multipoint`, ...) are collections of these type of points
20//! with different semantics (multiple parts or no, closed parts or no, ...)
21//!
22//! With the exception of the [`Multipatch`] shape, each shape as a variant for each type
23//! of point. ([`Multipatch`] always uses [`PointZ`])
24//! Eg: For the polyline, there is [`Polyline`], [`PolylineM`], [`PolylineZ`]
25//!
26//! # Reading
27//!
28//! For more details see the [reader](reader/index.html) module
29//!
30//! # Writing
31//!
32//! To write a file see the [writer](writer/index.html) module
33//!
34//! # Features
35//!
36//! The `geo-types` feature can be enabled to have access to `From` and `TryFrom`
37//! implementations allowing to convert (or try to) back and forth between shapefile's type and
38//! the one in `geo_types`
39//!
40//! [`Point`]: record/point/struct.Point.html
41//! [`PointM`]: record/point/struct.PointM.html
42//! [`PointZ`]: record/point/struct.PointZ.html
43//! [`Polyline`]: record/polyline/type.Polyline.html
44//! [`PolylineM`]: record/polyline/type.PolylineM.html
45//! [`PolylineZ`]: record/polyline/type.PolylineZ.html
46//! [`Multipatch`]: record/multipatch/struct.Multipatch.html
47extern crate byteorder;
48pub extern crate dbase;
49
50pub mod header;
51pub mod reader;
52pub mod record;
53pub mod writer;
54
55#[cfg(feature = "geo-traits")]
56mod geo_traits_impl;
57
58use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
59use std::fmt;
60use std::io::{Read, Write};
61
62pub use reader::{read, read_as, read_shapes, read_shapes_as, Reader, ShapeReader};
63pub use record::Multipatch;
64pub use record::{convert_shapes_to_vec_of, HasShapeType, ReadableShape};
65pub use record::{Multipoint, MultipointM, MultipointZ};
66pub use record::{Patch, Shape, NO_DATA};
67pub use record::{Point, PointM, PointZ};
68pub use record::{Polygon, PolygonM, PolygonRing, PolygonZ};
69pub use record::{Polyline, PolylineM, PolylineZ};
70pub use writer::{ShapeWriter, Writer};
71
72extern crate core;
73#[cfg(feature = "geo-types")]
74extern crate geo_types;
75
76/// All Errors that can happen when using this library
77#[derive(Debug)]
78pub enum Error {
79    /// Wrapper around standard io::Error that might occur when reading/writing
80    IoError(std::io::Error),
81    /// The file read had an invalid File code (meaning it's not a Shapefile)
82    InvalidFileCode(i32),
83    /// The file read had an invalid [ShapeType](enum.ShapeType.html) code
84    /// (either in the file header or any record type)
85    InvalidShapeType(i32),
86    /// The Multipatch shape read from the file had an invalid [PatchType](enum.PatchType.html) code
87    InvalidPatchType(i32),
88    /// Error returned when trying to read the shape records as a certain shape type
89    /// but the actual shape type does not correspond to the one asked
90    MismatchShapeType {
91        /// The requested ShapeType
92        requested: ShapeType,
93        /// The actual type of the shape
94        actual: ShapeType,
95    },
96    InvalidShapeRecordSize,
97    DbaseError(dbase::Error),
98    MissingDbf,
99    MissingIndexFile,
100}
101
102impl From<std::io::Error> for Error {
103    fn from(error: std::io::Error) -> Error {
104        Error::IoError(error)
105    }
106}
107
108impl From<dbase::Error> for Error {
109    fn from(e: dbase::Error) -> Error {
110        Error::DbaseError(e)
111    }
112}
113
114impl fmt::Display for Error {
115    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116        match self {
117            Error::IoError(e) => write!(f, "{}", e),
118            Error::InvalidFileCode(code) => write!(
119                f,
120                "The file code ' {} ' is invalid, is this a Shapefile ?",
121                code
122            ),
123            Error::InvalidShapeType(code) => write!(
124                f,
125                "The code ' {} ' does not correspond to any of the ShapeType code defined by ESRI",
126                code
127            ),
128            Error::MismatchShapeType { requested, actual } => write!(
129                f,
130                "The requested type: '{}' does not correspond to the actual shape type: '{}'",
131                requested, actual
132            ),
133            e => write!(f, "{:?}", e),
134        }
135    }
136}
137
138impl std::error::Error for Error {}
139
140/// The enum for the ShapeType as defined in the
141/// specification
142#[derive(Debug, PartialEq, Copy, Clone)]
143pub enum ShapeType {
144    NullShape = 0,
145    Point = 1,
146    Polyline = 3,
147    Polygon = 5,
148    Multipoint = 8,
149
150    PointZ = 11,
151    PolylineZ = 13,
152    PolygonZ = 15,
153    MultipointZ = 18,
154
155    PointM = 21,
156    PolylineM = 23,
157    PolygonM = 25,
158    MultipointM = 28,
159
160    Multipatch = 31,
161}
162
163impl ShapeType {
164    pub(crate) fn read_from<T: Read>(source: &mut T) -> Result<ShapeType, Error> {
165        let code = source.read_i32::<LittleEndian>()?;
166        Self::from(code).ok_or_else(|| Error::InvalidShapeType(code))
167    }
168
169    pub(crate) fn write_to<T: Write>(self, dest: &mut T) -> Result<(), std::io::Error> {
170        dest.write_i32::<LittleEndian>(self as i32)?;
171        Ok(())
172    }
173
174    /// Returns the ShapeType corresponding to the input code
175    /// if the code is valid
176    /// ```
177    /// use shapefile::ShapeType;
178    ///
179    /// assert_eq!(ShapeType::from(25), Some(ShapeType::PolygonM));
180    /// assert_eq!(ShapeType::from(60), None);
181    /// ```
182    pub fn from(code: i32) -> Option<ShapeType> {
183        match code {
184            0 => Some(ShapeType::NullShape),
185            1 => Some(ShapeType::Point),
186            3 => Some(ShapeType::Polyline),
187            5 => Some(ShapeType::Polygon),
188            8 => Some(ShapeType::Multipoint),
189            11 => Some(ShapeType::PointZ),
190            13 => Some(ShapeType::PolylineZ),
191            15 => Some(ShapeType::PolygonZ),
192            18 => Some(ShapeType::MultipointZ),
193            21 => Some(ShapeType::PointM),
194            23 => Some(ShapeType::PolylineM),
195            25 => Some(ShapeType::PolygonM),
196            28 => Some(ShapeType::MultipointM),
197            31 => Some(ShapeType::Multipatch),
198            _ => None,
199        }
200    }
201
202    /// Returns whether the ShapeType has the third dimension Z
203    pub fn has_z(self) -> bool {
204        matches!(
205            self,
206            ShapeType::PointZ
207                | ShapeType::PolylineZ
208                | ShapeType::PolygonZ
209                | ShapeType::MultipointZ
210                | ShapeType::Multipatch
211        )
212    }
213
214    /// Returns whether the ShapeType has the optional measure dimension
215    pub fn has_m(self) -> bool {
216        matches!(
217            self,
218            ShapeType::PointZ
219                | ShapeType::PolylineZ
220                | ShapeType::PolygonZ
221                | ShapeType::MultipointZ
222                | ShapeType::PointM
223                | ShapeType::PolylineM
224                | ShapeType::PolygonM
225                | ShapeType::MultipointM
226        )
227    }
228
229    /// Returns true if the shape may have multiple parts
230    pub fn is_multipart(self) -> bool {
231        !matches!(
232            self,
233            ShapeType::Point
234                | ShapeType::PointM
235                | ShapeType::PointZ
236                | ShapeType::Multipoint
237                | ShapeType::MultipointM
238                | ShapeType::MultipointZ
239        )
240    }
241}
242
243impl fmt::Display for ShapeType {
244    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245        match self {
246            ShapeType::NullShape => write!(f, "NullShape"),
247            ShapeType::Point => write!(f, "Point"),
248            ShapeType::Polyline => write!(f, "Polyline"),
249            ShapeType::Polygon => write!(f, "Polygon"),
250            ShapeType::Multipoint => write!(f, "Multipoint"),
251            ShapeType::PointZ => write!(f, "PointZ"),
252            ShapeType::PolylineZ => write!(f, "PolylineZ"),
253            ShapeType::PolygonZ => write!(f, "PolygonZ"),
254            ShapeType::MultipointZ => write!(f, "MultipointZ"),
255            ShapeType::PointM => write!(f, "PointM"),
256            ShapeType::PolylineM => write!(f, "PolylineM"),
257            ShapeType::PolygonM => write!(f, "PolygonM"),
258            ShapeType::MultipointM => write!(f, "MultipointM"),
259            ShapeType::Multipatch => write!(f, "Multipatch"),
260        }
261    }
262}