1use geo_traits::PointTrait;
16
17use crate::to_wkt::write_point;
18use crate::tokenizer::PeekableTokens;
19use crate::types::coord::Coord;
20use crate::types::Dimension;
21use crate::{FromTokens, Wkt, WktNum};
22use std::fmt;
23use std::str::FromStr;
24
25#[derive(Clone, Debug, Default, PartialEq)]
27pub struct Point<T: WktNum = f64> {
28 pub(crate) coord: Option<Coord<T>>,
29 pub(crate) dim: Dimension,
30}
31
32impl<T: WktNum> Point<T> {
33 pub fn new(coord: Option<Coord<T>>, dim: Dimension) -> Self {
35 Self { coord, dim }
36 }
37
38 pub fn from_coord(coord: Coord<T>) -> Self {
42 Self {
43 dim: coord.dimension(),
44 coord: Some(coord),
45 }
46 }
47
48 pub fn empty(dim: Dimension) -> Self {
50 Self::new(None, dim)
51 }
52
53 pub fn dimension(&self) -> Dimension {
55 self.dim
56 }
57
58 pub fn coord(&self) -> Option<&Coord<T>> {
60 self.coord.as_ref()
61 }
62
63 pub fn into_inner(self) -> (Option<Coord<T>>, Dimension) {
65 (self.coord, self.dim)
66 }
67}
68
69impl<T> From<Point<T>> for Wkt<T>
70where
71 T: WktNum,
72{
73 fn from(value: Point<T>) -> Self {
74 Wkt::Point(value)
75 }
76}
77
78impl<T> fmt::Display for Point<T>
79where
80 T: WktNum + fmt::Display,
81{
82 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
83 Ok(write_point(f, self)?)
84 }
85}
86
87impl<T> FromTokens<T> for Point<T>
88where
89 T: WktNum + FromStr + Default,
90{
91 fn from_tokens(tokens: &mut PeekableTokens<T>, dim: Dimension) -> Result<Self, &'static str> {
92 let result = <Coord<T> as FromTokens<T>>::from_tokens(tokens, dim);
93 result.map(|coord| Point {
94 coord: Some(coord),
95 dim,
96 })
97 }
98
99 fn new_empty(dim: Dimension) -> Self {
100 Self::empty(dim)
101 }
102}
103
104impl<T: WktNum> PointTrait for Point<T> {
105 type CoordType<'a>
106 = &'a Coord<T>
107 where
108 Self: 'a;
109
110 fn coord(&self) -> Option<Self::CoordType<'_>> {
111 self.coord.as_ref()
112 }
113}
114
115impl<'a, T: WktNum> PointTrait for &'a Point<T> {
116 type CoordType<'b>
117 = &'a Coord<T>
118 where
119 Self: 'b;
120
121 fn coord(&self) -> Option<Self::CoordType<'_>> {
122 self.coord.as_ref()
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::{Coord, Point};
129 use crate::types::Dimension;
130 use crate::Wkt;
131 use std::str::FromStr;
132
133 #[test]
134 fn basic_point() {
135 let wkt: Wkt<f64> = Wkt::from_str("POINT (10 -20)").ok().unwrap();
136 let (coord, dim) = match wkt {
137 Wkt::Point(Point { coord, dim }) => (coord.unwrap(), dim),
138 _ => unreachable!(),
139 };
140 assert_eq!(10.0, coord.x);
141 assert_eq!(-20.0, coord.y);
142 assert_eq!(None, coord.z);
143 assert_eq!(None, coord.m);
144 assert_eq!(dim, Dimension::XY);
145 }
146
147 #[test]
148 fn basic_point_z() {
149 let wkt = Wkt::from_str("POINT Z(-117 33 10)").ok().unwrap();
150 let (coord, dim) = match wkt {
151 Wkt::Point(Point { coord, dim }) => (coord.unwrap(), dim),
152 _ => unreachable!(),
153 };
154 assert_eq!(-117.0, coord.x);
155 assert_eq!(33.0, coord.y);
156 assert_eq!(Some(10.0), coord.z);
157 assert_eq!(None, coord.m);
158 assert_eq!(dim, Dimension::XYZ);
159 }
160
161 #[test]
162 fn basic_point_z_one_word() {
163 let wkt = Wkt::from_str("POINTZ(-117 33 10)").ok().unwrap();
164 let (coord, dim) = match wkt {
165 Wkt::Point(Point { coord, dim }) => (coord.unwrap(), dim),
166 _ => unreachable!(),
167 };
168 assert_eq!(-117.0, coord.x);
169 assert_eq!(33.0, coord.y);
170 assert_eq!(Some(10.0), coord.z);
171 assert_eq!(None, coord.m);
172 assert_eq!(dim, Dimension::XYZ);
173 }
174
175 #[test]
176 fn basic_point_whitespace() {
177 let wkt: Wkt<f64> = Wkt::from_str(" \n\t\rPOINT \n\t\r( \n\r\t10 \n\t\r-20 \n\t\r) \n\t\r")
178 .ok()
179 .unwrap();
180 let (coord, dim) = match wkt {
181 Wkt::Point(Point { coord, dim }) => (coord.unwrap(), dim),
182 _ => unreachable!(),
183 };
184 assert_eq!(10.0, coord.x);
185 assert_eq!(-20.0, coord.y);
186 assert_eq!(None, coord.z);
187 assert_eq!(None, coord.m);
188 assert_eq!(dim, Dimension::XY);
189 }
190
191 #[test]
192 fn parse_empty_point() {
193 let wkt: Wkt<f64> = Wkt::from_str("POINT EMPTY").ok().unwrap();
194 match wkt {
195 Wkt::Point(Point { coord, dim }) => {
196 assert!(coord.is_none());
197 assert_eq!(dim, Dimension::XY);
198 }
199 _ => unreachable!(),
200 };
201
202 let wkt: Wkt<f64> = Wkt::from_str("POINT Z EMPTY").ok().unwrap();
203 match wkt {
204 Wkt::Point(Point { coord, dim }) => {
205 assert!(coord.is_none());
206 assert_eq!(dim, Dimension::XYZ);
207 }
208 _ => unreachable!(),
209 };
210
211 let wkt: Wkt<f64> = Wkt::from_str("POINT M EMPTY").ok().unwrap();
212 match wkt {
213 Wkt::Point(Point { coord, dim }) => {
214 assert!(coord.is_none());
215 assert_eq!(dim, Dimension::XYM);
216 }
217 _ => unreachable!(),
218 };
219
220 let wkt: Wkt<f64> = Wkt::from_str("POINT ZM EMPTY").ok().unwrap();
221 match wkt {
222 Wkt::Point(Point { coord, dim }) => {
223 assert!(coord.is_none());
224 assert_eq!(dim, Dimension::XYZM);
225 }
226 _ => unreachable!(),
227 };
228 }
229
230 #[test]
231 fn invalid_points() {
232 <Wkt<f64>>::from_str("POINT ()").err().unwrap();
233 <Wkt<f64>>::from_str("POINT (10)").err().unwrap();
234 <Wkt<f64>>::from_str("POINT 10").err().unwrap();
235 }
236
237 #[test]
238 fn write_empty_point() {
239 let point: Point<f64> = Point::empty(Dimension::XY);
240 assert_eq!("POINT EMPTY", format!("{}", point));
241
242 let point: Point<f64> = Point::empty(Dimension::XYZ);
243 assert_eq!("POINT Z EMPTY", format!("{}", point));
244
245 let point: Point<f64> = Point::empty(Dimension::XYM);
246 assert_eq!("POINT M EMPTY", format!("{}", point));
247
248 let point: Point<f64> = Point::empty(Dimension::XYZM);
249 assert_eq!("POINT ZM EMPTY", format!("{}", point));
250 }
251
252 #[test]
253 fn write_2d_point() {
254 let point = Point::from_coord(Coord {
255 x: 10.12345,
256 y: 20.67891,
257 z: None,
258 m: None,
259 });
260
261 assert_eq!("POINT(10.12345 20.67891)", format!("{}", point));
262 }
263
264 #[test]
265 fn write_point_with_z_coord() {
266 let point = Point::from_coord(Coord {
267 x: 10.12345,
268 y: 20.67891,
269 z: Some(-32.56455),
270 m: None,
271 });
272
273 assert_eq!("POINT Z(10.12345 20.67891 -32.56455)", format!("{}", point));
274 }
275
276 #[test]
277 fn write_point_with_m_coord() {
278 let point = Point::from_coord(Coord {
279 x: 10.12345,
280 y: 20.67891,
281 z: None,
282 m: Some(10.),
283 });
284
285 assert_eq!("POINT M(10.12345 20.67891 10)", format!("{}", point));
286 }
287
288 #[test]
289 fn write_point_with_zm_coord() {
290 let point = Point::from_coord(Coord {
291 x: 10.12345,
292 y: 20.67891,
293 z: Some(-32.56455),
294 m: Some(10.),
295 });
296
297 assert_eq!(
298 "POINT ZM(10.12345 20.67891 -32.56455 10)",
299 format!("{}", point)
300 );
301 }
302}