1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use super::{Error, ShapeType};

use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Write};

pub(crate) const HEADER_SIZE: i32 = 100;
const FILE_CODE: i32 = 9994;
const SIZE_OF_SKIP: usize = std::mem::size_of::<i32>() * 5;

/// struct representing the Header of a shapefile
/// can be retrieved via the reader used to read
//TODO replace  pointmin/max with bbox + z_range
#[derive(Copy, Clone, PartialEq)]
pub struct Header {
    /// Total file length (Header + Shapes) in 16bit word
    pub file_length: i32,
    /// min values of x, y, z for all the shapes
    pub point_min: [f64; 3],
    /// max values of x, y, z for all the shapes
    pub point_max: [f64; 3],
    /// min and max values for the measure dimension
    pub m_range: [f64; 2],
    /// Type of all the shapes in the file
    /// (as mixing shapes is not allowed)
    pub shape_type: ShapeType,
    /// Version of the shapefile specification
    pub version: i32,
}

impl Default for Header {
    fn default() -> Self {
        Header {
            point_min: [0.0; 3],
            point_max: [0.0; 3],
            m_range: [0.0; 2],
            shape_type: ShapeType::NullShape,
            file_length: HEADER_SIZE / 2,
            version: 1000,
        }
    }
}

impl Header {
    pub fn read_from<T: Read>(mut source: &mut T) -> Result<Header, Error> {
        let file_code = source.read_i32::<BigEndian>()?;

        if file_code != FILE_CODE {
            return Err(Error::InvalidFileCode(file_code));
        }

        let mut skip: [u8; SIZE_OF_SKIP] = [0; SIZE_OF_SKIP];
        source.read_exact(&mut skip)?;

        let file_length_16_bit = source.read_i32::<BigEndian>()?;
        let version = source.read_i32::<LittleEndian>()?;
        let shape_type = ShapeType::read_from(&mut source)?;

        let mut hdr = Header::default();
        hdr.shape_type = shape_type;
        hdr.version = version;
        hdr.file_length = file_length_16_bit;

        hdr.point_min[0] = source.read_f64::<LittleEndian>()?;
        hdr.point_min[1] = source.read_f64::<LittleEndian>()?;

        hdr.point_max[0] = source.read_f64::<LittleEndian>()?;
        hdr.point_max[1] = source.read_f64::<LittleEndian>()?;

        hdr.point_min[2] = source.read_f64::<LittleEndian>()?;
        hdr.point_max[2] = source.read_f64::<LittleEndian>()?;

        hdr.m_range[0] = source.read_f64::<LittleEndian>()?;
        hdr.m_range[1] = source.read_f64::<LittleEndian>()?;

        Ok(hdr)
    }

    pub(crate) fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), std::io::Error> {
        dest.write_i32::<BigEndian>(FILE_CODE)?;

        let skip: [u8; SIZE_OF_SKIP] = [0; SIZE_OF_SKIP];
        dest.write_all(&skip)?;

        dest.write_i32::<BigEndian>(self.file_length)?;
        dest.write_i32::<LittleEndian>(self.version)?;
        dest.write_i32::<LittleEndian>(self.shape_type as i32)?;

        dest.write_f64::<LittleEndian>(self.point_min[0])?;
        dest.write_f64::<LittleEndian>(self.point_min[1])?;
        dest.write_f64::<LittleEndian>(self.point_max[0])?;
        dest.write_f64::<LittleEndian>(self.point_max[1])?;

        dest.write_f64::<LittleEndian>(self.point_min[2])?;
        dest.write_f64::<LittleEndian>(self.point_max[2])?;

        dest.write_f64::<LittleEndian>(self.m_range[0])?;
        dest.write_f64::<LittleEndian>(self.m_range[1])?;

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use byteorder::WriteBytesExt;
    use std::io::{Seek, SeekFrom};

    #[test]
    fn wrong_file_code() {
        use std::io::Cursor;

        let mut src = Cursor::new(vec![]);
        src.write_i32::<BigEndian>(42).unwrap();

        src.seek(SeekFrom::Start(0)).unwrap();
        assert!(Header::read_from(&mut src).is_err());
    }
}