11// Pybind11 wrappers for shapelycpp geometry types.
22//
3- // Thin layer: accept Python types (py::list , scalars) → construct C++ geometry
4- // types → call native methods → wrap results back to Python.
3+ // Thin layer: accept Python types (py::array_t<T> , scalars) → construct C++
4+ // geometry types → call native methods → wrap results back to Python.
55//
6- // Inspired by the numpypy/pycpp pattern. Each wrapper matches a shapely Python
7- // API signature. Cross-type operations are exposed as free functions because
8- // pybind11 cannot bind template member functions with mixed template args .
6+ // All factories are overloaded by coordinate type — no _f32 suffixes.
7+ // linestring/polygon/linearring use py::array_t<T> so pybind11 distinguishes
8+ // by array dtype. point has both scalar (x,y) and array ([x,y]) forms .
99
1010#pragma once
1111
2424namespace py = pybind11;
2525using namespace shapely ::geometry;
2626
27+ namespace shapely_py {
28+
2729// ============================================================================
2830// Internal helpers
2931// ============================================================================
3032
31- namespace shapely_py {
32-
33- // / Convert Python list of (x,y) tuples to a flat coordinate array.
34- template <typename T>
35- py::array_t <T> py_coords_to_array (const py::list& py_coords) {
36- py::ssize_t n = py::len (py_coords);
37- auto arr = py::array_t <T>({n, static_cast <py::ssize_t >(2 )});
38- auto buf = arr.template request ();
39- T* ptr = static_cast <T*>(buf.ptr );
40- for (py::ssize_t i = 0 ; i < n; ++i) {
41- auto t = py_coords[i].cast <py::tuple>();
42- ptr[i * 2 ] = t[0 ].cast <T>();
43- ptr[i * 2 + 1 ] = t[1 ].cast <T>();
44- }
45- return arr;
46- }
47-
4833// / Copy native T* data to a Python numpy array.
4934template <typename T>
5035py::array_t <T> native_to_array (const T* data, size_t rows, size_t cols) {
@@ -54,86 +39,72 @@ py::array_t<T> native_to_array(const T* data, size_t rows, size_t cols) {
5439 return result;
5540}
5641
57- } // namespace shapely_py
58-
5942// ============================================================================
60- // Factory functions
43+ // Factory functions — all overloaded, no _f32 suffixes
6144// ============================================================================
6245
63- namespace shapely_py {
64-
46+ // -- Point: scalar (x,y) + array ([x,y]) forms --
6547inline Point<double > point (double x, double y) { return Point<double >(x, y); }
66- inline Point<float > point_f32 (float x, float y) { return Point<float >(x, y); }
48+ inline Point<float > point (float x, float y) { return Point<float >(x, y); }
6749
68- inline LineString<double > linestring (const py::list& coords) {
69- auto arr = py_coords_to_array<double >(coords);
50+ inline Point<double > point (const py::array_t <double >& arr) {
7051 auto buf = arr.request ();
71- return LineString< double >( static_cast <const double *>(buf.ptr ),
72- buf. shape [ 0 ], buf. shape [ 1 ] );
52+ const double * p = static_cast <const double *>(buf.ptr );
53+ return Point< double >(buf. size > 0 ? p[ 0 ] : 0 , buf. size > 1 ? p[ 1 ] : 0 );
7354}
74- inline LineString<float > linestring_f32 (const py::list& coords) {
75- auto arr = py_coords_to_array<float >(coords);
55+ inline Point<float > point (const py::array_t <float >& arr) {
7656 auto buf = arr.request ();
77- return LineString< float >( static_cast <const float *>(buf.ptr ),
78- buf. shape [ 0 ], buf. shape [ 1 ] );
57+ const float * p = static_cast <const float *>(buf.ptr );
58+ return Point< float >(buf. size > 0 ? p[ 0 ] : 0 , buf. size > 1 ? p[ 1 ] : 0 );
7959}
8060
81- inline Polygon< double > polygon ( const py::list& coords) {
82- auto arr = py_coords_to_array <double >(coords);
61+ // -- LineString --
62+ inline LineString< double > linestring ( const py:: array_t <double >& arr) {
8363 auto buf = arr.request ();
84- return Polygon<double >(static_cast <const double *>(buf.ptr ),
85- buf.shape [0 ], buf.shape [1 ]);
64+ return LineString<double >(static_cast <const double *>(buf.ptr ), buf.shape [0 ], buf.shape [1 ]);
8665}
87- inline Polygon<float > polygon_f32 (const py::list& coords) {
88- auto arr = py_coords_to_array<float >(coords);
66+ inline LineString<float > linestring (const py::array_t <float >& arr) {
8967 auto buf = arr.request ();
90- return Polygon<float >(static_cast <const float *>(buf.ptr ),
91- buf.shape [0 ], buf.shape [1 ]);
68+ return LineString<float >(static_cast <const float *>(buf.ptr ), buf.shape [0 ], buf.shape [1 ]);
9269}
9370
94- inline LinearRing< double > linearring ( const py::list& coords) {
95- auto arr = py_coords_to_array <double >(coords);
71+ // -- Polygon --
72+ inline Polygon< double > polygon ( const py:: array_t <double >& arr) {
9673 auto buf = arr.request ();
97- return LinearRing<double >(static_cast <const double *>(buf.ptr ),
98- buf.shape [0 ], buf.shape [1 ]);
74+ return Polygon<double >(static_cast <const double *>(buf.ptr ), buf.shape [0 ], buf.shape [1 ]);
75+ }
76+ inline Polygon<float > polygon (const py::array_t <float >& arr) {
77+ auto buf = arr.request ();
78+ return Polygon<float >(static_cast <const float *>(buf.ptr ), buf.shape [0 ], buf.shape [1 ]);
9979}
10080
101- } // namespace shapely_py
81+ // -- LinearRing (double only for now) --
82+ inline LinearRing<double > linearring (const py::array_t <double >& arr) {
83+ auto buf = arr.request ();
84+ return LinearRing<double >(static_cast <const double *>(buf.ptr ), buf.shape [0 ], buf.shape [1 ]);
85+ }
10286
10387// ============================================================================
104- // Cross-type distance wrappers
88+ // Cross-type distance
10589// ============================================================================
106- // pybind11 cannot bind templated member functions with args of a different
107- // template parameter (e.g. LineString<double>::distance(Polygon<double>)).
108- // Expose these via free functions.
109-
110- namespace shapely_py {
11190
11291inline double distance_pt_ls (const Point<double >& p, const LineString<double >& l) { return p.distance (l); }
11392inline double distance_pt_poly (const Point<double >& p, const Polygon<double >& poly) { return p.distance (poly); }
11493inline double distance_ls_poly (const LineString<double >& l, const Polygon<double >& poly) { return l.distance (poly); }
11594inline double distance_poly_ls (const Polygon<double >& poly, const LineString<double >& l) { return poly.distance (l); }
11695
117- } // namespace shapely_py
118-
11996// ============================================================================
120- // Cross-type intersects wrappers
97+ // Cross-type intersects
12198// ============================================================================
12299
123- namespace shapely_py {
124-
125100inline bool intersects_ls_poly (const LineString<double >& l, const Polygon<double >& poly) { return l.intersects (poly); }
126101inline bool intersects_poly_ls (const Polygon<double >& poly, const LineString<double >& l) { return poly.intersects (l); }
127102inline bool intersects_poly_poly (const Polygon<double >& p1, const Polygon<double >& p2) { return p1.intersects (p2); }
128103
129- } // namespace shapely_py
130-
131104// ============================================================================
132- // Predicate wrappers ( all cross-type pairs, float64)
105+ // Predicates — all 9 cross-type pairs
133106// ============================================================================
134107
135- namespace shapely_py {
136-
137108// -- Point ↔ Point --
138109inline bool pt_contains_pt (const Point<double >& s, const Point<double >& o) { return s.contains (o); }
139110inline bool pt_within_pt (const Point<double >& s, const Point<double >& o) { return s.within (o); }
@@ -269,23 +240,19 @@ inline bool poly_intersects_poly(const Polygon<double>& s, const Polygon<double>
269240inline std::string poly_relate_poly (const Polygon<double >& s, const Polygon<double >& o) { return s.relate (o); }
270241inline double poly_hausdorff_distance_poly (const Polygon<double >& s, const Polygon<double >& o) { return s.hausdorff_distance (o); }
271242
272- } // namespace shapely_py
273-
274243// ============================================================================
275244// Centroid, project, interpolate
276245// ============================================================================
277246
278- namespace shapely_py {
279-
280- inline std::tuple<double , double > centroid_point (const Point<double >& p) { auto r = p.centroid (); return {r.x , r.y }; }
281- inline std::tuple<double , double > centroid_linestring (const LineString<double >& l) { auto r = l.centroid (); return {r.x , r.y }; }
282- inline std::tuple<double , double > centroid_polygon (const Polygon<double >& p) { auto r = p.centroid (); return {r.x , r.y }; }
247+ inline std::tuple<double ,double > centroid_point (const Point<double >& p) { auto r=p.centroid (); return {r.x ,r.y }; }
248+ inline std::tuple<double ,double > centroid_linestring (const LineString<double >& l) { auto r=l.centroid (); return {r.x ,r.y }; }
249+ inline std::tuple<double ,double > centroid_polygon (const Polygon<double >& p) { auto r=p.centroid (); return {r.x ,r.y }; }
283250
284251inline double project_ls_pt (const LineString<double >& l, const Point<double >& p) { return l.project (p); }
285- inline std::tuple<double , double > interpolate_ls (const LineString<double >& l, double d) { auto r = l.interpolate (d); return {r.x , r.y }; }
252+ inline std::tuple<double ,double > interpolate_ls (const LineString<double >& l, double d) { auto r= l.interpolate (d); return {r.x ,r.y }; }
286253
287254inline double intersection_area_poly_poly (const Polygon<double >& p1, const Polygon<double >& p2) {
288- auto inter = p1.intersection (p2); return inter .area ();
255+ return p1.intersection (p2).area ();
289256}
290257
291258inline py::array_t <double > polygon_exterior (const Polygon<double >& p) {
@@ -298,8 +265,6 @@ inline py::array_t<double> polygon_exterior(const Polygon<double>& p) {
298265// ============================================================================
299266// Pybind11 binding helper macros
300267// ============================================================================
301- // Use these in your PYBIND11_MODULE to consistently bind geometry classes
302- // and cross-type predicates. See tests/module.cpp for usage examples.
303268
304269#define BIND_PREDS (m, SRC, TGT ) \
305270 m.def(#SRC " _contains_" #TGT, &shapely_py::SRC ## _contains_ ## TGT); \
0 commit comments