Skip to content

Commit 7e88739

Browse files
author
peng.li24
committed
[feat] massive API expansion: predicates, accessors, LinearRing
Add 140+ methods across Point/LineString/Polygon/LinearRing: - All DE-9IM predicates: contains, within, crosses, disjoint, overlaps, touches, covers, covered_by, equals, equals_exact, relates - Accessors: wkt, wkb_hex, type, geom_type, has_z, bounds, area, length - Properties: is_empty, is_simple, is_valid, is_closed, is_ring, is_ccw - Geometry: centroid, normalize, coords, xy, exterior, interiors - Distance metrics: hausdorff_distance - New LinearRing class with full shapely-compatible API Architecture: - shapely/detail/geos_utils.h: shared GEOS utility functions - Template-based cross-type predicates via friend declarations - pybind11 bindings for all new methods with systematic naming - 41 new comprehensive coupled C++ vs Python tests (142 total) Also includes CI fix: install shapely via pip for version detection
1 parent 6fc4fe1 commit 7e88739

10 files changed

Lines changed: 1757 additions & 462 deletions

File tree

example/main.cpp

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,59 @@
1-
// C++ 特有文件,Python 端无直接对应。
2-
// shapelycpp pure native C++ 使用示例 (zero pybind11 dependency).
1+
// shapelycpp pure native C++ usage example (zero pybind11 dependency)
32

43
#include <iostream>
54
#include "shapely/geometry/point.h"
65
#include "shapely/geometry/linestring.h"
6+
#include "shapely/geometry/polygon.h"
7+
#include "shapely/geometry/linearring.h"
8+
9+
using namespace shapely::geometry;
710

811
int main() {
9-
// Point operations
10-
shapely::geometry::Point<double> p1(0.0, 0.0);
11-
shapely::geometry::Point<double> p2(3.0, 4.0);
12+
// -- Point --
13+
Point<double> p1(0.0, 0.0), p2(3.0, 4.0);
14+
std::cout << "Point distance: " << p1.distance(p2) << " (expected 5.0)" << std::endl;
15+
std::cout << "Point WKT: " << p1.wkt() << std::endl;
16+
std::cout << "Point is_valid: " << p1.is_valid() << std::endl;
17+
std::cout << "Point bounds: [" << p1.bounds()[0] << ", " << p1.bounds()[1]
18+
<< ", " << p1.bounds()[2] << ", " << p1.bounds()[3] << "]" << std::endl;
19+
20+
// -- Buffer --
21+
auto buf = p1.buffer(10.0);
22+
std::cout << "Buffer area: " << buf.area() << std::endl;
23+
std::cout << "Buffer WKT: " << buf.wkt() << std::endl;
24+
25+
// -- Predicates --
26+
double sq_coords[] = {0,0, 10,0, 10,10, 0,10};
27+
Polygon<double> sq(sq_coords, 4, 2);
28+
Point<double> inside(5, 5), outside(20, 20);
29+
std::cout << "Square contains inside point: " << sq.contains(inside) << " (expected 1)" << std::endl;
30+
std::cout << "Square contains outside point: " << sq.contains(outside) << " (expected 0)" << std::endl;
31+
std::cout << "Square disjoint outside point: " << sq.disjoint(outside) << " (expected 1)" << std::endl;
32+
33+
// -- LineString --
34+
double ls_coords[] = {0,0, 10,0};
35+
LineString<double> ls(ls_coords, 2);
36+
std::cout << "LineString length: " << ls.length() << " (expected 10.0)" << std::endl;
37+
std::cout << "LineString is_closed: " << ls.is_closed() << " (expected 0)" << std::endl;
38+
std::cout << "LineString WKT: " << ls.wkt() << std::endl;
39+
40+
// -- intersects --
41+
double tri_coords[] = {5,-5, 15,0, 5,5};
42+
Polygon<double> tri(tri_coords, 3, 2);
43+
std::cout << "LineString intersects triangle: " << ls.intersects(tri) << " (expected 1)" << std::endl;
1244

13-
double d = p1.distance(p2);
14-
std::cout << "Distance: " << d << " (expected 5.0)" << std::endl;
45+
// -- LinearRing --
46+
double ring_coords[] = {0,0, 10,0, 10,10, 0,10};
47+
LinearRing<double> ring(ring_coords, 4);
48+
std::cout << "LinearRing is_closed: " << ring.is_closed() << " (expected 1)" << std::endl;
49+
std::cout << "LinearRing is_ccw: " << ring.is_ccw() << std::endl;
50+
std::cout << "LinearRing length: " << ring.length() << std::endl;
51+
std::cout << "LinearRing WKT: " << ring.wkt() << std::endl;
1552

16-
// Buffer
17-
auto buffered = p1.buffer(10.0);
18-
std::cout << "Buffer created OK" << std::endl;
53+
// -- Polygon exterior --
54+
auto exterior = sq.exterior();
55+
std::cout << "Polygon exterior rows: " << exterior.rows() << " (expected 5)" << std::endl;
1956

57+
std::cout << "\nAll examples passed!" << std::endl;
2058
return 0;
2159
}

shapely/detail/geos_utils.h

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// Internal GEOS utility functions shared across geometry types.
2+
// NOT part of public API — do not include directly.
3+
4+
#pragma once
5+
6+
#include <string>
7+
#include <vector>
8+
#include <sstream>
9+
#include <geos/geom/Geometry.h>
10+
#include <geos/geom/Point.h>
11+
#include <geos/geom/LineString.h>
12+
#include <geos/geom/Polygon.h>
13+
#include <geos/geom/CoordinateSequence.h>
14+
#include <geos/geom/Envelope.h>
15+
#include <geos/io/WKTWriter.h>
16+
#include <geos/io/WKBWriter.h>
17+
#include <geos/algorithm/distance/DiscreteHausdorffDistance.h>
18+
#include <geos/simplify/TopologyPreservingSimplifier.h>
19+
#include <geos/operation/valid/IsValidOp.h>
20+
21+
namespace shapely {
22+
namespace detail {
23+
24+
// --- WKT / WKB serialization ------------------------------------------------
25+
26+
inline std::string geos_to_wkt(const geos::geom::Geometry* g) {
27+
if (!g || g->isEmpty()) return "GEOMETRYCOLLECTION EMPTY";
28+
geos::io::WKTWriter writer;
29+
return writer.write(g);
30+
}
31+
32+
inline std::string geos_to_wkb_hex(const geos::geom::Geometry* g) {
33+
if (!g || g->isEmpty()) return "";
34+
geos::io::WKBWriter writer;
35+
std::ostringstream oss;
36+
writer.writeHEX(*g, oss);
37+
return oss.str();
38+
}
39+
40+
// --- Predicates -------------------------------------------------------------
41+
42+
inline bool geos_contains(const geos::geom::Geometry* a, const geos::geom::Geometry* b) {
43+
if (!a || !b) return false;
44+
return a->contains(b);
45+
}
46+
47+
inline bool geos_within(const geos::geom::Geometry* a, const geos::geom::Geometry* b) {
48+
if (!a || !b) return false;
49+
return a->within(b);
50+
}
51+
52+
inline bool geos_crosses(const geos::geom::Geometry* a, const geos::geom::Geometry* b) {
53+
if (!a || !b) return false;
54+
return a->crosses(b);
55+
}
56+
57+
inline bool geos_disjoint(const geos::geom::Geometry* a, const geos::geom::Geometry* b) {
58+
if (!a || !b) return true;
59+
return a->disjoint(b);
60+
}
61+
62+
inline bool geos_overlaps(const geos::geom::Geometry* a, const geos::geom::Geometry* b) {
63+
if (!a || !b) return false;
64+
return a->overlaps(b);
65+
}
66+
67+
inline bool geos_touches(const geos::geom::Geometry* a, const geos::geom::Geometry* b) {
68+
if (!a || !b) return false;
69+
return a->touches(b);
70+
}
71+
72+
inline bool geos_covers(const geos::geom::Geometry* a, const geos::geom::Geometry* b) {
73+
if (!a || !b) return false;
74+
return a->covers(b);
75+
}
76+
77+
inline bool geos_covered_by(const geos::geom::Geometry* a, const geos::geom::Geometry* b) {
78+
if (!a || !b) return false;
79+
return a->coveredBy(b);
80+
}
81+
82+
inline bool geos_equals(const geos::geom::Geometry* a, const geos::geom::Geometry* b) {
83+
if (!a && !b) return true;
84+
if (!a || !b) return false;
85+
return a->equals(b);
86+
}
87+
88+
inline bool geos_equals_exact(const geos::geom::Geometry* a, const geos::geom::Geometry* b, double tol) {
89+
if (!a && !b) return true;
90+
if (!a || !b) return false;
91+
return a->equalsExact(b, tol);
92+
}
93+
94+
inline std::string geos_relate(const geos::geom::Geometry* a, const geos::geom::Geometry* b) {
95+
if (!a || !b) return "";
96+
return a->relate(b)->toString();
97+
}
98+
99+
inline bool geos_relate_pattern(const geos::geom::Geometry* a, const geos::geom::Geometry* b, const std::string& pat) {
100+
if (!a || !b) return false;
101+
return a->relate(b, pat);
102+
}
103+
104+
// --- Geometry property helpers ----------------------------------------------
105+
106+
inline std::string geos_geom_type(const geos::geom::Geometry* g) {
107+
if (!g) return "GeometryCollection";
108+
return g->getGeometryType();
109+
}
110+
111+
inline bool geos_is_simple(const geos::geom::Geometry* g) {
112+
return g && g->isSimple();
113+
}
114+
115+
inline bool geos_is_valid(const geos::geom::Geometry* g) {
116+
return g && g->isValid();
117+
}
118+
119+
inline bool geos_is_empty(const geos::geom::Geometry* g) {
120+
return !g || g->isEmpty();
121+
}
122+
123+
inline int geos_has_z(const geos::geom::Geometry* g) {
124+
return g ? (g->getCoordinateDimension() > 2) : 0;
125+
}
126+
127+
inline double geos_area(const geos::geom::Geometry* g) {
128+
return g ? g->getArea() : 0.0;
129+
}
130+
131+
inline double geos_length(const geos::geom::Geometry* g) {
132+
return g ? g->getLength() : 0.0;
133+
}
134+
135+
inline double geos_hausdorff_distance(const geos::geom::Geometry* a, const geos::geom::Geometry* b) {
136+
if (!a || !b) return 0.0;
137+
return geos::algorithm::distance::DiscreteHausdorffDistance(*a, *b).distance();
138+
}
139+
140+
inline std::vector<double> geos_bounds(const geos::geom::Geometry* g) {
141+
if (!g || g->isEmpty()) return {0, 0, 0, 0};
142+
const geos::geom::Envelope* env = g->getEnvelopeInternal();
143+
return {env->getMinX(), env->getMinY(), env->getMaxX(), env->getMaxY()};
144+
}
145+
146+
} // namespace detail
147+
} // namespace shapely

0 commit comments

Comments
 (0)