1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//! Exchange vector geometries between Rust and Python using [pyo3](https://pyo3.rs) and [Pythons `__geo_interface__` protocol](https://gist.github.com/sgillies/2217756).
//!
//! The `__geo_interface__` protocol is implemented by most popular geospatial python modules like `shapely`, `geojson`, `geopandas`, ....
//! While this protocol also defines `Features` and `FeatureCollections`, this library so far only focuses on the `Geometry` type, as
//! this one can be directly mapped to the types of the `geo-types` crate.
//!
//! The main struct of this crate is [`Geometry`]. This the docs there for usage examples.
//!
//! ## Features
//!
//! As rust types exposed to python may not have generic type parameters, there are multiple implementations of the `Geometry` type based
//! on different types for the coordinate values. The default is `f64`, other types can be enabled using the `f32`, `u8`, `u16`, `u32`, `u64`,
//! `i8`, `i16`, `i32` and `i64` feature gates. The implementation are then available as `py_geo_interface::wrappers::[datatype]::Geometry`.
//! The default and probably most common used `f64`-variant is also available as `py_geo_interface::Geometry`.
//!
//! The `wkb` feature adds support for exchanging geometries using the Well-Known-Binary format. The `wkb`-property of `shapely`
//! geometries will be used when found. Additionally, the `Geometry`-type exposed to python will have a `wkb`-property
//! itself. WKB is only supported for the `f64`-variant of the `Geometry`, the feature is disabled per default.
//!
//! ## Examples
//!
//! ### Read python types implementing `__geo_interface__` into `geo-types`:
//!
//! #[include]
//! ```rust
//! use geo_types::{Geometry as GtGeometry, Point};
//! use pyo3::{prepare_freethreaded_python, Python};
//! use py_geo_interface::Geometry;
//!
//! prepare_freethreaded_python();
//!
//! let geom = Python::with_gil(|py| {
//!
//!     // Define a python class implementing the geo_interface. This could also be a shapely or geojson
//!     // object instead. These provide the same interface.
//!     py.run(r#"
//! class Something:
//!     @property
//!     def __geo_interface__(self):
//!          return {"type": "Point", "coordinates": [5., 3.]}
//! "#, None, None).unwrap();
//!
//!     // create an instance of the class and extract the geometry
//!     py.eval(r#"Something()"#, None, None)?.extract::<Geometry>()
//! }).unwrap();
//! assert_eq!(geom.0, GtGeometry::Point(Point::new(5.0_f64, 3.0_f64)));
//! ```
//!
//! ### Pass geometries from Rust to Python:
//!
//! ```rust
//! use geo_types::{Geometry as GtGeometry, Point};
//! use pyo3::{prepare_freethreaded_python, Python};
//! use pyo3::types::{PyDict, PyTuple};
//! use pyo3::IntoPy;
//! use py_geo_interface::Geometry;
//!
//! prepare_freethreaded_python();
//!
//! Python::with_gil(|py| {
//!
//!     let geom: Geometry = Point::new(10.6_f64, 23.3_f64).into();
//!     let mut locals = PyDict::new(py);
//!     locals.set_item("geom", geom.into_py(py)).unwrap();
//!
//!     py.run(r#"
//! assert geom.__geo_interface__["type"] == "Point"
//! assert geom.__geo_interface__["coordinates"] == (10.6, 23.3)
//! "#, None, Some(locals)).unwrap();
//! });
//! ```

pub mod from_py;
pub mod to_py;
pub mod wrappers;

#[cfg(feature = "wkb")]
pub mod wkb;

use crate::from_py::{ExtractFromPyFloat, ExtractFromPyInt};
#[cfg(feature = "wkb")]
use crate::wkb::WKBSupport;
use geo_types::CoordNum;
use pyo3::prelude::*;

#[cfg(feature = "wkb")]
pub trait PyCoordNum:
    CoordNum + IntoPy<Py<PyAny>> + ExtractFromPyFloat + ExtractFromPyInt + WKBSupport
{
}

#[cfg(not(feature = "wkb"))]
pub trait PyCoordNum: CoordNum + IntoPy<Py<PyAny>> + ExtractFromPyFloat + ExtractFromPyInt {}

#[cfg(feature = "wkb")]
impl<T: CoordNum + IntoPy<Py<PyAny>> + ExtractFromPyFloat + ExtractFromPyInt + WKBSupport>
    PyCoordNum for T
{
}

#[cfg(not(feature = "wkb"))]
impl<T: CoordNum + IntoPy<Py<PyAny>> + ExtractFromPyFloat + ExtractFromPyInt> PyCoordNum for T {}

#[cfg(feature = "f64")]
pub use crate::wrappers::f64::Geometry;
#[cfg(feature = "f64")]
pub use crate::wrappers::f64::GeometryVec;
#[cfg(feature = "f64")]
pub use crate::wrappers::f64::GeometryVecFc;