Fixes empty WKT. Fixes #20753, Fixes #19190, Fixes #20754 (#9645)

* Fixes empty WKT. Fixes #20753, Fixes #19190, Fixes #20754
This commit is contained in:
lbartoletti 2019-07-30 09:34:53 +02:00 committed by Hugo Mercier
parent 45fba385c5
commit 794a8efc81
25 changed files with 578 additions and 204 deletions

View File

@ -26,7 +26,7 @@ Point geometry type, with support for z-dimension and m-values.
public:
QgsPoint( double x = 0.0, double y = 0.0, SIP_PYOBJECT z = Py_None, SIP_PYOBJECT m = Py_None, QgsWkbTypes::Type wkbType = QgsWkbTypes::Unknown ) [( double x = 0.0, double y = 0.0, double z = 0.0, double m = 0.0, QgsWkbTypes::Type wkbType = QgsWkbTypes::Unknown )];
QgsPoint( SIP_PYOBJECT x = Py_None, SIP_PYOBJECT y = Py_None, SIP_PYOBJECT z = Py_None, SIP_PYOBJECT m = Py_None, QgsWkbTypes::Type wkbType = QgsWkbTypes::Unknown ) [( double x = 0.0, double y = 0.0, double z = 0.0, double m = 0.0, QgsWkbTypes::Type wkbType = QgsWkbTypes::Unknown )];
%Docstring
Construct a point with the provided initial coordinate values.
@ -53,9 +53,29 @@ based on the following rules:
pt.wkbType() # QgsWkbTypes.PointZ
%End
%MethodCode
double x;
double y;
double z;
double m;
if ( a0 == Py_None )
{
x = std::numeric_limits<double>::quiet_NaN();
}
else
{
x = PyFloat_AsDouble( a0 );
}
if ( a1 == Py_None )
{
y = std::numeric_limits<double>::quiet_NaN();
}
else
{
y = PyFloat_AsDouble( a1 );
}
if ( a2 == Py_None )
{
z = std::numeric_limits<double>::quiet_NaN();
@ -74,7 +94,7 @@ based on the following rules:
m = PyFloat_AsDouble( a3 );
}
sipCpp = new sipQgsPoint( a0, a1, z, m, a4 );
sipCpp = new sipQgsPoint( x, y, z, m, a4 );
%End
explicit QgsPoint( const QgsPointXY &p );

View File

@ -184,6 +184,16 @@ in a specified bearing.
:param bearing: angle to project in, clockwise in degrees starting from north
.. versionadded:: 2.16
%End
bool isEmpty() const;
%Docstring
Returns ``True`` if the geometry is empty.
Unlike :py:class:`QgsPoint`, this class is also used to retrieve graphical coordinates like QPointF.
It therefore has the default coordinates (0.0).
A QgsPointXY is considered empty, when the coordinates have not been explicitly filled in.
.. versionadded:: 3.10
%End
bool compare( const QgsPointXY &other, double epsilon = 4 * DBL_EPSILON ) const;

View File

@ -314,6 +314,9 @@ bool QgsCircularString::fromWkt( const QString &wkt )
return false;
mWkbType = parts.first;
if ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 )
return true;
setPoints( QgsGeometryUtils::pointsFromWKT( parts.second, is3D(), isMeasure() ) );
return true;
}
@ -337,9 +340,15 @@ QByteArray QgsCircularString::asWkb() const
QString QgsCircularString::asWkt( int precision ) const
{
QString wkt = wktTypeStr() + ' ';
QgsPointSequence pts;
points( pts );
wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
if ( isEmpty() )
wkt += QStringLiteral( "EMPTY" );
else
{
QgsPointSequence pts;
points( pts );
wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
}
return wkt;
}

View File

@ -179,6 +179,9 @@ bool QgsCompoundCurve::fromWkt( const QString &wkt )
return false;
mWkbType = parts.first;
if ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 )
return true;
QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
@ -248,22 +251,28 @@ QByteArray QgsCompoundCurve::asWkb() const
QString QgsCompoundCurve::asWkt( int precision ) const
{
QString wkt = wktTypeStr() + QLatin1String( " (" );
for ( const QgsCurve *curve : mCurves )
QString wkt = wktTypeStr();
if ( isEmpty() )
wkt += QStringLiteral( " EMPTY" );
else
{
QString childWkt = curve->asWkt( precision );
if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
wkt += QLatin1String( " (" );
for ( const QgsCurve *curve : mCurves )
{
// Type names of linear geometries are omitted
childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
QString childWkt = curve->asWkt( precision );
if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
{
// Type names of linear geometries are omitted
childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
}
wkt += childWkt + ',';
}
wkt += childWkt + ',';
if ( wkt.endsWith( ',' ) )
{
wkt.chop( 1 );
}
wkt += ')';
}
if ( wkt.endsWith( ',' ) )
{
wkt.chop( 1 );
}
wkt += ')';
return wkt;
}

View File

@ -215,6 +215,9 @@ bool QgsCurvePolygon::fromWkt( const QString &wkt )
mWkbType = parts.first;
if ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 )
return true;
QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
@ -315,32 +318,39 @@ QByteArray QgsCurvePolygon::asWkb() const
QString QgsCurvePolygon::asWkt( int precision ) const
{
QString wkt = wktTypeStr() + QLatin1String( " (" );
if ( mExteriorRing )
QString wkt = wktTypeStr();
if ( isEmpty() )
wkt += QStringLiteral( " EMPTY" );
else
{
QString childWkt = mExteriorRing->asWkt( precision );
if ( qgsgeometry_cast<QgsLineString *>( mExteriorRing.get() ) )
wkt += QLatin1String( " (" );
if ( mExteriorRing )
{
// Type names of linear geometries are omitted
childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
QString childWkt = mExteriorRing->asWkt( precision );
if ( qgsgeometry_cast<QgsLineString *>( mExteriorRing.get() ) )
{
// Type names of linear geometries are omitted
childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
}
wkt += childWkt + ',';
}
wkt += childWkt + ',';
}
for ( const QgsCurve *curve : mInteriorRings )
{
QString childWkt = curve->asWkt( precision );
if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
for ( const QgsCurve *curve : mInteriorRings )
{
// Type names of linear geometries are omitted
childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
QString childWkt = curve->asWkt( precision );
if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
{
// Type names of linear geometries are omitted
childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
}
wkt += childWkt + ',';
}
wkt += childWkt + ',';
if ( wkt.endsWith( ',' ) )
{
wkt.chop( 1 ); // Remove last ','
}
wkt += ')';
}
if ( wkt.endsWith( ',' ) )
{
wkt.chop( 1 ); // Remove last ','
}
wkt += ')';
return wkt;
}

View File

@ -1065,7 +1065,7 @@ static QgsCircle __recMinimalEnclosingCircle( QgsMultiPointXY points, QgsMultiPo
QgsGeometry QgsGeometry::minimalEnclosingCircle( QgsPointXY &center, double &radius, unsigned int segments ) const
{
center = QgsPointXY( );
center = QgsPointXY();
radius = 0;
if ( !d->geometry )
@ -1675,21 +1675,21 @@ double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double de
QgsAbstractGeometry::vertex_iterator QgsGeometry::vertices_begin() const
{
if ( !d->geometry )
if ( !d->geometry || d->geometry.get()->isEmpty() )
return QgsAbstractGeometry::vertex_iterator();
return d->geometry->vertices_begin();
}
QgsAbstractGeometry::vertex_iterator QgsGeometry::vertices_end() const
{
if ( !d->geometry )
if ( !d->geometry || d->geometry.get()->isEmpty() )
return QgsAbstractGeometry::vertex_iterator();
return d->geometry->vertices_end();
}
QgsVertexIterator QgsGeometry::vertices() const
{
if ( !d->geometry )
if ( !d->geometry || d->geometry.get()->isEmpty() )
return QgsVertexIterator();
return QgsVertexIterator( d->geometry.get() );
}

View File

@ -382,21 +382,28 @@ QByteArray QgsGeometryCollection::asWkb() const
QString QgsGeometryCollection::asWkt( int precision ) const
{
QString wkt = wktTypeStr() + QLatin1String( " (" );
for ( const QgsAbstractGeometry *geom : mGeometries )
QString wkt = wktTypeStr();
if ( isEmpty() )
wkt += QStringLiteral( " EMPTY" );
else
{
QString childWkt = geom->asWkt( precision );
if ( wktOmitChildType() )
wkt += QLatin1String( " (" );
for ( const QgsAbstractGeometry *geom : mGeometries )
{
childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
QString childWkt = geom->asWkt( precision );
if ( wktOmitChildType() )
{
childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
}
wkt += childWkt + ',';
}
wkt += childWkt + ',';
if ( wkt.endsWith( ',' ) )
{
wkt.chop( 1 ); // Remove last ','
}
wkt += ')';
}
if ( wkt.endsWith( ',' ) )
{
wkt.chop( 1 ); // Remove last ','
}
wkt += ')';
return wkt;
}
@ -642,6 +649,9 @@ bool QgsGeometryCollection::fromCollectionWkt( const QString &wkt, const QVector
}
mWkbType = parts.first;
if ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 )
return true;
QString defChildWkbType = QStringLiteral( "%1%2%3 " ).arg( defaultChildWkbType, is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defChildWkbType );

View File

@ -93,6 +93,10 @@ std::unique_ptr<QgsAbstractGeometry> QgsGeometryFactory::geomFromWkt( const QStr
{
geom = qgis::make_unique< QgsPolygon >();
}
else if ( trimmed.startsWith( QLatin1String( "Triangle" ), Qt::CaseInsensitive ) )
{
geom = qgis::make_unique< QgsTriangle >();
}
else if ( trimmed.startsWith( QLatin1String( "CurvePolygon" ), Qt::CaseInsensitive ) )
{
geom = qgis::make_unique< QgsCurvePolygon >();

View File

@ -1233,12 +1233,27 @@ double QgsGeometryUtils::normalizedAngle( double angle )
QPair<QgsWkbTypes::Type, QString> QgsGeometryUtils::wktReadBlock( const QString &wkt )
{
QgsWkbTypes::Type wkbType = QgsWkbTypes::parseType( wkt );
QRegularExpression cooRegEx( QStringLiteral( "^[^\\(]*\\((.*)\\)[^\\)]*$" ) );
cooRegEx.setPatternOptions( QRegularExpression::DotMatchesEverythingOption );
QRegularExpressionMatch match = cooRegEx.match( wkt );
QString contents = match.hasMatch() ? match.captured( 1 ) : QString();
QString wktParsed = wkt;
QString contents;
if ( wkt.contains( QString( "EMPTY" ), Qt::CaseInsensitive ) )
{
QRegularExpression wktRegEx( QStringLiteral( "^\\s*(\\w+)\\s+(\\w+)\\s*$" ) );
wktRegEx.setPatternOptions( QRegularExpression::DotMatchesEverythingOption );
QRegularExpressionMatch match = wktRegEx.match( wkt );
if ( match.hasMatch() )
{
wktParsed = match.captured( 1 );
contents = match.captured( 2 ).toUpper();
}
}
else
{
QRegularExpression cooRegEx( QStringLiteral( "^[^\\(]*\\((.*)\\)[^\\)]*$" ) );
cooRegEx.setPatternOptions( QRegularExpression::DotMatchesEverythingOption );
QRegularExpressionMatch match = cooRegEx.match( wktParsed );
contents = match.hasMatch() ? match.captured( 1 ) : QString();
}
QgsWkbTypes::Type wkbType = QgsWkbTypes::parseType( wktParsed );
return qMakePair( wkbType, contents );
}

View File

@ -337,7 +337,6 @@ QgsRectangle QgsLineString::calculateBoundingBox() const
* full unit tests.
* See details in QEP #17
****************************************************************************/
bool QgsLineString::fromWkt( const QString &wkt )
{
clear();
@ -348,6 +347,9 @@ bool QgsLineString::fromWkt( const QString &wkt )
return false;
mWkbType = parts.first;
if ( parts.second == "EMPTY" )
return true;
setPoints( QgsGeometryUtils::pointsFromWKT( parts.second, is3D(), isMeasure() ) );
return true;
}
@ -377,9 +379,15 @@ QByteArray QgsLineString::asWkb() const
QString QgsLineString::asWkt( int precision ) const
{
QString wkt = wktTypeStr() + ' ';
QgsPointSequence pts;
points( pts );
wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
if ( isEmpty() )
wkt += QStringLiteral( "EMPTY" );
else
{
QgsPointSequence pts;
points( pts );
wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
}
return wkt;
}

View File

@ -163,6 +163,9 @@ bool QgsPoint::fromWkt( const QString &wkt )
return false;
mWkbType = parts.first;
if ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 )
return true;
QRegularExpression rx( QStringLiteral( "\\s" ) );
QStringList coordinates = parts.second.split( rx, QString::SkipEmptyParts );
if ( coordinates.size() < 2 )
@ -225,13 +228,20 @@ QByteArray QgsPoint::asWkb() const
QString QgsPoint::asWkt( int precision ) const
{
QString wkt = wktTypeStr() + QLatin1String( " (" );
wkt += qgsDoubleToString( mX, precision ) + ' ' + qgsDoubleToString( mY, precision );
if ( is3D() )
wkt += ' ' + qgsDoubleToString( mZ, precision );
if ( isMeasure() )
wkt += ' ' + qgsDoubleToString( mM, precision );
wkt += ')';
QString wkt = wktTypeStr();
if ( isEmpty() )
wkt += QStringLiteral( " EMPTY" );
else
{
wkt += QLatin1String( " (" );
wkt += qgsDoubleToString( mX, precision ) + ' ' + qgsDoubleToString( mY, precision );
if ( is3D() )
wkt += ' ' + qgsDoubleToString( mZ, precision );
if ( isMeasure() )
wkt += ' ' + qgsDoubleToString( mM, precision );
wkt += ')';
}
return wkt;
}
@ -282,11 +292,16 @@ json QgsPoint::asJsonObject( int precision ) const
json j
{
{ "type", "Point" },
{ "coordinates", { qgsRound( mX, precision ), qgsRound( mY, precision ) } },
{ "coordinates", json::array() },
};
if ( is3D() )
if ( ! isEmpty() )
{
j["coordinates"].push_back( qgsRound( mZ, precision ) );
j["coordinates"].push_back( qgsRound( mX, precision ) );
j["coordinates"].push_back( qgsRound( mY, precision ) );
if ( is3D() )
{
j["coordinates"].push_back( qgsRound( mZ, precision ) );
}
}
return j;
}
@ -298,7 +313,7 @@ void QgsPoint::draw( QPainter &p ) const
void QgsPoint::clear()
{
mX = mY = 0.;
mX = mY = std::numeric_limits<double>::quiet_NaN();
if ( is3D() )
mZ = 0.;
else
@ -686,7 +701,7 @@ QgsPoint QgsPoint::project( double distance, double azimuth, double inclination
bool QgsPoint::isEmpty() const
{
return false;
return std::isnan( mX ) || std::isnan( mY );
}
QgsRectangle QgsPoint::boundingBox() const

View File

@ -71,13 +71,33 @@ class CORE_EXPORT QgsPoint: public QgsAbstractGeometry
* \endcode
*/
#ifndef SIP_RUN
QgsPoint( double x = 0.0, double y = 0.0, double z = std::numeric_limits<double>::quiet_NaN(), double m = std::numeric_limits<double>::quiet_NaN(), QgsWkbTypes::Type wkbType = QgsWkbTypes::Unknown );
QgsPoint( double x = std::numeric_limits<double>::quiet_NaN(), double y = std::numeric_limits<double>::quiet_NaN(), double z = std::numeric_limits<double>::quiet_NaN(), double m = std::numeric_limits<double>::quiet_NaN(), QgsWkbTypes::Type wkbType = QgsWkbTypes::Unknown );
#else
QgsPoint( double x = 0.0, double y = 0.0, SIP_PYOBJECT z = Py_None, SIP_PYOBJECT m = Py_None, QgsWkbTypes::Type wkbType = QgsWkbTypes::Unknown ) [( double x = 0.0, double y = 0.0, double z = 0.0, double m = 0.0, QgsWkbTypes::Type wkbType = QgsWkbTypes::Unknown )];
QgsPoint( SIP_PYOBJECT x = Py_None, SIP_PYOBJECT y = Py_None, SIP_PYOBJECT z = Py_None, SIP_PYOBJECT m = Py_None, QgsWkbTypes::Type wkbType = QgsWkbTypes::Unknown ) [( double x = 0.0, double y = 0.0, double z = 0.0, double m = 0.0, QgsWkbTypes::Type wkbType = QgsWkbTypes::Unknown )];
% MethodCode
double x;
double y;
double z;
double m;
if ( a0 == Py_None )
{
x = std::numeric_limits<double>::quiet_NaN();
}
else
{
x = PyFloat_AsDouble( a0 );
}
if ( a1 == Py_None )
{
y = std::numeric_limits<double>::quiet_NaN();
}
else
{
y = PyFloat_AsDouble( a1 );
}
if ( a2 == Py_None )
{
z = std::numeric_limits<double>::quiet_NaN();
@ -96,7 +116,7 @@ class CORE_EXPORT QgsPoint: public QgsAbstractGeometry
m = PyFloat_AsDouble( a3 );
}
sipCpp = new sipQgsPoint( a0, a1, z, m, a4 );
sipCpp = new sipQgsPoint( x, y, z, m, a4 );
% End
#endif
@ -115,7 +135,7 @@ class CORE_EXPORT QgsPoint: public QgsAbstractGeometry
*
* \note Not available in Python bindings
*/
explicit QgsPoint( QgsWkbTypes::Type wkbType, double x = 0.0, double y = 0.0, double z = std::numeric_limits<double>::quiet_NaN(), double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
explicit QgsPoint( QgsWkbTypes::Type wkbType, double x = std::numeric_limits<double>::quiet_NaN(), double y = std::numeric_limits<double>::quiet_NaN(), double z = std::numeric_limits<double>::quiet_NaN(), double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
bool operator==( const QgsAbstractGeometry &other ) const override
{
@ -126,8 +146,17 @@ class CORE_EXPORT QgsPoint: public QgsAbstractGeometry
const QgsWkbTypes::Type type = wkbType();
bool equal = pt->wkbType() == type;
equal &= qgsDoubleNear( pt->x(), mX, 1E-8 );
equal &= qgsDoubleNear( pt->y(), mY, 1E-8 );
if ( std::isnan( pt->x() ) || std::isnan( mX ) )
equal &= std::isnan( pt->x() ) && std::isnan( mX ) ;
else
equal &= qgsDoubleNear( pt->x(), mX, 1E-8 );
if ( std::isnan( pt->y() ) || std::isnan( mY ) )
equal &= std::isnan( pt->y() ) && std::isnan( mY ) ;
else
equal &= qgsDoubleNear( pt->y(), mY, 1E-8 );
if ( QgsWkbTypes::hasZ( type ) )
equal &= qgsDoubleNear( pt->z(), mZ, 1E-8 ) || ( std::isnan( pt->z() ) && std::isnan( mZ ) );
if ( QgsWkbTypes::hasM( type ) )

View File

@ -114,13 +114,15 @@ bool QgsRegularPolygon::operator !=( const QgsRegularPolygon &rp ) const
bool QgsRegularPolygon::isEmpty() const
{
return ( ( mNumberSides < 3 ) ||
( mCenter.isEmpty() ) ||
( mFirstVertex.isEmpty() ) ||
( mCenter == mFirstVertex )
);
}
void QgsRegularPolygon::setCenter( const QgsPoint &center )
{
double azimuth = mCenter.azimuth( mFirstVertex );
double azimuth = mFirstVertex.isEmpty() ? 0 : mCenter.azimuth( mFirstVertex );
// TODO: double inclination = mCenter.inclination(mFirstVertex);
mCenter = center;
mFirstVertex = center.project( mRadius, azimuth );
@ -129,7 +131,7 @@ void QgsRegularPolygon::setCenter( const QgsPoint &center )
void QgsRegularPolygon::setRadius( const double radius )
{
mRadius = std::fabs( radius );
double azimuth = mCenter.azimuth( mFirstVertex );
double azimuth = mFirstVertex.isEmpty() ? 0 : mCenter.azimuth( mFirstVertex );
// TODO: double inclination = mCenter.inclination(mFirstVertex);
mFirstVertex = mCenter.project( mRadius, azimuth );
}

View File

@ -165,6 +165,9 @@ bool QgsTriangle::fromWkt( const QString &wkt )
mWkbType = parts.first;
if ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 )
return true;
QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );

View File

@ -264,6 +264,9 @@ inline QString qgsDoubleToString( double a, int precision = 17 )
*/
inline bool qgsDoubleNear( double a, double b, double epsilon = 4 * std::numeric_limits<double>::epsilon() )
{
if ( std::isnan( a ) || std::isnan( b ) )
return std::isnan( a ) && std::isnan( b ) ;
const double diff = a - b;
return diff > -epsilon && diff <= epsilon;
}
@ -276,6 +279,9 @@ inline bool qgsDoubleNear( double a, double b, double epsilon = 4 * std::numeric
*/
inline bool qgsFloatNear( float a, float b, float epsilon = 4 * FLT_EPSILON )
{
if ( std::isnan( a ) || std::isnan( b ) )
return std::isnan( a ) && std::isnan( b ) ;
const float diff = a - b;
return diff > -epsilon && diff <= epsilon;
}
@ -283,6 +289,9 @@ inline bool qgsFloatNear( float a, float b, float epsilon = 4 * FLT_EPSILON )
//! Compare two doubles using specified number of significant digits
inline bool qgsDoubleNearSig( double a, double b, int significantDigits = 10 )
{
if ( std::isnan( a ) || std::isnan( b ) )
return std::isnan( a ) && std::isnan( b ) ;
// The most simple would be to print numbers as %.xe and compare as strings
// but that is probably too costly
// Then the fastest would be to set some bits directly, but little/big endian

View File

@ -26,15 +26,26 @@
#include "qgsexception.h"
QgsPointXY::QgsPointXY( const QgsPointXY &p )
: mX( p.x() )
, mY( p.y() )
, mIsEmpty( p.isEmpty() )
{
mX = p.x();
mY = p.y();
}
QgsPointXY::QgsPointXY( const QgsPoint &point )
: mX( point.x() )
, mY( point.y() )
{
if ( point.isEmpty() )
{
mX = 0.0;
mY = 0.0;
mIsEmpty = true;
}
else
{
mX = point.x();
mY = point.y();
mIsEmpty = false;
}
}
QString QgsPointXY::toString( int precision ) const
@ -57,7 +68,13 @@ QString QgsPointXY::toString( int precision ) const
QString QgsPointXY::asWkt() const
{
return QStringLiteral( "POINT(%1 %2)" ).arg( qgsDoubleToString( mX ), qgsDoubleToString( mY ) );
QString wkt = QStringLiteral( "POINT" );
if ( isEmpty() )
wkt += QStringLiteral( " EMPTY" );
else
wkt += QStringLiteral( "(%1 %2)" ).arg( qgsDoubleToString( mX ), qgsDoubleToString( mY ) );
return wkt;
}
double QgsPointXY::azimuth( const QgsPointXY &other ) const

View File

@ -62,6 +62,7 @@ class CORE_EXPORT QgsPointXY
QgsPointXY( double x, double y )
: mX( x )
, mY( y )
, mIsEmpty( false )
{}
/**
@ -72,6 +73,7 @@ class CORE_EXPORT QgsPointXY
QgsPointXY( QPointF point )
: mX( point.x() )
, mY( point.y() )
, mIsEmpty( false )
{}
/**
@ -82,6 +84,7 @@ class CORE_EXPORT QgsPointXY
QgsPointXY( QPoint point )
: mX( point.x() )
, mY( point.y() )
, mIsEmpty( false )
{}
/**
@ -104,6 +107,7 @@ class CORE_EXPORT QgsPointXY
void setX( double x )
{
mX = x;
mIsEmpty = false;
}
/**
@ -113,6 +117,7 @@ class CORE_EXPORT QgsPointXY
void setY( double y )
{
mY = y;
mIsEmpty = false;
}
//! Sets the x and y value of the point
@ -120,6 +125,7 @@ class CORE_EXPORT QgsPointXY
{
mX = x;
mY = y;
mIsEmpty = false;
}
/**
@ -218,6 +224,15 @@ class CORE_EXPORT QgsPointXY
*/
QgsPointXY project( double distance, double bearing ) const;
/**
* Returns TRUE if the geometry is empty.
* Unlike QgsPoint, this class is also used to retrieve graphical coordinates like QPointF.
* It therefore has the default coordinates (0.0).
* A QgsPointXY is considered empty, when the coordinates have not been explicitly filled in.
* \since QGIS 3.10
*/
bool isEmpty() const { return mIsEmpty; }
/**
* Compares this point with another point with a fuzzy tolerance
* \param other point to compare with
@ -233,13 +248,35 @@ class CORE_EXPORT QgsPointXY
//! equality operator
bool operator==( const QgsPointXY &other )
{
return ( qgsDoubleNear( mX, other.x() ) && qgsDoubleNear( mY, other.y() ) );
if ( isEmpty() && other.isEmpty() )
return true;
if ( isEmpty() && !other.isEmpty() )
return false;
if ( ! isEmpty() && other.isEmpty() )
return false;
bool equal = true;
equal &= qgsDoubleNear( other.x(), mX, 1E-8 );
equal &= qgsDoubleNear( other.y(), mY, 1E-8 );
return equal;
}
//! Inequality operator
bool operator!=( const QgsPointXY &other ) const
{
return !( qgsDoubleNear( mX, other.x() ) && qgsDoubleNear( mY, other.y() ) );
if ( isEmpty() && other.isEmpty() )
return false;
if ( isEmpty() && !other.isEmpty() )
return true;
if ( ! isEmpty() && other.isEmpty() )
return true;
bool equal = true;
equal &= qgsDoubleNear( other.x(), mX, 1E-8 );
equal &= qgsDoubleNear( other.y(), mY, 1E-8 );
return !equal;
}
//! Multiply x and y by the given value
@ -256,6 +293,7 @@ class CORE_EXPORT QgsPointXY
{
mX = other.x();
mY = other.y();
mIsEmpty = other.isEmpty();
}
return *this;
@ -333,10 +371,13 @@ class CORE_EXPORT QgsPointXY
private:
//! x coordinate
double mX = 0.0;
double mX = 0; //std::numeric_limits<double>::quiet_NaN();
//! y coordinate
double mY = 0.0;
double mY = 0; //std::numeric_limits<double>::quiet_NaN();
//! is point empty?
bool mIsEmpty = true;
friend uint qHash( const QgsPointXY &pnt );
@ -346,10 +387,18 @@ Q_DECLARE_METATYPE( QgsPointXY )
inline bool operator==( const QgsPointXY &p1, const QgsPointXY &p2 ) SIP_SKIP
{
if ( qgsDoubleNear( p1.x(), p2.x() ) && qgsDoubleNear( p1.y(), p2.y() ) )
{ return true; }
bool equal = true;
if ( std::isnan( p1.x() ) || std::isnan( p2.x() ) )
equal &= std::isnan( p1.x() ) && std::isnan( p2.x() ) ;
else
{ return false; }
equal &= qgsDoubleNear( p1.x(), p2.x(), 1E-8 );
if ( std::isnan( p1.y() ) || std::isnan( p2.y() ) )
equal &= std::isnan( p1.y() ) && std::isnan( p2.y() ) ;
else
equal &= qgsDoubleNear( p1.y(), p2.y(), 1E-8 );
return equal;
}
inline std::ostream &operator << ( std::ostream &os, const QgsPointXY &p ) SIP_SKIP

View File

@ -2863,13 +2863,15 @@ void TestQgsProcessing::parameterPoint()
// nonsense string
params.insert( "non_optional", QString( "i'm not a crs, and nothing you can do will make me one" ) );
point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
QCOMPARE( point.x(), 0.0 );
QCOMPARE( point.y(), 0.0 );
QVERIFY( point.isEmpty() );
QGSCOMPARENEAR( point.x(), 0.0, 0.001 );
QGSCOMPARENEAR( point.y(), 0.0, 0.001 );
params.insert( "non_optional", QString( " ( ) " ) );
point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
QCOMPARE( point.x(), 0.0 );
QCOMPARE( point.y(), 0.0 );
QVERIFY( point.isEmpty() );
QGSCOMPARENEAR( point.x(), 0.0, 0.001 );
QGSCOMPARENEAR( point.y(), 0.0, 0.001 );
// QgsPointXY
params.insert( "non_optional", QgsPointXY( 11.1, 12.2 ) );

View File

@ -786,7 +786,7 @@ class TestQgsExpression: public QObject
QTest::newRow( "nodes_to_points collection 2" ) << "geom_to_wkt(nodes_to_points(geom_from_wkt('GEOMETRYCOLLECTION(POINTZM(0 1 2 3), POINTZM(0 0 3 4), POINTZM(1 1 5 6), POLYGONZM((-1 -1 7 8, 4 0 1 2, 4 2 7 6, 0 2 1 3, -1 -1 7 8),(-0.1 -0.1 5 4, 0.4 0 9 8, 0.4 0.2 7 10, 0 0.2 0 0, -0.1 -0.1 5 4),(-1 -1 0 0, 4 0 0 1, 4 2 1 2, 0 2 2 3, -1 -1 0 0)), POINTZM(1 0 1 2))')))" << false
<< QVariant( QStringLiteral( "MultiPointZM ((0 1 2 3),(0 0 3 4),(1 1 5 6),(-1 -1 7 8),(4 0 1 2),(4 2 7 6),(0 2 1 3),(-1 -1 7 8),(-0.1 -0.1 5 4),(0.4 0 9 8),(0.4 0.2 7 10),(0 0.2 0 0),(-0.1 -0.1 5 4),(-1 -1 0 0),(4 0 0 1),(4 2 1 2),(0 2 2 3),(-1 -1 0 0),(1 0 1 2))" ) );
QTest::newRow( "nodes_to_points empty collection" ) << "geom_to_wkt(nodes_to_points(geom_from_wkt('GEOMETRYCOLLECTION()')))" << false <<
QVariant( QStringLiteral( "MultiPoint ()" ) );
QVariant( QStringLiteral( "MultiPoint EMPTY" ) );
QTest::newRow( "nodes_to_points no close polygon" ) << "geom_to_wkt(nodes_to_points(geom_from_wkt('POLYGON((-1 -1, 4 0, 4 2, 0 2, -1 -1))'),true))" << false << QVariant( QStringLiteral( "MultiPoint ((-1 -1),(4 0),(4 2),(0 2))" ) );
QTest::newRow( "nodes_to_points no close polygon with rings" ) << "geom_to_wkt(nodes_to_points(geom_from_wkt('POLYGON((-1 -1, 4 0, 4 2, 0 2, -1 -1),(-0.1 -0.1, 0.4 0, 0.4 0.2, 0 0.2, -0.1 -0.1),(-0.3 -0.9, -0.3 0, 4 -0.1, 0.1 2.1, -0.3 -0.9))'),true))" << false
<< QVariant( QStringLiteral( "MultiPoint ((-1 -1),(4 0),(4 2),(0 2),(-0.1 -0.1),(0.4 0),(0.4 0.2),(0 0.2),(-0.3 -0.9),(-0.3 0),(4 -0.1),(0.1 2.1))" ) );
@ -796,18 +796,18 @@ class TestQgsExpression: public QObject
<< QVariant( QStringLiteral( "MultiPoint ((0 0),(1 1),(2 2))" ) );
QTest::newRow( "segments_to_lines not geom" ) << "segments_to_lines('g')" << true << QVariant();
QTest::newRow( "segments_to_lines null" ) << "segments_to_lines(NULL)" << false << QVariant();
QTest::newRow( "segments_to_lines point" ) << "geom_to_wkt(segments_to_lines(geom_from_wkt('POINT(1 2)')))" << false << QVariant( QStringLiteral( "MultiLineString ()" ) );
QTest::newRow( "segments_to_lines point" ) << "geom_to_wkt(segments_to_lines(geom_from_wkt('POINT(1 2)')))" << false << QVariant( QStringLiteral( "MultiLineString EMPTY" ) );
QTest::newRow( "segments_to_lines polygon" ) << "geom_to_wkt(segments_to_lines(geom_from_wkt('POLYGON((-1 -1, 4 0, 4 2, 0 2, -1 -1))')))" << false << QVariant( QStringLiteral( "MultiLineString ((-1 -1, 4 0),(4 0, 4 2),(4 2, 0 2),(0 2, -1 -1))" ) );
QTest::newRow( "segments_to_lines polygon with rings" ) << "geom_to_wkt(segments_to_lines(geom_from_wkt('POLYGON((-1 -1, 4 0, 4 2, 0 2, -1 -1),(-0.1 -0.1, 0.4 0, 0.4 0.2, 0 0.2, -0.1 -0.1),(-0.3 -0.9, -0.3 0, 4 -0.1, 0.1 2.1, -0.3 -0.9))')))" << false
<< QVariant( QStringLiteral( "MultiLineString ((-1 -1, 4 0),(4 0, 4 2),(4 2, 0 2),(0 2, -1 -1),(-0.1 -0.1, 0.4 0),(0.4 0, 0.4 0.2),(0.4 0.2, 0 0.2),(0 0.2, -0.1 -0.1),(-0.3 -0.9, -0.3 0),(-0.3 0, 4 -0.1),(4 -0.1, 0.1 2.1),(0.1 2.1, -0.3 -0.9))" ) );
QTest::newRow( "segments_to_lines line" ) << "geom_to_wkt(segments_to_lines(geom_from_wkt('LINESTRING(0 0, 1 1, 2 2)')))" << false
<< QVariant( QStringLiteral( "MultiLineString ((0 0, 1 1),(1 1, 2 2))" ) );
QTest::newRow( "segments_to_lines collection 1" ) << "geom_to_wkt(segments_to_lines(geom_from_wkt('GEOMETRYCOLLECTION(POINT(0 1), POINT(0 0), POINT(1 0), POINT(1 1))')))" << false
<< QVariant( QStringLiteral( "MultiLineString ()" ) );
<< QVariant( QStringLiteral( "MultiLineString EMPTY" ) );
QTest::newRow( "segments_to_lines collection 2" ) << "geom_to_wkt(segments_to_lines(geom_from_wkt('GEOMETRYCOLLECTION(POINTZM(0 1 2 3), LINESTRINGZM(0 0 1 2, 1 1 3 4, 2 2 5 6), POINTZM(1 1 5 6), POLYGONZM((-1 -1 7 8, 4 0 1 2, 4 2 7 6, 0 2 1 3, -1 -1 7 8)), POINTZM(1 0 1 2))')))" << false
<< QVariant( QStringLiteral( "MultiLineStringZM ((0 0 1 2, 1 1 3 4),(1 1 3 4, 2 2 5 6),(-1 -1 7 8, 4 0 1 2),(4 0 1 2, 4 2 7 6),(4 2 7 6, 0 2 1 3),(0 2 1 3, -1 -1 7 8))" ) );
QTest::newRow( "segments_to_lines empty collection" ) << "geom_to_wkt(segments_to_lines(geom_from_wkt('GEOMETRYCOLLECTION()')))" << false <<
QVariant( QStringLiteral( "MultiLineString ()" ) );
QVariant( QStringLiteral( "MultiLineString EMPTY" ) );
QTest::newRow( "length line" ) << "length(geom_from_wkt('LINESTRING(0 0, 4 0)'))" << false << QVariant( 4.0 );
QTest::newRow( "length polygon" ) << "length(geom_from_wkt('POLYGON((0 0, 4 0, 4 2, 0 2, 0 0))'))" << false << QVariant();
QTest::newRow( "length point" ) << "length(geom_from_wkt('POINT(0 0)'))" << false << QVariant();
@ -878,7 +878,7 @@ class TestQgsExpression: public QObject
QTest::newRow( "buffer_by_m not geom" ) << "buffer_by_m('g', 8)" << true << QVariant();
QTest::newRow( "buffer_by_m null" ) << "buffer_by_m(NULL, 8)" << false << QVariant();
QTest::newRow( "buffer_by_m point" ) << "geom_to_wkt(buffer_by_m(geometry:=geom_from_wkt('POINT(1 2)'),segments:=10))" << true << QVariant();
QTest::newRow( "buffer_by_m line" ) << "geom_to_wkt(buffer_by_m(geometry:=geom_from_wkt('LineString(0 0, 10 0)'),segments:=3))" << false << QVariant( QStringLiteral( "GeometryCollection ()" ) );
QTest::newRow( "buffer_by_m line" ) << "geom_to_wkt(buffer_by_m(geometry:=geom_from_wkt('LineString(0 0, 10 0)'),segments:=3))" << false << QVariant( QStringLiteral( "GeometryCollection EMPTY" ) );
QTest::newRow( "buffer_by_m linem" ) << "geom_to_wkt(buffer_by_m(geometry:=geom_from_wkt('LineStringM(0 0 1, 10 0 2)'),segments:=3))" << false << QVariant( QStringLiteral( "MultiPolygon (((-0 -0.5, -0.25 -0.4330127, -0.4330127 -0.25, -0.5 0, -0.4330127 0.25, -0.25 0.4330127, 0 0.5, 10 1, 10.5 0.8660254, 10.8660254 0.5, 11 -0, 10.8660254 -0.5, 10.5 -0.8660254, 10 -1, -0 -0.5)))" ) );
QTest::newRow( "single_sided_buffer not geom" ) << "single_sided_buffer('g', 5)" << true << QVariant();
QTest::newRow( "single_sided_buffer null" ) << "single_sided_buffer(NULL, 5)" << false << QVariant();
@ -934,7 +934,7 @@ class TestQgsExpression: public QObject
QTest::newRow( "is_closed closed" ) << "is_closed(geom_from_wkt('LINESTRING(0 0, 1 1, 2 2, 0 0)'))" << false << QVariant( true );
QTest::newRow( "is_closed multiline" ) << "is_closed(geom_from_wkt('MultiLineString ((6501338.13976828 4850981.51459331, 6501343.09036573 4850984.01453377, 6501338.13976828 4850988.96491092, 6501335.63971657 4850984.01453377, 6501338.13976828 4850981.51459331))'))" << false << QVariant( true );
QTest::newRow( "is_closed multiline" ) << "is_closed(geom_from_wkt('MultiLineString ((6501338.13976828 4850981.51459331, 6501343.09036573 4850984.01453377, 6501338.13976828 4850988.96491092, 6501335.63971657 4850984.01453377, 6501438.13976828 4850981.51459331))'))" << false << QVariant( false );
QTest::newRow( "is_closed multiline" ) << "is_closed(geom_from_wkt('MultiLineString ()'))" << false << QVariant();
QTest::newRow( "is_closed multiline" ) << "is_closed(geom_from_wkt('MultiLineString EMPTY'))" << false << QVariant();
QTest::newRow( "make_point" ) << "geom_to_wkt(make_point(2.2,4.4))" << false << QVariant( "Point (2.2 4.4)" );
QTest::newRow( "make_point z" ) << "geom_to_wkt(make_point(2.2,4.4,5.5))" << false << QVariant( "PointZ (2.2 4.4 5.5)" );
QTest::newRow( "make_point zm" ) << "geom_to_wkt(make_point(2.2,4.4,5.5,6.6))" << false << QVariant( "PointZM (2.2 4.4 5.5 6.6)" );

View File

@ -418,6 +418,9 @@ void TestQgsGeometry::isEmpty()
QgsGeometry geom;
QVERIFY( geom.isNull() );
geom.set( new QgsPoint() );
QVERIFY( geom.isEmpty() );
geom.set( new QgsPoint( 1.0, 2.0 ) );
QVERIFY( !geom.isNull() );
@ -480,6 +483,13 @@ void TestQgsGeometry::vertexIterator()
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 3, 4 ) );
QVERIFY( !it2.hasNext() );
QgsGeometry emptyGeom = QgsGeometry::fromWkt( "LINESTRING EMPTY" );
QgsVertexIterator it3 = emptyGeom.vertices();
QVERIFY( !it3.hasNext() );
emptyGeom = QgsGeometry::fromWkt( "POINT EMPTY" );
QgsVertexIterator it4 = emptyGeom.vertices();
QVERIFY( !it4.hasNext() );
}
void TestQgsGeometry::partIterator()
@ -512,6 +522,18 @@ void TestQgsGeometry::partIterator()
void TestQgsGeometry::point()
{
//test QgsPointV2
QgsPoint pEmpty;
QVERIFY( pEmpty.isEmpty() );
QCOMPARE( pEmpty.wkbType(), QgsWkbTypes::Point );
QCOMPARE( pEmpty.asWkt(), QStringLiteral( "Point EMPTY" ) );
pEmpty.setX( 1.0 );
QVERIFY( pEmpty.isEmpty() );
QCOMPARE( pEmpty.wkbType(), QgsWkbTypes::Point );
QCOMPARE( pEmpty.asWkt(), QStringLiteral( "Point EMPTY" ) );
pEmpty.setY( 2.0 );
QVERIFY( !pEmpty.isEmpty() );
QCOMPARE( pEmpty.wkbType(), QgsWkbTypes::Point );
QCOMPARE( pEmpty.asWkt(), QStringLiteral( "Point (1 2)" ) );
//test constructors
QgsPoint p1( 5.0, 6.0 );
@ -623,6 +645,23 @@ void TestQgsGeometry::point()
#endif
//test equality operator
QgsPoint pRight, pLeft;
QVERIFY( pRight.isEmpty() );
QVERIFY( pLeft.isEmpty() );
QVERIFY( pLeft == pRight );
pRight.setX( 1 );
pLeft.setY( 1 );
QVERIFY( pRight.isEmpty() );
QVERIFY( pLeft.isEmpty() );
QVERIFY( pLeft != pRight );
pRight.setY( 1 );
pLeft.setX( 1 );
QVERIFY( !pRight.isEmpty() );
QVERIFY( !pLeft.isEmpty() );
QVERIFY( pLeft == pRight );
QVERIFY( QgsPoint( QgsWkbTypes::Point, 2 / 3.0, 1 / 3.0 ) == QgsPoint( QgsWkbTypes::Point, 2 / 3.0, 1 / 3.0 ) );
QVERIFY( !( QgsPoint( QgsWkbTypes::PointZ, 2 / 3.0, 1 / 3.0 ) == QgsPoint( QgsWkbTypes::Point, 2 / 3.0, 1 / 3.0 ) ) );
QVERIFY( !( QgsPoint( QgsWkbTypes::Point, 1 / 3.0, 1 / 3.0 ) == QgsPoint( QgsWkbTypes::Point, 2 / 3.0, 1 / 3.0 ) ) );
@ -694,8 +733,8 @@ void TestQgsGeometry::point()
QgsPoint p11( 5.0, 6.0 );
p11.clear();
QCOMPARE( p11.wkbType(), QgsWkbTypes::Point );
QCOMPARE( p11.x(), 0.0 );
QCOMPARE( p11.y(), 0.0 );
QVERIFY( std::isnan( p11.x() ) );
QVERIFY( std::isnan( p11.y() ) );
//toQPointF
QgsPoint p11a( 5.0, 9.0 );
@ -1810,8 +1849,8 @@ void TestQgsGeometry::circularString()
//bad start/end points. Test that this doesn't crash.
l19.clear();
QCOMPARE( l19.startPoint(), QgsPoint() );
QCOMPARE( l19.endPoint(), QgsPoint() );
QVERIFY( l19.startPoint().isEmpty() );
QVERIFY( l19.endPoint().isEmpty() );
//curveToLine - no segmentation required, so should return a clone
l19.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 )
@ -2353,7 +2392,7 @@ void TestQgsGeometry::circularString()
//centroid
QgsCircularString l34;
QCOMPARE( l34.centroid(), QgsPoint() );
QCOMPARE( l34.centroid(), QgsPoint( 0, 0 ) );
l34.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) );
QCOMPARE( l34.centroid(), QgsPoint( 5, 10 ) );
l34.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 20, 10 ) << QgsPoint( 2, 9 ) );
@ -2364,7 +2403,7 @@ void TestQgsGeometry::circularString()
//closest segment
QgsCircularString l35;
int leftOf = 0;
p = QgsPoint(); // reset all coords to zero
p = QgsPoint( 0, 0 ); // reset all coords to zero
( void )l35.closestSegment( QgsPoint( 1, 2 ), p, v ); //empty line, just want no crash
l35.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) );
QVERIFY( l35.closestSegment( QgsPoint( 5, 10 ), p, v ) < 0 );
@ -3785,8 +3824,8 @@ void TestQgsGeometry::lineString()
//bad start/end points. Test that this doesn't crash.
l19.clear();
QCOMPARE( l19.startPoint(), QgsPoint() );
QCOMPARE( l19.endPoint(), QgsPoint() );
QVERIFY( l19.startPoint().isEmpty() );
QVERIFY( l19.endPoint().isEmpty() );
//curveToLine - no segmentation required, so should return a clone
l19.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 )
@ -4387,7 +4426,7 @@ void TestQgsGeometry::lineString()
//centroid
QgsLineString l34;
QCOMPARE( l34.centroid(), QgsPoint() );
QVERIFY( l34.centroid().isEmpty() );
l34.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) );
QCOMPARE( l34.centroid(), QgsPoint( 5, 10 ) );
l34.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 20, 10 ) );
@ -4404,7 +4443,7 @@ void TestQgsGeometry::lineString()
//closest segment
QgsLineString l35;
int leftOf = 0;
p = QgsPoint(); // reset all coords to zero
p = QgsPoint( 0, 0 ); // reset all coords to zero
( void )l35.closestSegment( QgsPoint( 1, 2 ), p, v ); //empty line, just want no crash
l35.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) );
QVERIFY( l35.closestSegment( QgsPoint( 5, 10 ), p, v ) < 0 );
@ -7101,12 +7140,12 @@ void TestQgsGeometry::triangle()
QVERIFY( t9.isEquilateral() );
// vertex
QCOMPARE( t9.vertexAt( -1 ), QgsPoint( 0, 0 ) );
QVERIFY( t9.vertexAt( -1 ).isEmpty() );
QCOMPARE( t9.vertexAt( 0 ), QgsPoint( 10, 10 ) );
QCOMPARE( t9.vertexAt( 1 ), QgsPoint( 16, 10 ) );
QCOMPARE( t9.vertexAt( 2 ), QgsPoint( 13, 15.1962 ) );
QCOMPARE( t9.vertexAt( 3 ), QgsPoint( 10, 10 ) );
QCOMPARE( t9.vertexAt( 4 ), QgsPoint( 0, 0 ) );
QVERIFY( t9.vertexAt( 4 ).isEmpty() );
// altitudes
QgsTriangle t10( QgsPoint( 20, 2 ), QgsPoint( 16, 6 ), QgsPoint( 26, 2 ) );
@ -7119,13 +7158,13 @@ void TestQgsGeometry::triangle()
// orthocenter
QCOMPARE( QgsPoint(), QgsTriangle().orthocenter() );
QVERIFY( QgsTriangle().orthocenter().isEmpty() );
QCOMPARE( QgsPoint( 16, -8 ), t10.orthocenter() );
QCOMPARE( QgsPoint( 0, 5 ), t7.orthocenter() );
QGSCOMPARENEARPOINT( QgsPoint( 13, 11.7321 ), t9.orthocenter(), 0.0001 );
// circumscribed circle
QCOMPARE( QgsPoint(), QgsTriangle().circumscribedCenter() );
QVERIFY( QgsTriangle().circumscribedCenter().isEmpty() );
QCOMPARE( 0.0, QgsTriangle().circumscribedRadius() );
QCOMPARE( QgsPoint( 2.5, 2.5 ), t7.circumscribedCenter() );
QGSCOMPARENEAR( 3.5355, t7.circumscribedRadius(), 0.0001 );
@ -7137,7 +7176,7 @@ void TestQgsGeometry::triangle()
QGSCOMPARENEAR( 3.4641, t9.circumscribedCircle().radius(), 0.0001 );
// inscribed circle
QCOMPARE( QgsPoint(), QgsTriangle().inscribedCenter() );
QVERIFY( QgsTriangle().inscribedCenter().isEmpty() );
QCOMPARE( 0.0, QgsTriangle().inscribedRadius() );
QGSCOMPARENEARPOINT( QgsPoint( 1.4645, 3.5355 ), t7.inscribedCenter(), 0.001 );
QGSCOMPARENEAR( 1.4645, t7.inscribedRadius(), 0.0001 );
@ -7240,14 +7279,14 @@ void TestQgsGeometry::triangle()
id.vertex = 0;
QVERIFY( t11.moveVertex( id, pt1 ) );
QCOMPARE( t11.asWkt(), QString( "Triangle ((5 5, 100 100, 0 200, 5 5))" ) );
pt1 = QgsPoint();
pt1 = QgsPoint( 0, 0 );
QVERIFY( t11.moveVertex( id, pt1 ) );
QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) );
id.vertex = 4;
pt1 = QgsPoint( 5, 5 );
QVERIFY( t11.moveVertex( id, pt1 ) );
QCOMPARE( t11.asWkt(), QString( "Triangle ((5 5, 100 100, 0 200, 5 5))" ) );
pt1 = QgsPoint();
pt1 = QgsPoint( 0, 0 );
QVERIFY( t11.moveVertex( id, pt1 ) );
QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) );
id.vertex = 1;
@ -7297,7 +7336,7 @@ void TestQgsGeometry::ellipse()
{
//test constructors
QgsEllipse elp1;
QVERIFY( elp1.center() == QgsPoint() );
QVERIFY( elp1.center().isEmpty() );
QCOMPARE( elp1.semiMajorAxis(), 0.0 );
QCOMPARE( elp1.semiMinorAxis(), 0.0 );
QCOMPARE( elp1.azimuth(), 90.0 );
@ -7331,8 +7370,8 @@ void TestQgsGeometry::ellipse()
QCOMPARE( elp4.toString(), QString( "Ellipse (Center: Point (5 10), Semi-Major Axis: 3, Semi-Minor Axis: 2, Azimuth: 135)" ) );
//test equality operator
QVERIFY( QgsEllipse() == QgsEllipse( QgsPoint(), 0, 0, 90 ) );
QVERIFY( !( QgsEllipse() == QgsEllipse( QgsPoint(), 0, 0, 0.0005 ) ) );
QCOMPARE( QgsEllipse().isEmpty(), QgsEllipse( QgsPoint(), 0, 0, 90 ).isEmpty() );
QVERIFY( !( QgsEllipse() == QgsEllipse( QgsPoint( 0, 0 ), 0, 0, 0.0005 ) ) );
QVERIFY( elp2 == QgsEllipse( QgsPoint( 5, 10 ), 2, 3, 0 ) );
QVERIFY( elp2 != elp3 );
QVERIFY( elp3 != elp4 );
@ -7585,7 +7624,7 @@ void TestQgsGeometry::circle()
{
//test constructors
QgsCircle circ1;
QVERIFY( circ1.center() == QgsPoint() );
QVERIFY( circ1.center().isEmpty() );
QCOMPARE( circ1.radius(), 0.0 );
QCOMPARE( circ1.azimuth(), 0.0 );
QVERIFY( circ1.isEmpty() );
@ -7608,8 +7647,8 @@ void TestQgsGeometry::circle()
QCOMPARE( circ3.toString(), QString( "Circle (Center: Point (5 10), Radius: 3, Azimuth: 45)" ) );
//test equality operator
QVERIFY( QgsCircle() == QgsCircle( QgsPoint(), 0, 0 ) );
QVERIFY( !( QgsCircle() == QgsCircle( QgsPoint(), 0, 0.0005 ) ) );
QCOMPARE( QgsCircle().isEmpty(), QgsCircle( QgsPoint(), 0, 0 ).isEmpty() );
QVERIFY( !( QgsCircle() == QgsCircle( QgsPoint( 0, 0 ), 0, 0.0005 ) ) );
QVERIFY( circ2 == QgsCircle( QgsPoint( 5, 10 ), 3, 0 ) );
QVERIFY( circ2 != circ3 );
@ -7644,10 +7683,10 @@ void TestQgsGeometry::circle()
QVERIFY( QgsCircle().from2Points( QgsPoint( 0, -5 ), QgsPoint( 0, 5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 0 ) );
// byExtent
QVERIFY( QgsCircle().fromExtent( QgsPoint( -5, -5 ), QgsPoint( 5, 5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 0 ) );
QVERIFY( QgsCircle().fromExtent( QgsPoint( -7.5, -2.5 ), QgsPoint( 2.5, 200.5 ) ) == QgsCircle() );
QVERIFY( QgsCircle().fromExtent( QgsPoint( -7.5, -2.5 ), QgsPoint( 2.5, 200.5 ) ).isEmpty() );
// by3Points
QVERIFY( QgsCircle().from3Points( QgsPoint( -5, 0 ), QgsPoint( 5, 0 ), QgsPoint( 0, 5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5 ) );
QVERIFY( QgsCircle().from3Points( QgsPoint( 5, 0 ), QgsPoint( 6, 0 ), QgsPoint( 7, 0 ) ) == QgsCircle() );
QVERIFY( QgsCircle().from3Points( QgsPoint( 5, 0 ), QgsPoint( 6, 0 ), QgsPoint( 7, 0 ) ).isEmpty() );
// byCenterDiameter
QVERIFY( QgsCircle().fromCenterDiameter( QgsPoint( 0, 0 ), 10 ) == QgsCircle( QgsPoint( 0, 0 ), 5, 0 ) );
QVERIFY( QgsCircle().fromCenterDiameter( QgsPoint( 2, 100 ), -10 ) == QgsCircle( QgsPoint( 2, 100 ), 5, 0 ) );
@ -7876,48 +7915,48 @@ void TestQgsGeometry::quadrilateral()
// default
QgsQuadrilateral quad_init;
QgsPointSequence pts = quad_init.points();
QCOMPARE( pts.at( 0 ), QgsPoint() );
QCOMPARE( pts.at( 1 ), QgsPoint() );
QCOMPARE( pts.at( 2 ), QgsPoint() );
QCOMPARE( pts.at( 3 ), QgsPoint() );
QVERIFY( pts.at( 0 ).isEmpty() );
QVERIFY( pts.at( 1 ).isEmpty() );
QVERIFY( pts.at( 2 ).isEmpty() );
QVERIFY( pts.at( 3 ).isEmpty() );
QVERIFY( !quad_init.isValid() );
// colinear
QgsQuadrilateral quad4points_col( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 10 ), QgsPoint( 10, 10 ) );
QVERIFY( !quad4points_col.isValid() );
pts = quad4points_col.points();
QCOMPARE( pts.at( 0 ), QgsPoint() );
QCOMPARE( pts.at( 1 ), QgsPoint() );
QCOMPARE( pts.at( 2 ), QgsPoint() );
QCOMPARE( pts.at( 3 ), QgsPoint() );
QVERIFY( pts.at( 0 ).isEmpty() );
QVERIFY( pts.at( 1 ).isEmpty() );
QVERIFY( pts.at( 2 ).isEmpty() );
QVERIFY( pts.at( 3 ).isEmpty() );
QgsQuadrilateral quad4pointsXY_col( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 10 ), QgsPoint( 10, 10 ) );
QVERIFY( !quad4pointsXY_col.isValid() );
pts = quad4pointsXY_col.points();
QCOMPARE( pts.at( 0 ), QgsPoint() );
QCOMPARE( pts.at( 1 ), QgsPoint() );
QCOMPARE( pts.at( 2 ), QgsPoint() );
QCOMPARE( pts.at( 3 ), QgsPoint() );
QVERIFY( pts.at( 0 ).isEmpty() );
QVERIFY( pts.at( 1 ).isEmpty() );
QVERIFY( pts.at( 2 ).isEmpty() );
QVERIFY( pts.at( 3 ).isEmpty() );
// anti parallelogram
QgsQuadrilateral quad4points_anti( QgsPoint( 0, 0 ), QgsPoint( 5, 5 ), QgsPoint( 5, 0 ), QgsPoint( 0, 5 ) );
QVERIFY( !quad4points_anti.isValid() );
pts = quad4points_anti.points();
QCOMPARE( pts.at( 0 ), QgsPoint() );
QCOMPARE( pts.at( 1 ), QgsPoint() );
QCOMPARE( pts.at( 2 ), QgsPoint() );
QCOMPARE( pts.at( 3 ), QgsPoint() );
QVERIFY( pts.at( 0 ).isEmpty() );
QVERIFY( pts.at( 1 ).isEmpty() );
QVERIFY( pts.at( 2 ).isEmpty() );
QVERIFY( pts.at( 3 ).isEmpty() );
QgsQuadrilateral quad4pointsXY_anti( QgsPoint( 0, 0 ), QgsPoint( 5, 5 ), QgsPoint( 5, 0 ), QgsPoint( 0, 5 ) );
QVERIFY( !quad4pointsXY_anti.isValid() );
pts = quad4pointsXY_anti.points();
QCOMPARE( pts.at( 0 ), QgsPoint() );
QCOMPARE( pts.at( 1 ), QgsPoint() );
QCOMPARE( pts.at( 2 ), QgsPoint() );
QCOMPARE( pts.at( 3 ), QgsPoint() );
QVERIFY( pts.at( 0 ).isEmpty() );
QVERIFY( pts.at( 1 ).isEmpty() );
QVERIFY( pts.at( 2 ).isEmpty() );
QVERIFY( pts.at( 3 ).isEmpty() );
// valid
QgsQuadrilateral quad4points_valid( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5 ), QgsPoint( 5, 0 ) );
@ -8083,47 +8122,47 @@ void TestQgsGeometry::regularPolygon()
{
// constructors
QgsRegularPolygon rp1 = QgsRegularPolygon();
QCOMPARE( rp1.center(), QgsPoint() );
QCOMPARE( rp1.firstVertex(), QgsPoint() );
QVERIFY( rp1.center().isEmpty() );
QVERIFY( rp1.firstVertex().isEmpty() );
QCOMPARE( rp1.numberSides(), static_cast< unsigned int >( 0 ) );
QCOMPARE( rp1.radius(), 0.0 );
QVERIFY( rp1.isEmpty() );
QgsRegularPolygon rp2;
QgsRegularPolygon( QgsPoint(), 5, 0, 2, QgsRegularPolygon::InscribedCircle );
QgsRegularPolygon( QgsPoint( 0, 0 ), 5, 0, 2, QgsRegularPolygon::InscribedCircle );
QVERIFY( rp2.isEmpty() );
QgsRegularPolygon( QgsPoint(), 5, 0, 5, static_cast< QgsRegularPolygon::ConstructionOption >( 4 ) );
QgsRegularPolygon( QgsPoint( 0, 0 ), 5, 0, 5, static_cast< QgsRegularPolygon::ConstructionOption >( 4 ) );
QVERIFY( rp2.isEmpty() );
rp2 = QgsRegularPolygon( QgsPoint(), 5, 0, 5, QgsRegularPolygon::InscribedCircle );
rp2 = QgsRegularPolygon( QgsPoint( 0, 0 ), 5, 0, 5, QgsRegularPolygon::InscribedCircle );
QVERIFY( !rp2.isEmpty() );
QCOMPARE( rp2.center(), QgsPoint() );
QCOMPARE( rp2.center(), QgsPoint( 0, 0 ) );
QCOMPARE( rp2.firstVertex(), QgsPoint( 0, 5 ) );
QCOMPARE( rp2.numberSides(), static_cast< unsigned int>( 5 ) );
QCOMPARE( rp2.radius(), 5.0 );
QGSCOMPARENEAR( rp2.apothem(), 4.0451, 10E-4 );
QVERIFY( rp2 == QgsRegularPolygon( QgsPoint(), -5, 0, 5, QgsRegularPolygon::InscribedCircle ) );
QVERIFY( rp2 == QgsRegularPolygon( QgsPoint( 0, 0 ), -5, 0, 5, QgsRegularPolygon::InscribedCircle ) );
QgsRegularPolygon rp3 = QgsRegularPolygon( QgsPoint(), rp2.apothem(), 36.0, 5, QgsRegularPolygon::CircumscribedCircle );
QgsRegularPolygon rp3 = QgsRegularPolygon( QgsPoint( 0, 0 ), rp2.apothem(), 36.0, 5, QgsRegularPolygon::CircumscribedCircle );
QVERIFY( rp2 == rp3 );
QVERIFY( rp2 == QgsRegularPolygon( QgsPoint(), -rp2.apothem(), 36.0, 5, QgsRegularPolygon::CircumscribedCircle ) );
QVERIFY( rp2 == QgsRegularPolygon( QgsPoint( 0, 0 ), -rp2.apothem(), 36.0, 5, QgsRegularPolygon::CircumscribedCircle ) );
QVERIFY( rp1 != rp3 );
QVERIFY( rp1 != QgsRegularPolygon( QgsPoint( 5, 5 ), rp2.apothem(), 36.0, 5, QgsRegularPolygon::CircumscribedCircle ) );
QVERIFY( rp1 != QgsRegularPolygon( QgsPoint( 0, 0 ), 5, 36.0, 5, QgsRegularPolygon::CircumscribedCircle ) );
QVERIFY( rp1 != QgsRegularPolygon( QgsPoint( 0, 0 ), 5, 36.0, 5, QgsRegularPolygon::InscribedCircle ) );
QgsRegularPolygon rp4 = QgsRegularPolygon( QgsPoint(), QgsPoint( 0, 5 ), 2, QgsRegularPolygon::InscribedCircle );
QgsRegularPolygon rp4 = QgsRegularPolygon( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), 2, QgsRegularPolygon::InscribedCircle );
QVERIFY( rp4.isEmpty() );
rp4 = QgsRegularPolygon( QgsPoint(), QgsPoint( 0, 5 ), 5, static_cast< QgsRegularPolygon::ConstructionOption >( 4 ) );
QVERIFY( rp4.isEmpty() );
rp4 = QgsRegularPolygon( QgsPoint(), QgsPoint( 0, 5 ), 5, QgsRegularPolygon::InscribedCircle );
rp4 = QgsRegularPolygon( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), 5, QgsRegularPolygon::InscribedCircle );
QVERIFY( rp4 == rp2 );
QgsRegularPolygon rp5 = QgsRegularPolygon( QgsPoint(), QgsPoint( 0, 0 ).project( rp2.apothem(), 36.0 ), 2, QgsRegularPolygon::CircumscribedCircle );
QgsRegularPolygon rp5 = QgsRegularPolygon( QgsPoint( 0, 0 ), QgsPoint( 0, 0 ).project( rp2.apothem(), 36.0 ), 2, QgsRegularPolygon::CircumscribedCircle );
QVERIFY( rp5.isEmpty() );
rp5 = QgsRegularPolygon( QgsPoint(), QgsPoint( 0, 0 ).project( rp2.apothem(), 36.0 ), 5, static_cast< QgsRegularPolygon::ConstructionOption >( 4 ) );
rp5 = QgsRegularPolygon( QgsPoint( 0, 0 ), QgsPoint( 0, 0 ).project( rp2.apothem(), 36.0 ), 5, static_cast< QgsRegularPolygon::ConstructionOption >( 4 ) );
QVERIFY( rp5.isEmpty() );
rp5 = QgsRegularPolygon( QgsPoint(), QgsPoint( 0, 0 ).project( rp2.apothem(), 36.0 ), 5, QgsRegularPolygon::CircumscribedCircle );
rp5 = QgsRegularPolygon( QgsPoint( 0, 0 ), QgsPoint( 0, 0 ).project( rp2.apothem(), 36.0 ), 5, QgsRegularPolygon::CircumscribedCircle );
QVERIFY( rp5 == rp2 );
QgsRegularPolygon rp6 = QgsRegularPolygon( QgsPoint( 0, 5 ), QgsPoint( 0, 0 ).project( 5.0, 72 ), 5 );
@ -8159,7 +8198,7 @@ void TestQgsGeometry::regularPolygon()
QCOMPARE( rp7.firstVertex(), QgsPoint( 4, 4 ) );
QCOMPARE( rp7.radius(), rp7.center().distance3D( QgsPoint( 4, 4 ) ) );
rp7 = QgsRegularPolygon( QgsPoint(), QgsPoint( 0, 5 ), 5, QgsRegularPolygon::InscribedCircle );
rp7 = QgsRegularPolygon( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), 5, QgsRegularPolygon::InscribedCircle );
rp7.setCenter( QgsPoint( 5, 5 ) );
QCOMPARE( rp7.radius(), 5.0 );
QCOMPARE( rp7.firstVertex(), QgsPoint( 5, 10 ) );
@ -8196,7 +8235,7 @@ void TestQgsGeometry::regularPolygon()
rp8 = QgsRegularPolygon(); // empty
QgsPointSequence points = rp8.points();
QVERIFY( points.isEmpty() );
rp8 = QgsRegularPolygon( QgsPoint(), QgsPoint( 0, 5 ), 3, QgsRegularPolygon::InscribedCircle );
rp8 = QgsRegularPolygon( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), 3, QgsRegularPolygon::InscribedCircle );
points = rp8.points();
QCOMPARE( points.count(), 3 );
QCOMPARE( points.at( 0 ), QgsPoint( 0, 5 ) );
@ -8246,7 +8285,7 @@ void TestQgsGeometry::regularPolygon()
QVERIFY( ptsPol.at( 4 ) == QgsPoint( 0, 0 ) );
ptsPol.pop_back();
std::unique_ptr< QgsLineString > l( QgsRegularPolygon( QgsPoint(), QgsPoint( 0, 5 ), 1, QgsRegularPolygon::InscribedCircle ).toLineString() );
std::unique_ptr< QgsLineString > l( QgsRegularPolygon( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), 1, QgsRegularPolygon::InscribedCircle ).toLineString() );
QVERIFY( l->isEmpty() );
l.reset( rp10.toLineString( ) );
QCOMPARE( l->numPoints(), 5 );
@ -10322,8 +10361,8 @@ void TestQgsGeometry::compoundCurve()
//bad start/end points. Test that this doesn't crash.
c19.clear();
QCOMPARE( c19.startPoint(), QgsPoint() );
QCOMPARE( c19.endPoint(), QgsPoint() );
QVERIFY( c19.startPoint().isEmpty() );
QVERIFY( c19.endPoint().isEmpty() );
//curveToLine
c19.clear();
@ -11134,7 +11173,7 @@ void TestQgsGeometry::compoundCurve()
//centroid
QgsCircularString l34;
QgsCompoundCurve c34;
QCOMPARE( c34.centroid(), QgsPoint() );
QCOMPARE( c34.centroid(), QgsPoint( 0, 0 ) );
l34.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) );
c34.addCurve( l34.clone() );
QCOMPARE( c34.centroid(), QgsPoint( 5, 10 ) );
@ -11154,7 +11193,7 @@ void TestQgsGeometry::compoundCurve()
QgsCompoundCurve c35;
QgsCircularString l35;
int leftOf = 0;
p = QgsPoint(); // reset all coords to zero
p = QgsPoint( 0, 0 ); // reset all coords to zero
( void )c35.closestSegment( QgsPoint( 1, 2 ), p, vId ); //empty line, just want no crash
l35.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) );
c35.addCurve( l35.clone() );
@ -16375,19 +16414,75 @@ void TestQgsGeometry::comparePolygons()
QVERIFY( !QgsGeometry::compare( poly3, poly4 ) );
}
#if defined(__clang__) || defined(__GNUG__)
#include <cxxabi.h>
#include <ctime>
QString generateSpacesString( int numberOfSpace )
{
QStringList spaces;
spaces << QString( " " ) << QString( "\t" ) << QString( "\n" ) << QString( "\r" );
QString ret;
for ( int i = 0; i < numberOfSpace; ++i )
{
int r = rand() % ( spaces.count() );
ret += spaces.at( r );
}
return ret;
}
#endif
// Helper function (in anonymous namespace to prevent possible link with the extirior)
namespace
{
template<typename T>
inline void testCreateEmptyWithSameType( bool canBeEmpty = true )
inline void testCreateEmptyWithSameType()
{
std::unique_ptr<QgsAbstractGeometry> geom { new T() };
std::unique_ptr<QgsAbstractGeometry> created { TestQgsGeometry::createEmpty( geom.get() ) };
if ( canBeEmpty )
QVERIFY( created->isEmpty() );
#if defined(__clang__) || defined(__GNUG__)
srand( ( unsigned )time( NULL ) );
const std::type_info &ti = typeid( T );
int status;
char *realname = abi::__cxa_demangle( ti.name(), 0, 0, &status );
QString type = realname;
// remove Qgs prefix
type = type.right( type.count() - 3 );
QStringList extensionZM;
extensionZM << QString() << QString( "Z" ) << QString( "M" ) << QString( "ZM" );
for ( QString ext : extensionZM )
{
QVERIFY( created->isEmpty() );
QString wkt = type + ext;
QString result = wkt + QLatin1String( " EMPTY" );
QStringList emptyStringList;
emptyStringList << QString( "EMPTY" ) << QString( "Empty" ) << QString( "empty" ) << QString( "EmptY" ) << QString( "EmPtY" );
for ( int i = 0 ; i < 10 ; ++i )
{
QString spacesBefore = generateSpacesString( i );
QString spacesMiddle = i == 0 ? QStringLiteral( " " ) : generateSpacesString( i );
QString spacesAfter = generateSpacesString( i );
for ( int j = 0; j < emptyStringList.count() ; ++j )
{
QString generatedWkt = spacesBefore + wkt + spacesMiddle + emptyStringList.at( j ) + spacesAfter;
qDebug() << "Generated WKT:" << generatedWkt << " expected: " << result;
QgsGeometry gWkt = QgsGeometry().fromWkt( generatedWkt );
QVERIFY( gWkt.asWkt().compare( result, Qt::CaseInsensitive ) == 0 );
QVERIFY( geom->fromWkt( generatedWkt ) );
QVERIFY( geom->asWkt().compare( result, Qt::CaseInsensitive ) == 0 );
}
}
}
free( realname );
#endif
// Check that it is the correct type
QVERIFY( static_cast<T *>( created.get() ) != nullptr );
}
@ -16422,13 +16517,13 @@ void TestQgsGeometry::createEmptyWithSameType()
qDebug( "createEmptyWithSameType(): QgsPoint" );
testCreateEmptyWithSameType<QgsPoint>( false );
testCreateEmptyWithSameType<QgsPoint>();
qDebug( "createEmptyWithSameType(): QgsCurvePolygon" );
testCreateEmptyWithSameType<QgsCurvePolygon>();
qDebug( "createEmptyWithSameType(): QgsPolygonV2" );
qDebug( "createEmptyWithSameType(): QgsPolygon" );
testCreateEmptyWithSameType<QgsPolygon>();
qDebug( "createEmptyWithSameType(): QgsTriangle" );
@ -17487,7 +17582,7 @@ void TestQgsGeometry::convertGeometryCollectionToSubclass()
QgsGeometry gc3 = gc0;
QVERIFY( gc3.convertGeometryCollectionToSubclass( QgsWkbTypes::LineGeometry ) );
QCOMPARE( gc3.asWkt(), QStringLiteral( "MultiLineString ()" ) ); // I think this is not correct, WKT should be "MultiLineString Empty"
QCOMPARE( gc3.asWkt(), QStringLiteral( "MultiLineString EMPTY" ) );
QVERIFY( gc3.isEmpty() );
// trying to convert a geometry that is not a geometry collection
@ -17498,7 +17593,6 @@ void TestQgsGeometry::convertGeometryCollectionToSubclass()
void TestQgsGeometry::emptyJson()
{
QString expected;
// TODO: harmonize Json output. Should be ... [] }
expected = QStringLiteral( "{\"coordinates\":[],\"type\":\"LineString\"}" );
QCOMPARE( QgsCircularString().asJson(), expected );
QCOMPARE( QgsCompoundCurve().asJson(), expected );
@ -17517,7 +17611,7 @@ void TestQgsGeometry::emptyJson()
expected = QStringLiteral( "{\"coordinates\":[],\"type\":\"MultiPolygon\"}" );
QCOMPARE( QgsMultiSurface().asJson(), expected );
expected = QStringLiteral( "{\"coordinates\":[0.0,0.0],\"type\":\"Point\"}" ); // should be []
expected = QStringLiteral( "{\"coordinates\":[],\"type\":\"Point\"}" );
QCOMPARE( QgsPoint().asJson(), expected );
expected = QStringLiteral( "{\"coordinates\":[],\"type\":\"Polygon\"}" );

View File

@ -198,11 +198,12 @@ void TestQgsGeometryUtils::testSegmentMidPoint_data()
QTest::addColumn<double>( "pt2x" );
QTest::addColumn<double>( "pt2y" );
QTest::addColumn<double>( "radius" );
QTest::addColumn<bool>( "left" );
QTest::addColumn<double>( "mouseX" );
QTest::addColumn<double>( "mouseY" );
QTest::addColumn<double>( "expectedX" );
QTest::addColumn<double>( "expectedY" );
QTest::newRow( "testSegmentMidPoint1" ) << 0.0 << 0.0 << 1.0 << 0.0 << 0.5 << true << 0.5 << 0.5;
QTest::newRow( "testSegmentMidPoint1" ) << 0.0 << 0.0 << 1.0 << 0.0 << 0.5 << 1.0 << 0.0 << 0.5 << 0.5;
}
void TestQgsGeometryUtils::testSegmentMidPoint()
@ -212,13 +213,14 @@ void TestQgsGeometryUtils::testSegmentMidPoint()
QFETCH( double, pt2x );
QFETCH( double, pt2y );
QFETCH( double, radius );
QFETCH( bool, left );
QFETCH( double, mouseX );
QFETCH( double, mouseY );
QFETCH( double, expectedX );
QFETCH( double, expectedY );
QgsPoint midPoint;
bool ok = QgsGeometryUtils::segmentMidPoint( QgsPoint( pt1x, pt1y ), QgsPoint( pt2x, pt2y ),
midPoint, radius, left );
midPoint, radius, QgsPoint( mouseX, mouseY ) );
QVERIFY( ok );
QGSCOMPARENEAR( midPoint.x(), expectedX, 4 * std::numeric_limits<double>::epsilon() );

View File

@ -41,6 +41,7 @@ class TestQgsPointXY: public QObject
void toQPointF();
void operators();
void toString();
void asWkt();
void sqrDist();
void distance();
void compare();
@ -48,6 +49,7 @@ class TestQgsPointXY: public QObject
void vector(); //tests for QgsVector
void asVariant();
void referenced();
void isEmpty();
private:
QgsPointXY mPoint1;
@ -86,6 +88,11 @@ void TestQgsPointXY::equality()
QVERIFY( point4 != point1 );
QVERIFY( !( point4 == point3 ) );
QVERIFY( point4 != point3 );
QVERIFY( QgsPointXY() != point1 );
QVERIFY( QgsPointXY() != QgsPointXY( 0, 0 ) );
QVERIFY( point1 != QgsPointXY() );
QVERIFY( QgsPointXY( 0, 0 ) != QgsPointXY() );
}
void TestQgsPointXY::gettersSetters()
@ -186,6 +193,13 @@ void TestQgsPointXY::toString()
mReport += "<p>" + mPoint3.toString( 2 ) + "</p>";
mReport += "<p>" + mPoint4.toString( 2 ) + "</p>";
QCOMPARE( mPoint1.toString( 2 ), QString( "20.00,-20.00" ) );
QCOMPARE( QgsPointXY().toString( 2 ), QString( "0.00,0.00" ) );
}
void TestQgsPointXY::asWkt()
{
QCOMPARE( QgsPointXY().asWkt(), QString( "POINT EMPTY" ) );
QCOMPARE( mPoint1.asWkt(), QString( "POINT(20 -20)" ) );
}
void TestQgsPointXY::sqrDist()
@ -363,5 +377,30 @@ void TestQgsPointXY::referenced()
QCOMPARE( p2.crs().authid(), QStringLiteral( "EPSG:28356" ) );
}
void TestQgsPointXY::isEmpty()
{
QgsPointXY pointEmpty;
QVERIFY( pointEmpty.isEmpty() );
QCOMPARE( pointEmpty.x(), 0.0 );
QCOMPARE( pointEmpty.y(), 0.0 );
pointEmpty.setX( 7 );
QVERIFY( ! pointEmpty.isEmpty() );
QCOMPARE( pointEmpty.x(), 7.0 );
QCOMPARE( pointEmpty.y(), 0.0 );
pointEmpty = QgsPointXY();
QVERIFY( pointEmpty.isEmpty() );
QCOMPARE( pointEmpty.x(), 0.0 );
QCOMPARE( pointEmpty.y(), 0.0 );
pointEmpty.setY( 4 );
QVERIFY( ! pointEmpty.isEmpty() );
QCOMPARE( pointEmpty.x(), 0.0 );
QCOMPARE( pointEmpty.y(), 4.0 );
QVERIFY( QgsPointXY( QgsPoint() ).isEmpty() );
// "can't" be empty
QVERIFY( ! QgsPointXY( QPoint() ).isEmpty() );
QVERIFY( ! QgsPointXY( QPointF() ).isEmpty() );
}
QGSTEST_MAIN( TestQgsPointXY )
#include "testqgspoint.moc"

View File

@ -1168,7 +1168,7 @@ QList<QgsGeometryCheckError *> TestQgsGeometryChecks::searchCheckErrors( const Q
{
continue;
}
if ( pos != QgsPointXY() && ( !qgsDoubleNear( error->location().x(), pos.x(), tol ) || !qgsDoubleNear( error->location().y(), pos.y(), tol ) ) )
if ( !pos.isEmpty() && ( !qgsDoubleNear( error->location().x(), pos.x(), tol ) || !qgsDoubleNear( error->location().y(), pos.y(), tol ) ) )
{
continue;
}

View File

@ -813,7 +813,7 @@ class TestQgsDistanceArea(unittest.TestCase):
g = da.splitGeometryAtAntimeridian(QgsGeometry.fromWkt('PointM(1 2 3)'))
self.assertEqual(g.asWkt(), 'PointM (1 2 3)')
g = da.splitGeometryAtAntimeridian(QgsGeometry.fromWkt('LineString()'))
self.assertEqual(g.asWkt(), 'MultiLineString ()')
self.assertEqual(g.asWkt(), 'MultiLineString EMPTY')
# lines
g = da.splitGeometryAtAntimeridian(QgsGeometry.fromWkt('LineString(0 0, -170 0)'))

View File

@ -1303,7 +1303,9 @@ class TestQgsGeometry(unittest.TestCase):
polyline = QgsGeometry.fromPolylineXY(points)
for i in range(0, len(points)):
self.assertEqual(QgsPoint(points[i]), polyline.vertexAt(i), "Mismatch at %d" % i)
# WORKAROUND to avoid a system error
# self.assertEqual(QgsPoint(points[i]), polyline.vertexAt(i), "Mismatch at %d" % i)
self.assertEqual(QgsPoint(points[i].x(), points[i].y()), polyline.vertexAt(i), "Mismatch at %d" % i)
# 2-3 6-+-7
# | | | |
@ -1315,15 +1317,18 @@ class TestQgsGeometry(unittest.TestCase):
polyline = QgsGeometry.fromMultiPolylineXY(points)
p = polyline.vertexAt(-100)
self.assertEqual(p, QgsPoint(0, 0), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
self.assertEqual(p, QgsPoint(math.nan, math.nan), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
p = polyline.vertexAt(100)
self.assertEqual(p, QgsPoint(0, 0), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
self.assertEqual(p, QgsPoint(math.nan, math.nan), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
i = 0
for j in range(0, len(points)):
for k in range(0, len(points[j])):
self.assertEqual(QgsPoint(points[j][k]), polyline.vertexAt(i), "Mismatch at %d / %d,%d" % (i, j, k))
# WORKAROUND
# self.assertEqual(QgsPoint(points[j][k]), polyline.vertexAt(i), "Mismatch at %d / %d,%d" % (i, j, k))
pt = points[j][k]
self.assertEqual(QgsPoint(pt.x(), pt.y()), polyline.vertexAt(i), "Mismatch at %d / %d,%d" % (i, j, k))
i += 1
# 5---4
@ -1337,15 +1342,18 @@ class TestQgsGeometry(unittest.TestCase):
polygon = QgsGeometry.fromPolygonXY(points)
p = polygon.vertexAt(-100)
self.assertEqual(p, QgsPoint(0, 0), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
self.assertEqual(p, QgsPoint(), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
p = polygon.vertexAt(100)
self.assertEqual(p, QgsPoint(0, 0), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
self.assertEqual(p, QgsPoint(), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
i = 0
for j in range(0, len(points)):
for k in range(0, len(points[j])):
self.assertEqual(QgsPoint(points[j][k]), polygon.vertexAt(i), "Mismatch at %d / %d,%d" % (i, j, k))
# WORKAROUND
# self.assertEqual(QgsPoint(points[j][k]), polygon.vertexAt(i), "Mismatch at %d / %d,%d" % (i, j, k))
pt = points[j][k]
self.assertEqual(QgsPoint(pt.x(), pt.y()), polygon.vertexAt(i), "Mismatch at %d / %d,%d" % (i, j, k))
i += 1
# 3-+-+-2
@ -1362,15 +1370,18 @@ class TestQgsGeometry(unittest.TestCase):
polygon = QgsGeometry.fromPolygonXY(points)
p = polygon.vertexAt(-100)
self.assertEqual(p, QgsPoint(0, 0), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
self.assertEqual(p, QgsPoint(), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
p = polygon.vertexAt(100)
self.assertEqual(p, QgsPoint(0, 0), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
self.assertEqual(p, QgsPoint(), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
i = 0
for j in range(0, len(points)):
for k in range(0, len(points[j])):
self.assertEqual(QgsPoint(points[j][k]), polygon.vertexAt(i), "Mismatch at %d / %d,%d" % (i, j, k))
# WORKAROUND
# self.assertEqual(QgsPoint(points[j][k]), polygon.vertexAt(i), "Mismatch at %d / %d,%d" % (i, j, k))
pt = points[j][k]
self.assertEqual(QgsPoint(pt.x(), pt.y()), polygon.vertexAt(i), "Mismatch at %d / %d,%d" % (i, j, k))
i += 1
# 5-+-4 0-+-9
@ -1386,17 +1397,20 @@ class TestQgsGeometry(unittest.TestCase):
polygon = QgsGeometry.fromMultiPolygonXY(points)
p = polygon.vertexAt(-100)
self.assertEqual(p, QgsPoint(0, 0), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
self.assertEqual(p, QgsPoint(), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
p = polygon.vertexAt(100)
self.assertEqual(p, QgsPoint(0, 0), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
self.assertEqual(p, QgsPoint(), "Expected 0,0, Got {}.{}".format(p.x(), p.y()))
i = 0
for j in range(0, len(points)):
for k in range(0, len(points[j])):
for l in range(0, len(points[j][k])):
p = polygon.vertexAt(i)
self.assertEqual(QgsPoint(points[j][k][l]), p, "Got {},{} Expected {} at {} / {},{},{}".format(p.x(), p.y(), points[j][k][l].toString(), i, j, k, l))
# WORKAROUND
# self.assertEqual(QgsPoint(points[j][k][l]), p, "Got {},{} Expected {} at {} / {},{},{}".format(p.x(), p.y(), points[j][k][l].toString(), i, j, k, l))
pt = points[j][k][l]
self.assertEqual(QgsPoint(pt.x(), pt.y()), p, "Got {},{} Expected {} at {} / {},{},{}".format(p.x(), p.y(), pt.toString(), i, j, k, l))
i += 1
def testMultipoint(self):
@ -4144,7 +4158,7 @@ class TestQgsGeometry(unittest.TestCase):
Test curve straightDistance2d() and sinuosity()
"""
linestring = QgsGeometry.fromWkt('LineString()')
self.assertEqual(linestring.constGet().straightDistance2d(), 0.0)
self.assertTrue(math.isnan(linestring.constGet().straightDistance2d()))
self.assertTrue(math.isnan(linestring.constGet().sinuosity()))
linestring = QgsGeometry.fromWkt('LineString(0 0, 10 0)')
self.assertEqual(linestring.constGet().straightDistance2d(), 10.0)
@ -4857,7 +4871,7 @@ class TestQgsGeometry(unittest.TestCase):
def testSubdivide(self):
tests = [["LINESTRING (1 1,1 9,9 9,9 1)", 8, "MULTILINESTRING ((1 1,1 9,9 9,9 1))"],
["Point (1 1)", 8, "MultiPoint ((1 1))"],
["GeometryCollection ()", 8, "GeometryCollection ()"],
["GeometryCollection ()", 8, "GeometryCollection EMPTY"],
["LINESTRING (1 1,1 2,1 3,1 4,1 5,1 6,1 7,1 8,1 9)", 8, "MultiLineString ((1 1, 1 2, 1 3, 1 4, 1 5),(1 5, 1 6, 1 7, 1 8, 1 9))"],
["LINESTRING(0 0, 100 100, 150 150)", 8, 'MultiLineString ((0 0, 100 100, 150 150))'],
['POLYGON((132 10,119 23,85 35,68 29,66 28,49 42,32 56,22 64,32 110,40 119,36 150,57 158,75 171,92 182,114 184,132 186,146 178,176 184,179 162,184 141,190 122,190 100,185 79,186 56,186 52,178 34,168 18,147 13,132 10))',
@ -4887,19 +4901,23 @@ class TestQgsGeometry(unittest.TestCase):
def testClipped(self):
tests = [["LINESTRING (1 1,1 9,9 9,9 1)", QgsRectangle(0, 0, 10, 10), "LINESTRING (1 1,1 9,9 9,9 1)"],
["LINESTRING (-1 -9,-1 11,9 11)", QgsRectangle(0, 0, 10, 10), "GEOMETRYCOLLECTION ()"],
["LINESTRING (-1 -9,-1 11,9 11)", QgsRectangle(0, 0, 10, 10),
"GEOMETRYCOLLECTION EMPTY"],
["LINESTRING (-1 5,5 5,9 9)", QgsRectangle(0, 0, 10, 10), "LINESTRING (0 5,5 5,9 9)"],
["LINESTRING (5 5,8 5,12 5)", QgsRectangle(0, 0, 10, 10), "LINESTRING (5 5,8 5,10 5)"],
["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))"],
["LINESTRING (0 3,0 5,0 7)", QgsRectangle(0, 0, 10, 10), "GEOMETRYCOLLECTION ()"],
["LINESTRING (0 3,0 5,-1 7)", QgsRectangle(0, 0, 10, 10), "GEOMETRYCOLLECTION ()"],
["LINESTRING (0 3,0 5,0 7)", QgsRectangle(0, 0, 10, 10),
"GEOMETRYCOLLECTION EMPTY"],
["LINESTRING (0 3,0 5,-1 7)", QgsRectangle(0, 0, 10, 10),
"GEOMETRYCOLLECTION EMPTY"],
["LINESTRING (0 3,0 5,2 7)", QgsRectangle(0, 0, 10, 10), "LINESTRING (0 5,2 7)"],
["LINESTRING (2 1,0 0,1 2)", QgsRectangle(0, 0, 10, 10), "LINESTRING (2 1,0 0,1 2)"],
["LINESTRING (3 3,0 3,0 5,2 7)", QgsRectangle(0, 0, 10, 10), "MULTILINESTRING ((3 3,0 3),(0 5,2 7))"],
["LINESTRING (5 5,10 5,20 5)", QgsRectangle(0, 0, 10, 10), "LINESTRING (5 5,10 5)"],
["LINESTRING (3 3,0 6,3 9)", QgsRectangle(0, 0, 10, 10), "LINESTRING (3 3,0 6,3 9)"],
["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))"],
["POLYGON ((15 15,15 16,16 16,16 15,15 15))", QgsRectangle(0, 0, 10, 10), "GEOMETRYCOLLECTION ()"],
["POLYGON ((15 15,15 16,16 16,16 15,15 15))", QgsRectangle(0, 0, 10, 10),
"GEOMETRYCOLLECTION EMPTY"],
["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))"],
["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))"],
["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))"]
@ -4970,7 +4988,7 @@ class TestQgsGeometry(unittest.TestCase):
"tapered buffer: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
def testVariableWidthBufferByM(self):
tests = [['LineString (6 2, 9 2, 9 3, 11 5)', 3, 'GeometryCollection ()'],
tests = [['LineString (6 2, 9 2, 9 3, 11 5)', 3, 'GeometryCollection EMPTY'],
['LineStringM (6 2 1, 9 2 1.5, 9 3 0.5, 11 5 2)', 3, 'MultiPolygon (((6 1.5, 5.75 1.56698729810778059, 5.56698729810778037 1.75, 5.5 2, 5.56698729810778037 2.24999999999999956, 5.75 2.43301270189221919, 6 2.5, 8.54510095773215994 2.71209174647768014, 8.78349364905388974 3.125, 10.13397459621556074 5.49999999999999911, 10.5 5.86602540378443837, 11 6, 11.5 5.86602540378443837, 11.86602540378443926 5.5, 12 5, 11.86602540378443926 4.5, 11.5 4.13397459621556163, 9.34232758349701164 2.90707123255090094, 9.649519052838329 2.375, 9.75 1.99999999999999978, 9.649519052838329 1.62500000000000022, 9.375 1.350480947161671, 9 1.25, 6 1.5)))'],
['MultiLineStringM ((6 2 1, 9 2 1.5, 9 3 0.5, 11 5 2),(1 2 0.5, 3 2 0.2))', 3,
'MultiPolygon (((6 1.5, 5.75 1.56698729810778059, 5.56698729810778037 1.75, 5.5 2, 5.56698729810778037 2.24999999999999956, 5.75 2.43301270189221919, 6 2.5, 8.54510095773215994 2.71209174647768014, 8.78349364905388974 3.125, 10.13397459621556074 5.49999999999999911, 10.5 5.86602540378443837, 11 6, 11.5 5.86602540378443837, 11.86602540378443926 5.5, 12 5, 11.86602540378443926 4.5, 11.5 4.13397459621556163, 9.34232758349701164 2.90707123255090094, 9.649519052838329 2.375, 9.75 1.99999999999999978, 9.649519052838329 1.62500000000000022, 9.375 1.350480947161671, 9 1.25, 6 1.5)),((1 1.75, 0.875 1.78349364905389041, 0.78349364905389041 1.875, 0.75 2, 0.7834936490538903 2.125, 0.875 2.21650635094610982, 1 2.25, 3 2.10000000000000009, 3.04999999999999982 2.08660254037844384, 3.08660254037844384 2.04999999999999982, 3.10000000000000009 2, 3.08660254037844384 1.94999999999999996, 3.04999999999999982 1.91339745962155616, 3 1.89999999999999991, 1 1.75)))']