mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-04 00:04:03 -04:00
qgsgeometryutils(chamfer/fillet): add exception management to chamfer/fillet
This commit is contained in:
parent
a267b95c32
commit
3593cbbea9
@ -66,6 +66,10 @@ try:
|
||||
QgsGeometryUtils.interpolatePointOnSegment = staticmethod(QgsGeometryUtils.interpolatePointOnSegment)
|
||||
QgsGeometryUtils.createChamfer = staticmethod(QgsGeometryUtils.createChamfer)
|
||||
QgsGeometryUtils.createFillet = staticmethod(QgsGeometryUtils.createFillet)
|
||||
QgsGeometryUtils.createChamferGeometry = staticmethod(QgsGeometryUtils.createChamferGeometry)
|
||||
QgsGeometryUtils.createFilletGeometry = staticmethod(QgsGeometryUtils.createFilletGeometry)
|
||||
QgsGeometryUtils.chamferVertex = staticmethod(QgsGeometryUtils.chamferVertex)
|
||||
QgsGeometryUtils.filletVertex = staticmethod(QgsGeometryUtils.filletVertex)
|
||||
QgsGeometryUtils.__group__ = ['geometry']
|
||||
except (NameError, AttributeError):
|
||||
pass
|
||||
|
@ -1190,7 +1190,7 @@ interpolation.
|
||||
const QgsPoint &segment2Start, const QgsPoint &segment2End,
|
||||
double distance1, double distance2,
|
||||
QgsPoint &chamferStart /Out/, QgsPoint &chamferEnd /Out/,
|
||||
double epsilon = 1e-8 ) /HoldGIL/;
|
||||
double epsilon = 1e-8 ) /HoldGIL/;
|
||||
%Docstring
|
||||
Creates a chamfer between two line segments using :py:class:`QgsPoint`.
|
||||
|
||||
@ -1207,6 +1207,8 @@ Creates a chamfer between two line segments using :py:class:`QgsPoint`.
|
||||
- chamferStart: calculated start point of the chamfer
|
||||
- chamferEnd: calculated end point of the chamfer
|
||||
|
||||
:raises QgsInvalidArgumentException:
|
||||
|
||||
.. versionadded:: 4.0
|
||||
%End
|
||||
|
||||
@ -1216,7 +1218,7 @@ Creates a chamfer between two line segments using :py:class:`QgsPoint`.
|
||||
QgsPoint &filletPoint1 /Out/,
|
||||
QgsPoint &filletMidPoint /Out/,
|
||||
QgsPoint &filletPoint2 /Out/,
|
||||
double epsilon = 1e-8 ) /HoldGIL/;
|
||||
double epsilon = 1e-8 ) /HoldGIL/;
|
||||
%Docstring
|
||||
Creates a fillet (rounded corner) between two line segments using
|
||||
:py:class:`QgsPoint`. Returns the three fillet arc points
|
||||
@ -1235,13 +1237,16 @@ parameters to define a CircularString.
|
||||
- filletMidPoint: midpoint of the fillet arc
|
||||
- filletPoint2: second tangent point of the fillet arc
|
||||
|
||||
:raises QgsInvalidArgumentException:
|
||||
|
||||
.. versionadded:: 4.0
|
||||
%End
|
||||
|
||||
static std::unique_ptr< QgsLineString >createChamferGeometry(
|
||||
static std::unique_ptr< QgsLineString > createChamferGeometry(
|
||||
const QgsPoint &segment1Start, const QgsPoint &segment1End,
|
||||
const QgsPoint &segment2Start, const QgsPoint &segment2End,
|
||||
double distance1, double distance2 );
|
||||
double distance1, double distance2
|
||||
);
|
||||
%Docstring
|
||||
Creates a complete chamfer geometry connecting two segments.
|
||||
|
||||
@ -1256,13 +1261,16 @@ Creates a complete chamfer geometry connecting two segments.
|
||||
:return: :py:class:`QgsLineString` geometry connecting the segments
|
||||
through the chamfer
|
||||
|
||||
:raises QgsInvalidArgumentException:
|
||||
|
||||
.. versionadded:: 4.0
|
||||
%End
|
||||
|
||||
static std::unique_ptr< QgsAbstractGeometry >createFilletGeometry(
|
||||
static std::unique_ptr< QgsAbstractGeometry > createFilletGeometry(
|
||||
const QgsPoint &segment1Start, const QgsPoint &segment1End,
|
||||
const QgsPoint &segment2Start, const QgsPoint &segment2End,
|
||||
double radius, int segments );
|
||||
double radius, int segments
|
||||
);
|
||||
%Docstring
|
||||
Creates a complete fillet geometry connecting two segments.
|
||||
|
||||
@ -1276,12 +1284,15 @@ Creates a complete fillet geometry connecting two segments.
|
||||
|
||||
:return: geometry connecting the segments through the fillet
|
||||
|
||||
:raises QgsInvalidArgumentException:
|
||||
|
||||
.. versionadded:: 4.0
|
||||
%End
|
||||
|
||||
static std::unique_ptr< QgsAbstractGeometry >chamferVertex(
|
||||
static std::unique_ptr< QgsAbstractGeometry > chamferVertex(
|
||||
const QgsCurve *curve, int vertexIndex,
|
||||
double distance1, double distance2 );
|
||||
double distance1, double distance2
|
||||
);
|
||||
%Docstring
|
||||
Applies chamfer to a vertex in a curve geometry.
|
||||
|
||||
@ -1292,12 +1303,15 @@ Applies chamfer to a vertex in a curve geometry.
|
||||
|
||||
:return: new geometry with chamfer applied, or None on failure
|
||||
|
||||
:raises QgsInvalidArgumentException:
|
||||
|
||||
.. versionadded:: 4.0
|
||||
%End
|
||||
|
||||
static std::unique_ptr< QgsAbstractGeometry >filletVertex(
|
||||
static std::unique_ptr< QgsAbstractGeometry > filletVertex(
|
||||
const QgsCurve *curve, int vertexIndex,
|
||||
double radius, int segments );
|
||||
double radius, int segments
|
||||
);
|
||||
%Docstring
|
||||
Applies fillet to a vertex in a curve geometry.
|
||||
|
||||
@ -1309,6 +1323,8 @@ Applies fillet to a vertex in a curve geometry.
|
||||
|
||||
:return: new geometry with fillet applied, or None on failure
|
||||
|
||||
:raises QgsInvalidArgumentException:
|
||||
|
||||
.. versionadded:: 4.0
|
||||
%End
|
||||
|
||||
|
@ -66,6 +66,10 @@ try:
|
||||
QgsGeometryUtils.interpolatePointOnSegment = staticmethod(QgsGeometryUtils.interpolatePointOnSegment)
|
||||
QgsGeometryUtils.createChamfer = staticmethod(QgsGeometryUtils.createChamfer)
|
||||
QgsGeometryUtils.createFillet = staticmethod(QgsGeometryUtils.createFillet)
|
||||
QgsGeometryUtils.createChamferGeometry = staticmethod(QgsGeometryUtils.createChamferGeometry)
|
||||
QgsGeometryUtils.createFilletGeometry = staticmethod(QgsGeometryUtils.createFilletGeometry)
|
||||
QgsGeometryUtils.chamferVertex = staticmethod(QgsGeometryUtils.chamferVertex)
|
||||
QgsGeometryUtils.filletVertex = staticmethod(QgsGeometryUtils.filletVertex)
|
||||
QgsGeometryUtils.__group__ = ['geometry']
|
||||
except (NameError, AttributeError):
|
||||
pass
|
||||
|
@ -1190,7 +1190,7 @@ interpolation.
|
||||
const QgsPoint &segment2Start, const QgsPoint &segment2End,
|
||||
double distance1, double distance2,
|
||||
QgsPoint &chamferStart /Out/, QgsPoint &chamferEnd /Out/,
|
||||
double epsilon = 1e-8 ) /HoldGIL/;
|
||||
double epsilon = 1e-8 ) throw( QgsInvalidArgumentException ) /HoldGIL/;
|
||||
%Docstring
|
||||
Creates a chamfer between two line segments using :py:class:`QgsPoint`.
|
||||
|
||||
@ -1207,6 +1207,8 @@ Creates a chamfer between two line segments using :py:class:`QgsPoint`.
|
||||
- chamferStart: calculated start point of the chamfer
|
||||
- chamferEnd: calculated end point of the chamfer
|
||||
|
||||
:raises QgsInvalidArgumentException:
|
||||
|
||||
.. versionadded:: 4.0
|
||||
%End
|
||||
|
||||
@ -1216,7 +1218,7 @@ Creates a chamfer between two line segments using :py:class:`QgsPoint`.
|
||||
QgsPoint &filletPoint1 /Out/,
|
||||
QgsPoint &filletMidPoint /Out/,
|
||||
QgsPoint &filletPoint2 /Out/,
|
||||
double epsilon = 1e-8 ) /HoldGIL/;
|
||||
double epsilon = 1e-8 ) throw( QgsInvalidArgumentException ) /HoldGIL/;
|
||||
%Docstring
|
||||
Creates a fillet (rounded corner) between two line segments using
|
||||
:py:class:`QgsPoint`. Returns the three fillet arc points
|
||||
@ -1235,13 +1237,16 @@ parameters to define a CircularString.
|
||||
- filletMidPoint: midpoint of the fillet arc
|
||||
- filletPoint2: second tangent point of the fillet arc
|
||||
|
||||
:raises QgsInvalidArgumentException:
|
||||
|
||||
.. versionadded:: 4.0
|
||||
%End
|
||||
|
||||
static std::unique_ptr< QgsLineString >createChamferGeometry(
|
||||
static std::unique_ptr< QgsLineString > createChamferGeometry(
|
||||
const QgsPoint &segment1Start, const QgsPoint &segment1End,
|
||||
const QgsPoint &segment2Start, const QgsPoint &segment2End,
|
||||
double distance1, double distance2 );
|
||||
double distance1, double distance2
|
||||
) throw( QgsInvalidArgumentException );
|
||||
%Docstring
|
||||
Creates a complete chamfer geometry connecting two segments.
|
||||
|
||||
@ -1256,13 +1261,16 @@ Creates a complete chamfer geometry connecting two segments.
|
||||
:return: :py:class:`QgsLineString` geometry connecting the segments
|
||||
through the chamfer
|
||||
|
||||
:raises QgsInvalidArgumentException:
|
||||
|
||||
.. versionadded:: 4.0
|
||||
%End
|
||||
|
||||
static std::unique_ptr< QgsAbstractGeometry >createFilletGeometry(
|
||||
static std::unique_ptr< QgsAbstractGeometry > createFilletGeometry(
|
||||
const QgsPoint &segment1Start, const QgsPoint &segment1End,
|
||||
const QgsPoint &segment2Start, const QgsPoint &segment2End,
|
||||
double radius, int segments );
|
||||
double radius, int segments
|
||||
) throw( QgsInvalidArgumentException );
|
||||
%Docstring
|
||||
Creates a complete fillet geometry connecting two segments.
|
||||
|
||||
@ -1276,12 +1284,15 @@ Creates a complete fillet geometry connecting two segments.
|
||||
|
||||
:return: geometry connecting the segments through the fillet
|
||||
|
||||
:raises QgsInvalidArgumentException:
|
||||
|
||||
.. versionadded:: 4.0
|
||||
%End
|
||||
|
||||
static std::unique_ptr< QgsAbstractGeometry >chamferVertex(
|
||||
static std::unique_ptr< QgsAbstractGeometry > chamferVertex(
|
||||
const QgsCurve *curve, int vertexIndex,
|
||||
double distance1, double distance2 );
|
||||
double distance1, double distance2
|
||||
) throw( QgsInvalidArgumentException );
|
||||
%Docstring
|
||||
Applies chamfer to a vertex in a curve geometry.
|
||||
|
||||
@ -1292,12 +1303,15 @@ Applies chamfer to a vertex in a curve geometry.
|
||||
|
||||
:return: new geometry with chamfer applied, or None on failure
|
||||
|
||||
:raises QgsInvalidArgumentException:
|
||||
|
||||
.. versionadded:: 4.0
|
||||
%End
|
||||
|
||||
static std::unique_ptr< QgsAbstractGeometry >filletVertex(
|
||||
static std::unique_ptr< QgsAbstractGeometry > filletVertex(
|
||||
const QgsCurve *curve, int vertexIndex,
|
||||
double radius, int segments );
|
||||
double radius, int segments
|
||||
) throw( QgsInvalidArgumentException );
|
||||
%Docstring
|
||||
Applies fillet to a vertex in a curve geometry.
|
||||
|
||||
@ -1309,6 +1323,8 @@ Applies fillet to a vertex in a curve geometry.
|
||||
|
||||
:return: new geometry with fillet applied, or None on failure
|
||||
|
||||
:raises QgsInvalidArgumentException:
|
||||
|
||||
.. versionadded:: 4.0
|
||||
%End
|
||||
|
||||
|
@ -4538,18 +4538,50 @@ QgsGeometry QgsGeometry::chamfer( int vertexIndex, double distance1, double dist
|
||||
return QgsGeometry();
|
||||
}
|
||||
|
||||
const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( d->geometry->simplifiedTypeRef() );
|
||||
const QgsCurve *curve;
|
||||
if ( type() == Qgis::GeometryType::Polygon )
|
||||
{
|
||||
if ( const QgsCurvePolygon *cpoly = qgsgeometry_cast<const QgsCurvePolygon *>( d->geometry->simplifiedTypeRef() ) )
|
||||
curve = qgsgeometry_cast<const QgsCurve *>( cpoly->exteriorRing() );
|
||||
else
|
||||
curve = nullptr;
|
||||
}
|
||||
else if ( type() == Qgis::GeometryType::Line )
|
||||
curve = qgsgeometry_cast<const QgsCurve *>( d->geometry->simplifiedTypeRef() );
|
||||
else
|
||||
curve = nullptr;
|
||||
|
||||
if ( !curve )
|
||||
{
|
||||
mLastError = QStringLiteral( "Chamfer needs curve geometry." );
|
||||
return QgsGeometry();
|
||||
}
|
||||
|
||||
std::unique_ptr<QgsAbstractGeometry> result;
|
||||
try
|
||||
{
|
||||
result = QgsGeometryUtils::chamferVertex( curve, vertexIndex, distance1, distance2 );
|
||||
}
|
||||
catch ( QgsInvalidArgumentException &e )
|
||||
{
|
||||
mLastError = e.what();
|
||||
return QgsGeometry();
|
||||
}
|
||||
|
||||
std::unique_ptr<QgsAbstractGeometry> result( QgsGeometryUtils::chamferVertex( curve, vertexIndex, distance1, distance2 ) );
|
||||
if ( !result )
|
||||
{
|
||||
mLastError = QStringLiteral( "Chamfer generates a null geometry." );
|
||||
return QgsGeometry();
|
||||
}
|
||||
|
||||
if ( type() == Qgis::GeometryType::Polygon )
|
||||
{
|
||||
QgsLineString *linestring = qgsgeometry_cast<QgsLineString *>( result.release() );
|
||||
QgsPolygon *poly = new QgsPolygon( linestring );
|
||||
QgsDebugMsgLevel( QStringLiteral( "returning polygon" ), 1 );
|
||||
return QgsGeometry( dynamic_cast<QgsAbstractGeometry *>( poly ) );
|
||||
}
|
||||
QgsDebugMsgLevel( QStringLiteral( "returning linestring" ), 1 );
|
||||
return QgsGeometry( std::move( result ) );
|
||||
}
|
||||
|
||||
@ -4560,18 +4592,50 @@ QgsGeometry QgsGeometry::fillet( int vertexIndex, double radius, int segments )
|
||||
return QgsGeometry();
|
||||
}
|
||||
|
||||
const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( d->geometry->simplifiedTypeRef() );
|
||||
const QgsCurve *curve;
|
||||
if ( type() == Qgis::GeometryType::Polygon )
|
||||
{
|
||||
if ( const QgsCurvePolygon *cpoly = qgsgeometry_cast<const QgsCurvePolygon *>( d->geometry->simplifiedTypeRef() ) )
|
||||
curve = qgsgeometry_cast<const QgsCurve *>( cpoly->exteriorRing() );
|
||||
else
|
||||
curve = nullptr;
|
||||
}
|
||||
else if ( type() == Qgis::GeometryType::Line )
|
||||
curve = qgsgeometry_cast<const QgsCurve *>( d->geometry->simplifiedTypeRef() );
|
||||
else
|
||||
curve = nullptr;
|
||||
|
||||
if ( !curve )
|
||||
{
|
||||
mLastError = QStringLiteral( "Fillet needs curve geometry." );
|
||||
return QgsGeometry();
|
||||
}
|
||||
|
||||
std::unique_ptr<QgsAbstractGeometry> result;
|
||||
try
|
||||
{
|
||||
result = QgsGeometryUtils::filletVertex( curve, vertexIndex, radius, segments );
|
||||
}
|
||||
catch ( QgsInvalidArgumentException &e )
|
||||
{
|
||||
mLastError = e.what();
|
||||
return QgsGeometry();
|
||||
}
|
||||
|
||||
std::unique_ptr<QgsAbstractGeometry> result( QgsGeometryUtils::filletVertex( curve, vertexIndex, radius, segments ) );
|
||||
if ( !result )
|
||||
{
|
||||
mLastError = QStringLiteral( "Fillet generates a null geometry." );
|
||||
return QgsGeometry();
|
||||
}
|
||||
|
||||
if ( type() == Qgis::GeometryType::Polygon )
|
||||
{
|
||||
QgsLineString *linestring = qgsgeometry_cast<QgsLineString *>( result.release() );
|
||||
QgsPolygon *poly = new QgsPolygon( linestring );
|
||||
QgsDebugMsgLevel( QStringLiteral( "returning polygon" ), 1 );
|
||||
return QgsGeometry( dynamic_cast<QgsAbstractGeometry *>( poly ) );
|
||||
}
|
||||
QgsDebugMsgLevel( QStringLiteral( "returning linestring" ), 1 );
|
||||
return QgsGeometry( std::move( result ) );
|
||||
}
|
||||
|
||||
@ -4579,11 +4643,22 @@ QgsGeometry QgsGeometry::chamfer( const QgsPoint &segment1Start, const QgsPoint
|
||||
const QgsPoint &segment2Start, const QgsPoint &segment2End,
|
||||
double distance1, double distance2 ) const
|
||||
{
|
||||
std::unique_ptr<QgsLineString> result( QgsGeometryUtils::createChamferGeometry(
|
||||
segment1Start, segment1End, segment2Start, segment2End, distance1, distance2 ) );
|
||||
std::unique_ptr<QgsLineString> result;
|
||||
try
|
||||
{
|
||||
result = QgsGeometryUtils::createChamferGeometry(
|
||||
segment1Start, segment1End, segment2Start, segment2End, distance1, distance2
|
||||
);
|
||||
}
|
||||
catch ( QgsInvalidArgumentException &e )
|
||||
{
|
||||
mLastError = e.what();
|
||||
return QgsGeometry();
|
||||
}
|
||||
|
||||
if ( !result )
|
||||
{
|
||||
mLastError = QStringLiteral( "Chamfer generates a null geometry." );
|
||||
return QgsGeometry();
|
||||
}
|
||||
|
||||
@ -4594,11 +4669,22 @@ QgsGeometry QgsGeometry::fillet( const QgsPoint &segment1Start, const QgsPoint &
|
||||
const QgsPoint &segment2Start, const QgsPoint &segment2End,
|
||||
double radius, int segments ) const
|
||||
{
|
||||
std::unique_ptr<QgsAbstractGeometry> result( QgsGeometryUtils::createFilletGeometry(
|
||||
segment1Start, segment1End, segment2Start, segment2End, radius, segments ) );
|
||||
std::unique_ptr<QgsAbstractGeometry> result;
|
||||
try
|
||||
{
|
||||
result = QgsGeometryUtils::createFilletGeometry(
|
||||
segment1Start, segment1End, segment2Start, segment2End, radius, segments
|
||||
);
|
||||
}
|
||||
catch ( QgsInvalidArgumentException &e )
|
||||
{
|
||||
mLastError = e.what();
|
||||
return QgsGeometry();
|
||||
}
|
||||
|
||||
if ( !result )
|
||||
{
|
||||
mLastError = QStringLiteral( "Fillet generates a null geometry." );
|
||||
return QgsGeometry();
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ email : marco.hugentobler at sourcepole dot com
|
||||
#include "qgscurve.h"
|
||||
#include "qgsabstractgeometry.h"
|
||||
#include "qgsvertexid.h"
|
||||
#include "qgslogger.h"
|
||||
|
||||
#include <memory>
|
||||
#include <QStringList>
|
||||
@ -1323,23 +1324,21 @@ bool QgsGeometryUtils::createChamfer( const QgsPoint &segment1Start, const QgsPo
|
||||
|
||||
// Validate input parameters
|
||||
if ( distance1 <= 0 || distance2 <= 0 )
|
||||
return false;
|
||||
throw QgsInvalidArgumentException( "Negative distances." );
|
||||
|
||||
// Create chamfer points using the utility function
|
||||
double chamferStartX, chamferStartY, chamferEndX, chamferEndY;
|
||||
|
||||
if ( !QgsGeometryUtilsBase::createChamfer(
|
||||
segment1Start.x(), segment1Start.y(), segment1End.x(), segment1End.y(),
|
||||
segment2Start.x(), segment2Start.y(), segment2End.x(), segment2End.y(),
|
||||
distance1, distance2,
|
||||
chamferStartX, chamferStartY,
|
||||
chamferEndX, chamferEndY,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
epsilon ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
QgsGeometryUtilsBase::createChamfer(
|
||||
segment1Start.x(), segment1Start.y(), segment1End.x(), segment1End.y(),
|
||||
segment2Start.x(), segment2Start.y(), segment2End.x(), segment2End.y(),
|
||||
distance1, distance2,
|
||||
chamferStartX, chamferStartY,
|
||||
chamferEndX, chamferEndY,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
epsilon
|
||||
);
|
||||
|
||||
chamferStart = interpolatePointOnSegment( chamferStartX, chamferStartY, segment1Start, segment1End );
|
||||
chamferEnd = interpolatePointOnSegment( chamferEndX, chamferEndY, segment2Start, segment2End );
|
||||
@ -1356,22 +1355,20 @@ bool QgsGeometryUtils::createFillet( const QgsPoint &segment1Start, const QgsPoi
|
||||
double epsilon )
|
||||
{
|
||||
if ( radius <= 0 )
|
||||
return false;
|
||||
throw QgsInvalidArgumentException( "Radius <= 0." );
|
||||
|
||||
// Create fillet arc using the utility function
|
||||
double filletPointsX[3], filletPointsY[3];
|
||||
|
||||
if ( !QgsGeometryUtilsBase::createFillet(
|
||||
segment1Start.x(), segment1Start.y(), segment1End.x(), segment1End.y(),
|
||||
segment2Start.x(), segment2Start.y(), segment2End.x(), segment2End.y(),
|
||||
radius,
|
||||
filletPointsX, filletPointsY,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
epsilon ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
QgsGeometryUtilsBase::createFillet(
|
||||
segment1Start.x(), segment1Start.y(), segment1End.x(), segment1End.y(),
|
||||
segment2Start.x(), segment2Start.y(), segment2End.x(), segment2End.y(),
|
||||
radius,
|
||||
filletPointsX, filletPointsY,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
epsilon
|
||||
);
|
||||
|
||||
filletPoint1 = interpolatePointOnSegment( filletPointsX[0], filletPointsY[0], segment1Start, segment1End );
|
||||
filletMidPoint = createPointWithMatchingDimensions( filletPointsX[1], filletPointsY[1], segment1Start );
|
||||
@ -1397,14 +1394,11 @@ bool QgsGeometryUtils::createFilletArray( const QgsPoint &segment1Start, const Q
|
||||
double epsilon )
|
||||
{
|
||||
QgsPoint p1, p2, p3;
|
||||
if ( createFillet( segment1Start, segment1End, segment2Start, segment2End, radius, p1, p2, p3, epsilon ) )
|
||||
{
|
||||
filletPoints[0] = p1;
|
||||
filletPoints[1] = p2;
|
||||
filletPoints[2] = p3;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
createFillet( segment1Start, segment1End, segment2Start, segment2End, radius, p1, p2, p3, epsilon );
|
||||
filletPoints[0] = p1;
|
||||
filletPoints[1] = p2;
|
||||
filletPoints[2] = p3;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<QgsLineString> QgsGeometryUtils::createChamferGeometry(
|
||||
@ -1413,8 +1407,7 @@ std::unique_ptr<QgsLineString> QgsGeometryUtils::createChamferGeometry(
|
||||
double distance1, double distance2 )
|
||||
{
|
||||
QgsPoint chamferStart, chamferEnd;
|
||||
if ( !createChamfer( segment1Start, segment1End, segment2Start, segment2End, distance1, distance2, chamferStart, chamferEnd ) )
|
||||
return nullptr;
|
||||
createChamfer( segment1Start, segment1End, segment2Start, segment2End, distance1, distance2, chamferStart, chamferEnd );
|
||||
|
||||
return std::make_unique<QgsLineString>(
|
||||
QVector<QgsPoint> { segment1Start, chamferStart, chamferEnd, segment2Start } );
|
||||
@ -1426,8 +1419,7 @@ std::unique_ptr<QgsAbstractGeometry> QgsGeometryUtils::createFilletGeometry(
|
||||
double radius, int segments )
|
||||
{
|
||||
QgsPoint filletPoints[3];
|
||||
if ( !createFilletArray( segment1Start, segment1End, segment2Start, segment2End, radius, filletPoints ) )
|
||||
return nullptr;
|
||||
createFilletArray( segment1Start, segment1End, segment2Start, segment2End, radius, filletPoints );
|
||||
|
||||
// Calculate far endpoints for complete geometry
|
||||
double intersectionX, intersectionY;
|
||||
@ -1498,15 +1490,17 @@ std::unique_ptr<QgsAbstractGeometry> QgsGeometryUtils::chamferVertex(
|
||||
const QgsCurve *curve, int vertexIndex,
|
||||
double distance1, double distance2 )
|
||||
{
|
||||
if ( !curve || vertexIndex <= 0 || vertexIndex >= curve->numPoints() - 1 )
|
||||
return nullptr;
|
||||
if ( !curve )
|
||||
throw QgsInvalidArgumentException( "Curve is null." );
|
||||
if ( vertexIndex <= 0 || vertexIndex >= curve->numPoints() - 1 )
|
||||
throw QgsInvalidArgumentException( "Vertex index out of range." );
|
||||
|
||||
// Apply symmetric distance if distance2 is negative
|
||||
if ( distance2 <= 0 )
|
||||
distance2 = distance1;
|
||||
|
||||
if ( distance1 <= 0 || distance2 <= 0 )
|
||||
return nullptr;
|
||||
throw QgsInvalidArgumentException( "Negative distances." );
|
||||
|
||||
// Extract the three consecutive vertices
|
||||
const QgsPoint pPrev = curve->vertexAt( QgsVertexId( 0, 0, vertexIndex - 1 ) );
|
||||
@ -1515,8 +1509,7 @@ std::unique_ptr<QgsAbstractGeometry> QgsGeometryUtils::chamferVertex(
|
||||
|
||||
// Create chamfer
|
||||
QgsPoint chamferStart, chamferEnd;
|
||||
if ( !createChamfer( pPrev, p, p, pNext, distance1, distance2, chamferStart, chamferEnd ) )
|
||||
return nullptr;
|
||||
createChamfer( pPrev, p, p, pNext, distance1, distance2, chamferStart, chamferEnd );
|
||||
|
||||
// Handle LineString geometries
|
||||
if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
|
||||
@ -1568,7 +1561,7 @@ std::unique_ptr<QgsAbstractGeometry> QgsGeometryUtils::chamferVertex(
|
||||
|
||||
if ( targetCurveIndex == -1 )
|
||||
{
|
||||
return nullptr;
|
||||
throw QgsInvalidArgumentException( "While generating output: unable to find curve within compound." );
|
||||
}
|
||||
|
||||
const QgsCurve *targetCurve = compound->curveAt( targetCurveIndex );
|
||||
@ -1625,25 +1618,31 @@ std::unique_ptr<QgsAbstractGeometry> QgsGeometryUtils::chamferVertex(
|
||||
return newCompound;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
throw QgsInvalidArgumentException( "While generating output: curse is not a QgsLineString nor a QgsCompoundCurve." );
|
||||
}
|
||||
|
||||
std::unique_ptr<QgsAbstractGeometry> QgsGeometryUtils::filletVertex(
|
||||
const QgsCurve *curve, int vertexIndex,
|
||||
double radius, int segments )
|
||||
{
|
||||
if ( !curve || vertexIndex <= 0 || vertexIndex >= curve->numPoints() - 1 || radius <= 0 )
|
||||
return nullptr;
|
||||
if ( !curve )
|
||||
throw QgsInvalidArgumentException( "Curve is null." );
|
||||
if ( vertexIndex <= 0 || vertexIndex >= curve->numPoints() - 1 )
|
||||
throw QgsInvalidArgumentException( "Vertex index out of range." );
|
||||
if ( radius <= 0 )
|
||||
throw QgsInvalidArgumentException( "Radius <= 0." );
|
||||
|
||||
// Extract the three consecutive vertices
|
||||
const QgsPoint pPrev = curve->vertexAt( QgsVertexId( 0, 0, vertexIndex - 1 ) );
|
||||
const QgsPoint p = curve->vertexAt( QgsVertexId( 0, 0, vertexIndex ) );
|
||||
const QgsPoint pNext = curve->vertexAt( QgsVertexId( 0, 0, vertexIndex + 1 ) );
|
||||
|
||||
double rad = std::min( radius, pPrev.distance( p ) * 0.95 );
|
||||
rad = std::min( rad, pNext.distance( p ) * 0.95 );
|
||||
|
||||
// Create fillet
|
||||
QgsPoint filletPoints[3];
|
||||
if ( !createFilletArray( pPrev, p, p, pNext, radius, filletPoints ) )
|
||||
return nullptr;
|
||||
createFilletArray( pPrev, p, p, pNext, rad, filletPoints );
|
||||
|
||||
// Handle LineString geometries
|
||||
if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
|
||||
@ -1706,7 +1705,7 @@ std::unique_ptr<QgsAbstractGeometry> QgsGeometryUtils::filletVertex(
|
||||
|
||||
if ( targetCurveIndex == -1 )
|
||||
{
|
||||
return nullptr;
|
||||
throw QgsInvalidArgumentException( "While generating output: unable to find curve within compound." );
|
||||
}
|
||||
|
||||
const QgsCurve *targetCurve = compound->curveAt( targetCurveIndex );
|
||||
@ -1779,5 +1778,5 @@ std::unique_ptr<QgsAbstractGeometry> QgsGeometryUtils::filletVertex(
|
||||
return newCompound;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
throw QgsInvalidArgumentException( "While generating output: curse is not a QgsLineString nor a QgsCompoundCurve." );
|
||||
}
|
||||
|
@ -1308,13 +1308,14 @@ class CORE_EXPORT QgsGeometryUtils
|
||||
* \param chamferEnd calculated end point of the chamfer
|
||||
* \param epsilon tolerance for geometric calculations
|
||||
* \returns true if chamfer was successfully created
|
||||
* \throws QgsInvalidArgumentException
|
||||
* \since QGIS 4.0
|
||||
*/
|
||||
static bool createChamfer( const QgsPoint &segment1Start, const QgsPoint &segment1End,
|
||||
const QgsPoint &segment2Start, const QgsPoint &segment2End,
|
||||
double distance1, double distance2,
|
||||
QgsPoint &chamferStart SIP_OUT, QgsPoint &chamferEnd SIP_OUT,
|
||||
double epsilon = 1e-8 ) SIP_HOLDGIL;
|
||||
double epsilon = 1e-8 ) SIP_THROW( QgsInvalidArgumentException ) SIP_HOLDGIL;
|
||||
|
||||
/**
|
||||
* Creates a fillet (rounded corner) between two line segments using QgsPoint.
|
||||
@ -1330,6 +1331,7 @@ class CORE_EXPORT QgsGeometryUtils
|
||||
* \param filletPoint2 second tangent point of the fillet arc
|
||||
* \param epsilon tolerance for geometric calculations
|
||||
* \returns true if fillet was successfully created
|
||||
* \throws QgsInvalidArgumentException
|
||||
* \since QGIS 4.0
|
||||
*/
|
||||
static bool createFillet( const QgsPoint &segment1Start, const QgsPoint &segment1End,
|
||||
@ -1338,7 +1340,7 @@ class CORE_EXPORT QgsGeometryUtils
|
||||
QgsPoint &filletPoint1 SIP_OUT,
|
||||
QgsPoint &filletMidPoint SIP_OUT,
|
||||
QgsPoint &filletPoint2 SIP_OUT,
|
||||
double epsilon = 1e-8 ) SIP_HOLDGIL;
|
||||
double epsilon = 1e-8 ) SIP_THROW( QgsInvalidArgumentException ) SIP_HOLDGIL;
|
||||
|
||||
/**
|
||||
* Creates a complete chamfer geometry connecting two segments.
|
||||
@ -1349,12 +1351,14 @@ class CORE_EXPORT QgsGeometryUtils
|
||||
* \param distance1 chamfer distance along first segment
|
||||
* \param distance2 chamfer distance along second segment (if negative, uses distance1)
|
||||
* \returns QgsLineString geometry connecting the segments through the chamfer
|
||||
* \throws QgsInvalidArgumentException
|
||||
* \since QGIS 4.0
|
||||
*/
|
||||
static std::unique_ptr< QgsLineString >createChamferGeometry(
|
||||
static std::unique_ptr< QgsLineString > createChamferGeometry(
|
||||
const QgsPoint &segment1Start, const QgsPoint &segment1End,
|
||||
const QgsPoint &segment2Start, const QgsPoint &segment2End,
|
||||
double distance1, double distance2 );
|
||||
double distance1, double distance2
|
||||
) SIP_THROW( QgsInvalidArgumentException );
|
||||
|
||||
/**
|
||||
* Creates a complete fillet geometry connecting two segments.
|
||||
@ -1365,12 +1369,14 @@ class CORE_EXPORT QgsGeometryUtils
|
||||
* \param radius fillet radius
|
||||
* \param segments number of segments for arc discretization (≤0 for circular arc)
|
||||
* \returns geometry connecting the segments through the fillet
|
||||
* \throws QgsInvalidArgumentException
|
||||
* \since QGIS 4.0
|
||||
*/
|
||||
static std::unique_ptr< QgsAbstractGeometry >createFilletGeometry(
|
||||
static std::unique_ptr< QgsAbstractGeometry > createFilletGeometry(
|
||||
const QgsPoint &segment1Start, const QgsPoint &segment1End,
|
||||
const QgsPoint &segment2Start, const QgsPoint &segment2End,
|
||||
double radius, int segments );
|
||||
double radius, int segments
|
||||
) SIP_THROW( QgsInvalidArgumentException );
|
||||
|
||||
/**
|
||||
* Applies chamfer to a vertex in a curve geometry.
|
||||
@ -1379,11 +1385,13 @@ class CORE_EXPORT QgsGeometryUtils
|
||||
* \param distance1 chamfer distance along first segment
|
||||
* \param distance2 chamfer distance along second segment
|
||||
* \returns new geometry with chamfer applied, or None on failure
|
||||
* \throws QgsInvalidArgumentException
|
||||
* \since QGIS 4.0
|
||||
*/
|
||||
static std::unique_ptr< QgsAbstractGeometry >chamferVertex(
|
||||
static std::unique_ptr< QgsAbstractGeometry > chamferVertex(
|
||||
const QgsCurve *curve, int vertexIndex,
|
||||
double distance1, double distance2 );
|
||||
double distance1, double distance2
|
||||
) SIP_THROW( QgsInvalidArgumentException );
|
||||
|
||||
/**
|
||||
* Applies fillet to a vertex in a curve geometry.
|
||||
@ -1392,15 +1400,18 @@ class CORE_EXPORT QgsGeometryUtils
|
||||
* \param radius fillet radius
|
||||
* \param segments number of segments for arc discretization (≤0 for circular arc)
|
||||
* \returns new geometry with fillet applied, or None on failure
|
||||
* \throws QgsInvalidArgumentException
|
||||
* \since QGIS 4.0
|
||||
*/
|
||||
static std::unique_ptr< QgsAbstractGeometry >filletVertex(
|
||||
static std::unique_ptr< QgsAbstractGeometry > filletVertex(
|
||||
const QgsCurve *curve, int vertexIndex,
|
||||
double radius, int segments );
|
||||
double radius, int segments
|
||||
) SIP_THROW( QgsInvalidArgumentException );
|
||||
|
||||
/**
|
||||
* Convenient method of createFillet using array output.
|
||||
* \note Not available in Python bindings.
|
||||
* \throws QgsInvalidArgumentException
|
||||
* \since QGIS 4.0
|
||||
*/
|
||||
static bool createFilletArray( const QgsPoint &segment1Start, const QgsPoint &segment1End,
|
||||
|
@ -16,6 +16,7 @@ email : loic dot bartoletti at oslandia dot com
|
||||
#include "qgsgeometryutils_base.h"
|
||||
#include "qgsvector3d.h"
|
||||
#include "qgsvector.h"
|
||||
#include "qgsexception.h"
|
||||
|
||||
double QgsGeometryUtilsBase::sqrDistToLine( double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon )
|
||||
{
|
||||
@ -759,7 +760,7 @@ bool QgsGeometryUtilsBase::createFillet(
|
||||
|
||||
if ( !isIntersection )
|
||||
{
|
||||
return false;
|
||||
throw QgsInvalidArgumentException( "Segments do not intersect." );
|
||||
}
|
||||
|
||||
// Calculate distances from intersection to all segment endpoints
|
||||
@ -800,7 +801,7 @@ bool QgsGeometryUtilsBase::createFillet(
|
||||
// Validate angle - must be meaningful for fillet creation
|
||||
if ( std::abs( angle ) < epsilon || std::abs( angle - M_PI ) < epsilon )
|
||||
{
|
||||
return false; // Parallel or anti-parallel rays
|
||||
throw QgsInvalidArgumentException( "Parallel or anti-parallel segments." );
|
||||
}
|
||||
|
||||
// Use the interior angle (always ≤ π) for fillet calculations
|
||||
@ -813,7 +814,8 @@ bool QgsGeometryUtilsBase::createFillet(
|
||||
const double halfAngle = workingAngle / 2.0;
|
||||
if ( std::abs( std::sin( halfAngle ) ) < epsilon )
|
||||
{
|
||||
return false; // Avoid division by very small numbers
|
||||
// Avoid division by very small numbers.
|
||||
throw QgsInvalidArgumentException( "Segment angle near 0 will generate wrong division" );
|
||||
}
|
||||
|
||||
// Calculate distance from intersection to tangent points using trigonometry
|
||||
@ -833,12 +835,12 @@ bool QgsGeometryUtilsBase::createFillet(
|
||||
// This allows fillets on non-touching segments (like chamfer behavior)
|
||||
if ( intersectionOnSeg1 && distanceToTangent > maxDist1 - epsilon )
|
||||
{
|
||||
return false;
|
||||
throw QgsInvalidArgumentException( "Intersection 1 on segment but too far." );
|
||||
}
|
||||
|
||||
if ( intersectionOnSeg2 && distanceToTangent > maxDist2 - epsilon )
|
||||
{
|
||||
return false;
|
||||
throw QgsInvalidArgumentException( "Intersection 2 on segment but too far." );
|
||||
}
|
||||
|
||||
// Calculate tangent points along the rays
|
||||
@ -929,13 +931,13 @@ bool QgsGeometryUtilsBase::createChamfer(
|
||||
double *trim2EndX, double *trim2EndY,
|
||||
const double epsilon )
|
||||
{
|
||||
// Apply symmetric distance if distance2 is negative
|
||||
if ( distance2 <= 0 )
|
||||
distance2 = distance1;
|
||||
|
||||
// Only for positive distance
|
||||
if ( distance1 < 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( distance1 <= 0 || distance2 <= 0 )
|
||||
throw QgsInvalidArgumentException( "Negative distances." );
|
||||
|
||||
// Find intersection point between segments (or their infinite line extensions)
|
||||
double intersectionX, intersectionY;
|
||||
@ -947,7 +949,7 @@ bool QgsGeometryUtilsBase::createChamfer(
|
||||
|
||||
if ( !isIntersection )
|
||||
{
|
||||
return false;
|
||||
throw QgsInvalidArgumentException( "Segments do not intersect." );
|
||||
}
|
||||
|
||||
// Apply symmetric distance if distance2 is negative
|
||||
|
@ -174,6 +174,8 @@ class TestQgsGeometry : public QgsTest
|
||||
|
||||
void wktParser();
|
||||
|
||||
void chamferFillet();
|
||||
|
||||
private:
|
||||
//! Must be called before each render test
|
||||
void initPainterTest();
|
||||
@ -3184,5 +3186,44 @@ void TestQgsGeometry::wktParser()
|
||||
QVERIFY( mline.fromWkt( "MultiLineString EMPTY" ) );
|
||||
QCOMPARE( mline.asWkt(), QStringLiteral( "MultiLineString EMPTY" ) );
|
||||
}
|
||||
|
||||
void TestQgsGeometry::chamferFillet()
|
||||
{
|
||||
QgsGeometry g, g2;
|
||||
|
||||
g = QgsGeometry::fromWkt( QStringLiteral( "Point( 4 5 )" ) );
|
||||
QCOMPARE( g.lastError(), "" );
|
||||
g.chamfer( 1, 0.5, 0.5 );
|
||||
QCOMPARE( g.lastError(), "Chamfer needs curve geometry." );
|
||||
|
||||
g = QgsGeometry::fromWkt( QStringLiteral( "LineString(0 1, 1 2))" ) );
|
||||
QCOMPARE( g.lastError(), "" );
|
||||
g2 = g.chamfer( 1, 0.5, 0.5 );
|
||||
QCOMPARE( g.lastError(), "Vertex index out of range." );
|
||||
|
||||
g = QgsGeometry::fromWkt( QStringLiteral( "LineString(0 1, 1 2))" ) );
|
||||
QCOMPARE( g.lastError(), "" );
|
||||
g2 = g.chamfer( 5, 0.5, 0.5 );
|
||||
QCOMPARE( g.lastError(), "Vertex index out of range." );
|
||||
|
||||
g = QgsGeometry::fromWkt( QStringLiteral( "LineString(0 1, 1 2, 3 1))" ) );
|
||||
QCOMPARE( g.lastError(), "" );
|
||||
g2 = g.chamfer( 1, 0.5, 0.5 );
|
||||
QCOMPARE( g.lastError(), "" );
|
||||
QCOMPARE( g2.asWkt( 2 ), "LineString (0 1, 0.65 1.65, 1.45 1.78, 3 1)" );
|
||||
|
||||
g = QgsGeometry::fromWkt( QStringLiteral( "Polygon(( 5 15, 10 15, 10 20, 5 20, 5 15 ))" ) );
|
||||
QCOMPARE( g.lastError(), "" );
|
||||
g2 = g.chamfer( 1, 3.0, 2.5 );
|
||||
QCOMPARE( g.lastError(), "" );
|
||||
QCOMPARE( g2.asWkt( 2 ), "Polygon ((5 15, 7 15, 10 17.5, 10 20, 5 20, 5 15))" );
|
||||
|
||||
g = QgsGeometry::fromWkt( QStringLiteral( "Polygon(( 5 15, 10 15, 10 20, 5 20, 5 15 ), (6 16, 8 16, 8 18, 6 16 ))" ) );
|
||||
QCOMPARE( g.lastError(), "" );
|
||||
g2 = g.chamfer( 1, 3.0, 2.5 );
|
||||
QCOMPARE( g.lastError(), "" );
|
||||
QCOMPARE( g2.asWkt( 2 ), "Polygon ((5 15, 7 15, 10 17.5, 10 20, 5 20, 5 15), (6 16, 8 16, 8 18, 6 16 ))" );
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsGeometry )
|
||||
#include "testqgsgeometry.moc"
|
||||
|
@ -172,14 +172,26 @@ void TestQgsGeometryUtilsBase::testCreateChamferBase()
|
||||
double trim1StartX, trim1StartY, trim1EndX, trim1EndY;
|
||||
double trim2StartX, trim2StartY, trim2EndX, trim2EndY;
|
||||
|
||||
bool result = QgsGeometryUtilsBase::createChamfer(
|
||||
segment1StartX, segment1StartY, segment1EndX, segment1EndY,
|
||||
segment2StartX, segment2StartY, segment2EndX, segment2EndY,
|
||||
distance1, distance2,
|
||||
chamferStartX, chamferStartY, chamferEndX, chamferEndY,
|
||||
&trim1StartX, &trim1StartY, &trim1EndX, &trim1EndY,
|
||||
&trim2StartX, &trim2StartY, &trim2EndX, &trim2EndY
|
||||
);
|
||||
bool result;
|
||||
try
|
||||
{
|
||||
result = QgsGeometryUtilsBase::createChamfer(
|
||||
segment1StartX, segment1StartY, segment1EndX, segment1EndY,
|
||||
segment2StartX, segment2StartY, segment2EndX, segment2EndY,
|
||||
distance1, distance2,
|
||||
chamferStartX, chamferStartY, chamferEndX, chamferEndY,
|
||||
&trim1StartX, &trim1StartY, &trim1EndX, &trim1EndY,
|
||||
&trim2StartX, &trim2StartY, &trim2EndX, &trim2EndY
|
||||
);
|
||||
}
|
||||
catch ( QgsInvalidArgumentException &e )
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
QVERIFY2( false, "Caught unhandled exception" );
|
||||
}
|
||||
|
||||
QCOMPARE( result, expectedSuccess );
|
||||
|
||||
@ -315,14 +327,26 @@ void TestQgsGeometryUtilsBase::testCreateFilletBase()
|
||||
double trim1StartX, trim1StartY, trim1EndX, trim1EndY;
|
||||
double trim2StartX, trim2StartY, trim2EndX, trim2EndY;
|
||||
|
||||
bool result = QgsGeometryUtilsBase::createFillet(
|
||||
segment1StartX, segment1StartY, segment1EndX, segment1EndY,
|
||||
segment2StartX, segment2StartY, segment2EndX, segment2EndY,
|
||||
radius,
|
||||
filletPointsX, filletPointsY,
|
||||
&trim1StartX, &trim1StartY, &trim1EndX, &trim1EndY,
|
||||
&trim2StartX, &trim2StartY, &trim2EndX, &trim2EndY
|
||||
);
|
||||
bool result;
|
||||
try
|
||||
{
|
||||
result = QgsGeometryUtilsBase::createFillet(
|
||||
segment1StartX, segment1StartY, segment1EndX, segment1EndY,
|
||||
segment2StartX, segment2StartY, segment2EndX, segment2EndY,
|
||||
radius,
|
||||
filletPointsX, filletPointsY,
|
||||
&trim1StartX, &trim1StartY, &trim1EndX, &trim1EndY,
|
||||
&trim2StartX, &trim2StartY, &trim2EndX, &trim2EndY
|
||||
);
|
||||
}
|
||||
catch ( QgsInvalidArgumentException &e )
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
QVERIFY2( false, "Caught unhandled exception" );
|
||||
}
|
||||
|
||||
QCOMPARE( result, expectedSuccess );
|
||||
|
||||
|
@ -12,6 +12,8 @@ __copyright__ = "Copyright 2025, The QGIS Project"
|
||||
|
||||
from qgis.core import (
|
||||
Qgis,
|
||||
QgsInvalidArgumentException,
|
||||
QgsGeometryUtils,
|
||||
QgsCircularString,
|
||||
QgsCompoundCurve,
|
||||
QgsGeometry,
|
||||
@ -226,11 +228,12 @@ class TestQgsGeometry(QgisTestCase):
|
||||
segment2_start = QgsPoint(0.0, 1.0)
|
||||
segment2_end = QgsPoint(1.0, 1.0)
|
||||
|
||||
result = QgsGeometry().chamfer(
|
||||
g = QgsGeometry()
|
||||
result = g.chamfer(
|
||||
segment1_start, segment1_end, segment2_start, segment2_end, 0.1, 0.1
|
||||
)
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
self.assertEqual(g.lastError(), "Segments do not intersect.")
|
||||
|
||||
def test_chamfer_segments_negative_distances(self):
|
||||
"""Test that negative distances return empty geometry"""
|
||||
@ -243,11 +246,12 @@ class TestQgsGeometry(QgisTestCase):
|
||||
segment2_start = QgsPoint(0.0, 1.0)
|
||||
segment2_end = QgsPoint(0.0, 0.0)
|
||||
|
||||
result = QgsGeometry().chamfer(
|
||||
g = QgsGeometry()
|
||||
result = g.chamfer(
|
||||
segment1_start, segment1_end, segment2_start, segment2_end, -0.1, 0.1
|
||||
)
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
self.assertEqual(g.lastError(), "Negative distances.")
|
||||
|
||||
# CHAMFER TESTS - VERTEX-BASED OVERLOAD
|
||||
|
||||
@ -283,8 +287,8 @@ class TestQgsGeometry(QgisTestCase):
|
||||
linestring = QgsGeometry.fromWkt("LineString (0 0, 1 0, 1 1)")
|
||||
|
||||
result = linestring.chamfer(0, 0.1, 0.1)
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
self.assertEqual(linestring.lastError(), "Vertex index out of range.")
|
||||
|
||||
def test_chamfer_vertex_invalid_index_last(self):
|
||||
"""Test chamfer with invalid vertex index (last vertex)"""
|
||||
@ -293,6 +297,7 @@ class TestQgsGeometry(QgisTestCase):
|
||||
result = linestring.chamfer(2, 0.1, 0.1)
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
self.assertEqual(linestring.lastError(), "Vertex index out of range.")
|
||||
|
||||
def test_chamfer_vertex_acute_angle(self):
|
||||
"""Test chamfer at vertex with acute angle"""
|
||||
@ -312,6 +317,15 @@ class TestQgsGeometry(QgisTestCase):
|
||||
expected_wkt = "LineString (0 0, 2 0, 3.7 0, 4 0.3, 4 2, 4 4)"
|
||||
self.assertEqual(result.asWkt(2), expected_wkt)
|
||||
|
||||
def test_geometryutils_chamfer_vertex_complex_linestring(self):
|
||||
"""Test chamfer on complex linestring with multiple vertices from QgsGeometryUtils."""
|
||||
linestring = QgsGeometry.fromWkt("LineString (0 0, 2 0, 4 0, 4 2, 4 4)")
|
||||
|
||||
result = QgsGeometryUtils.chamferVertex(linestring.get(), 2, 0.3, 0.3)
|
||||
|
||||
expected_wkt = "LineString (0 0, 2 0, 3.7 0, 4 0.3, 4 2, 4 4)"
|
||||
self.assertEqual(result.asWkt(2), expected_wkt)
|
||||
|
||||
# FILLET TESTS - SEGMENT-BASED OVERLOAD
|
||||
|
||||
def test_fillet_segments_basic_right_angle_non_touching(self):
|
||||
@ -571,11 +585,12 @@ class TestQgsGeometry(QgisTestCase):
|
||||
segment2_start = QgsPoint(0.0, 0.1)
|
||||
segment2_end = QgsPoint(0.0, 0.0)
|
||||
|
||||
result = QgsGeometry().fillet(
|
||||
g = QgsGeometry()
|
||||
result = g.fillet(
|
||||
segment1_start, segment1_end, segment2_start, segment2_end, 1.0
|
||||
)
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
self.assertEqual(g.lastError(), "Intersection 1 on segment but too far.")
|
||||
|
||||
def test_fillet_segments_zero_radius_failure(self):
|
||||
"""Test that zero radius returns empty geometry"""
|
||||
@ -588,11 +603,12 @@ class TestQgsGeometry(QgisTestCase):
|
||||
segment2_start = QgsPoint(0.0, 1.0)
|
||||
segment2_end = QgsPoint(0.0, 0.0)
|
||||
|
||||
result = QgsGeometry().fillet(
|
||||
g = QgsGeometry()
|
||||
result = g.fillet(
|
||||
segment1_start, segment1_end, segment2_start, segment2_end, 0.0
|
||||
)
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
self.assertEqual(g.lastError(), "Radius <= 0.")
|
||||
|
||||
def test_fillet_segments_acute_angle(self):
|
||||
"""Test fillet creation with acute angle"""
|
||||
@ -662,24 +678,26 @@ class TestQgsGeometry(QgisTestCase):
|
||||
linestring = QgsGeometry.fromWkt("LineString (0 0, 1 0, 1 1)")
|
||||
|
||||
result = linestring.fillet(0, 0.1)
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
self.assertEqual(linestring.lastError(), "Vertex index out of range.")
|
||||
|
||||
def test_fillet_vertex_invalid_index_last(self):
|
||||
"""Test fillet with invalid vertex index (last vertex)"""
|
||||
linestring = QgsGeometry.fromWkt("LineString (0 0, 1 0, 1 1)")
|
||||
|
||||
result = linestring.fillet(2, 0.1)
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
self.assertEqual(linestring.lastError(), "Vertex index out of range.")
|
||||
|
||||
def test_fillet_vertex_large_radius_failure(self):
|
||||
"""Test fillet with radius too large for available geometry"""
|
||||
linestring = QgsGeometry.fromWkt("LineString (0 0, 0.1 0, 0.1 0.1)")
|
||||
|
||||
result = linestring.fillet(1, 1.0)
|
||||
self.assertFalse(result.isEmpty())
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
expected_wkt = "LineString (0 0, 0.02 0, 0.04 0.01, 0.06 0.02, 0.07 0.03, 0.08 0.04, 0.09 0.06, 0.1 0.08, 0.1 0.1)"
|
||||
self.assertEqual(result.asWkt(2), expected_wkt)
|
||||
|
||||
# EDGE CASES AND ERROR HANDLING
|
||||
|
||||
@ -712,7 +730,6 @@ class TestQgsGeometry(QgisTestCase):
|
||||
point = QgsGeometry.fromWkt("Point (0 0)")
|
||||
|
||||
result = point.fillet(0, 0.1)
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
|
||||
def test_two_point_linestring_chamfer(self):
|
||||
@ -720,32 +737,66 @@ class TestQgsGeometry(QgisTestCase):
|
||||
linestring = QgsGeometry.fromWkt("LineString (0 0, 1 1)")
|
||||
|
||||
result = linestring.chamfer(1, 0.1, 0.1)
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
self.assertEqual(linestring.lastError(), "Vertex index out of range.")
|
||||
|
||||
def test_two_point_linestring_fillet(self):
|
||||
"""Test fillet behavior with minimum linestring"""
|
||||
linestring = QgsGeometry.fromWkt("LineString (0 0, 1 1)")
|
||||
|
||||
result = linestring.fillet(1, 0.1)
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
self.assertEqual(linestring.lastError(), "Vertex index out of range.")
|
||||
|
||||
def test_geometryutils_two_point_linestring_fillet(self):
|
||||
"""Test fillet behavior with minimum linestring from QgsGeometryUtils. Should throw exception"""
|
||||
linestring = QgsGeometry.fromWkt("LineString (0 0, 1 1)")
|
||||
|
||||
try:
|
||||
result = QgsGeometryUtils.filletVertex(linestring.get(), 1, 0.1, 8)
|
||||
self.fail("Should have failed!")
|
||||
except QgsInvalidArgumentException as e:
|
||||
self.assertEqual(e.__str__(), "Vertex index out of range.")
|
||||
|
||||
def test_geometryutils_two_point_linestring_chamfer(self):
|
||||
"""Test chamfer behavior with minimum linestring from QgsGeometryUtils. Should throw exception"""
|
||||
linestring = QgsGeometry.fromWkt("LineString (0 0, 1 1)")
|
||||
|
||||
try:
|
||||
result = QgsGeometryUtils.chamferVertex(linestring.get(), 1, 0.1, 0.1)
|
||||
self.fail("Should have failed!")
|
||||
except QgsInvalidArgumentException as e:
|
||||
self.assertEqual(e.__str__(), "Vertex index out of range.")
|
||||
|
||||
def test_geometryutils_bad_distance_linestring_chamfer(self):
|
||||
"""Test chamfer behavior with minimum linestring from QgsGeometryUtils. Should throw exception"""
|
||||
linestring = QgsGeometry.fromWkt("LineString (0 0, 1 1, 1 2, 2 3)")
|
||||
|
||||
try:
|
||||
result = QgsGeometryUtils.chamferVertex(linestring.get(), 1, -0.1, -0.1)
|
||||
self.fail("Should have failed!")
|
||||
except QgsInvalidArgumentException as e:
|
||||
self.assertEqual(e.__str__(), "Negative distances.")
|
||||
|
||||
def test_polygon_geometry_handling_chamfer(self):
|
||||
"""Test chamfer behavior with polygon geometries (should fail gracefully)"""
|
||||
"""Test chamfer behavior with polygon geometries"""
|
||||
polygon = QgsGeometry.fromWkt("Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))")
|
||||
|
||||
result = polygon.chamfer(1, 0.1)
|
||||
self.assertFalse(result.isEmpty())
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
expected_wkt = "Polygon ((0 0, 0.9 0, 1 0.1, 1 1, 0 1, 0 0))"
|
||||
self.assertEqual(result.asWkt(2), expected_wkt)
|
||||
|
||||
def test_polygon_geometry_handling_fillet(self):
|
||||
"""Test fillet behavior with polygon geometries (should fail gracefully)"""
|
||||
polygon = QgsGeometry.fromWkt("Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))")
|
||||
|
||||
result = polygon.fillet(1, 0.1)
|
||||
self.assertFalse(result.isEmpty())
|
||||
|
||||
self.assertTrue(result.isEmpty())
|
||||
expected_wkt = "Polygon ((0 0, 0.92 0, 0.93 0.01, 0.95 0.01, 0.96 0.02, 0.98 0.04, 0.99 0.05, 0.99 0.07, 1 0.08, 1 1, 0 1, 0 0))"
|
||||
self.assertEqual(result.asWkt(2), expected_wkt)
|
||||
|
||||
# PRECISION AND PERFORMANCE TESTS
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user