use std::fs::File;
use std::io::{BufReader, Read, Seek, SeekFrom};
use std::iter::FusedIterator;
use std::path::Path;
use byteorder::{BigEndian, ReadBytesExt};
use header;
use record;
use {Error, Shape};
use record::ReadableShape;
const INDEX_RECORD_SIZE: usize = 2 * std::mem::size_of::<i32>();
pub(crate) struct ShapeIndex {
pub offset: i32,
pub record_size: i32,
}
fn read_index_file<T: Read>(mut source: T) -> Result<Vec<ShapeIndex>, Error> {
let header = header::Header::read_from(&mut source)?;
let num_shapes = ((header.file_length * 2) - header::HEADER_SIZE) / INDEX_RECORD_SIZE as i32;
let mut shapes_index = Vec::<ShapeIndex>::with_capacity(num_shapes as usize);
for _ in 0..num_shapes {
let offset = source.read_i32::<BigEndian>()?;
let record_size = source.read_i32::<BigEndian>()?;
shapes_index.push(ShapeIndex {
offset,
record_size,
});
}
Ok(shapes_index)
}
fn read_one_shape_as<T: Read, S: ReadableShape>(
mut source: &mut T,
) -> Result<(record::RecordHeader, S), Error> {
let hdr = record::RecordHeader::read_from(&mut source)?;
let record_size = hdr.record_size * 2;
let shape = S::read_from(&mut source, record_size)?;
Ok((hdr, shape))
}
pub struct ShapeIterator<T: Read, S: ReadableShape> {
_shape: std::marker::PhantomData<S>,
source: T,
current_pos: usize,
file_length: usize,
}
impl<T: Read, S: ReadableShape> Iterator for ShapeIterator<T, S> {
type Item = Result<S, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.current_pos >= self.file_length {
None
} else {
let (hdr, shape) = match read_one_shape_as::<T, S>(&mut self.source) {
Err(e) => return Some(Err(e)),
Ok(hdr_and_shape) => hdr_and_shape,
};
self.current_pos += record::RecordHeader::SIZE;
self.current_pos += hdr.record_size as usize * 2;
Some(Ok(shape))
}
}
}
impl<T: Read, S: ReadableShape> FusedIterator for ShapeIterator<T, S> {}
pub struct ShapeRecordIterator<T: Read, S: ReadableShape> {
shape_iter: ShapeIterator<T, S>,
dbf_reader: dbase::Reader<T>,
}
impl<T: Read, S: ReadableShape> Iterator for ShapeRecordIterator<T, S> {
type Item = Result<(S, dbase::Record), Error>;
fn next(&mut self) -> Option<Self::Item> {
let shape = match self.shape_iter.next()? {
Err(e) => return Some(Err(e)),
Ok(shp) => shp,
};
let record = match self.dbf_reader.next()? {
Err(e) => return Some(Err(Error::DbaseError(e))),
Ok(rcd) => rcd,
};
Some(Ok((shape, record)))
}
}
impl<T: Read, S: ReadableShape> FusedIterator for ShapeRecordIterator<T, S> {}
pub struct Reader<T: Read> {
source: T,
header: header::Header,
shapes_index: Option<Vec<ShapeIndex>>,
dbf_reader: Option<dbase::Reader<T>>,
}
impl<T: Read> Reader<T> {
pub fn new(mut source: T) -> Result<Reader<T>, Error> {
let header = header::Header::read_from(&mut source)?;
Ok(Reader {
source,
header,
shapes_index: None,
dbf_reader: None,
})
}
pub fn header(&self) -> &header::Header {
&self.header
}
pub fn read_as<S: ReadableShape>(self) -> Result<Vec<S>, Error> {
self.iter_shapes_as::<S>().collect()
}
pub fn read(self) -> Result<Vec<Shape>, Error> {
self.into_iter().collect()
}
pub fn read_records(self) -> Result<Vec<dbase::Record>, Error> {
let dbf_reader = self.dbf_reader.ok_or(Error::MissingDbf)?;
dbf_reader.read().or_else(|e| Err(Error::DbaseError(e)))
}
pub fn iter_shapes_as<S: ReadableShape>(self) -> ShapeIterator<T, S> {
ShapeIterator {
_shape: std::marker::PhantomData,
source: self.source,
current_pos: header::HEADER_SIZE as usize,
file_length: (self.header.file_length * 2) as usize,
}
}
pub fn iter_shapes(self) -> ShapeIterator<T, Shape> {
ShapeIterator {
_shape: std::marker::PhantomData,
source: self.source,
current_pos: header::HEADER_SIZE as usize,
file_length: (self.header.file_length * 2) as usize,
}
}
pub fn iter_shapes_and_records_as<S: ReadableShape>(
mut self,
) -> Result<ShapeRecordIterator<T, S>, Error> {
let maybe_dbf_reader = self.dbf_reader.take();
if let Some(dbf_reader) = maybe_dbf_reader {
let shape_iter = self.iter_shapes_as::<S>();
Ok(ShapeRecordIterator {
shape_iter,
dbf_reader,
})
} else {
Err(Error::MissingDbf)
}
}
pub fn iter_shapes_and_records(self) -> Result<ShapeRecordIterator<T, Shape>, Error> {
self.iter_shapes_and_records_as::<Shape>()
}
pub fn add_index_source(&mut self, source: T) -> Result<(), Error> {
self.shapes_index = Some(read_index_file(source)?);
Ok(())
}
pub fn add_dbf_source(&mut self, source: T) -> Result<(), Error> {
let dbf_reader = dbase::Reader::new(source)?;
self.dbf_reader = Some(dbf_reader);
Ok(())
}
}
impl<T: Read> IntoIterator for Reader<T> {
type Item = Result<Shape, Error>;
type IntoIter = ShapeIterator<T, Shape>;
fn into_iter(self) -> Self::IntoIter {
self.iter_shapes()
}
}
impl Reader<BufReader<File>> {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let shape_path = path.as_ref().to_path_buf();
let shx_path = shape_path.with_extension("shx");
let dbf_path = shape_path.with_extension("dbf");
let source = BufReader::new(File::open(shape_path)?);
let mut reader = Self::new(source)?;
if shx_path.exists() {
let index_source = BufReader::new(File::open(shx_path)?);
reader.add_index_source(index_source)?;
}
if dbf_path.exists() {
let dbf_source = BufReader::new(File::open(dbf_path)?);
reader.add_dbf_source(dbf_source)?;
}
Ok(reader)
}
}
impl<T: Read + Seek> Reader<T> {
pub fn read_nth_shape_as<S: ReadableShape>(
&mut self,
index: usize,
) -> Option<Result<S, Error>> {
if let Some(ref shapes_index) = self.shapes_index {
let offset = {
let shape_idx = shapes_index.get(index)?;
(shape_idx.offset * 2) as u64
};
if let Err(e) = self.source.seek(SeekFrom::Start(offset)) {
return Some(Err(Error::IoError(e)));
}
let (_, shape) = match read_one_shape_as::<T, S>(&mut self.source) {
Err(e) => return Some(Err(e)),
Ok(hdr_and_shape) => hdr_and_shape,
};
if let Err(e) = self
.source
.seek(SeekFrom::Start(header::HEADER_SIZE as u64))
{
return Some(Err(Error::IoError(e)));
}
Some(Ok(shape))
} else {
Some(Err(Error::MissingIndexFile))
}
}
pub fn read_nth_shape(&mut self, index: usize) -> Option<Result<Shape, Error>> {
self.read_nth_shape_as::<Shape>(index)
}
}
pub fn read<T: AsRef<Path>>(path: T) -> Result<Vec<Shape>, Error> {
let reader = Reader::from_path(path)?;
reader.read()
}
pub fn read_as<T: AsRef<Path>, S: ReadableShape>(path: T) -> Result<Vec<S>, Error> {
let reader = Reader::from_path(path)?;
reader.read_as::<S>()
}
#[cfg(test)]
mod tests {}