use super::{Polyline, PolylineM, PolylineZ};
use core::fmt;
use record::io::MultiPartShapeWriter;
use record::polyline::GenericPolyline;
use record::traits::{GrowablePoint, HasXY, ShrinkablePoint};
use record::{
close_points_if_not_already, ring_type_from_points_ordering, ConcreteReadableShape, EsriShape,
GenericBBox, RingType, WritableShape,
};
use std::io::{Read, Write};
use std::mem::size_of;
use {Error, ShapeType};
use {HasShapeType, Point};
use {PointM, PointZ};
#[cfg(feature = "geo-types")]
use geo_types::{Coordinate, LineString};
use std::ops::Index;
use std::slice::SliceIndex;
#[derive(Debug, Clone, PartialEq)]
pub enum PolygonRing<PointType> {
Outer(Vec<PointType>),
Inner(Vec<PointType>),
}
impl<PointType> PolygonRing<PointType> {
#[inline]
pub fn len(&self) -> usize {
self.points().len()
}
#[inline]
pub fn points(&self) -> &[PointType] {
match self {
PolygonRing::Outer(points) => &points,
PolygonRing::Inner(points) => &points,
}
}
#[inline]
pub fn into_inner(self) -> Vec<PointType> {
match self {
PolygonRing::Outer(points) => points,
PolygonRing::Inner(points) => points,
}
}
#[inline]
fn points_vec_mut(&mut self) -> &mut Vec<PointType> {
match self {
PolygonRing::Outer(points) => points,
PolygonRing::Inner(points) => points,
}
}
}
impl<PointType> AsRef<[PointType]> for PolygonRing<PointType> {
fn as_ref(&self) -> &[PointType] {
self.points()
}
}
impl<PointType> PolygonRing<PointType>
where
PointType: Copy + PartialEq + HasXY,
{
fn close_and_reorder(&mut self) {
self.close_if_not_already_closed();
self.correctly_order_points();
}
fn close_if_not_already_closed(&mut self) {
close_points_if_not_already(self.points_vec_mut())
}
fn correctly_order_points(&mut self) {
let points = self.points_vec_mut();
let actual_ring_type = super::ring_type_from_points_ordering(&points);
match (self, actual_ring_type) {
(PolygonRing::Outer(points), RingType::InnerRing)
| (PolygonRing::Inner(points), RingType::OuterRing) => {
points.reverse();
}
_ => {}
}
}
}
impl<PointType, I: SliceIndex<[PointType]>> Index<I> for PolygonRing<PointType> {
type Output = I::Output;
fn index(&self, index: I) -> &Self::Output {
Index::index(self.points(), index)
}
}
impl<PointType: HasXY> From<Vec<PointType>> for PolygonRing<PointType> {
fn from(p: Vec<PointType>) -> Self {
match ring_type_from_points_ordering(&p) {
RingType::OuterRing => PolygonRing::Outer(p),
RingType::InnerRing => PolygonRing::Inner(p),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct GenericPolygon<PointType> {
bbox: GenericBBox<PointType>,
rings: Vec<PolygonRing<PointType>>,
}
impl<PointType> GenericPolygon<PointType>
where
PointType: ShrinkablePoint + GrowablePoint + PartialEq + HasXY + Copy,
{
pub fn new(mut ring: PolygonRing<PointType>) -> Self {
ring.close_and_reorder();
Self::with_rings(vec![ring])
}
}
impl<PointType> GenericPolygon<PointType>
where
PointType: GrowablePoint + ShrinkablePoint + PartialEq + HasXY + Copy,
{
pub fn with_rings(mut rings: Vec<PolygonRing<PointType>>) -> Self {
rings.iter_mut().for_each(PolygonRing::close_and_reorder);
let mut bbox = GenericBBox::<PointType>::from_points(rings[0].points());
for ring in &rings[1..] {
bbox.grow_from_points(ring.points());
}
Self { bbox, rings }
}
}
impl<PointType> GenericPolygon<PointType> {
#[inline]
pub fn bbox(&self) -> &GenericBBox<PointType> {
&self.bbox
}
#[inline]
pub fn rings(&self) -> &[PolygonRing<PointType>] {
&self.rings
}
#[inline]
pub fn ring(&self, index: usize) -> Option<&PolygonRing<PointType>> {
self.rings.get(index)
}
#[inline]
pub fn into_inner(self) -> Vec<PolygonRing<PointType>> {
self.rings
}
#[inline]
pub fn total_point_count(&self) -> usize {
self.rings.iter().map(|ring| ring.len()).sum()
}
}
impl<PointType: HasXY> From<GenericPolyline<PointType>> for GenericPolygon<PointType> {
fn from(polyline: GenericPolyline<PointType>) -> Self {
let mut rings = Vec::<PolygonRing<PointType>>::with_capacity(polyline.parts.len());
for part in polyline.parts {
rings.push(PolygonRing::from(part))
}
Self {
bbox: polyline.bbox,
rings,
}
}
}
pub type Polygon = GenericPolygon<Point>;
impl fmt::Display for Polygon {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Polygon({} rings)", self.rings.len())
}
}
impl HasShapeType for Polygon {
fn shapetype() -> ShapeType {
ShapeType::Polygon
}
}
impl ConcreteReadableShape for Polygon {
fn read_shape_content<T: Read>(source: &mut T, record_size: i32) -> Result<Self, Error> {
Polyline::read_shape_content(source, record_size).map(Polygon::from)
}
}
impl WritableShape for Polygon {
fn size_in_bytes(&self) -> usize {
let mut size = 0 as usize;
size += size_of::<f64>() * 4;
size += size_of::<i32>();
size += size_of::<i32>();
size += size_of::<i32>() * self.rings.len();
size += 2 * size_of::<f64>() * self.total_point_count();
size
}
fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), Error> {
let parts_iter = self.rings().iter().map(|ring| ring.points());
let writer = MultiPartShapeWriter::new(&self.bbox, parts_iter, dest);
writer.write_point_shape()?;
Ok(())
}
}
impl EsriShape for Polygon {
fn x_range(&self) -> [f64; 2] {
self.bbox.x_range()
}
fn y_range(&self) -> [f64; 2] {
self.bbox.y_range()
}
}
pub type PolygonM = GenericPolygon<PointM>;
impl fmt::Display for PolygonM {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "PolygonM({} rings)", self.rings.len())
}
}
impl HasShapeType for PolygonM {
fn shapetype() -> ShapeType {
ShapeType::PolygonM
}
}
impl ConcreteReadableShape for PolygonM {
fn read_shape_content<T: Read>(source: &mut T, record_size: i32) -> Result<Self, Error> {
PolylineM::read_shape_content(source, record_size).map(PolygonM::from)
}
}
impl WritableShape for PolygonM {
fn size_in_bytes(&self) -> usize {
let mut size = 0 as usize;
size += size_of::<f64>() * 4;
size += size_of::<i32>();
size += size_of::<i32>();
size += size_of::<i32>() * self.rings.len();
size += 3 * size_of::<f64>() * self.total_point_count();
size += 2 * size_of::<f64>();
size
}
fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), Error> {
let parts_iter = self.rings().iter().map(|ring| ring.points());
let writer = MultiPartShapeWriter::new(&self.bbox, parts_iter, dest);
writer.write_point_m_shape()?;
Ok(())
}
}
impl EsriShape for PolygonM {
fn x_range(&self) -> [f64; 2] {
self.bbox.x_range()
}
fn y_range(&self) -> [f64; 2] {
self.bbox.y_range()
}
fn m_range(&self) -> [f64; 2] {
self.bbox.m_range()
}
}
pub type PolygonZ = GenericPolygon<PointZ>;
impl fmt::Display for PolygonZ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "PolygonZ({} rings)", self.rings.len())
}
}
impl HasShapeType for PolygonZ {
fn shapetype() -> ShapeType {
ShapeType::PolygonZ
}
}
impl ConcreteReadableShape for PolygonZ {
fn read_shape_content<T: Read>(source: &mut T, record_size: i32) -> Result<Self, Error> {
PolylineZ::read_shape_content(source, record_size).map(PolygonZ::from)
}
}
impl WritableShape for PolygonZ {
fn size_in_bytes(&self) -> usize {
let mut size = 0 as usize;
size += size_of::<f64>() * 4;
size += size_of::<i32>();
size += size_of::<i32>();
size += size_of::<i32>() * self.rings.len();
size += 4 * size_of::<f64>() * self.total_point_count();
size += 2 * size_of::<f64>();
size += 2 * size_of::<f64>();
size
}
fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), Error> {
let parts_iter = self.rings().iter().map(|ring| ring.points());
let writer = MultiPartShapeWriter::new(&self.bbox, parts_iter, dest);
writer.write_point_z_shape()?;
Ok(())
}
}
impl EsriShape for PolygonZ {
fn x_range(&self) -> [f64; 2] {
self.bbox.x_range()
}
fn y_range(&self) -> [f64; 2] {
self.bbox.y_range()
}
fn z_range(&self) -> [f64; 2] {
self.bbox.z_range()
}
fn m_range(&self) -> [f64; 2] {
self.bbox.m_range()
}
}
#[cfg(feature = "geo-types")]
impl<PointType> From<GenericPolygon<PointType>> for geo_types::MultiPolygon<f64>
where
PointType: ShrinkablePoint + GrowablePoint + Copy,
geo_types::Coordinate<f64>: From<PointType>,
{
fn from(p: GenericPolygon<PointType>) -> Self {
let mut last_poly = None;
let mut polygons = Vec::<geo_types::Polygon<f64>>::new();
for ring in p.rings {
match ring {
PolygonRing::Outer(points) => {
let exterior = points
.into_iter()
.map(Coordinate::<f64>::from)
.collect::<Vec<Coordinate<f64>>>();
if let Some(poly) = last_poly.take() {
polygons.push(poly);
}
last_poly = Some(geo_types::Polygon::new(LineString::from(exterior), vec![]))
}
PolygonRing::Inner(points) => {
let interior = points
.into_iter()
.map(Coordinate::<f64>::from)
.collect::<Vec<Coordinate<f64>>>();
if let Some(poly) = last_poly.as_mut() {
poly.interiors_push(interior);
} else {
polygons.push(geo_types::Polygon::<f64>::new(
LineString::<f64>::from(Vec::<Coordinate<f64>>::new()),
vec![LineString::from(interior)],
));
}
}
}
}
if let Some(poly) = last_poly.take() {
polygons.push(poly);
}
polygons.into()
}
}
#[cfg(feature = "geo-types")]
impl<PointType> From<geo_types::Polygon<f64>> for GenericPolygon<PointType>
where
PointType: From<geo_types::Coordinate<f64>>
+ GrowablePoint
+ ShrinkablePoint
+ PartialEq
+ HasXY
+ Copy,
{
fn from(polygon: geo_types::Polygon<f64>) -> Self {
let (outer, inners) = polygon.into_inner();
let mut rings = Vec::<PolygonRing<PointType>>::with_capacity(inners.len() + 1);
rings.push(PolygonRing::Outer(
outer.0.into_iter().map(PointType::from).collect(),
));
for inner in inners {
rings.push(PolygonRing::Inner(
inner.0.into_iter().map(PointType::from).collect(),
));
}
Self::with_rings(rings)
}
}
#[cfg(feature = "geo-types")]
impl<PointType> From<geo_types::MultiPolygon<f64>> for GenericPolygon<PointType>
where
PointType: HasXY
+ From<geo_types::Coordinate<f64>>
+ GrowablePoint
+ ShrinkablePoint
+ PartialEq
+ HasXY
+ Copy,
{
fn from(multi_polygon: geo_types::MultiPolygon<f64>) -> Self {
let mut all_rings = Vec::<PolygonRing<PointType>>::new();
for polygon in multi_polygon {
let mut rings = GenericPolygon::<PointType>::from(polygon).into_inner();
all_rings.append(&mut rings);
}
Self::with_rings(all_rings)
}
}
#[cfg(test)]
#[cfg(feature = "geo-types")]
mod test_geo_types {
use super::*;
#[test]
fn shapefile_polygon_to_geotypes_polygon() {
let simple_polygon = Polygon::new(PolygonRing::Outer(vec![
Point::new(-1.1, -1.01),
Point::new(-1.2, 1.02),
Point::new(1.3, 1.03),
Point::new(1.4, -1.04),
Point::new(-1.1, -1.01),
]));
let converted_multipolygon = geo_types::MultiPolygon::<f64>::from(simple_polygon);
let converted_polygon = converted_multipolygon.into_iter().next().unwrap();
let expected_geotypes_polygon = geo_types::Polygon::new(
LineString::from(vec![
(-1.1, -1.01),
(-1.2, 1.02),
(1.3, 1.03),
(1.4, -1.04),
(-1.1, -1.01),
]),
vec![],
);
assert_eq!(converted_polygon, expected_geotypes_polygon);
}
#[test]
fn shapefile_polygon_to_geotypes_polygon_auto_close() {
let simple_polygon = Polygon::new(PolygonRing::Outer(vec![
Point::new(-1.1, -1.01),
Point::new(-1.2, 1.02),
Point::new(1.3, 1.03),
Point::new(1.4, -1.04),
]));
let converted_polygon = geo_types::MultiPolygon::<f64>::from(simple_polygon);
let converted_polygon = converted_polygon.into_iter().next().unwrap();
let (geotypes_exterior, _) = converted_polygon.into_inner();
assert_eq!(
geotypes_exterior,
LineString::from(vec![
(-1.1, -1.01),
(-1.2, 1.02),
(1.3, 1.03),
(1.4, -1.04),
(-1.1, -1.01)
])
);
}
#[test]
fn geotypes_polygon_to_shapefile_polygon() {
let geotypes_polygon = geo_types::Polygon::new(
LineString::from(vec![
(-1.1, -1.01),
(-1.2, 1.02),
(1.3, 1.03),
(1.4, -1.04),
(-1.1, -1.01),
]),
vec![],
);
let converted_polygon = Polygon::from(geotypes_polygon);
let expected_polygon = Polygon::new(PolygonRing::Outer(vec![
Point::new(-1.1, -1.01),
Point::new(-1.2, 1.02),
Point::new(1.3, 1.03),
Point::new(1.4, -1.04),
Point::new(-1.1, -1.01),
]));
assert_eq!(converted_polygon, expected_polygon);
}
#[test]
fn shapefile_polygon_to_geotypes_polygon_with_inner_ring() {
let one_ring_polygon = Polygon::with_rings(vec![
PolygonRing::Outer(vec![
Point::new(-1.1, -1.01),
Point::new(-1.2, 1.02),
Point::new(1.3, 1.03),
Point::new(1.4, -1.04),
Point::new(-1.1, -1.01),
]),
PolygonRing::Inner(vec![
Point::new(-0.51, -0.501),
Point::new(0.54, -0.504),
Point::new(0.53, 0.503),
Point::new(-0.52, 0.502),
Point::new(-0.51, -0.501),
]),
]);
let converted_multipolygon = geo_types::MultiPolygon::<f64>::from(one_ring_polygon);
let converted_polygon = converted_multipolygon.into_iter().next().unwrap();
let expected_geotypes_polygon = geo_types::Polygon::new(
LineString::from(vec![
(-1.1, -1.01),
(-1.2, 1.02),
(1.3, 1.03),
(1.4, -1.04),
(-1.1, -1.01),
]),
vec![LineString::from(vec![
(-0.51, -0.501),
(0.54, -0.504),
(0.53, 0.503),
(-0.52, 0.502),
(-0.51, -0.501),
])],
);
assert_eq!(converted_polygon, expected_geotypes_polygon);
}
#[test]
fn geotypes_polygon_to_shapefile_polygon_inner_ring() {
let geotypes_polygon = geo_types::Polygon::new(
LineString::from(vec![
(-1.1, -1.01),
(-1.2, 1.02),
(1.3, 1.03),
(1.4, -1.04),
(-1.1, -1.01),
]),
vec![LineString::from(vec![
(-0.51, -0.501),
(-0.52, 0.502),
(0.53, 0.503),
(0.54, -0.504),
(-0.51, -0.501),
])],
);
let converted_polygon = Polygon::from(geotypes_polygon);
let expected_polygon = Polygon::with_rings(vec![
PolygonRing::Outer(vec![
Point::new(-1.1, -1.01),
Point::new(-1.2, 1.02),
Point::new(1.3, 1.03),
Point::new(1.4, -1.04),
Point::new(-1.1, -1.01),
]),
PolygonRing::Inner(vec![
Point::new(-0.51, -0.501),
Point::new(0.54, -0.504),
Point::new(0.53, 0.503),
Point::new(-0.52, 0.502),
Point::new(-0.51, -0.501),
]),
]);
assert_eq!(converted_polygon, expected_polygon);
}
}