shapefile_gbk/
header.rs

1use super::{Error, ShapeType};
2
3use crate::record::BBoxZ;
4use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
5use std::io::{Read, Write};
6
7pub(crate) const HEADER_SIZE: i32 = 100;
8const FILE_CODE: i32 = 9994;
9/// Size of reserved bytes in the header, that have do defined use
10const SIZE_OF_SKIP: usize = std::mem::size_of::<i32>() * 5;
11
12/// struct representing the Header of a shapefile
13/// can be retrieved via the reader used to read
14#[derive(Copy, Clone, PartialEq)]
15pub struct Header {
16    /// Total file length (Header + Shapes) in 16bit word
17    pub file_length: i32,
18    /// The bbox contained all the shapes in this shapefile
19    ///
20    /// For shapefiles where the shapes do not have `m` or `z` values
21    /// the associated min and max will be `0`s.
22    pub bbox: BBoxZ,
23    /// Type of all the shapes in the file
24    /// (as mixing shapes is not allowed)
25    pub shape_type: ShapeType,
26    /// Version of the shapefile specification
27    pub version: i32,
28}
29
30impl Default for Header {
31    fn default() -> Self {
32        Header {
33            bbox: BBoxZ::default(),
34            shape_type: ShapeType::NullShape,
35            file_length: HEADER_SIZE / 2,
36            version: 1000,
37        }
38    }
39}
40
41impl Header {
42    pub fn read_from<T: Read>(mut source: &mut T) -> Result<Header, Error> {
43        let file_code = source.read_i32::<BigEndian>()?;
44
45        if file_code != FILE_CODE {
46            return Err(Error::InvalidFileCode(file_code));
47        }
48
49        let mut skip: [u8; SIZE_OF_SKIP] = [0; SIZE_OF_SKIP];
50        source.read_exact(&mut skip)?;
51
52        let file_length = source.read_i32::<BigEndian>()?;
53        let version = source.read_i32::<LittleEndian>()?;
54        let shape_type = ShapeType::read_from(&mut source)?;
55
56        let mut hdr = Header {
57            shape_type,
58            version,
59            file_length,
60            ..Default::default()
61        };
62
63        hdr.bbox.min.x = source.read_f64::<LittleEndian>()?;
64        hdr.bbox.min.y = source.read_f64::<LittleEndian>()?;
65        hdr.bbox.max.x = source.read_f64::<LittleEndian>()?;
66        hdr.bbox.max.y = source.read_f64::<LittleEndian>()?;
67        hdr.bbox.min.z = source.read_f64::<LittleEndian>()?;
68        hdr.bbox.max.z = source.read_f64::<LittleEndian>()?;
69        hdr.bbox.min.m = source.read_f64::<LittleEndian>()?;
70        hdr.bbox.max.m = source.read_f64::<LittleEndian>()?;
71
72        Ok(hdr)
73    }
74
75    pub(crate) fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), std::io::Error> {
76        dest.write_i32::<BigEndian>(FILE_CODE)?;
77
78        let skip: [u8; SIZE_OF_SKIP] = [0; SIZE_OF_SKIP];
79        dest.write_all(&skip)?;
80
81        dest.write_i32::<BigEndian>(self.file_length)?;
82        dest.write_i32::<LittleEndian>(self.version)?;
83        dest.write_i32::<LittleEndian>(self.shape_type as i32)?;
84
85        dest.write_f64::<LittleEndian>(self.bbox.min.x)?;
86        dest.write_f64::<LittleEndian>(self.bbox.min.y)?;
87        dest.write_f64::<LittleEndian>(self.bbox.max.x)?;
88        dest.write_f64::<LittleEndian>(self.bbox.max.y)?;
89        dest.write_f64::<LittleEndian>(self.bbox.min.z)?;
90        dest.write_f64::<LittleEndian>(self.bbox.max.z)?;
91        dest.write_f64::<LittleEndian>(self.bbox.min.m)?;
92        dest.write_f64::<LittleEndian>(self.bbox.max.m)?;
93
94        Ok(())
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    use std::io::{Seek, SeekFrom};
103
104    #[test]
105    fn wrong_file_code() {
106        use std::io::Cursor;
107
108        let mut src = Cursor::new(vec![]);
109        src.write_i32::<BigEndian>(42).unwrap();
110
111        src.seek(SeekFrom::Start(0)).unwrap();
112        assert!(Header::read_from(&mut src).is_err());
113    }
114}