use std::fs::File;
use std::io::{BufReader, Read, Seek, SeekFrom, Write};
use std::path::Path;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::header;
use crate::record;
use crate::record::ReadableShape;
use crate::{Error, Shape};
const INDEX_RECORD_SIZE: usize = 2 * std::mem::size_of::<i32>();
#[derive(Copy, Clone)]
pub(crate) struct ShapeIndex {
pub offset: i32,
pub record_size: i32,
}
impl ShapeIndex {
pub(crate) fn write_to<W: Write>(self, dest: &mut W) -> std::io::Result<()> {
dest.write_i32::<BigEndian>(self.offset)?;
dest.write_i32::<BigEndian>(self.record_size)?;
Ok(())
}
}
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<'a, T: Read, S: ReadableShape> {
_shape: std::marker::PhantomData<S>,
source: &'a mut T,
current_pos: usize,
file_length: usize,
shapes_indices: Option<std::slice::Iter<'a, ShapeIndex>>,
}
impl<'a, T: Read + Seek, S: ReadableShape> Iterator for ShapeIterator<'a, T, S> {
type Item = Result<S, crate::Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.current_pos >= self.file_length {
None
} else {
if let Some(ref mut shapes_indices) = self.shapes_indices {
let start_pos = shapes_indices.next()?.offset * 2;
if let Err(err) = self.source.seek(SeekFrom::Start(start_pos as u64)) {
return Some(Err(err.into()));
}
self.current_pos = start_pos as usize;
}
let (hdr, shape) = match read_one_shape_as::<T, S>(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))
}
}
}
pub struct ShapeRecordIterator<
'a,
T: Read + Seek,
D: Read + Seek,
S: ReadableShape,
R: dbase::ReadableRecord,
> {
shape_iter: ShapeIterator<'a, T, S>,
record_iter: dbase::RecordIterator<'a, D, R>,
}
impl<'a, T: Read + Seek, D: Read + Seek, S: ReadableShape, R: dbase::ReadableRecord> Iterator
for ShapeRecordIterator<'a, T, D, S, R>
{
type Item = Result<(S, R), 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.record_iter.next()? {
Err(e) => return Some(Err(Error::DbaseError(e))),
Ok(rcd) => rcd,
};
Some(Ok((shape, record)))
}
}
pub struct ShapeReader<T> {
source: T,
header: header::Header,
shapes_index: Option<Vec<ShapeIndex>>,
}
impl<T: Read> ShapeReader<T> {
pub fn new(mut source: T) -> Result<Self, Error> {
let header = header::Header::read_from(&mut source)?;
Ok(Self {
source,
header,
shapes_index: None,
})
}
pub fn with_shx<ShxSource>(mut source: T, shx_source: ShxSource) -> Result<Self, Error>
where
ShxSource: Read,
{
let shapes_index = Some(read_index_file(shx_source)?);
let header = header::Header::read_from(&mut source)?;
Ok(Self {
source,
header,
shapes_index,
})
}
pub fn header(&self) -> &header::Header {
&self.header
}
}
impl<T: Read + Seek> ShapeReader<T> {
pub fn read_as<S: ReadableShape>(mut self) -> Result<Vec<S>, Error> {
self.iter_shapes_as::<S>().collect()
}
pub fn read(mut self) -> Result<Vec<Shape>, Error> {
self.iter_shapes_as::<Shape>().collect()
}
pub fn iter_shapes_as<S: ReadableShape>(&mut self) -> ShapeIterator<'_, T, S> {
ShapeIterator {
_shape: std::marker::PhantomData,
source: &mut self.source,
current_pos: header::HEADER_SIZE as usize,
file_length: (self.header.file_length * 2) as usize,
shapes_indices: self.shapes_index.as_ref().map(|s| s.iter()),
}
}
pub fn iter_shapes(&mut self) -> ShapeIterator<'_, T, Shape> {
self.iter_shapes_as::<Shape>()
}
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 {
if index >= shapes_index.len() {
return None;
}
if let Err(e) = self.seek(index) {
return Some(Err(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 seek(&mut self, index: usize) -> Result<(), Error> {
if let Some(ref shapes_index) = self.shapes_index {
let offset = shapes_index
.get(index)
.map(|shape_idx| (shape_idx.offset * 2) as u64);
match offset {
Some(n) => self.source.seek(SeekFrom::Start(n)),
None => self.source.seek(SeekFrom::End(0)),
}?;
Ok(())
} else {
Err(Error::MissingIndexFile)
}
}
pub fn shape_count(&self) -> Result<usize, Error> {
if let Some(ref shapes_index) = self.shapes_index {
Ok(shapes_index.len())
} else {
Err(Error::MissingIndexFile)
}
}
}
impl ShapeReader<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 source = BufReader::new(File::open(shape_path)?);
if shx_path.exists() {
let index_source = BufReader::new(File::open(shx_path)?);
Self::with_shx(source, index_source)
} else {
Self::new(source)
}
}
}
pub struct Reader<T: Read + Seek, D: Read + Seek> {
shape_reader: ShapeReader<T>,
dbase_reader: dbase::Reader<D>,
}
impl<T: Read + Seek, D: Read + Seek> Reader<T, D> {
pub fn new(shape_reader: ShapeReader<T>, dbase_reader: dbase::Reader<D>) -> Self {
Self {
shape_reader,
dbase_reader,
}
}
pub fn header(&self) -> &header::Header {
self.shape_reader.header()
}
pub fn iter_shapes_and_records_as<S: ReadableShape, R: dbase::ReadableRecord>(
&mut self,
) -> ShapeRecordIterator<'_, T, D, S, R> {
ShapeRecordIterator {
shape_iter: self.shape_reader.iter_shapes_as::<S>(),
record_iter: self.dbase_reader.iter_records_as::<R>(),
}
}
pub fn iter_shapes_and_records(
&mut self,
) -> ShapeRecordIterator<'_, T, D, Shape, dbase::Record> {
self.iter_shapes_and_records_as::<Shape, dbase::Record>()
}
pub fn read_as<S: ReadableShape, R: dbase::ReadableRecord>(
&mut self,
) -> Result<Vec<(S, R)>, Error> {
self.iter_shapes_and_records_as::<S, R>().collect()
}
pub fn read(&mut self) -> Result<Vec<(Shape, dbase::Record)>, Error> {
self.read_as::<Shape, dbase::Record>()
}
pub fn seek(&mut self, index: usize) -> Result<(), Error> {
self.shape_reader.seek(index)?;
self.dbase_reader.seek(index)?;
Ok(())
}
pub fn shape_count(&self) -> Result<usize, Error> {
self.shape_reader.shape_count()
}
pub fn into_table_info(self) -> dbase::TableInfo {
self.dbase_reader.into_table_info()
}
}
impl Reader<BufReader<File>, BufReader<File>> {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let shape_path = path.as_ref().to_path_buf();
let dbf_path = shape_path.with_extension("dbf");
if dbf_path.exists() {
let shape_reader = ShapeReader::from_path(path)?;
let dbf_source = BufReader::new(File::open(dbf_path)?);
let dbf_reader = dbase::Reader::new(dbf_source)?;
Ok(Self {
shape_reader,
dbase_reader: dbf_reader,
})
} else {
Err(Error::MissingDbf)
}
}
}
pub fn read<T: AsRef<Path>>(path: T) -> Result<Vec<(Shape, dbase::Record)>, Error> {
read_as::<T, Shape, dbase::Record>(path)
}
pub fn read_as<T: AsRef<Path>, S: ReadableShape, R: dbase::ReadableRecord>(
path: T,
) -> Result<Vec<(S, R)>, Error> {
Reader::from_path(path).and_then(|mut rdr| rdr.read_as::<S, R>())
}
pub fn read_shapes_as<T: AsRef<Path>, S: ReadableShape>(path: T) -> Result<Vec<S>, Error> {
ShapeReader::from_path(path).and_then(|rdr| rdr.read_as::<S>())
}
pub fn read_shapes<T: AsRef<Path>>(path: T) -> Result<Vec<Shape>, Error> {
read_shapes_as::<T, Shape>(path)
}
#[cfg(test)]
mod tests {}