1use std::fmt;
2
3use crate::sql_types::Geography;
4use crate::sql_types::Geometry;
5
6#[derive(Debug, Clone, PartialEq)]
17pub struct PointConstructorError {
18 pub reason: String,
19}
20
21impl fmt::Display for PointConstructorError {
22 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23 write!(f, "can't construct point: {}", self.reason)
24 }
25}
26
27impl std::error::Error for PointConstructorError {}
28
29#[derive(Copy, Clone, Debug, PartialEq)]
40#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
41#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
42#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
43#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
44#[cfg_attr(feature = "schemars", derive(JsonSchema))]
45pub struct Point {
46 pub x: f64,
47 pub y: f64,
48 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
49 pub srid: Option<u32>,
50}
51
52#[derive(Copy, Clone, Debug, PartialEq)]
63#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
64#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
65#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
66#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
67#[cfg_attr(feature = "schemars", derive(JsonSchema))]
68pub struct PointZ {
69 pub x: f64,
70 pub y: f64,
71 pub z: f64,
72 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
73 pub srid: Option<u32>,
74}
75
76#[derive(Copy, Clone, Debug, PartialEq)]
87#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
88#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
89#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
90#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
91#[cfg_attr(feature = "schemars", derive(JsonSchema))]
92pub struct PointM {
93 pub x: f64,
94 pub y: f64,
95 pub m: f64,
96 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
97 pub srid: Option<u32>,
98}
99
100#[derive(Copy, Clone, Debug, PartialEq)]
111#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
112#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
113#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
114#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
115#[cfg_attr(feature = "schemars", derive(JsonSchema))]
116pub struct PointZM {
117 pub x: f64,
118 pub y: f64,
119 pub z: f64,
120 pub m: f64,
121 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
122 pub srid: Option<u32>,
123}
124
125pub trait PointT {
127 fn new_point(
128 x: f64,
129 y: f64,
130 srid: Option<u32>,
131 z: Option<f64>,
132 m: Option<f64>,
133 ) -> Result<Self, PointConstructorError>
134 where
135 Self: Sized;
136 fn get_x(&self) -> f64;
137 fn get_y(&self) -> f64;
138 fn get_srid(&self) -> Option<u32>;
139 fn get_z(&self) -> Option<f64>;
140 fn get_m(&self) -> Option<f64>;
141 fn dimension(&self) -> u32;
142}
143
144#[derive(Clone, Debug, PartialEq)]
155#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
156#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
157#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
158#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
159#[cfg_attr(feature = "schemars", derive(JsonSchema))]
160pub struct MultiPoint<T> {
161 pub points: Vec<T>,
162 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
163 pub srid: Option<u32>,
164}
165
166#[derive(Clone, Debug, PartialEq)]
177#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
178#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
179#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
180#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
181#[cfg_attr(feature = "schemars", derive(JsonSchema))]
182pub struct LineString<T> {
183 pub points: Vec<T>,
184 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
185 pub srid: Option<u32>,
186}
187
188#[derive(Clone, Debug, PartialEq)]
199#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
200#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
201#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
202#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
203#[cfg_attr(feature = "schemars", derive(JsonSchema))]
204pub struct MultiLineString<T> {
205 pub lines: Vec<LineString<T>>,
206 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
207 pub srid: Option<u32>,
208}
209
210#[derive(Clone, Debug, PartialEq)]
221#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
222#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
223#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
224#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
225#[cfg_attr(feature = "schemars", derive(JsonSchema))]
226pub struct Polygon<T> {
227 pub rings: Vec<Vec<T>>,
228 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
229 pub srid: Option<u32>,
230}
231
232#[derive(Clone, Debug, PartialEq)]
243#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
244#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
245#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
246#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
247#[cfg_attr(feature = "schemars", derive(JsonSchema))]
248pub struct MultiPolygon<T> {
249 pub polygons: Vec<Polygon<T>>,
250 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
251 pub srid: Option<u32>,
252}
253
254#[derive(Clone, Debug, PartialEq)]
258#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
259#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
260#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
261#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
262#[cfg_attr(feature = "serde_geojson", derive(Deserialize))]
263#[cfg_attr(
264 feature = "serde_geojson",
265 serde(
266 tag = "type",
267 bound(
268 deserialize = "T: crate::geojson::GeoJsonGeometry<f64> + PointT + Clone + serde::Deserialize<'de>"
269 )
270 )
271)]
272#[cfg_attr(feature = "schemars", derive(JsonSchema))]
273pub enum GeometryContainer<T> {
274 Point(T),
275 LineString(LineString<T>),
276 Polygon(Polygon<T>),
277 MultiPoint(MultiPoint<T>),
278 MultiLineString(MultiLineString<T>),
279 MultiPolygon(MultiPolygon<T>),
280 GeometryCollection(GeometryCollection<T>),
281}
282
283#[derive(Clone, Debug, PartialEq)]
294#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
295#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
296#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
297#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
298#[cfg_attr(feature = "schemars", derive(JsonSchema))]
299pub struct GeometryCollection<T> {
300 pub geometries: Vec<GeometryContainer<T>>,
301 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
302 pub srid: Option<u32>,
303}
304
305#[cfg(feature = "serde_geojson")]
306#[derive(Clone, Debug, PartialEq, Deserialize)]
307#[serde(
308 tag = "type",
309 bound(
310 deserialize = "T: crate::geojson::GeoJsonGeometry<f64> + PointT + Clone + serde::Deserialize<'de>, P: serde::Deserialize<'de>"
311 )
312)]
313pub struct Feature<T, P: serde::Serialize> {
314 pub id: Option<String>,
315 pub geometry: Option<GeometryContainer<T>>,
316 pub properties: Option<P>,
317}
318
319#[cfg(feature = "serde_geojson")]
320#[derive(Clone, Debug, PartialEq, Deserialize)]
321#[serde(
322 tag = "type",
323 bound(
324 deserialize = "T: crate::geojson::GeoJsonGeometry<f64> + PointT + Clone + serde::Deserialize<'de>, P: serde::Deserialize<'de>"
325 )
326)]
327pub struct FeatureCollection<T, P: serde::Serialize> {
328 pub features: Vec<Feature<T, P>>,
329}
330
331#[cfg(test)]
332#[cfg(feature = "serde")]
333mod tests {
334 use super::*;
336
337 #[test]
338 fn test_point_serde() {
339 let point = Point::new(72.0, 64.0, None);
340 let expected_point = "{\"x\":72.0,\"y\":64.0}";
341 let point_from_json = serde_json::from_str(expected_point).unwrap();
342 assert_eq!(point, point_from_json);
343 let point_json = serde_json::to_string(&point).unwrap();
344 assert_eq!(expected_point, point_json);
345
346 #[cfg(feature = "schemars")]
347 {
348 let schema = schema_for!(Point);
349 let schema_json = serde_json::to_string(&schema).unwrap();
350 let expected_schema = r#"{"$schema":"http://json-schema.org/draft-07/schema#","title":"Point","description":"Use that structure in `Insertable` or `Queryable` struct if you work with Point geometry. ``` #[macro_use] extern crate diesel; use postgis_diesel::types::Point; #[derive(Queryable)] struct QueryablePointExample { id: i32, point: Point, } ```","type":"object","required":["x","y"],"properties":{"srid":{"type":["integer","null"],"format":"uint32","minimum":0.0},"x":{"type":"number","format":"double"},"y":{"type":"number","format":"double"}}}"#;
351 assert_eq!(expected_schema, schema_json);
352
353 let schema_for_value = schema_for_value!(point);
354 let schema_for_value_json = serde_json::to_string(&schema_for_value).unwrap();
355 println!("{}", schema_for_value_json);
356 let expected_schema_for_value = r#"{"$schema":"http://json-schema.org/draft-07/schema#","title":"Point","examples":[{"x":72.0,"y":64.0}],"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"}}}"#;
357 assert_eq!(expected_schema_for_value, schema_for_value_json);
358 }
359 }
360}