use std::io::{Read, Write};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use record::EsriShape;
use std::mem::size_of;
use ::{ShapeType, NO_DATA};
use super::Error;
use record::ConcreteReadableShape;
use record::{is_no_data, BBox, HasShapeType, WritableShape};
use std::fmt;
#[cfg(feature = "geo-types")]
use geo_types;
#[derive(PartialEq, Debug, Default, Copy, Clone)]
pub struct Point {
pub x: f64,
pub y: f64,
}
impl Point {
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
}
impl HasShapeType for Point {
fn shapetype() -> ShapeType {
ShapeType::Point
}
}
impl ConcreteReadableShape for Point {
fn read_shape_content<T: Read>(source: &mut T, record_size: i32) -> Result<Self, Error> {
if record_size == 2 * size_of::<f64>() as i32 {
let x = source.read_f64::<LittleEndian>()?;
let y = source.read_f64::<LittleEndian>()?;
Ok(Self { x, y })
} else {
Err(Error::InvalidShapeRecordSize)
}
}
}
impl WritableShape for Point {
fn size_in_bytes(&self) -> usize {
2 * size_of::<f64>()
}
fn write_to<T: Write>(self, dest: &mut T) -> Result<(), Error> {
dest.write_f64::<LittleEndian>(self.x)?;
dest.write_f64::<LittleEndian>(self.y)?;
Ok(())
}
}
impl EsriShape for Point {
fn bbox(&self) -> BBox {
BBox {
xmin: self.x,
ymin: self.y,
xmax: self.x,
ymax: self.y,
}
}
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Point(x: {}, y: {})", self.x, self.y)
}
}
#[cfg(feature = "geo-types")]
impl From<Point> for geo_types::Point<f64> {
fn from(p: Point) -> Self {
geo_types::Point::new(p.x, p.y)
}
}
#[cfg(feature = "geo-types")]
impl From<geo_types::Point<f64>> for Point {
fn from(p: geo_types::Point<f64>) -> Self {
Point::new(p.x(), p.y())
}
}
#[cfg(feature = "geo-types")]
impl From<geo_types::Coordinate<f64>> for Point {
fn from(c: geo_types::Coordinate<f64>) -> Self {
Point::new(c.x,c.y)
}
}
#[cfg(feature = "geo-types")]
impl From<Point> for geo_types::Coordinate<f64> {
fn from(p: Point) -> Self {
geo_types::Coordinate{
x:p.x,
y: p.y
}
}
}
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct PointM {
pub x: f64,
pub y: f64,
pub m: f64,
}
impl PointM {
pub fn new(x: f64, y: f64, m: f64) -> Self {
Self { x, y, m }
}
}
impl HasShapeType for PointM {
fn shapetype() -> ShapeType {
ShapeType::PointM
}
}
impl ConcreteReadableShape for PointM {
fn read_shape_content<T: Read>(source: &mut T, record_size: i32) -> Result<Self, Error> {
if record_size == 3 * size_of::<f64>() as i32 {
let x = source.read_f64::<LittleEndian>()?;
let y = source.read_f64::<LittleEndian>()?;
let m = source.read_f64::<LittleEndian>()?;
Ok(Self { x, y, m })
} else {
Err(Error::InvalidShapeRecordSize)
}
}
}
impl WritableShape for PointM {
fn size_in_bytes(&self) -> usize {
3 * size_of::<f64>()
}
fn write_to<T: Write>(self, dest: &mut T) -> Result<(), Error> {
dest.write_f64::<LittleEndian>(self.x)?;
dest.write_f64::<LittleEndian>(self.y)?;
dest.write_f64::<LittleEndian>(self.m)?;
Ok(())
}
}
impl EsriShape for PointM {
fn bbox(&self) -> BBox {
BBox {
xmin: self.x,
ymin: self.y,
xmax: self.x,
ymax: self.y,
}
}
fn m_range(&self) -> [f64; 2] {
if is_no_data(self.m) {
[0.0, 0.0]
} else {
[self.m, self.m]
}
}
}
impl fmt::Display for PointM {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Point(x: {}, y: {}, m: {})", self.x, self.y, self.m)
}
}
impl Default for PointM {
fn default() -> Self {
Self {
x: 0.0,
y: 0.0,
m: NO_DATA
}
}
}
#[cfg(feature = "geo-types")]
impl From<PointM> for geo_types::Point<f64> {
fn from(p: PointM) -> Self {
geo_types::Point::new(p.x, p.y)
}
}
#[cfg(feature = "geo-types")]
impl From<geo_types::Point<f64>> for PointM {
fn from(p: geo_types::Point<f64>) -> Self {
PointM{x: p.x(), y: p.y(), ..Default::default()}
}
}
#[cfg(feature = "geo-types")]
impl From<geo_types::Coordinate<f64>> for PointM {
fn from(c: geo_types::Coordinate<f64>) -> Self {
PointM::new(c.x,c.y, NO_DATA)
}
}
#[cfg(feature = "geo-types")]
impl From<PointM> for geo_types::Coordinate<f64> {
fn from(p: PointM) -> Self {
geo_types::Coordinate{
x:p.x,
y: p.y
}
}
}
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct PointZ {
pub x: f64,
pub y: f64,
pub z: f64,
pub m: f64,
}
impl PointZ {
pub fn new(x: f64, y: f64, z: f64, m: f64) -> Self {
Self { x, y, z, m }
}
}
impl HasShapeType for PointZ {
fn shapetype() -> ShapeType {
ShapeType::PointZ
}
}
impl ConcreteReadableShape for PointZ {
fn read_shape_content<T: Read>(source: &mut T, record_size: i32) -> Result<Self, Error> {
if record_size == 4 * size_of::<f64>() as i32 {
let x = source.read_f64::<LittleEndian>()?;
let y = source.read_f64::<LittleEndian>()?;
let z = source.read_f64::<LittleEndian>()?;
let m = source.read_f64::<LittleEndian>()?;
Ok(Self { x, y, z, m })
} else {
Err(Error::InvalidShapeRecordSize)
}
}
}
impl WritableShape for PointZ {
fn size_in_bytes(&self) -> usize {
4 * size_of::<f64>()
}
fn write_to<T: Write>(self, dest: &mut T) -> Result<(), Error> {
dest.write_f64::<LittleEndian>(self.x)?;
dest.write_f64::<LittleEndian>(self.y)?;
dest.write_f64::<LittleEndian>(self.z)?;
dest.write_f64::<LittleEndian>(self.m)?;
Ok(())
}
}
impl EsriShape for PointZ {
fn bbox(&self) -> BBox {
BBox {
xmin: self.x,
ymin: self.y,
xmax: self.x,
ymax: self.y,
}
}
fn z_range(&self) -> [f64; 2] {
[self.z, self.z]
}
fn m_range(&self) -> [f64; 2] {
if is_no_data(self.m) {
[0.0, 0.0]
} else {
[self.m, self.m]
}
}
}
impl Default for PointZ {
fn default() -> Self {
Self {
x: 0.0,
y: 0.0,
z: 0.0,
m: NO_DATA
}
}
}
impl fmt::Display for PointZ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Point(x: {}, y: {}, z: {}, m: {})",
self.x, self.y, self.z, self.m
)
}
}
#[cfg(feature = "geo-types")]
impl From<PointZ> for geo_types::Point<f64> {
fn from(p: PointZ) -> Self {
geo_types::Point::new(p.x, p.y)
}
}
#[cfg(feature = "geo-types")]
impl From<geo_types::Point<f64>> for PointZ {
fn from(p: geo_types::Point<f64>) -> Self {
PointZ{x: p.x(), y: p.y(), ..Default::default()}
}
}
#[cfg(feature = "geo-types")]
impl From<geo_types::Coordinate<f64>> for PointZ {
fn from(c: geo_types::Coordinate<f64>) -> Self {
PointZ::new(c.x,c.y, 0.0, NO_DATA)
}
}
#[cfg(feature = "geo-types")]
impl From<PointZ> for geo_types::Coordinate<f64> {
fn from(p: PointZ) -> Self {
geo_types::Coordinate {
x: p.x,
y: p.y
}
}
}
#[cfg(test)]
#[cfg(feature = "geo-types")]
mod test {
use super::*;
#[test]
fn geo_types_point_conversion() {
let p = Point::new(14.0, 42.65);
let gp: geo_types::Point<f64> = p.into();
assert_eq!(gp.x(), 14.0);
assert_eq!(gp.y(), 42.65);
let p: Point = gp.into();
assert_eq!(p.x, 14.0);
assert_eq!(p.y, 42.65);
}
#[test]
fn geo_types_point_m_conversion() {
let p = PointM::new(14.0, 42.65, 652.3);
let gp: geo_types::Point<f64> = p.into();
assert_eq!(gp.x(), 14.0);
assert_eq!(gp.y(), 42.65);
let p: PointM = gp.into();
assert_eq!(p.x, 14.0);
assert_eq!(p.y, 42.65);
assert_eq!(p.m, NO_DATA);
}
#[test]
fn geo_types_point_z_conversion() {
let p = PointZ::new(14.0, 42.65, 111.0, 652.3);
let gp: geo_types::Point<f64> = p.into();
assert_eq!(gp.x(), 14.0);
assert_eq!(gp.y(), 42.65);
let p: PointZ = gp.into();
assert_eq!(p.x, 14.0);
assert_eq!(p.y, 42.65);
assert_eq!(p.z, 0.0);
assert_eq!(p.m, NO_DATA);
}
}