Skip to content

Commit

Permalink
Ensure that QgsGeometry::offsetCurve does not reverse curve orientation
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Nov 9, 2018
1 parent d1e09a2 commit e5946c5
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 2 deletions.
16 changes: 14 additions & 2 deletions src/core/geometry/qgsgeometry.cpp
Expand Up @@ -1746,14 +1746,26 @@ QgsGeometry QgsGeometry::offsetCurve( double distance, int segments, JoinStyle j
{
QgsGeos geos( d->geometry.get() );
mLastError.clear();
QgsAbstractGeometry *offsetGeom = geos.offsetCurve( distance, segments, joinStyle, miterLimit, &mLastError );

// GEOS can flip the curve orientation in some circumstances. So record previous orientation and correct if required
const QgsCurve::Orientation prevOrientation = qgsgeometry_cast< const QgsCurve * >( d->geometry.get() )->orientation();

std::unique_ptr< QgsAbstractGeometry > offsetGeom( geos.offsetCurve( distance, segments, joinStyle, miterLimit, &mLastError ) );
if ( !offsetGeom )
{
QgsGeometry result;
result.mLastError = mLastError;
return result;
}
return QgsGeometry( offsetGeom );

const QgsCurve::Orientation newOrientation = qgsgeometry_cast< const QgsCurve * >( offsetGeom.get() )->orientation();
if ( newOrientation != prevOrientation )
{
// GEOS has flipped line orientation, flip it back
std::unique_ptr< QgsAbstractGeometry > flipped( qgsgeometry_cast< const QgsCurve * >( offsetGeom.get() )->reversed() );
offsetGeom = std::move( flipped );
}
return QgsGeometry( std::move( offsetGeom ) );
}
}

Expand Down
16 changes: 16 additions & 0 deletions tests/src/python/test_qgsgeometry.py
Expand Up @@ -4483,6 +4483,22 @@ def testBoundingBoxIntersectsRectangle(self):
res = g1.boundingBoxIntersects(t[1])
self.assertEqual(res, t[2], "mismatch for {} to {}, expected:\n{}\nGot:\n{}\n".format(g1.asWkt(), t[1].toString(), t[2], res))

def testOffsetCurve(self):
tests = [
["LINESTRING (0 0, 0 100, 100 100)", 1, "LineString (-1 0, -1 101, 100 101)"],
["LINESTRING (0 0, 0 100, 100 100)", -1, "LineString (1 0, 1 99, 100 99)"],
["LINESTRING (100 100, 0 100, 0 0)", 1, "LineString (100 99, 1 99, 1 0)"],
["LINESTRING (100 100, 0 100, 0 0)", -1, "LineString (100 101, -1 101, -1 0)"],
["MULTILINESTRING ((0 0, 0 100, 100 100),(100 100, 0 100, 0 0))", 1, "MultiLineString ((-1 0, -1 101, 100 101),(100 99, 1 99, 1 0))"]
]
for t in tests:
g1 = QgsGeometry.fromWkt(t[0])
res = g1.offsetCurve(t[1], 2, QgsGeometry.JoinStyleMiter, 5)

self.assertEqual(res.asWkt(1), t[2],
"mismatch for {} to {}, expected:\n{}\nGot:\n{}\n".format(t[0], t[1],
t[2], res.asWkt(1)))

def renderGeometry(self, geom, use_pen, as_polygon=False, as_painter_path=False):
image = QImage(200, 200, QImage.Format_RGB32)
image.fill(QColor(0, 0, 0))
Expand Down

0 comments on commit e5946c5

Please sign in to comment.