Skip to content

Commit bde0c72

Browse files
committed
Expose GEOS clip by rect algorithm via QgsGeometry API
Performs a fast, non-robust intersection between the geometry and a rectangle. The returned geometry may be invalid.
1 parent b620b6e commit bde0c72

6 files changed

Lines changed: 84 additions & 15 deletions

File tree

python/core/geometry/qgsgeometry.sip

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,16 @@ Returns a geometry representing the points shared by this geometry and other.
828828
:rtype: QgsGeometry
829829
%End
830830

831+
QgsGeometry clipped( const QgsRectangle &rectangle );
832+
%Docstring
833+
Clips the geometry using the specified ``rectangle``.
834+
835+
Performs a fast, non-robust intersection between the geometry and
836+
a ``rectangle``. The returned geometry may be invalid.
837+
.. versionadded:: 3.0
838+
:rtype: QgsGeometry
839+
%End
840+
831841
QgsGeometry combine( const QgsGeometry &geometry ) const;
832842
%Docstring
833843
Returns a geometry representing all the points in this geometry and other (a
@@ -1129,7 +1139,6 @@ Ring 0 is outer ring and can't be deleted.
11291139
.. versionadded:: 2.10
11301140
%End
11311141

1132-
11331142
void draw( QPainter &p ) const;
11341143
%Docstring
11351144
Draws the geometry onto a QPainter

src/core/geometry/qgsgeometry.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,17 +2089,17 @@ void QgsGeometry::mapToPixel( const QgsMapToPixel &mtp )
20892089
}
20902090
}
20912091

2092-
#if 0
2093-
void QgsGeometry::clip( const QgsRectangle &rect )
2092+
QgsGeometry QgsGeometry::clipped( const QgsRectangle &rectangle )
20942093
{
2095-
if ( d->geometry )
2094+
if ( !d->geometry || rectangle.isNull() || rectangle.isEmpty() )
20962095
{
2097-
detach();
2098-
d->geometry->clip( rect );
2099-
removeWkbGeos();
2096+
return QgsGeometry();
21002097
}
2098+
2099+
QgsGeos geos( d->geometry );
2100+
QgsAbstractGeometry *resultGeom = geos.clip( rectangle );
2101+
return QgsGeometry( resultGeom );
21012102
}
2102-
#endif
21032103

21042104
void QgsGeometry::draw( QPainter &p ) const
21052105
{

src/core/geometry/qgsgeometry.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,15 @@ class CORE_EXPORT QgsGeometry
765765
//! Returns a geometry representing the points shared by this geometry and other.
766766
QgsGeometry intersection( const QgsGeometry &geometry ) const;
767767

768+
/**
769+
* Clips the geometry using the specified \a rectangle.
770+
*
771+
* Performs a fast, non-robust intersection between the geometry and
772+
* a \a rectangle. The returned geometry may be invalid.
773+
* \since QGIS 3.0
774+
*/
775+
QgsGeometry clipped( const QgsRectangle &rectangle );
776+
768777
/** Returns a geometry representing all the points in this geometry and other (a
769778
* union geometry operation).
770779
* \note this operation is not called union since its a reserved word in C++.
@@ -999,13 +1008,6 @@ class CORE_EXPORT QgsGeometry
9991008
*/
10001009
void mapToPixel( const QgsMapToPixel &mtp );
10011010

1002-
// not implemented for 2.10
1003-
/* Clips the geometry using the specified rectangle
1004-
* \param rect clip rectangle
1005-
* \since QGIS 2.10
1006-
*/
1007-
// void clip( const QgsRectangle& rect );
1008-
10091011
/** Draws the geometry onto a QPainter
10101012
* \param p destination QPainter
10111013
* \since QGIS 2.10

src/core/geometry/qgsgeos.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,30 @@ QgsAbstractGeometry *QgsGeos::difference( const QgsAbstractGeometry &geom, QStri
189189
return overlay( geom, DIFFERENCE, errorMsg );
190190
}
191191

192+
QgsAbstractGeometry *QgsGeos::clip( const QgsRectangle &rect, QString *errorMsg ) const
193+
{
194+
if ( !mGeos || rect.isNull() || rect.isEmpty() )
195+
{
196+
return nullptr;
197+
}
198+
199+
try
200+
{
201+
GEOSGeomScopedPtr opGeom;
202+
opGeom.reset( GEOSClipByRect_r( geosinit.ctxt, mGeos, rect.xMinimum(), rect.yMinimum(), rect.xMaximum(), rect.yMaximum() ) );
203+
QgsAbstractGeometry *opResult = fromGeos( opGeom.get() );
204+
return opResult;
205+
}
206+
catch ( GEOSException &e )
207+
{
208+
if ( errorMsg )
209+
{
210+
*errorMsg = e.what();
211+
}
212+
return nullptr;
213+
}
214+
}
215+
192216
QgsAbstractGeometry *QgsGeos::combine( const QgsAbstractGeometry &geom, QString *errorMsg ) const
193217
{
194218
return overlay( geom, UNION, errorMsg );

src/core/geometry/qgsgeos.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
4545

4646
QgsAbstractGeometry *intersection( const QgsAbstractGeometry &geom, QString *errorMsg = nullptr ) const override;
4747
QgsAbstractGeometry *difference( const QgsAbstractGeometry &geom, QString *errorMsg = nullptr ) const override;
48+
49+
/**
50+
* Performs a fast, non-robust intersection between the geometry and
51+
* a \a rectangle. The returned geometry may be invalid.
52+
*/
53+
QgsAbstractGeometry *clip( const QgsRectangle &rectangle, QString *errorMsg = nullptr ) const;
54+
4855
QgsAbstractGeometry *combine( const QgsAbstractGeometry &geom, QString *errorMsg = nullptr ) const override;
4956
QgsAbstractGeometry *combine( const QList< QgsAbstractGeometry *> &, QString *errorMsg = nullptr ) const override;
5057
QgsAbstractGeometry *symDifference( const QgsAbstractGeometry &geom, QString *errorMsg = nullptr ) const override;

tests/src/python/test_qgsgeometry.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4165,6 +4165,33 @@ def testPoint(self):
41654165
self.assertEqual(point_zm.z(), 3)
41664166
self.assertEqual(point_zm.m(), 4)
41674167

4168+
def testClipped(self):
4169+
tests = [["LINESTRING (1 1,1 9,9 9,9 1)", QgsRectangle(0, 0, 10, 10), "LINESTRING (1 1,1 9,9 9,9 1)"],
4170+
["LINESTRING (-1 -9,-1 11,9 11)", QgsRectangle(0, 0, 10, 10), "GEOMETRYCOLLECTION ()"],
4171+
["LINESTRING (-1 5,5 5,9 9)", QgsRectangle(0, 0, 10, 10), "LINESTRING (0 5,5 5,9 9)"],
4172+
["LINESTRING (5 5,8 5,12 5)", QgsRectangle(0, 0, 10, 10), "LINESTRING (5 5,8 5,10 5)"],
4173+
["LINESTRING (5 -1,5 5,1 2,-3 2,1 6)", QgsRectangle(0, 0, 10, 10), "MULTILINESTRING ((5 0,5 5,1 2,0 2),(0 5,1 6))"],
4174+
["LINESTRING (0 3,0 5,0 7)", QgsRectangle(0, 0, 10, 10), "GEOMETRYCOLLECTION ()"],
4175+
["LINESTRING (0 3,0 5,-1 7)", QgsRectangle(0, 0, 10, 10), "GEOMETRYCOLLECTION ()"],
4176+
["LINESTRING (0 3,0 5,2 7)", QgsRectangle(0, 0, 10, 10), "LINESTRING (0 5,2 7)"],
4177+
["LINESTRING (2 1,0 0,1 2)", QgsRectangle(0, 0, 10, 10), "LINESTRING (2 1,0 0,1 2)"],
4178+
["LINESTRING (3 3,0 3,0 5,2 7)", QgsRectangle(0, 0, 10, 10), "MULTILINESTRING ((3 3,0 3),(0 5,2 7))"],
4179+
["LINESTRING (5 5,10 5,20 5)", QgsRectangle(0, 0, 10, 10), "LINESTRING (5 5,10 5)"],
4180+
["LINESTRING (3 3,0 6,3 9)", QgsRectangle(0, 0, 10, 10), "LINESTRING (3 3,0 6,3 9)"],
4181+
["POLYGON ((5 5,5 6,6 6,6 5,5 5))", QgsRectangle(0, 0, 10, 10), "POLYGON ((5 5,5 6,6 6,6 5,5 5))"],
4182+
["POLYGON ((15 15,15 16,16 16,16 15,15 15))", QgsRectangle(0, 0, 10, 10), "GEOMETRYCOLLECTION ()"],
4183+
["POLYGON ((-1 -1,-1 11,11 11,11 -1,-1 -1))", QgsRectangle(0, 0, 10, 10), "Polygon ((0 0, 0 10, 10 10, 10 0, 0 0))"],
4184+
["POLYGON ((-1 -1,-1 5,5 5,5 -1,-1 -1))", QgsRectangle(0, 0, 10, 10), "Polygon ((0 0, 0 5, 5 5, 5 0, 0 0))"],
4185+
["POLYGON ((-2 -2,-2 5,5 5,5 -2,-2 -2), (3 3,4 4,4 2,3 3))", QgsRectangle(0, 0, 10, 10), "Polygon ((0 0, 0 5, 5 5, 5 0, 0 0),(3 3, 4 4, 4 2, 3 3))"]
4186+
]
4187+
for t in tests:
4188+
input = QgsGeometry.fromWkt(t[0])
4189+
o = input.clipped(t[1])
4190+
exp = t[2]
4191+
result = o.exportToWkt()
4192+
self.assertTrue(compareWkt(result, exp, 0.00001),
4193+
"clipped: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
4194+
41684195

41694196
if __name__ == '__main__':
41704197
unittest.main()

0 commit comments

Comments
 (0)