use std::fmt;
use std::io::{Read, Write};
use std::mem::size_of;
use std::slice::SliceIndex;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use record::io::*;
use record::traits::{HasXY, MultipointShape};
use record::ConcreteReadableShape;
use record::{BBox, EsriShape};
use record::{HasShapeType, WritableShape};
use record::{Point, PointM, PointZ};
use {Error, ShapeType};
#[cfg(feature = "geo-types")]
use geo_types;
pub struct GenericMultipoint<PointType> {
pub bbox: BBox,
pub points: Vec<PointType>,
}
impl<PointType> MultipointShape<PointType> for GenericMultipoint<PointType> {
fn point<I: SliceIndex<[PointType]>>(
&self,
index: I,
) -> Option<&<I as SliceIndex<[PointType]>>::Output> {
self.points.get(index)
}
fn points(&self) -> &[PointType] {
&self.points
}
}
impl<PointType: HasXY> GenericMultipoint<PointType> {
pub fn new(points: Vec<PointType>) -> Self {
let bbox = BBox::from_points(&points);
Self { bbox, points }
}
}
#[cfg(feature = "geo-types")]
impl<PointType> From<GenericMultipoint<PointType>> for geo_types::MultiPoint<f64>
where geo_types::Point<f64>: From<PointType>
{
fn from(multi_points: GenericMultipoint<PointType>) -> Self {
multi_points.points
.into_iter()
.map(|p| geo_types::Point::from(p))
.collect::<Vec<geo_types::Point<f64>>>()
.into()
}
}
#[cfg(feature = "geo-types")]
impl<PointType> From<geo_types::MultiPoint<f64>> for GenericMultipoint<PointType>
where PointType: From<geo_types::Point<f64>> + HasXY{
fn from(mp: geo_types::MultiPoint<f64>) -> Self {
let points = mp.into_iter().map(|p| p.into()).collect();
Self::new(points)
}
}
pub type Multipoint = GenericMultipoint<Point>;
impl Multipoint {
pub(crate) fn size_of_record(num_points: i32) -> usize {
let mut size = 0usize;
size += 4 * size_of::<f64>();
size += size_of::<i32>();
size += size_of::<Point>() * num_points as usize;
size
}
}
impl fmt::Display for Multipoint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Multipoint({} points)", self.points.len())
}
}
impl HasShapeType for Multipoint {
fn shapetype() -> ShapeType {
ShapeType::Multipoint
}
}
impl ConcreteReadableShape for Multipoint {
fn read_shape_content<T: Read>(mut source: &mut T, record_size: i32) -> Result<Self, Error> {
let bbox = BBox::read_from(&mut source)?;
let num_points = source.read_i32::<LittleEndian>()?;
if record_size == Self::size_of_record(num_points) as i32 {
let points = read_xy_in_vec_of::<Point, T>(&mut source, num_points)?;
Ok(Self { bbox, points })
} else {
Err(Error::InvalidShapeRecordSize)
}
}
}
impl WritableShape for Multipoint {
fn size_in_bytes(&self) -> usize {
let mut size = 0usize;
size += 4 * size_of::<f64>();
size += size_of::<i32>();
size += 2 * size_of::<f64>() * self.points.len();
size
}
fn write_to<T: Write>(self, mut dest: &mut T) -> Result<(), Error> {
self.bbox.write_to(&mut dest)?;
dest.write_i32::<LittleEndian>(self.points.len() as i32)?;
for point in self.points {
dest.write_f64::<LittleEndian>(point.x)?;
dest.write_f64::<LittleEndian>(point.y)?;
}
Ok(())
}
}
impl EsriShape for Multipoint {
fn bbox(&self) -> BBox {
self.bbox
}
}
pub type MultipointM = GenericMultipoint<PointM>;
impl MultipointM {
pub(crate) fn size_of_record(num_points: i32, is_m_used: bool) -> usize {
let mut size = Multipoint::size_of_record(num_points);
if is_m_used {
size += 2 * size_of::<f64>();
size += size_of::<f64>() * num_points as usize;
}
size
}
}
impl fmt::Display for MultipointM {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MultipointM({} points)", self.points.len())
}
}
impl HasShapeType for MultipointM {
fn shapetype() -> ShapeType {
ShapeType::MultipointM
}
}
impl ConcreteReadableShape for MultipointM {
fn read_shape_content<T: Read>(mut source: &mut T, record_size: i32) -> Result<Self, Error> {
let bbox = BBox::read_from(&mut source)?;
let num_points = source.read_i32::<LittleEndian>()?;
let size_with_m = Self::size_of_record(num_points, true) as i32;
let size_without_m = Self::size_of_record(num_points, false) as i32;
if (record_size != size_with_m) & (record_size != size_without_m) {
Err(Error::InvalidShapeRecordSize)
} else {
let m_is_used = size_with_m == record_size;
let mut points = read_xy_in_vec_of::<PointM, T>(&mut source, num_points)?;
if m_is_used {
let _m_range = read_range(&mut source)?;
read_ms_into(&mut source, &mut points)?;
}
Ok(Self { bbox, points })
}
}
}
impl WritableShape for MultipointM {
fn size_in_bytes(&self) -> usize {
let mut size = 0usize;
size += 4 * size_of::<f64>();
size += size_of::<i32>();
size += 3 * size_of::<f64>() * self.points.len();
size += 2 * size_of::<f64>();
size
}
fn write_to<T: Write>(self, mut dest: &mut T) -> Result<(), Error> {
self.bbox.write_to(&mut dest)?;
dest.write_i32::<LittleEndian>(self.points.len() as i32)?;
write_points(&mut dest, &self.points)?;
write_range(&mut dest, self.m_range())?;
write_ms(&mut dest, &self.points)?;
Ok(())
}
}
impl EsriShape for MultipointM {
fn bbox(&self) -> BBox {
self.bbox
}
fn m_range(&self) -> [f64; 2] {
calc_m_range(&self.points)
}
}
pub type MultipointZ = GenericMultipoint<PointZ>;
impl fmt::Display for MultipointZ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MultipointZ({} points)", self.points.len())
}
}
impl MultipointZ {
pub(crate) fn size_of_record(num_points: i32, is_m_used: bool) -> usize {
let mut size = Multipoint::size_of_record(num_points);
size += 2 * size_of::<f64>();
size += size_of::<f64>() * num_points as usize;
if is_m_used {
size += 2 * size_of::<f64>();
size += size_of::<f64>() * num_points as usize;
}
size
}
}
impl HasShapeType for MultipointZ {
fn shapetype() -> ShapeType {
ShapeType::MultipointZ
}
}
impl ConcreteReadableShape for MultipointZ {
fn read_shape_content<T: Read>(mut source: &mut T, record_size: i32) -> Result<Self, Error> {
let bbox = BBox::read_from(&mut source)?;
let num_points = source.read_i32::<LittleEndian>()?;
let size_with_m = Self::size_of_record(num_points, true) as i32;
let size_without_m = Self::size_of_record(num_points, false) as i32;
if (record_size != size_with_m) & (record_size != size_without_m) {
Err(Error::InvalidShapeRecordSize)
} else {
let m_is_used = size_with_m == record_size;
let mut points = read_xy_in_vec_of::<PointZ, T>(&mut source, num_points)?;
let _z_range = read_range(&mut source)?;
read_zs_into(&mut source, &mut points)?;
if m_is_used {
let _m_range = read_range(&mut source)?;
read_ms_into(&mut source, &mut points)?;
}
Ok(Self { bbox, points })
}
}
}
impl WritableShape for MultipointZ {
fn size_in_bytes(&self) -> usize {
let mut size = 0usize;
size += 4 * size_of::<f64>();
size += size_of::<i32>();
size += 4 * size_of::<f64>() * self.points.len();
size += 2 * size_of::<f64>();
size += 2 * size_of::<f64>();
size
}
fn write_to<T: Write>(self, mut dest: &mut T) -> Result<(), Error> {
self.bbox.write_to(&mut dest)?;
dest.write_i32::<LittleEndian>(self.points.len() as i32)?;
write_points(&mut dest, &self.points)?;
write_range(&mut dest, self.z_range())?;
write_zs(&mut dest, &self.points)?;
write_range(&mut dest, self.m_range())?;
write_ms(&mut dest, &self.points)?;
Ok(())
}
}
impl EsriShape for MultipointZ {
fn bbox(&self) -> BBox {
self.bbox
}
fn z_range(&self) -> [f64; 2] {
calc_z_range(&self.points)
}
fn m_range(&self) -> [f64; 2] {
calc_m_range(&self.points)
}
}
#[cfg(test)]
#[cfg(feature = "geo-types")]
mod tests {
use super::*;
use ::{geo_types, NO_DATA};
#[test]
fn test_multipoint_to_geo_types_multipoint() {
let points = vec![
Point::new(1.0, 1.0),
Point::new(2.0, 2.0),
];
let shapefile_multipoint = Multipoint::new(points);
let geo_types_multipoint = geo_types::MultiPoint::from(shapefile_multipoint);
let mut iter = geo_types_multipoint.into_iter();
let p1 = iter.next().unwrap();
let p2 = iter.next().unwrap();
assert_eq!(p1.x(), 1.0);
assert_eq!(p1.y(), 1.0);
assert_eq!(p2.x(), 2.0);
assert_eq!(p2.y(), 2.0);
}
#[test]
fn test_multipoint_m_to_geo_types_multipoint() {
let points = vec![
PointM::new(120.0, 56.0, 42.2),
PointM::new(6.0, 18.7, 462.54),
];
let shapefile_multipoint = MultipointM::new(points);
let geo_types_multipoint = geo_types::MultiPoint::from(shapefile_multipoint);
let mut iter = geo_types_multipoint.into_iter();
let p1 = iter.next().unwrap();
let p2 = iter.next().unwrap();
assert_eq!(p1.x(), 120.0);
assert_eq!(p1.y(), 56.0);
assert_eq!(p2.x(), 6.0);
assert_eq!(p2.y(), 18.7);
let geo_types_multipoint: geo_types::MultiPoint<_> = vec![p1, p2].into();
let shapefile_multipoint = MultipointM::from(geo_types_multipoint);
assert_eq!(shapefile_multipoint.points[0].x, 120.0);
assert_eq!(shapefile_multipoint.points[0].y, 56.0);
assert_eq!(shapefile_multipoint.points[0].m, NO_DATA);
assert_eq!(shapefile_multipoint.points[1].x, 6.0);
assert_eq!(shapefile_multipoint.points[1].y, 18.7);
assert_eq!(shapefile_multipoint.points[0].m, NO_DATA);
}
#[test]
fn test_multipoint_z_to_geo_types_multipoint() {
let points = vec![
PointZ::new(1.0, 1.0, 17.0, 18.0),
PointZ::new(2.0, 2.0, 15.0, 16.0),
];
let shapefile_multipoint = MultipointZ::new(points);
let geo_types_multipoint = geo_types::MultiPoint::from(shapefile_multipoint);
let mut iter = geo_types_multipoint.into_iter();
let p1 = iter.next().unwrap();
let p2 = iter.next().unwrap();
assert_eq!(p1.x(), 1.0);
assert_eq!(p1.y(), 1.0);
assert_eq!(p2.x(), 2.0);
assert_eq!(p2.y(), 2.0);
let geo_types_multipoint: geo_types::MultiPoint<_> = vec![p1, p2].into();
let shapefile_multipoint = MultipointZ::from(geo_types_multipoint);
assert_eq!(shapefile_multipoint.points[0].x, 1.0);
assert_eq!(shapefile_multipoint.points[0].y, 1.0);
assert_eq!(shapefile_multipoint.points[0].z, 0.0);
assert_eq!(shapefile_multipoint.points[0].m, NO_DATA);
assert_eq!(shapefile_multipoint.points[1].x, 2.0);
assert_eq!(shapefile_multipoint.points[1].y, 2.0);
assert_eq!(shapefile_multipoint.points[0].z, 0.0);
assert_eq!(shapefile_multipoint.points[0].m, NO_DATA);
}
}