@@ -32,8 +32,9 @@ using namespace shapely::geometry;
3232namespace shapely_py {
3333
3434// ============================================================================
35- // Internal helpers
35+ // Internal helpers — hidden in detail namespace
3636// ============================================================================
37+ namespace detail {
3738
3839// / Copy native T* data to a Python numpy array.
3940template <typename T>
@@ -99,6 +100,8 @@ inline std::vector<double> array_to_double_vec(const py::array& arr) {
99100 return tmp;
100101}
101102
103+ } // namespace detail
104+
102105// ============================================================================
103106// Factory functions — all overloaded, no _f32 suffixes
104107// ============================================================================
@@ -109,14 +112,14 @@ inline Point<float> point(float x, float y) { return Point<float>(x, y); }
109112
110113inline Point<double > point (const py::array_t <double >& arr) {
111114 auto buf = arr.request ();
112- ensure_c_contiguous (buf);
115+ detail:: ensure_c_contiguous (buf);
113116 const double * p = static_cast <const double *>(buf.ptr );
114117 return Point<double >(buf.size > 0 ? p[0 ] : 0 , buf.size > 1 ? p[1 ] : 0 );
115118}
116119// float32 auto-upcast: prevent GEOS precision mismatch vs Python shapely
117120inline Point<double > point (const py::array_t <float >& arr) {
118121 auto buf = arr.request ();
119- ensure_c_contiguous (buf);
122+ detail:: ensure_c_contiguous (buf);
120123 const float * p = static_cast <const float *>(buf.ptr );
121124 return Point<double >(buf.size > 0 ? static_cast <double >(p[0 ]) : 0 ,
122125 buf.size > 1 ? static_cast <double >(p[1 ]) : 0 );
@@ -125,7 +128,7 @@ inline Point<double> point(const py::array_t<float>& arr) {
125128// Handles 1D [x,y] and 2D (N,≥2) arrays — takes first 2 elements.
126129inline Point<double > point (const py::array& arr) {
127130 auto buf = arr.request ();
128- ensure_c_contiguous (buf);
131+ detail:: ensure_c_contiguous (buf);
129132 if (buf.size < 2 ) return Point<double >(0 , 0 );
130133 if (arr.dtype ().is (py::dtype::of<double >())) {
131134 const double * p = static_cast <const double *>(buf.ptr );
@@ -142,18 +145,18 @@ inline Point<double> point(const py::array& arr) {
142145// -- LineString --
143146inline LineString<double > linestring (const py::array_t <double >& arr) {
144147 auto buf = arr.request ();
145- ensure_c_contiguous (buf);
148+ detail:: ensure_c_contiguous (buf);
146149 return LineString<double >(static_cast <const double *>(buf.ptr ), buf.shape [0 ], buf.shape [1 ]);
147150}
148151// float32 auto-upcast: prevent GEOS precision mismatch vs Python shapely
149152inline LineString<double > linestring (const py::array_t <float >& arr) {
150153 py::ssize_t n = arr.request ().shape [0 ];
151- auto tmp = array_to_double_vec (arr);
154+ auto tmp = detail:: array_to_double_vec (arr);
152155 return LineString<double >(tmp.data (), static_cast <size_t >(n), 2 );
153156}
154157inline LineString<double > linestring (const py::array& arr) {
155158 py::ssize_t n = arr.request ().shape [0 ];
156- auto tmp = array_to_double_vec (arr);
159+ auto tmp = detail:: array_to_double_vec (arr);
157160 return LineString<double >(tmp.data (), static_cast <size_t >(n), 2 );
158161}
159162
@@ -173,48 +176,48 @@ inline LineString<double> linestring(const std::vector<std::array<double, 2>>& p
173176// -- Polygon --
174177inline Polygon<double > polygon (const py::array_t <double >& arr) {
175178 auto buf = arr.request ();
176- ensure_c_contiguous (buf);
179+ detail:: ensure_c_contiguous (buf);
177180 return Polygon<double >(static_cast <const double *>(buf.ptr ), buf.shape [0 ], buf.shape [1 ]);
178181}
179182// float32 auto-upcast: prevent GEOS precision mismatch vs Python shapely
180183inline Polygon<double > polygon (const py::array_t <float >& arr) {
181184 py::ssize_t n = arr.request ().shape [0 ];
182- auto tmp = array_to_double_vec (arr);
185+ auto tmp = detail:: array_to_double_vec (arr);
183186 return Polygon<double >(tmp.data (), static_cast <size_t >(n), 2 );
184187}
185188inline Polygon<double > polygon (const py::array& arr) {
186189 py::ssize_t n = arr.request ().shape [0 ];
187- auto tmp = array_to_double_vec (arr);
190+ auto tmp = detail:: array_to_double_vec (arr);
188191 return Polygon<double >(tmp.data (), static_cast <size_t >(n), 2 );
189192}
190193
191194// -- LinearRing (double only for now) --
192195inline LinearRing<double > linearring (const py::array_t <double >& arr) {
193196 auto buf = arr.request ();
194- ensure_c_contiguous (buf);
197+ detail:: ensure_c_contiguous (buf);
195198 return LinearRing<double >(static_cast <const double *>(buf.ptr ), buf.shape [0 ], buf.shape [1 ]);
196199}
197200inline LinearRing<double > linearring (const py::array& arr) {
198201 py::ssize_t n = arr.request ().shape [0 ];
199- auto tmp = array_to_double_vec (arr);
202+ auto tmp = detail:: array_to_double_vec (arr);
200203 return LinearRing<double >(tmp.data (), static_cast <size_t >(n), 2 );
201204}
202205
203206// -- MultiPoint: single array of shape (n_pts, 2) --
204207inline MultiPoint<double > multipoint (const py::array_t <double >& arr) {
205208 auto buf = arr.request ();
206- ensure_c_contiguous (buf);
209+ detail:: ensure_c_contiguous (buf);
207210 return MultiPoint<double >(static_cast <const double *>(buf.ptr ), buf.shape [0 ], buf.shape [1 ]);
208211}
209212// float32 auto-upcast: prevent GEOS precision mismatch vs Python shapely
210213inline MultiPoint<double > multipoint (const py::array_t <float >& arr) {
211214 py::ssize_t n = arr.request ().shape [0 ];
212- auto tmp = array_to_double_vec (arr);
215+ auto tmp = detail:: array_to_double_vec (arr);
213216 return MultiPoint<double >(tmp.data (), static_cast <size_t >(n), 2 );
214217}
215218inline MultiPoint<double > multipoint (const py::array& arr) {
216219 py::ssize_t n = arr.request ().shape [0 ];
217- auto tmp = array_to_double_vec (arr);
220+ auto tmp = detail:: array_to_double_vec (arr);
218221 return MultiPoint<double >(tmp.data (), static_cast <size_t >(n), 2 );
219222}
220223
@@ -223,7 +226,7 @@ inline MultiLineString<double> multilinestring(const std::vector<py::array_t<dou
223226 MultiLineString<double > mls;
224227 for (auto & arr : arrays) {
225228 auto buf = arr.request ();
226- ensure_c_contiguous (buf);
229+ detail:: ensure_c_contiguous (buf);
227230 mls.add_line (static_cast <const double *>(buf.ptr ), buf.shape [0 ], buf.shape [1 ]);
228231 }
229232 return mls;
@@ -233,7 +236,7 @@ inline MultiLineString<double> multilinestring(const std::vector<py::array_t<flo
233236 MultiLineString<double > mls;
234237 for (auto & arr : arrays) {
235238 py::ssize_t n = arr.request ().shape [0 ];
236- auto tmp = array_to_double_vec (arr);
239+ auto tmp = detail:: array_to_double_vec (arr);
237240 mls.add_line (tmp.data (), static_cast <size_t >(n), 2 );
238241 }
239242 return mls;
@@ -242,7 +245,7 @@ inline MultiLineString<double> multilinestring(const std::vector<py::array>& arr
242245 MultiLineString<double > mls;
243246 for (auto & arr : arrays) {
244247 py::ssize_t n = arr.request ().shape [0 ];
245- auto tmp = array_to_double_vec (arr);
248+ auto tmp = detail:: array_to_double_vec (arr);
246249 mls.add_line (tmp.data (), static_cast <size_t >(n), 2 );
247250 }
248251 return mls;
@@ -253,7 +256,7 @@ inline MultiPolygon<double> multipolygon(const std::vector<py::array_t<double>>&
253256 MultiPolygon<double > mp;
254257 for (auto & arr : arrays) {
255258 auto buf = arr.request ();
256- ensure_c_contiguous (buf);
259+ detail:: ensure_c_contiguous (buf);
257260 mp.add_polygon (static_cast <const double *>(buf.ptr ), buf.shape [0 ], buf.shape [1 ]);
258261 }
259262 return mp;
@@ -263,7 +266,7 @@ inline MultiPolygon<double> multipolygon(const std::vector<py::array_t<float>>&
263266 MultiPolygon<double > mp;
264267 for (auto & arr : arrays) {
265268 py::ssize_t n = arr.request ().shape [0 ];
266- auto tmp = array_to_double_vec (arr);
269+ auto tmp = detail:: array_to_double_vec (arr);
267270 mp.add_polygon (tmp.data (), static_cast <size_t >(n), 2 );
268271 }
269272 return mp;
@@ -272,32 +275,33 @@ inline MultiPolygon<double> multipolygon(const std::vector<py::array>& arrays) {
272275 MultiPolygon<double > mp;
273276 for (auto & arr : arrays) {
274277 py::ssize_t n = arr.request ().shape [0 ];
275- auto tmp = array_to_double_vec (arr);
278+ auto tmp = detail:: array_to_double_vec (arr);
276279 mp.add_polygon (tmp.data (), static_cast <size_t >(n), 2 );
277280 }
278281 return mp;
279282}
280283
281284// ============================================================================
282- // Cross-type distance
285+ // Cross-type wrappers (distance, intersects, predicates, centroid, etc.)
286+ // Thin wrappers that bridge pybind11's cross-type dispatch limitation.
287+ // Hidden in detail — downstream code should use shapely::geometry methods directly.
283288// ============================================================================
289+ namespace detail {
290+
291+ // -- Cross-type distance --
284292
285293inline double distance_pt_ls (const Point<double >& p, const LineString<double >& l) { return p.distance (l); }
286294inline double distance_pt_poly (const Point<double >& p, const Polygon<double >& poly) { return p.distance (poly); }
287295inline double distance_ls_poly (const LineString<double >& l, const Polygon<double >& poly) { return l.distance (poly); }
288296inline double distance_poly_ls (const Polygon<double >& poly, const LineString<double >& l) { return poly.distance (l); }
289297
290- // ============================================================================
291- // Cross-type intersects
292- // ============================================================================
298+ // -- Cross-type intersects --
293299
294300inline bool intersects_ls_poly (const LineString<double >& l, const Polygon<double >& poly) { return l.intersects (poly); }
295301inline bool intersects_poly_ls (const Polygon<double >& poly, const LineString<double >& l) { return poly.intersects (l); }
296302inline bool intersects_poly_poly (const Polygon<double >& p1, const Polygon<double >& p2) { return p1.intersects (p2); }
297303
298- // ============================================================================
299- // Predicates — all 9 cross-type pairs
300- // ============================================================================
304+ // -- Predicates — all 9 cross-type pairs --
301305
302306// -- Point ↔ Point --
303307inline bool pt_contains_pt (const Point<double >& s, const Point<double >& o) { return s.contains (o); }
@@ -434,50 +438,44 @@ inline bool poly_intersects_poly(const Polygon<double>& s, const Polygon<double>
434438inline std::string poly_relate_poly (const Polygon<double >& s, const Polygon<double >& o) { return s.relate (o); }
435439inline double poly_hausdorff_distance_poly (const Polygon<double >& s, const Polygon<double >& o) { return s.hausdorff_distance (o); }
436440
437- // ============================================================================
438- // Centroid, project, interpolate
439- // ============================================================================
441+ // -- Centroid --
440442
441443inline std::tuple<double ,double > centroid_point (const Point<double >& p) { auto r=p.centroid (); return {r.x ,r.y }; }
442444inline std::tuple<double ,double > centroid_linestring (const LineString<double >& l) { auto r=l.centroid (); return {r.x ,r.y }; }
443445inline std::tuple<double ,double > centroid_polygon (const Polygon<double >& p) { auto r=p.centroid (); return {r.x ,r.y }; }
444446
445- inline double project_ls_pt (const LineString<double >& l, const Point<double >& p) { return l.project (p); }
446- inline std::tuple<double ,double > interpolate_ls (const LineString<double >& l, double d, bool normalized = false ) {
447- auto r = l.interpolate (d, normalized);
448- return {r.x , r.y };
449- }
450-
451447inline double intersection_area_poly_poly (const Polygon<double >& p1, const Polygon<double >& p2) {
452448 return p1.intersection (p2).area ();
453449}
454450
455451inline py::array_t <double > polygon_exterior (const Polygon<double >& p) {
456452 auto ext = p.exterior ();
457- return native_to_array (ext.data (), ext.rows (), ext.cols ());
453+ return detail:: native_to_array (ext.data (), ext.rows (), ext.cols ());
458454}
459455
456+ } // namespace detail
457+
460458} // namespace shapely_py
461459
462460// ============================================================================
463461// Pybind11 binding helper macros
464462// ============================================================================
465463
466464#define BIND_PREDS (m, SRC, TGT ) \
467- m.def(#SRC " _contains_" #TGT , &shapely_py::SRC ## _contains_ ## TGT ); \
468- m.def(#SRC " _within_" #TGT , &shapely_py::SRC ## _within_ ## TGT ); \
469- m.def(#SRC " _crosses_" #TGT , &shapely_py::SRC ## _crosses_ ## TGT ); \
470- m.def(#SRC " _disjoint_" #TGT , &shapely_py::SRC ## _disjoint_ ## TGT ); \
471- m.def(#SRC " _overlaps_" #TGT , &shapely_py::SRC ## _overlaps_ ## TGT ); \
472- m.def(#SRC " _touches_" #TGT , &shapely_py::SRC ## _touches_ ## TGT ); \
473- m.def(#SRC " _covers_" #TGT , &shapely_py::SRC ## _covers_ ## TGT ); \
474- m.def(#SRC " _covered_by_" #TGT , &shapely_py::SRC ## _covered_by_ ## TGT ); \
475- m.def(#SRC " _equals_" #TGT , &shapely_py::SRC ## _equals_ ## TGT ); \
476- m.def(#SRC " _equals_exact_" #TGT , &shapely_py::SRC ## _equals_exact_ ## TGT , \
465+ m.def(#SRC " _contains_" #TGT , &shapely_py::detail:: SRC ## _contains_ ## TGT ); \
466+ m.def(#SRC " _within_" #TGT , &shapely_py::detail:: SRC ## _within_ ## TGT ); \
467+ m.def(#SRC " _crosses_" #TGT , &shapely_py::detail:: SRC ## _crosses_ ## TGT ); \
468+ m.def(#SRC " _disjoint_" #TGT , &shapely_py::detail:: SRC ## _disjoint_ ## TGT ); \
469+ m.def(#SRC " _overlaps_" #TGT , &shapely_py::detail:: SRC ## _overlaps_ ## TGT ); \
470+ m.def(#SRC " _touches_" #TGT , &shapely_py::detail:: SRC ## _touches_ ## TGT ); \
471+ m.def(#SRC " _covers_" #TGT , &shapely_py::detail:: SRC ## _covers_ ## TGT ); \
472+ m.def(#SRC " _covered_by_" #TGT , &shapely_py::detail:: SRC ## _covered_by_ ## TGT ); \
473+ m.def(#SRC " _equals_" #TGT , &shapely_py::detail:: SRC ## _equals_ ## TGT ); \
474+ m.def(#SRC " _equals_exact_" #TGT , &shapely_py::detail:: SRC ## _equals_exact_ ## TGT , \
477475 py::arg (" self" ), py::arg(" other" ), py::arg(" tolerance" )); \
478- m.def(#SRC " _intersects_" #TGT , &shapely_py::SRC ## _intersects_ ## TGT ); \
479- m.def(#SRC " _relate_" #TGT , &shapely_py::SRC ## _relate_ ## TGT ); \
480- m.def(#SRC " _hausdorff_distance_" #TGT , &shapely_py::SRC ## _hausdorff_distance_ ## TGT );
476+ m.def(#SRC " _intersects_" #TGT , &shapely_py::detail:: SRC ## _intersects_ ## TGT ); \
477+ m.def(#SRC " _relate_" #TGT , &shapely_py::detail:: SRC ## _relate_ ## TGT ); \
478+ m.def(#SRC " _hausdorff_distance_" #TGT , &shapely_py::detail:: SRC ## _hausdorff_distance_ ## TGT );
481479
482480#define BIND_ACCESSORS (CLS ) \
483481 .def(" wkt" , &CLS ::wkt) \
0 commit comments