Skip to content

Commit 644960e

Browse files
committed
Numerically more robust circle center calculation. Provided by Timo Iipponen
1 parent 9d81938 commit 644960e

File tree

1 file changed

+26
-19
lines changed

1 file changed

+26
-19
lines changed

src/core/geometry/qgsgeometryutils.cpp

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ double QgsGeometryUtils::ccwAngle( double dy, double dx )
280280

281281
void QgsGeometryUtils::circleCenterRadius( const QgsPointV2& pt1, const QgsPointV2& pt2, const QgsPointV2& pt3, double& radius, double& centerX, double& centerY )
282282
{
283-
double temp, bc, cd, det;
283+
double dx21, dy21, dx31, dy31, h21, h31, d;
284284

285285
//closed circle
286286
if ( qgsDoubleNear( pt1.x(), pt3.x() ) && qgsDoubleNear( pt1.y(), pt3.y() ) )
@@ -291,22 +291,29 @@ void QgsGeometryUtils::circleCenterRadius( const QgsPointV2& pt1, const QgsPoint
291291
return;
292292
}
293293

294-
temp = pt2.x() * pt2.x() + pt2.y() * pt2.y();
295-
bc = ( pt1.x() * pt1.x() + pt1.y() * pt1.y() - temp ) / 2.0;
296-
cd = ( temp - pt3.x() * pt3.x() - pt3.y() * pt3.y() ) / 2.0;
297-
det = ( pt1.x() - pt2.x() ) * ( pt2.y() - pt3.y() ) - ( pt2.x() - pt3.x() ) * ( pt1.y() - pt2.y() );
294+
// Using cartesian circumcenter eguations from page https://en.wikipedia.org/wiki/Circumscribed_circle
295+
dx21 = pt2.x() - pt1.x();
296+
dy21 = pt2.y() - pt1.y();
297+
dx31 = pt3.x() - pt1.x();
298+
dy31 = pt3.y() - pt1.y();
298299

299-
/* Check colinearity */
300-
if ( qgsDoubleNear( fabs( det ), 0.0, 0.00000000001 ) )
300+
h21 = pow( dx21, 2.0 ) + pow( dy21, 2.0 );
301+
h31 = pow( dx31, 2.0 ) + pow( dy31, 2.0 );
302+
303+
// 2*Cross product, d<0 means clockwise and d>0 counterclockwise sweeping angle
304+
d = 2 * ( dx21 * dy31 - dx31 * dy21 );
305+
306+
// Check colinearity, Cross product = 0
307+
if ( qgsDoubleNear( fabs( d ), 0.0, 0.00000000001 ) )
301308
{
302309
radius = -1.0;
303310
return;
304311
}
305312

306-
det = 1.0 / det;
307-
centerX = ( bc * ( pt2.y() - pt3.y() ) - cd * ( pt1.y() - pt2.y() ) ) * det;
308-
centerY = (( pt1.x() - pt2.x() ) * cd - ( pt2.x() - pt3.x() ) * bc ) * det;
309-
radius = sqrt(( centerX - pt1.x() ) * ( centerX - pt1.x() ) + ( centerY - pt1.y() ) * ( centerY - pt1.y() ) );
313+
// Calculate centroid coordinates and radius
314+
centerX = pt1.x() + ( h21 * dy31 - h31 * dy21 ) / d;
315+
centerY = pt1.y() - ( h21 * dx31 - h31 * dx21 ) / d;
316+
radius = sqrt( pow( centerX - pt1.x(), 2.0 ) + pow( centerY - pt1.y(), 2.0 ) );
310317
}
311318

312319
bool QgsGeometryUtils::circleClockwise( double angle1, double angle2, double angle3 )
@@ -481,7 +488,7 @@ QList<QgsPointV2> QgsGeometryUtils::pointsFromWKT( const QString &wktCoordinateL
481488
//first scan through for extra unexpected dimensions
482489
bool foundZ = false;
483490
bool foundM = false;
484-
Q_FOREACH ( const QString& pointCoordinates, coordList )
491+
Q_FOREACH( const QString& pointCoordinates, coordList )
485492
{
486493
QStringList coordinates = pointCoordinates.split( ' ', QString::SkipEmptyParts );
487494
if ( coordinates.size() == 3 && !foundZ && !foundM && !is3D && !isMeasure )
@@ -499,7 +506,7 @@ QList<QgsPointV2> QgsGeometryUtils::pointsFromWKT( const QString &wktCoordinateL
499506
}
500507
}
501508

502-
Q_FOREACH ( const QString& pointCoordinates, coordList )
509+
Q_FOREACH( const QString& pointCoordinates, coordList )
503510
{
504511
QStringList coordinates = pointCoordinates.split( ' ', QString::SkipEmptyParts );
505512
if ( coordinates.size() < dim )
@@ -542,7 +549,7 @@ QList<QgsPointV2> QgsGeometryUtils::pointsFromWKT( const QString &wktCoordinateL
542549
void QgsGeometryUtils::pointsToWKB( QgsWkbPtr& wkb, const QList<QgsPointV2> &points, bool is3D, bool isMeasure )
543550
{
544551
wkb << static_cast<quint32>( points.size() );
545-
Q_FOREACH ( const QgsPointV2& point, points )
552+
Q_FOREACH( const QgsPointV2& point, points )
546553
{
547554
wkb << point.x() << point.y();
548555
if ( is3D )
@@ -559,7 +566,7 @@ void QgsGeometryUtils::pointsToWKB( QgsWkbPtr& wkb, const QList<QgsPointV2> &poi
559566
QString QgsGeometryUtils::pointsToWKT( const QList<QgsPointV2>& points, int precision, bool is3D, bool isMeasure )
560567
{
561568
QString wkt = "(";
562-
Q_FOREACH ( const QgsPointV2& p, points )
569+
Q_FOREACH( const QgsPointV2& p, points )
563570
{
564571
wkt += qgsDoubleToString( p.x(), precision );
565572
wkt += ' ' + qgsDoubleToString( p.y(), precision );
@@ -581,8 +588,8 @@ QDomElement QgsGeometryUtils::pointsToGML2( const QList<QgsPointV2>& points, QDo
581588

582589
QString strCoordinates;
583590

584-
Q_FOREACH ( const QgsPointV2& p, points )
585-
strCoordinates += qgsDoubleToString( p.x(), precision ) + ',' + qgsDoubleToString( p.y(), precision ) + ' ';
591+
Q_FOREACH( const QgsPointV2& p, points )
592+
strCoordinates += qgsDoubleToString( p.x(), precision ) + ',' + qgsDoubleToString( p.y(), precision ) + ' ';
586593

587594
if ( strCoordinates.endsWith( ' ' ) )
588595
strCoordinates.chop( 1 ); // Remove trailing space
@@ -597,7 +604,7 @@ QDomElement QgsGeometryUtils::pointsToGML3( const QList<QgsPointV2>& points, QDo
597604
elemPosList.setAttribute( "srsDimension", is3D ? 3 : 2 );
598605

599606
QString strCoordinates;
600-
Q_FOREACH ( const QgsPointV2& p, points )
607+
Q_FOREACH( const QgsPointV2& p, points )
601608
{
602609
strCoordinates += qgsDoubleToString( p.x(), precision ) + ' ' + qgsDoubleToString( p.y(), precision ) + ' ';
603610
if ( is3D )
@@ -613,7 +620,7 @@ QDomElement QgsGeometryUtils::pointsToGML3( const QList<QgsPointV2>& points, QDo
613620
QString QgsGeometryUtils::pointsToJSON( const QList<QgsPointV2>& points, int precision )
614621
{
615622
QString json = "[ ";
616-
Q_FOREACH ( const QgsPointV2& p, points )
623+
Q_FOREACH( const QgsPointV2& p, points )
617624
{
618625
json += '[' + qgsDoubleToString( p.x(), precision ) + ", " + qgsDoubleToString( p.y(), precision ) + "], ";
619626
}

0 commit comments

Comments
 (0)