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