ogc_cql2/geom/
polygons.rs1#![warn(missing_docs)]
4
5use crate::{
9 CRS, GTrait, MyError, Polygon,
10 config::config,
11 geom::{XY3V, XY4V},
12 srid::SRID,
13};
14use core::fmt;
15use geos::{ConstGeometry, Geom, Geometry};
16use std::slice::Iter;
17use tracing::{error, warn};
18
19#[derive(Debug, Clone, PartialEq, PartialOrd)]
21pub struct Polygons {
22 polygons: XY4V,
23 srid: SRID,
24}
25
26impl GTrait for Polygons {
27 fn is_2d(&self) -> bool {
28 self.polygons[0][0][0].len() == 2
29 }
30
31 fn to_wkt_fmt(&self, precision: usize) -> String {
32 if self.is_2d() {
33 format!(
34 "MULTIPOLYGON {}",
35 Self::coords_with_dp(self.polygons.as_slice(), precision)
36 )
37 } else {
38 format!(
39 "MULTIPOLYGON Z {}",
40 Self::coords_with_dp(self.polygons.as_slice(), precision)
41 )
42 }
43 }
44
45 fn check_coordinates(&self, crs: &CRS) -> Result<(), MyError> {
46 if self
47 .polygons
48 .iter()
49 .all(|poly| crs.check_polygon(poly).is_ok())
50 {
51 Ok(())
52 } else {
53 Err(MyError::Runtime(
54 "At least one polygon has invalid coordinates".into(),
55 ))
56 }
57 }
58
59 fn type_(&self) -> &str {
60 "MultiPolygon"
61 }
62
63 fn srid(&self) -> SRID {
64 self.srid
65 }
66}
67
68impl Polygons {
69 pub fn num_polygons(&self) -> usize {
71 self.polygons.len()
72 }
73
74 pub fn polygons(&self) -> Iter<'_, XY3V> {
76 self.polygons.iter()
77 }
78
79 pub(crate) fn from_xy(polygons: XY4V) -> Self {
80 Self::from_xy_and_srid(polygons, *config().default_srid())
81 }
82
83 pub(crate) fn from_xy_and_srid(polygons: XY4V, srid: SRID) -> Self {
84 let polygons = polygons
85 .iter()
86 .map(|x| Polygon::ensure_precision_xy(x))
87 .collect();
88 Polygons { polygons, srid }
89 }
90
91 pub(crate) fn coords_as_txt(polygons: &[XY3V]) -> String {
92 Self::coords_with_dp(polygons, config().default_precision())
93 }
94
95 pub(crate) fn to_geos(&self) -> Result<Geometry, MyError> {
96 let mut polygons: Vec<Geometry> = vec![];
97 for p in &self.polygons {
98 let g = Polygon::to_geos_xy(p, &self.srid)?;
99 polygons.push(g);
100 }
101 let mut g = Geometry::create_multipolygon(polygons)?;
102 let srs_id = self.srid.as_usize()?;
103 g.set_srid(srs_id);
104
105 Ok(g)
106 }
107
108 pub(crate) fn from_geos_xy<T: Geom>(gg: T) -> Result<XY4V, MyError> {
109 let num_polygons = gg.get_num_geometries()?;
110 let mut result = Vec::with_capacity(num_polygons);
111 for ndx in 0..num_polygons {
112 let polygon = gg.get_geometry_n(ndx)?;
113 let xy = Polygon::from_geos_xy(polygon)?;
114 result.push(xy);
115 }
116 Ok(result)
117 }
118
119 pub(crate) fn set_srid_unchecked(&mut self, srid: &SRID) {
120 if self.srid != *srid {
121 warn!("Replacing current SRID ({}) w/ {srid}", self.srid);
122 self.srid = srid.to_owned();
123 }
124 }
125
126 fn coords_with_dp(polygons: &[XY3V], precision: usize) -> String {
127 let polygons: Vec<String> = polygons
128 .iter()
129 .map(|x| Polygon::coords_with_dp(x.as_slice(), precision))
130 .collect();
131 format!("({})", polygons.join(", "))
132 }
133}
134
135impl fmt::Display for Polygons {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
137 write!(f, "Polygons (...)")
138 }
139}
140
141impl TryFrom<Geometry> for Polygons {
142 type Error = MyError;
143
144 fn try_from(value: Geometry) -> Result<Self, Self::Error> {
145 let srs_id = value.get_srid().unwrap_or_else(|x| {
146 error!(
147 "Failed get_srid for GEOS MultiPolygon. Will use Undefined: {}",
148 x
149 );
150 Default::default()
151 });
152 let polygons = Polygons::from_geos_xy(value)?;
153 let srid = SRID::try_from(srs_id)?;
154 Ok(Polygons::from_xy_and_srid(polygons, srid))
155 }
156}
157
158impl TryFrom<ConstGeometry<'_>> for Polygons {
159 type Error = MyError;
160
161 fn try_from(value: ConstGeometry) -> Result<Self, Self::Error> {
162 let srs_id = value.get_srid().unwrap_or_else(|x| {
163 error!(
164 "Failed get_srid for GEOS MultiPolygon. Will use Undefined: {}",
165 x
166 );
167 Default::default()
168 });
169 let polygons = Polygons::from_geos_xy(value)?;
170 let srid = SRID::try_from(srs_id)?;
171 Ok(Polygons::from_xy_and_srid(polygons, srid))
172 }
173}