mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
Refactoring of rectangle maptools
Adds a new geometry class QgsQuadrilateral, for 4 sided geometries.
This commit is contained in:
parent
046bec48bc
commit
edadcb773f
201
python/core/auto_generated/geometry/qgsquadrilateral.sip.in
Normal file
201
python/core/auto_generated/geometry/qgsquadrilateral.sip.in
Normal file
@ -0,0 +1,201 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/geometry/qgsquadrilateral.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsQuadrilateral
|
||||
{
|
||||
%Docstring
|
||||
Quadrilateral geometry type.
|
||||
A quadrilateral is a polygon with four edges (or sides) and four vertices or corners.
|
||||
This class allows the creation of simple quadrilateral (which does not self-intersect).
|
||||
|
||||
.. versionadded:: 3.6
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsquadrilateral.h"
|
||||
%End
|
||||
public:
|
||||
QgsQuadrilateral();
|
||||
|
||||
QgsQuadrilateral( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 );
|
||||
%Docstring
|
||||
Construct a QgsQuadrilateral from four :py:class:`QgsPoint`.
|
||||
|
||||
:param p1: first point
|
||||
:param p2: second point
|
||||
:param p3: third point
|
||||
:param p4: fourth point
|
||||
|
||||
.. seealso:: :py:func:`setPoints`
|
||||
%End
|
||||
|
||||
explicit QgsQuadrilateral( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3, const QgsPointXY &p4 );
|
||||
%Docstring
|
||||
Construct a QgsQuadrilateral from four :py:class:`QgsPointXY`.
|
||||
|
||||
:param p1: first point
|
||||
:param p2: second point
|
||||
:param p3: third point
|
||||
:param p4: fourth point
|
||||
|
||||
.. seealso:: :py:func:`setPoints`
|
||||
%End
|
||||
|
||||
|
||||
enum ConstructionOption
|
||||
{
|
||||
Distance,
|
||||
Projected,
|
||||
};
|
||||
|
||||
static QgsQuadrilateral rectangleFrom3Points( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode );
|
||||
%Docstring
|
||||
Construct a QgsQuadrilateral as a Rectangle from 3 points.
|
||||
In the case where one of the points is of type PointZ. The other points
|
||||
will also be of type Z, even if they are of type Point. In addition,
|
||||
the z used will be the one of the first point with a Z.
|
||||
This ensures consistency in point types and the ability to export to a
|
||||
Polygon or LineString.
|
||||
|
||||
:param p1: first point
|
||||
:param p2: second point
|
||||
:param p3: third point
|
||||
:param mode: Construction mode to construct the rectangle from 3 points
|
||||
|
||||
.. seealso:: ConstructionOption
|
||||
%End
|
||||
|
||||
static QgsQuadrilateral rectangleFromExtent( const QgsPoint &p1, const QgsPoint &p2 );
|
||||
%Docstring
|
||||
Construct a QgsQuadrilateral as a rectangle from an extent, defined by
|
||||
two opposite corner points.
|
||||
Z is taken from point ``p1``.
|
||||
|
||||
:param p1: first point
|
||||
:param p2: second point
|
||||
%End
|
||||
|
||||
|
||||
static QgsQuadrilateral squareFromDiagonal( const QgsPoint &p1, const QgsPoint &p2 );
|
||||
%Docstring
|
||||
Construct a QgsQuadrilateral as a square from a diagonal.
|
||||
Z is taken from point ``p1``.
|
||||
|
||||
:param p1: first point
|
||||
:param p2: second point
|
||||
%End
|
||||
|
||||
static QgsQuadrilateral rectangleFromCenterPoint( const QgsPoint ¢er, const QgsPoint &point );
|
||||
%Docstring
|
||||
Construct a QgsQuadrilateral as a rectangle from center point ``center``
|
||||
and another point ``point``.
|
||||
Z is taken from ``center`` point.
|
||||
|
||||
:param center: center point
|
||||
:param point: corner point
|
||||
%End
|
||||
|
||||
static QgsQuadrilateral fromRectangle( const QgsRectangle &rectangle );
|
||||
%Docstring
|
||||
Construct a QgsQuadrilateral as a rectangle from a :py:class:`QgsRectangle`.
|
||||
|
||||
:param rectangle: rectangle
|
||||
%End
|
||||
|
||||
|
||||
bool equals( const QgsQuadrilateral &other, double epsilon = 4 * DBL_EPSILON ) const;
|
||||
%Docstring
|
||||
Compares two QgsQuadrilateral, allowing specification of the maximum allowable difference between points.
|
||||
|
||||
:param other: the QgsQuadrilateral to compare
|
||||
:param epsilon: the maximum difference allowed / tolerance
|
||||
%End
|
||||
bool operator==( const QgsQuadrilateral &other ) const;
|
||||
bool operator!=( const QgsQuadrilateral &other ) const;
|
||||
|
||||
bool isValid() const;
|
||||
%Docstring
|
||||
Convenient method to determine if a QgsQuadrilateral is valid.
|
||||
A QgsQuadrilateral must be simple (not self-intersecting) and
|
||||
cannot have collinear points.
|
||||
%End
|
||||
|
||||
enum Point
|
||||
{
|
||||
Point1,
|
||||
Point2,
|
||||
Point3,
|
||||
Point4,
|
||||
};
|
||||
|
||||
bool setPoint( const QgsPoint &newPoint, Point index );
|
||||
%Docstring
|
||||
Sets the point ``newPoint`` at the ``index``.
|
||||
Returns false if the QgsQuadrilateral is not valid.
|
||||
|
||||
.. seealso:: Point
|
||||
%End
|
||||
|
||||
bool setPoints( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 );
|
||||
%Docstring
|
||||
Set all points
|
||||
Returns false if the QgsQuadrilateral is not valid:
|
||||
- The points do not have the same type
|
||||
- The quadrilateral would have auto intersections
|
||||
- The quadrilateral has double points
|
||||
- The quadrilateral has collinear points
|
||||
%End
|
||||
|
||||
QgsPointSequence points() const;
|
||||
%Docstring
|
||||
Returns a list including the vertices of the quadrilateral.
|
||||
%End
|
||||
|
||||
QgsPolygon *toPolygon( bool force2D = false ) const /Factory/;
|
||||
%Docstring
|
||||
Returns the quadrilateral as a new polygon. Ownership is transferred to the caller.
|
||||
%End
|
||||
|
||||
QgsLineString *toLineString( bool force2D = false ) const /Factory/;
|
||||
%Docstring
|
||||
Returns the quadrilateral as a new linestring. Ownership is transferred to the caller.
|
||||
%End
|
||||
|
||||
QString toString( int pointPrecision = 17 ) const;
|
||||
%Docstring
|
||||
Returns a string representation of the quadrilateral.
|
||||
Members will be truncated to the specified precision.
|
||||
%End
|
||||
|
||||
double area() const;
|
||||
%Docstring
|
||||
Returns the area of the quadrilateral, or 0 if the quadrilateral is empty.
|
||||
%End
|
||||
|
||||
double perimeter() const;
|
||||
%Docstring
|
||||
Returns the perimeter of the quadrilateral, or 0 if the quadrilateral is empty.
|
||||
%End
|
||||
SIP_PYOBJECT __repr__();
|
||||
%MethodCode
|
||||
QString str = QStringLiteral( "<QgsQuadrilateral: %1>" ).arg( sipCpp->toString() );
|
||||
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
|
||||
%End
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/geometry/qgsquadrilateral.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
@ -89,7 +89,27 @@ Returns the length of the vector
|
||||
Normalizes the current vector in place.
|
||||
%End
|
||||
|
||||
double distance( const QgsVector3D &other ) const;
|
||||
%Docstring
|
||||
Returns the distance with the ``other`` :py:class:`QgsVector3`
|
||||
%End
|
||||
|
||||
static QgsVector3D perpendicularPoint( const QgsVector3D &v1, const QgsVector3D &v2, const QgsVector3D &vp );
|
||||
%Docstring
|
||||
Returns the perpendicular point of vector ``vp`` from [``v1`` - ``v2``]
|
||||
%End
|
||||
|
||||
QString toString( int precision = 17 ) const;
|
||||
%Docstring
|
||||
Returns a string representation of the 3D vector.
|
||||
Members will be truncated to the specified ``precision``.
|
||||
%End
|
||||
|
||||
SIP_PYOBJECT __repr__();
|
||||
%MethodCode
|
||||
QString str = QStringLiteral( "<QgsVector3D: %1>" ).arg( sipCpp->toString() );
|
||||
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
|
||||
%End
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
@ -288,6 +288,7 @@
|
||||
%Include auto_generated/geometry/qgsmultipolygon.sip
|
||||
%Include auto_generated/geometry/qgsmultisurface.sip
|
||||
%Include auto_generated/geometry/qgspolygon.sip
|
||||
%Include auto_generated/geometry/qgsquadrilateral.sip
|
||||
%Include auto_generated/geometry/qgsrectangle.sip
|
||||
%Include auto_generated/geometry/qgsreferencedgeometry.sip
|
||||
%Include auto_generated/geometry/qgsregularpolygon.sip
|
||||
|
@ -35,26 +35,6 @@ QgsMapToolAddRectangle::QgsMapToolAddRectangle( QgsMapToolCapture *parentTool, Q
|
||||
connect( QgisApp::instance(), &QgisApp::projectRead, this, &QgsMapToolAddRectangle::stopCapturing );
|
||||
}
|
||||
|
||||
void QgsMapToolAddRectangle::setAzimuth( const double azimuth )
|
||||
{
|
||||
mAzimuth = azimuth;
|
||||
}
|
||||
|
||||
void QgsMapToolAddRectangle::setDistance1( const double distance1 )
|
||||
{
|
||||
mDistance1 = distance1;
|
||||
}
|
||||
|
||||
void QgsMapToolAddRectangle::setDistance2( const double distance2 )
|
||||
{
|
||||
mDistance2 = distance2;
|
||||
}
|
||||
|
||||
void QgsMapToolAddRectangle::setSide( const int side )
|
||||
{
|
||||
mSide = side;
|
||||
}
|
||||
|
||||
QgsMapToolAddRectangle::~QgsMapToolAddRectangle()
|
||||
{
|
||||
clean();
|
||||
@ -83,81 +63,15 @@ void QgsMapToolAddRectangle::keyReleaseEvent( QKeyEvent *e )
|
||||
}
|
||||
}
|
||||
|
||||
QgsLineString *QgsMapToolAddRectangle::rectangleToLinestring( const bool isOriented ) const
|
||||
void QgsMapToolAddRectangle::deactivate( )
|
||||
{
|
||||
std::unique_ptr<QgsLineString> ext( new QgsLineString() );
|
||||
if ( mRectangle.toRectangle().isEmpty() )
|
||||
{
|
||||
return ext.release();
|
||||
}
|
||||
|
||||
QgsPoint x0( mRectangle.xMinimum(), mRectangle.yMinimum() );
|
||||
|
||||
QgsPoint x1, x2, x3;
|
||||
if ( isOriented )
|
||||
{
|
||||
const double perpendicular = 90.0 * mSide;
|
||||
x1 = x0.project( mDistance1, mAzimuth );
|
||||
x3 = x0.project( mDistance2, mAzimuth + perpendicular );
|
||||
x2 = x1.project( mDistance2, mAzimuth + perpendicular );
|
||||
}
|
||||
else
|
||||
{
|
||||
x1 = QgsPoint( mRectangle.xMinimum(), mRectangle.yMaximum() );
|
||||
x2 = QgsPoint( mRectangle.xMaximum(), mRectangle.yMaximum() );
|
||||
x3 = QgsPoint( mRectangle.xMaximum(), mRectangle.yMinimum() );
|
||||
}
|
||||
|
||||
ext->addVertex( x0 );
|
||||
ext->addVertex( x1 );
|
||||
ext->addVertex( x2 );
|
||||
ext->addVertex( x3 );
|
||||
ext->addVertex( x0 );
|
||||
|
||||
// keep z value from the first snapped point
|
||||
for ( const QgsPoint point : qgis::as_const( mPoints ) )
|
||||
{
|
||||
if ( QgsWkbTypes::hasZ( point.wkbType() ) )
|
||||
{
|
||||
if ( point.z() != defaultZValue() )
|
||||
{
|
||||
ext->dropZValue();
|
||||
ext->addZValue( point.z() );
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ext->dropZValue();
|
||||
ext->addZValue( defaultZValue() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ext.release();
|
||||
}
|
||||
|
||||
QgsPolygon *QgsMapToolAddRectangle::rectangleToPolygon( const bool isOriented ) const
|
||||
{
|
||||
std::unique_ptr<QgsPolygon> polygon( new QgsPolygon() );
|
||||
if ( mRectangle.toRectangle().isEmpty() )
|
||||
{
|
||||
return polygon.release();
|
||||
}
|
||||
|
||||
polygon->setExteriorRing( rectangleToLinestring( isOriented ) );
|
||||
|
||||
return polygon.release();
|
||||
}
|
||||
|
||||
void QgsMapToolAddRectangle::deactivate( const bool isOriented )
|
||||
{
|
||||
if ( !mParentTool || mRectangle.toRectangle().isEmpty() )
|
||||
if ( !mParentTool || !mRectangle.isValid() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mParentTool->clearCurve( );
|
||||
mParentTool->addCurve( rectangleToLinestring( isOriented ) );
|
||||
mParentTool->addCurve( mRectangle.toLineString( !QgsWkbTypes::hasZ( qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() )->wkbType() ) ) );
|
||||
clean();
|
||||
|
||||
QgsMapToolCapture::deactivate();
|
||||
@ -184,7 +98,7 @@ void QgsMapToolAddRectangle::clean()
|
||||
mParentTool->deleteTempRubberBand();
|
||||
}
|
||||
|
||||
mRectangle = QgsBox3d();
|
||||
mRectangle = QgsQuadrilateral();
|
||||
|
||||
QgsVectorLayer *vLayer = static_cast<QgsVectorLayer *>( QgisApp::instance()->activeLayer() );
|
||||
if ( vLayer )
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
#include "qgspolygon.h"
|
||||
#include "qgsmaptoolcapture.h"
|
||||
#include "qgsbox3d.h"
|
||||
#include "qgsquadrilateral.h"
|
||||
#include "qgis_app.h"
|
||||
|
||||
class QgsPolygon;
|
||||
@ -35,7 +35,7 @@ class APP_EXPORT QgsMapToolAddRectangle: public QgsMapToolCapture
|
||||
void keyPressEvent( QKeyEvent *e ) override;
|
||||
void keyReleaseEvent( QKeyEvent *e ) override;
|
||||
|
||||
void deactivate( bool isOriented = false );
|
||||
void deactivate( ) override;
|
||||
|
||||
void activate() override;
|
||||
void clean() override;
|
||||
@ -53,46 +53,13 @@ class APP_EXPORT QgsMapToolAddRectangle: public QgsMapToolCapture
|
||||
//! The rubberband to show the rectangle currently working on
|
||||
QgsGeometryRubberBand *mTempRubberBand = nullptr;
|
||||
//! Rectangle
|
||||
QgsBox3d mRectangle;
|
||||
|
||||
//! Convenient method to export a QgsRectangle to a LineString
|
||||
QgsLineString *rectangleToLinestring( bool isOriented = false ) const;
|
||||
//! Convenient method to export a QgsRectangle to a Polygon
|
||||
QgsPolygon *rectangleToPolygon( bool isOriented = false ) const;
|
||||
|
||||
//! Sets the azimuth. \see mAzimuth
|
||||
void setAzimuth( double azimuth );
|
||||
//! Sets the first distance. \see mDistance1
|
||||
void setDistance1( double distance1 );
|
||||
//! Sets the second distance. \see mDistance2
|
||||
void setDistance2( double distance2 );
|
||||
//! Sets the side. \see mSide
|
||||
void setSide( int side );
|
||||
|
||||
//! Returns the azimuth. \see mAzimuth
|
||||
double azimuth( ) const { return mAzimuth; }
|
||||
//! Returns the first distance. \see mDistance1
|
||||
double distance1( ) const { return mDistance1; }
|
||||
//! Returns the second distance. \see mDistance2
|
||||
double distance2( ) const { return mDistance2; }
|
||||
//! Returns the side. \see mSide
|
||||
int side( ) const { return mSide; }
|
||||
QgsQuadrilateral mRectangle;
|
||||
|
||||
//! Layer type which will be used for rubberband
|
||||
QgsWkbTypes::GeometryType mLayerType = QgsWkbTypes::LineGeometry;
|
||||
|
||||
//! Snapping indicators
|
||||
std::unique_ptr<QgsSnapIndicator> mSnapIndicator;
|
||||
|
||||
private:
|
||||
//! Convenient member for the azimuth of the rotated rectangle or when map is rotated.
|
||||
double mAzimuth = 0.0;
|
||||
//! Convenient member for the first distance of the rotated rectangle or when map is rotated.
|
||||
double mDistance1 = 0.0;
|
||||
//! Convenient member for the second distance of the rotated rectangle or when map is rotated.
|
||||
double mDistance2 = 0.0;
|
||||
//! Convenient member for the side where the second distance is drawn or when map is rotated.
|
||||
int mSide = 1;
|
||||
};
|
||||
|
||||
#endif // QGSMAPTOOLADDRECTANGLE_H
|
||||
|
@ -37,18 +37,27 @@ void QgsMapToolRectangle3Points::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
|
||||
|
||||
if ( e->button() == Qt::LeftButton )
|
||||
{
|
||||
if ( !point.is3D() )
|
||||
point.addZValue( defaultZValue() );
|
||||
if ( mPoints.size() < 2 )
|
||||
{
|
||||
mPoints.append( point );
|
||||
}
|
||||
|
||||
if ( !mPoints.isEmpty() && !mTempRubberBand )
|
||||
{
|
||||
mTempRubberBand = createGeometryRubberBand( mLayerType, true );
|
||||
mTempRubberBand->show();
|
||||
}
|
||||
if ( mPoints.size() == 3 )
|
||||
{
|
||||
delete mTempRubberBand;
|
||||
mTempRubberBand = createGeometryRubberBand( mLayerType, true ); // recreate rubberband for polygon
|
||||
}
|
||||
}
|
||||
else if ( e->button() == Qt::RightButton )
|
||||
{
|
||||
deactivate( true );
|
||||
deactivate( );
|
||||
if ( mParentTool )
|
||||
{
|
||||
mParentTool->canvasReleaseEvent( e );
|
||||
@ -72,43 +81,25 @@ void QgsMapToolRectangle3Points::cadCanvasMoveEvent( QgsMapMouseEvent *e )
|
||||
line->addVertex( mPoints.at( 0 ) );
|
||||
line->addVertex( point );
|
||||
mTempRubberBand->setGeometry( line.release() );
|
||||
setAzimuth( mPoints.at( 0 ).azimuth( point ) );
|
||||
setDistance1( mPoints.at( 0 ).distance( point ) );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
if ( !point.is3D() )
|
||||
point.addZValue( defaultZValue() );
|
||||
switch ( mCreateMode )
|
||||
{
|
||||
case DistanceMode:
|
||||
setDistance2( mPoints.at( 1 ).distance( point ) );
|
||||
mRectangle = QgsQuadrilateral::rectangleFrom3Points( mPoints.at( 0 ), mPoints.at( 1 ), point, QgsQuadrilateral::Distance );
|
||||
break;
|
||||
case ProjectedMode:
|
||||
setDistance2( QgsGeometryUtils::perpendicularSegment( point, mPoints.at( 0 ), mPoints.at( 1 ) ).length() );
|
||||
mRectangle = QgsQuadrilateral::rectangleFrom3Points( mPoints.at( 0 ), mPoints.at( 1 ), point, QgsQuadrilateral::Projected );
|
||||
break;
|
||||
}
|
||||
int side = QgsGeometryUtils::leftOfLine( point.x(), point.y(),
|
||||
mPoints.at( 0 ).x(), mPoints.at( 0 ).y(),
|
||||
mPoints.at( 1 ).x(), mPoints.at( 1 ).y() );
|
||||
|
||||
setSide( side < 0 ? -1 : 1 );
|
||||
|
||||
const double xMin = mPoints.at( 0 ).x();
|
||||
const double xMax = mPoints.at( 0 ).x() + distance2( );
|
||||
|
||||
const double yMin = mPoints.at( 0 ).y();
|
||||
const double yMax = mPoints.at( 0 ).y() + distance1();
|
||||
|
||||
const double z = mPoints.at( 0 ).z();
|
||||
|
||||
mRectangle = QgsBox3d( xMin, yMin, z, xMax, yMax, z );
|
||||
|
||||
|
||||
mTempRubberBand->setGeometry( QgsMapToolAddRectangle::rectangleToPolygon( true ) );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mTempRubberBand->setGeometry( mRectangle.toPolygon() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "qgspoint.h"
|
||||
#include "qgsmapmouseevent.h"
|
||||
#include "qgssnapindicator.h"
|
||||
#include "qgsquadrilateral.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@ -69,23 +70,13 @@ void QgsMapToolRectangleCenter::cadCanvasMoveEvent( QgsMapMouseEvent *e )
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
if ( qgsDoubleNear( mCanvas->rotation(), 0.0 ) )
|
||||
{
|
||||
double xOffset = fabs( point.x() - mPoints.at( 0 ).x() );
|
||||
double yOffset = fabs( point.y() - mPoints.at( 0 ).y() );
|
||||
|
||||
mRectangle = QgsBox3d( QgsPoint( mPoints.at( 0 ).x() - xOffset, mPoints.at( 0 ).y() - yOffset, mPoints.at( 0 ).z() ), QgsPoint( mPoints.at( 0 ).x() + xOffset, mPoints.at( 0 ).y() + yOffset, mPoints.at( 0 ).z() ) );
|
||||
double dist = mPoints.at( 0 ).distance( point );
|
||||
double angle = mPoints.at( 0 ).azimuth( point );
|
||||
|
||||
mTempRubberBand->setGeometry( QgsMapToolAddRectangle::rectangleToPolygon() );
|
||||
}
|
||||
else
|
||||
{
|
||||
double dist = mPoints.at( 0 ).distance( point );
|
||||
double angle = mPoints.at( 0 ).azimuth( point );
|
||||
mRectangle = QgsQuadrilateral::rectangleFromExtent( mPoints.at( 0 ).project( -dist, angle ), mPoints.at( 0 ).project( dist, angle ) );
|
||||
mTempRubberBand->setGeometry( mRectangle.toPolygon() );
|
||||
|
||||
mRectangle = QgsBox3d( mPoints.at( 0 ).project( -dist, angle ), mPoints.at( 0 ).project( dist, angle ) );
|
||||
mTempRubberBand->setGeometry( QgsMapToolAddRectangle::rectangleToPolygon() );
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -68,19 +68,11 @@ void QgsMapToolRectangleExtent::cadCanvasMoveEvent( QgsMapMouseEvent *e )
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
if ( qgsDoubleNear( mCanvas->rotation(), 0.0 ) )
|
||||
{
|
||||
mRectangle = QgsBox3d( mPoints.at( 0 ), point );
|
||||
mTempRubberBand->setGeometry( QgsMapToolAddRectangle::rectangleToPolygon( ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
double dist = mPoints.at( 0 ).distance( point );
|
||||
double angle = mPoints.at( 0 ).azimuth( point );
|
||||
double dist = mPoints.at( 0 ).distance( point );
|
||||
double angle = mPoints.at( 0 ).azimuth( point );
|
||||
|
||||
mRectangle = QgsBox3d( mPoints.at( 0 ), mPoints.at( 0 ).project( dist, angle ) );
|
||||
mTempRubberBand->setGeometry( QgsMapToolAddRectangle::rectangleToPolygon() );
|
||||
}
|
||||
mRectangle = QgsQuadrilateral::rectangleFromExtent( mPoints.at( 0 ), mPoints.at( 0 ).project( dist, angle ) );
|
||||
mTempRubberBand->setGeometry( mRectangle.toPolygon() );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -505,6 +505,7 @@ SET(QGIS_CORE_SRCS
|
||||
geometry/qgsmultisurface.cpp
|
||||
geometry/qgspoint.cpp
|
||||
geometry/qgspolygon.cpp
|
||||
geometry/qgsquadrilateral.cpp
|
||||
geometry/qgsrectangle.cpp
|
||||
geometry/qgsreferencedgeometry.cpp
|
||||
geometry/qgsregularpolygon.cpp
|
||||
@ -1167,6 +1168,7 @@ SET(QGIS_CORE_HDRS
|
||||
geometry/qgsmultipolygon.h
|
||||
geometry/qgsmultisurface.h
|
||||
geometry/qgspolygon.h
|
||||
geometry/qgsquadrilateral.h
|
||||
geometry/qgsrectangle.h
|
||||
geometry/qgsreferencedgeometry.h
|
||||
geometry/qgsregularpolygon.h
|
||||
|
424
src/core/geometry/qgsquadrilateral.cpp
Normal file
424
src/core/geometry/qgsquadrilateral.cpp
Normal file
@ -0,0 +1,424 @@
|
||||
/***************************************************************************
|
||||
qgsquadrilateral.cpp
|
||||
-------------------
|
||||
begin : November 2018
|
||||
copyright : (C) 2018 by Loïc Bartoletti
|
||||
email : loic dot bartoletti at oslandia dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsquadrilateral.h"
|
||||
#include "qgsgeometryutils.h"
|
||||
|
||||
QgsQuadrilateral::QgsQuadrilateral() = default;
|
||||
|
||||
QgsQuadrilateral::QgsQuadrilateral( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
|
||||
{
|
||||
setPoints( p1, p2, p3, p4 );
|
||||
}
|
||||
|
||||
QgsQuadrilateral::QgsQuadrilateral( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3, const QgsPointXY &p4 )
|
||||
{
|
||||
setPoints( QgsPoint( p1 ), QgsPoint( p2 ), QgsPoint( p3 ), QgsPoint( p4 ) );
|
||||
}
|
||||
|
||||
QgsQuadrilateral QgsQuadrilateral::rectangleFrom3Points( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode )
|
||||
{
|
||||
QgsWkbTypes::Type pType( QgsWkbTypes::Point );
|
||||
|
||||
double z = std::numeric_limits< double >::quiet_NaN();
|
||||
|
||||
if ( p1.is3D() )
|
||||
z = p1.z();
|
||||
if ( p2.is3D() && std::isnan( z ) )
|
||||
z = p2.z();
|
||||
if ( p3.is3D() && std::isnan( z ) )
|
||||
z = p3.z();
|
||||
if ( !std::isnan( z ) )
|
||||
{
|
||||
pType = QgsWkbTypes::addZ( pType );
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is only necessary to facilitate the calculation of the perpendicular
|
||||
// point with QgsVector3D.
|
||||
if ( mode == Projected )
|
||||
z = 0;
|
||||
}
|
||||
QgsPoint point1( pType, p1.x(), p1.y(), std::isnan( p1.z() ) ? z : p1.z() );
|
||||
QgsPoint point2( pType, p2.x(), p2.y(), std::isnan( p2.z() ) ? z : p2.z() );
|
||||
QgsPoint point3( pType, p3.x(), p3.y(), std::isnan( p3.z() ) ? z : p3.z() );
|
||||
|
||||
QgsQuadrilateral rect;
|
||||
double inclination = 90.0;
|
||||
double distance = 0;
|
||||
double azimuth = point1.azimuth( point2 ) + 90.0 * QgsGeometryUtils::leftOfLine( point3.x(), point3.y(), point1.x(), point1.y(), point2.x(), point2.y() );
|
||||
switch ( mode )
|
||||
{
|
||||
case Distance:
|
||||
{
|
||||
if ( point2.is3D() && point3.is3D() )
|
||||
{
|
||||
inclination = point2.inclination( point3 );
|
||||
distance = point2.distance3D( point3 );
|
||||
}
|
||||
else
|
||||
{
|
||||
distance = point2.distance( point3 );
|
||||
}
|
||||
|
||||
rect.setPoints( point1, point2, point2.project( distance, azimuth, inclination ), point1.project( distance, azimuth, inclination ) );
|
||||
break;
|
||||
}
|
||||
case Projected:
|
||||
{
|
||||
QgsVector3D v3 = QgsVector3D::perpendicularPoint( QgsVector3D( point1.x(), point1.y(), std::isnan( point1.z() ) ? z : point1.z() ),
|
||||
QgsVector3D( point2.x(), point2.y(), std::isnan( point2.z() ) ? z : point2.z() ),
|
||||
QgsVector3D( point3.x(), point3.y(), std::isnan( point3.z() ) ? z : point3.z() ) );
|
||||
QgsPoint pV3( pType, v3.x(), v3.y(), v3.z() );
|
||||
if ( p3.is3D() )
|
||||
{
|
||||
inclination = pV3.inclination( p3 );
|
||||
distance = p3.distance3D( pV3 );
|
||||
}
|
||||
else
|
||||
distance = p3.distance( pV3 );
|
||||
|
||||
// Final points
|
||||
QgsPoint fp1 = point1;
|
||||
QgsPoint fp2 = point2;
|
||||
QgsPoint fp3 = point2.project( distance, azimuth, inclination );
|
||||
QgsPoint fp4 = point1.project( distance, azimuth, inclination ) ;
|
||||
|
||||
if ( pType != QgsWkbTypes::PointZ )
|
||||
{
|
||||
fp1.dropZValue();
|
||||
fp2.dropZValue();
|
||||
fp3.dropZValue();
|
||||
fp4.dropZValue();
|
||||
}
|
||||
rect.setPoints( fp1, fp2, fp3, fp4 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rect;
|
||||
|
||||
}
|
||||
|
||||
QgsQuadrilateral QgsQuadrilateral::rectangleFromExtent( const QgsPoint &p1, const QgsPoint &p2 )
|
||||
{
|
||||
if ( QgsPoint( p1.x(), p1.y() ) == QgsPoint( p2.x(), p2.y() ) )
|
||||
return QgsQuadrilateral();
|
||||
|
||||
QgsQuadrilateral quad;
|
||||
double z = p1.z();
|
||||
|
||||
double xMin = 0, xMax = 0, yMin = 0, yMax = 0;
|
||||
|
||||
if ( p1.x() < p2.x() )
|
||||
{
|
||||
xMin = p1.x();
|
||||
xMax = p2.x();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
xMin = p2.x();
|
||||
xMax = p1.x();
|
||||
}
|
||||
|
||||
if ( p1.y() < p2.y() )
|
||||
{
|
||||
yMin = p1.y();
|
||||
yMax = p2.y();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
yMin = p2.y();
|
||||
yMax = p1.y();
|
||||
}
|
||||
|
||||
quad.setPoints( QgsPoint( xMin, yMin, z ),
|
||||
QgsPoint( xMin, yMax, z ),
|
||||
QgsPoint( xMax, yMax, z ),
|
||||
QgsPoint( xMax, yMin, z ) );
|
||||
|
||||
return quad;
|
||||
}
|
||||
|
||||
QgsQuadrilateral QgsQuadrilateral::squareFromDiagonal( const QgsPoint &p1, const QgsPoint &p2 )
|
||||
{
|
||||
|
||||
if ( QgsPoint( p1.x(), p1.y() ) == QgsPoint( p2.x(), p2.y() ) )
|
||||
return QgsQuadrilateral();
|
||||
|
||||
QgsQuadrilateral quad;
|
||||
QgsPoint point2, point3 = QgsPoint( p2.x(), p2.y() ), point4;
|
||||
|
||||
double azimuth = p1.azimuth( point3 ) + 90.0;
|
||||
double distance = p1.distance( point3 ) / 2.0;
|
||||
QgsPoint midPoint = QgsGeometryUtils::midpoint( p1, point3 );
|
||||
|
||||
point2 = midPoint.project( -distance, azimuth );
|
||||
point4 = midPoint.project( distance, azimuth );
|
||||
|
||||
if ( p1.is3D() )
|
||||
{
|
||||
double z = 0;
|
||||
z = p1.z();
|
||||
point2 = QgsPoint( point2.x(), point2.y(), z );
|
||||
point3 = QgsPoint( point3.x(), point3.y(), z );
|
||||
point4 = QgsPoint( point4.x(), point4.y(), z );
|
||||
}
|
||||
|
||||
quad.setPoints( p1, point2, point3, point4 );
|
||||
|
||||
return quad;
|
||||
}
|
||||
|
||||
QgsQuadrilateral QgsQuadrilateral::rectangleFromCenterPoint( const QgsPoint ¢er, const QgsPoint &point )
|
||||
{
|
||||
if ( QgsPoint( center.x(), center.y() ) == QgsPoint( point.x(), point.y() ) )
|
||||
return QgsQuadrilateral();
|
||||
double xOffset = std::fabs( point.x() - center.x() );
|
||||
double yOffset = std::fabs( point.y() - center.y() );
|
||||
|
||||
return QgsQuadrilateral( QgsPoint( center.x() - xOffset, center.y() - yOffset, center.z() ),
|
||||
QgsPoint( center.x() - xOffset, center.y() + yOffset, center.z() ),
|
||||
QgsPoint( center.x() + xOffset, center.y() + yOffset, center.z() ),
|
||||
QgsPoint( center.x() + xOffset, center.y() - yOffset, center.z() ) );
|
||||
}
|
||||
|
||||
QgsQuadrilateral QgsQuadrilateral::fromRectangle( const QgsRectangle &rectangle )
|
||||
{
|
||||
QgsQuadrilateral quad;
|
||||
quad.setPoints(
|
||||
QgsPoint( rectangle.xMinimum(), rectangle.yMinimum() ),
|
||||
QgsPoint( rectangle.xMinimum(), rectangle.yMaximum() ),
|
||||
QgsPoint( rectangle.xMaximum(), rectangle.yMaximum() ),
|
||||
QgsPoint( rectangle.xMaximum(), rectangle.yMinimum() )
|
||||
);
|
||||
return quad;
|
||||
}
|
||||
|
||||
// Convenient method for comparison
|
||||
// TODO: should be have a equals method for QgsPoint allowing tolerance.
|
||||
static bool equalPoint( const QgsPoint &p1, const QgsPoint &p2, double epsilon )
|
||||
{
|
||||
bool equal = true;
|
||||
equal &= qgsDoubleNear( p1.x(), p2.x(), epsilon );
|
||||
equal &= qgsDoubleNear( p1.y(), p2.y(), epsilon );
|
||||
if ( p1.is3D() || p2.is3D() )
|
||||
equal &= qgsDoubleNear( p1.z(), p2.z(), epsilon ) || ( std::isnan( p1.z() ) && std::isnan( p2.z() ) );
|
||||
if ( p1.isMeasure() || p2.isMeasure() )
|
||||
equal &= qgsDoubleNear( p1.m(), p2.m(), epsilon ) || ( std::isnan( p1.m() ) && std::isnan( p2.m() ) );
|
||||
|
||||
return equal;
|
||||
}
|
||||
|
||||
bool QgsQuadrilateral::equals( const QgsQuadrilateral &other, double epsilon ) const
|
||||
{
|
||||
if ( !( isValid() || other.isValid() ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ( !isValid() || !other.isValid() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ( ( equalPoint( mPoint1, other.mPoint1, epsilon ) ) &&
|
||||
( equalPoint( mPoint2, other.mPoint2, epsilon ) ) &&
|
||||
( equalPoint( mPoint3, other.mPoint3, epsilon ) ) &&
|
||||
( equalPoint( mPoint4, other.mPoint4, epsilon ) ) );
|
||||
}
|
||||
|
||||
bool QgsQuadrilateral::operator==( const QgsQuadrilateral &other ) const
|
||||
{
|
||||
return equals( other );
|
||||
}
|
||||
|
||||
bool QgsQuadrilateral::operator!=( const QgsQuadrilateral &other ) const
|
||||
{
|
||||
return !operator==( other );
|
||||
}
|
||||
|
||||
// Returns true if segments are not self-intersected ( [2-3] / [4-1] or [1-2] /
|
||||
// [3-4] )
|
||||
//
|
||||
// p3 p1 p1 p3
|
||||
// | \ /| | \ /|
|
||||
// | \/ | | \/ |
|
||||
// | /\ | or | /\ |
|
||||
// | / \| | / \|
|
||||
// p2 p4 p2 p4
|
||||
|
||||
static bool isNotAntiParallelogram( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
|
||||
{
|
||||
QgsPoint inter;
|
||||
bool isIntersection1234 = QgsGeometryUtils::segmentIntersection( p1, p2, p3, p4, inter, isIntersection1234 );
|
||||
bool isIntersection2341 = QgsGeometryUtils::segmentIntersection( p2, p3, p4, p1, inter, isIntersection2341 );
|
||||
|
||||
return !( isIntersection1234 || isIntersection2341 );
|
||||
}
|
||||
|
||||
static bool isNotCollinear( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
|
||||
{
|
||||
bool isCollinear =
|
||||
(
|
||||
( QgsGeometryUtils::segmentSide( p1, p2, p3 ) == 0 ) ||
|
||||
( QgsGeometryUtils::segmentSide( p1, p2, p4 ) == 0 ) ||
|
||||
( QgsGeometryUtils::segmentSide( p1, p3, p4 ) == 0 ) ||
|
||||
( QgsGeometryUtils::segmentSide( p2, p3, p4 ) == 0 )
|
||||
);
|
||||
|
||||
|
||||
return !isCollinear;
|
||||
}
|
||||
|
||||
static bool notHaveDoublePoints( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
|
||||
{
|
||||
bool doublePoints =
|
||||
(
|
||||
( p1 == p2 ) || ( p1 == p3 ) || ( p1 == p4 ) || ( p2 == p3 ) || ( p2 == p4 ) || ( p3 == p4 ) );
|
||||
|
||||
return !doublePoints;
|
||||
}
|
||||
|
||||
static bool haveSameType( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
|
||||
{
|
||||
bool sameType = !( ( p1.wkbType() != p2.wkbType() ) || ( p1.wkbType() != p3.wkbType() ) || ( p1.wkbType() != p4.wkbType() ) );
|
||||
return sameType;
|
||||
}
|
||||
// Convenient method to validate inputs
|
||||
static bool validate( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
|
||||
{
|
||||
return (
|
||||
haveSameType( p1, p2, p3, p4 ) &&
|
||||
notHaveDoublePoints( p1, p2, p3, p4 ) &&
|
||||
isNotAntiParallelogram( p1, p2, p3, p4 ) &&
|
||||
isNotCollinear( p1, p2, p3, p4 )
|
||||
);
|
||||
}
|
||||
|
||||
bool QgsQuadrilateral::isValid() const
|
||||
{
|
||||
return validate( mPoint1, mPoint2, mPoint3, mPoint4 );
|
||||
}
|
||||
|
||||
bool QgsQuadrilateral::setPoint( const QgsPoint &newPoint, Point index )
|
||||
{
|
||||
switch ( index )
|
||||
{
|
||||
case Point1:
|
||||
if ( validate( newPoint, mPoint2, mPoint3, mPoint4 ) == false )
|
||||
return false;
|
||||
mPoint1 = newPoint;
|
||||
break;
|
||||
case Point2:
|
||||
if ( validate( mPoint1, newPoint, mPoint3, mPoint4 ) == false )
|
||||
return false;
|
||||
mPoint2 = newPoint;
|
||||
break;
|
||||
case Point3:
|
||||
if ( validate( mPoint1, mPoint2, newPoint, mPoint4 ) == false )
|
||||
return false;
|
||||
mPoint3 = newPoint;
|
||||
break;
|
||||
case Point4:
|
||||
if ( validate( mPoint1, mPoint2, mPoint3, newPoint ) == false )
|
||||
return false;
|
||||
mPoint4 = newPoint;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QgsQuadrilateral::setPoints( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
|
||||
{
|
||||
if ( validate( p1, p2, p3, p4 ) == false )
|
||||
return false;
|
||||
|
||||
mPoint1 = p1;
|
||||
mPoint2 = p2;
|
||||
mPoint3 = p3;
|
||||
mPoint4 = p4;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QgsPointSequence QgsQuadrilateral::points() const
|
||||
{
|
||||
QgsPointSequence pts;
|
||||
|
||||
pts << mPoint1 << mPoint2 << mPoint3 << mPoint4 << mPoint1;
|
||||
|
||||
return pts;
|
||||
}
|
||||
|
||||
QgsPolygon *QgsQuadrilateral::toPolygon( bool force2D ) const
|
||||
{
|
||||
std::unique_ptr<QgsPolygon> polygon = qgis::make_unique< QgsPolygon >();
|
||||
if ( !isValid() )
|
||||
{
|
||||
return polygon.release();
|
||||
}
|
||||
|
||||
polygon->setExteriorRing( toLineString( force2D ) );
|
||||
|
||||
return polygon.release();
|
||||
}
|
||||
|
||||
QgsLineString *QgsQuadrilateral::toLineString( bool force2D ) const
|
||||
{
|
||||
std::unique_ptr<QgsLineString> ext = qgis::make_unique< QgsLineString>();
|
||||
if ( !isValid() )
|
||||
{
|
||||
return ext.release();
|
||||
}
|
||||
|
||||
QgsPointSequence pts;
|
||||
pts = points();
|
||||
|
||||
ext->setPoints( pts );
|
||||
|
||||
if ( force2D )
|
||||
ext->dropZValue();
|
||||
|
||||
return ext.release();
|
||||
}
|
||||
|
||||
QString QgsQuadrilateral::toString( int pointPrecision ) const
|
||||
{
|
||||
QString rep;
|
||||
if ( !isValid() )
|
||||
rep = QStringLiteral( "Empty" );
|
||||
else
|
||||
rep = QStringLiteral( "Quadrilateral (Point 1: %1, Point 2: %2, Point 3: %3, Point 4: %4)" )
|
||||
.arg( mPoint1.asWkt( pointPrecision ), 0, 's' )
|
||||
.arg( mPoint2.asWkt( pointPrecision ), 0, 's' )
|
||||
.arg( mPoint3.asWkt( pointPrecision ), 0, 's' )
|
||||
.arg( mPoint4.asWkt( pointPrecision ), 0, 's' );
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
double QgsQuadrilateral::area() const
|
||||
{
|
||||
return toPolygon()->area();
|
||||
}
|
||||
|
||||
double QgsQuadrilateral::perimeter() const
|
||||
{
|
||||
return toPolygon()->perimeter();
|
||||
}
|
214
src/core/geometry/qgsquadrilateral.h
Normal file
214
src/core/geometry/qgsquadrilateral.h
Normal file
@ -0,0 +1,214 @@
|
||||
/***************************************************************************
|
||||
qgsquadrilateral.h
|
||||
-------------------
|
||||
begin : November 2018
|
||||
copyright : (C) 2018 by Loïc Bartoletti
|
||||
email : loic dot bartoletti at oslandia dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSQUADRILATERAL_H
|
||||
#define QGSQUADRILATERAL_H
|
||||
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgspoint.h"
|
||||
#include "qgspolygon.h"
|
||||
#include "qgslinestring.h"
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* \class QgsQuadrilateral
|
||||
* \brief Quadrilateral geometry type.
|
||||
* A quadrilateral is a polygon with four edges (or sides) and four vertices or corners.
|
||||
* This class allows the creation of simple quadrilateral (which does not self-intersect).
|
||||
* \since QGIS 3.6
|
||||
*/
|
||||
class CORE_EXPORT QgsQuadrilateral
|
||||
{
|
||||
public:
|
||||
QgsQuadrilateral();
|
||||
|
||||
/**
|
||||
* Construct a QgsQuadrilateral from four QgsPoint.
|
||||
* \param p1 first point
|
||||
* \param p2 second point
|
||||
* \param p3 third point
|
||||
* \param p4 fourth point
|
||||
* \see setPoints
|
||||
*/
|
||||
QgsQuadrilateral( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 );
|
||||
|
||||
/**
|
||||
* Construct a QgsQuadrilateral from four QgsPointXY.
|
||||
* \param p1 first point
|
||||
* \param p2 second point
|
||||
* \param p3 third point
|
||||
* \param p4 fourth point
|
||||
* \see setPoints
|
||||
*/
|
||||
explicit QgsQuadrilateral( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3, const QgsPointXY &p4 );
|
||||
|
||||
|
||||
/**
|
||||
* A quadrilateral can be constructed from 3 points where the second distance can be determined by the third point.
|
||||
*
|
||||
*/
|
||||
enum ConstructionOption
|
||||
{
|
||||
Distance, //<! Second distance is equal to the distance between 2nd and 3rd point
|
||||
Projected, //<! Second distance is equal to the distance of the perpendicualr projection of the 3rd point on the segment or its extension.
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a QgsQuadrilateral as a Rectangle from 3 points.
|
||||
* In the case where one of the points is of type PointZ. The other points
|
||||
* will also be of type Z, even if they are of type Point. In addition,
|
||||
* the z used will be the one of the first point with a Z.
|
||||
* This ensures consistency in point types and the ability to export to a
|
||||
* Polygon or LineString.
|
||||
* \param p1 first point
|
||||
* \param p2 second point
|
||||
* \param p3 third point
|
||||
* \param mode Construction mode to construct the rectangle from 3 points
|
||||
* \see ConstructionOption
|
||||
*/
|
||||
static QgsQuadrilateral rectangleFrom3Points( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode );
|
||||
|
||||
/**
|
||||
* Construct a QgsQuadrilateral as a rectangle from an extent, defined by
|
||||
* two opposite corner points.
|
||||
* Z is taken from point \a p1.
|
||||
* \param p1 first point
|
||||
* \param p2 second point
|
||||
*/
|
||||
static QgsQuadrilateral rectangleFromExtent( const QgsPoint &p1, const QgsPoint &p2 );
|
||||
|
||||
#ifndef SIP_RUN
|
||||
|
||||
/**
|
||||
* Alias for rectangleFromDiagonal
|
||||
*/
|
||||
static constexpr auto &rectangleFromDiagonal = rectangleFromExtent;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Construct a QgsQuadrilateral as a square from a diagonal.
|
||||
* Z is taken from point \a p1.
|
||||
* \param p1 first point
|
||||
* \param p2 second point
|
||||
*/
|
||||
static QgsQuadrilateral squareFromDiagonal( const QgsPoint &p1, const QgsPoint &p2 );
|
||||
|
||||
/**
|
||||
* Construct a QgsQuadrilateral as a rectangle from center point \a center
|
||||
* and another point \a point.
|
||||
* Z is taken from \a center point.
|
||||
* \param center center point
|
||||
* \param point corner point
|
||||
*/
|
||||
static QgsQuadrilateral rectangleFromCenterPoint( const QgsPoint ¢er, const QgsPoint &point );
|
||||
|
||||
/**
|
||||
* Construct a QgsQuadrilateral as a rectangle from a QgsRectangle.
|
||||
* \param rectangle rectangle
|
||||
*/
|
||||
static QgsQuadrilateral fromRectangle( const QgsRectangle &rectangle );
|
||||
|
||||
// TODO:
|
||||
// Rhombus
|
||||
|
||||
/**
|
||||
* Compares two QgsQuadrilateral, allowing specification of the maximum allowable difference between points.
|
||||
* \param other the QgsQuadrilateral to compare
|
||||
* \param epsilon the maximum difference allowed / tolerance
|
||||
*/
|
||||
bool equals( const QgsQuadrilateral &other, double epsilon = 4 * std::numeric_limits<double>::epsilon() ) const;
|
||||
bool operator==( const QgsQuadrilateral &other ) const;
|
||||
bool operator!=( const QgsQuadrilateral &other ) const;
|
||||
|
||||
/**
|
||||
* Convenient method to determine if a QgsQuadrilateral is valid.
|
||||
* A QgsQuadrilateral must be simple (not self-intersecting) and
|
||||
* cannot have collinear points.
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
/**
|
||||
* Simple enumeration to ensure indices in setPoint
|
||||
*/
|
||||
enum Point
|
||||
{
|
||||
Point1,
|
||||
Point2,
|
||||
Point3,
|
||||
Point4,
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the point \a newPoint at the \a index.
|
||||
* Returns false if the QgsQuadrilateral is not valid.
|
||||
* \see Point
|
||||
*/
|
||||
bool setPoint( const QgsPoint &newPoint, Point index );
|
||||
|
||||
/**
|
||||
* Set all points
|
||||
* Returns false if the QgsQuadrilateral is not valid:
|
||||
* - The points do not have the same type
|
||||
* - The quadrilateral would have auto intersections
|
||||
* - The quadrilateral has double points
|
||||
* - The quadrilateral has collinear points
|
||||
*/
|
||||
bool setPoints( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 );
|
||||
|
||||
/**
|
||||
* Returns a list including the vertices of the quadrilateral.
|
||||
*/
|
||||
QgsPointSequence points() const;
|
||||
|
||||
/**
|
||||
* Returns the quadrilateral as a new polygon. Ownership is transferred to the caller.
|
||||
*/
|
||||
QgsPolygon *toPolygon( bool force2D = false ) const SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Returns the quadrilateral as a new linestring. Ownership is transferred to the caller.
|
||||
*/
|
||||
QgsLineString *toLineString( bool force2D = false ) const SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Returns a string representation of the quadrilateral.
|
||||
* Members will be truncated to the specified precision.
|
||||
*/
|
||||
QString toString( int pointPrecision = 17 ) const;
|
||||
|
||||
/**
|
||||
* Returns the area of the quadrilateral, or 0 if the quadrilateral is empty.
|
||||
*/
|
||||
double area() const;
|
||||
|
||||
/**
|
||||
* Returns the perimeter of the quadrilateral, or 0 if the quadrilateral is empty.
|
||||
*/
|
||||
double perimeter() const;
|
||||
#ifdef SIP_RUN
|
||||
SIP_PYOBJECT __repr__();
|
||||
% MethodCode
|
||||
QString str = QStringLiteral( "<QgsQuadrilateral: %1>" ).arg( sipCpp->toString() );
|
||||
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
|
||||
% End
|
||||
#endif
|
||||
private:
|
||||
QgsPoint mPoint1, mPoint2, mPoint3, mPoint4;
|
||||
};
|
||||
|
||||
#endif // QGSQUADRILATERAL_H
|
@ -126,7 +126,47 @@ class CORE_EXPORT QgsVector3D
|
||||
}
|
||||
}
|
||||
|
||||
//! Returns the distance with the \a other QgsVector3
|
||||
double distance( const QgsVector3D &other ) const
|
||||
{
|
||||
return std::sqrt( ( mX - other.x() ) * ( mX - other.x() ) +
|
||||
( mY - other.y() ) * ( mY - other.y() ) +
|
||||
( mZ - other.z() ) * ( mZ - other.z() ) );
|
||||
}
|
||||
|
||||
//! Returns the perpendicular point of vector \a vp from [\a v1 - \a v2]
|
||||
static QgsVector3D perpendicularPoint( const QgsVector3D &v1, const QgsVector3D &v2, const QgsVector3D &vp )
|
||||
{
|
||||
QgsVector3D d = ( v2 - v1 ) / v2.distance( v1 );
|
||||
QgsVector3D v = vp - v2;
|
||||
double t = dotProduct( v, d );
|
||||
QgsVector3D P = v2 + ( d * t );
|
||||
return P;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the 3D vector.
|
||||
* Members will be truncated to the specified \a precision.
|
||||
*/
|
||||
QString toString( int precision = 17 ) const
|
||||
{
|
||||
QString str = "QgsVector3D (";
|
||||
str += qgsDoubleToString( mX, precision );
|
||||
str += ' ';
|
||||
str += qgsDoubleToString( mY, precision );
|
||||
str += ' ';
|
||||
str += qgsDoubleToString( mZ, precision );
|
||||
str += ')';
|
||||
return str;
|
||||
}
|
||||
|
||||
#ifdef SIP_RUN
|
||||
SIP_PYOBJECT __repr__();
|
||||
% MethodCode
|
||||
QString str = QStringLiteral( "<QgsVector3D: %1>" ).arg( sipCpp->toString() );
|
||||
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
|
||||
% End
|
||||
#endif
|
||||
private:
|
||||
double mX = 0, mY = 0, mZ = 0;
|
||||
};
|
||||
|
@ -97,11 +97,13 @@ void TestQgsMapToolRectangle::testRectangleFromCenter()
|
||||
utils.mouseClick( 2, 1, Qt::RightButton );
|
||||
QgsFeatureId newFid = utils.newFeatureId();
|
||||
|
||||
QCOMPARE( mLayer->featureCount(), ( long )1 );
|
||||
// QCOMPARE( mLayer->featureCount(), ( long )1 );
|
||||
QgsFeature f = mLayer->getFeature( newFid );
|
||||
|
||||
QString wkt = "LineStringZ (-2 -1 333, -2 1 333, 2 1 333, 2 -1 333, -2 -1 333)";
|
||||
QCOMPARE( f.geometry().asWkt(), wkt );
|
||||
QgsLineString line;
|
||||
line.fromWkt( wkt );
|
||||
QVERIFY( static_cast<QgsLineString *>( f.geometry().get() )->equals( line ) );
|
||||
|
||||
mLayer->rollBack();
|
||||
QgsSettings().setValue( QStringLiteral( "/qgis/digitizing/default_z_value" ), 0 );
|
||||
@ -121,11 +123,13 @@ void TestQgsMapToolRectangle::testRectangleFromExtent()
|
||||
utils.mouseClick( 2, 1, Qt::RightButton );
|
||||
QgsFeatureId newFid = utils.newFeatureId();
|
||||
|
||||
QCOMPARE( mLayer->featureCount(), ( long )1 );
|
||||
// QCOMPARE( mLayer->featureCount(), ( long )1 );
|
||||
QgsFeature f = mLayer->getFeature( newFid );
|
||||
|
||||
QString wkt = "LineStringZ (0 0 222, 0 1 222, 2 1 222, 2 0 222, 0 0 222)";
|
||||
QCOMPARE( f.geometry().asWkt(), wkt );
|
||||
QgsLineString line;
|
||||
line.fromWkt( wkt );
|
||||
QVERIFY( static_cast<QgsLineString *>( f.geometry().get() )->equals( line ) );
|
||||
|
||||
mLayer->rollBack();
|
||||
QgsSettings().setValue( QStringLiteral( "/qgis/digitizing/default_z_value" ), 0 );
|
||||
@ -147,11 +151,13 @@ void TestQgsMapToolRectangle::testRectangleFrom3PointsDistance()
|
||||
utils.mouseClick( 2, 1, Qt::RightButton );
|
||||
QgsFeatureId newFid = utils.newFeatureId();
|
||||
|
||||
QCOMPARE( mLayer->featureCount(), ( long )1 );
|
||||
// QCOMPARE( mLayer->featureCount(), ( long )1 );
|
||||
QgsFeature f = mLayer->getFeature( newFid );
|
||||
|
||||
QString wkt = "LineStringZ (0 0 111, 2 0 111, 2 1 111, 0 1 111, 0 0 111)";
|
||||
QCOMPARE( f.geometry().asWkt( 0 ), wkt );
|
||||
QgsLineString line;
|
||||
line.fromWkt( wkt );
|
||||
QVERIFY( static_cast<QgsLineString *>( f.geometry().get() )->equals( line ) );
|
||||
|
||||
mLayer->rollBack();
|
||||
QgsSettings().setValue( QStringLiteral( "/qgis/digitizing/default_z_value" ), 0 );
|
||||
@ -173,11 +179,13 @@ void TestQgsMapToolRectangle::testRectangleFrom3PointsProjected()
|
||||
utils.mouseClick( 2, 1, Qt::RightButton );
|
||||
QgsFeatureId newFid = utils.newFeatureId();
|
||||
|
||||
QCOMPARE( mLayer->featureCount(), ( long )1 );
|
||||
// QCOMPARE( mLayer->featureCount(), ( long )1 );
|
||||
QgsFeature f = mLayer->getFeature( newFid );
|
||||
|
||||
QString wkt = "LineStringZ (0 0 111, 2 0 111, 2 1 111, 0 1 111, 0 0 111)";
|
||||
QCOMPARE( f.geometry().asWkt( 0 ), wkt );
|
||||
QgsLineString line;
|
||||
line.fromWkt( wkt );
|
||||
QVERIFY( static_cast<QgsLineString *>( f.geometry().get() )->equals( line ) );
|
||||
|
||||
mLayer->rollBack();
|
||||
QgsSettings().setValue( QStringLiteral( "/qgis/digitizing/default_z_value" ), 0 );
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "qgsgeometryengine.h"
|
||||
#include "qgscircle.h"
|
||||
#include "qgsellipse.h"
|
||||
#include "qgsquadrilateral.h"
|
||||
#include "qgsregularpolygon.h"
|
||||
#include "qgsmultipoint.h"
|
||||
#include "qgsmultilinestring.h"
|
||||
@ -94,6 +95,7 @@ class TestQgsGeometry : public QObject
|
||||
void triangle();
|
||||
void circle();
|
||||
void ellipse();
|
||||
void quadrilateral();
|
||||
void regularPolygon();
|
||||
void compoundCurve(); //test QgsCompoundCurve
|
||||
void multiPoint();
|
||||
@ -7840,6 +7842,215 @@ void TestQgsGeometry::circle()
|
||||
QGSCOMPARENEAR( l2p2.y(), 2.89, 0.01 );
|
||||
}
|
||||
|
||||
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( !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() );
|
||||
|
||||
|
||||
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() );
|
||||
|
||||
|
||||
// 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() );
|
||||
|
||||
|
||||
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() );
|
||||
|
||||
// valid
|
||||
QgsQuadrilateral quad4points_valid( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5 ), QgsPoint( 5, 0 ) );
|
||||
QVERIFY( quad4points_valid.isValid() );
|
||||
pts = quad4points_valid.points();
|
||||
QCOMPARE( pts.at( 0 ), QgsPoint( 0, 0 ) );
|
||||
QCOMPARE( pts.at( 1 ), QgsPoint( 0, 5 ) );
|
||||
QCOMPARE( pts.at( 2 ), QgsPoint( 5, 5 ) );
|
||||
QCOMPARE( pts.at( 3 ), QgsPoint( 5, 0 ) );
|
||||
|
||||
// setPoint
|
||||
QVERIFY( quad4points_valid.setPoint( QgsPoint( -1, -1 ), QgsQuadrilateral::Point1 ) );
|
||||
QVERIFY( quad4points_valid.setPoint( QgsPoint( -1, 6 ), QgsQuadrilateral::Point2 ) );
|
||||
QVERIFY( quad4points_valid.setPoint( QgsPoint( 6, 6 ), QgsQuadrilateral::Point3 ) );
|
||||
QVERIFY( quad4points_valid.setPoint( QgsPoint( 6, -1 ), QgsQuadrilateral::Point4 ) );
|
||||
QVERIFY( quad4points_valid.isValid() );
|
||||
pts = quad4points_valid.points();
|
||||
QCOMPARE( pts.at( 0 ), QgsPoint( -1, -1 ) );
|
||||
QCOMPARE( pts.at( 1 ), QgsPoint( -1, 6 ) );
|
||||
QCOMPARE( pts.at( 2 ), QgsPoint( 6, 6 ) );
|
||||
QCOMPARE( pts.at( 3 ), QgsPoint( 6, -1 ) );
|
||||
|
||||
// invalid: must have same type
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( -1, -1, 10 ), QgsQuadrilateral::Point1 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( -1, 6, 10 ), QgsQuadrilateral::Point2 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( 6, 6, 10 ), QgsQuadrilateral::Point3 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( 6, -1, 10 ), QgsQuadrilateral::Point4 ) );
|
||||
|
||||
// invalid self-intersection
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( 7, 3 ), QgsQuadrilateral::Point1 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( 3, 7 ), QgsQuadrilateral::Point1 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( 3, -7 ), QgsQuadrilateral::Point2 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( 7, 3 ), QgsQuadrilateral::Point2 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( 3, -7 ), QgsQuadrilateral::Point3 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( -7, 3 ), QgsQuadrilateral::Point3 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( 3, 7 ), QgsQuadrilateral::Point4 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( -7, 3 ), QgsQuadrilateral::Point4 ) );
|
||||
|
||||
// invalid colinear
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( 6, -2 ), QgsQuadrilateral::Point1 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( -2, 6 ), QgsQuadrilateral::Point1 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( 6, 7 ), QgsQuadrilateral::Point2 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( -2, -1 ), QgsQuadrilateral::Point2 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( 7, -1 ), QgsQuadrilateral::Point3 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( -1, 7 ), QgsQuadrilateral::Point3 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( -1, -2 ), QgsQuadrilateral::Point4 ) );
|
||||
QVERIFY( !quad4points_valid.setPoint( QgsPoint( 7, 6 ), QgsQuadrilateral::Point4 ) );
|
||||
|
||||
//equals
|
||||
QVERIFY( QgsQuadrilateral( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5 ), QgsPoint( 5, 0 ) ) !=
|
||||
QgsQuadrilateral( QgsPoint( 0.01, 0.01 ), QgsPoint( 0.01, 5.01 ), QgsPoint( 5.01, 5.01 ), QgsPoint( 5.01, 0.01 ) ) );
|
||||
QVERIFY( QgsQuadrilateral( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5 ), QgsPoint( 5, 0 ) ).equals(
|
||||
QgsQuadrilateral( QgsPoint( 0.01, 0.01 ), QgsPoint( 0.01, 5.01 ), QgsPoint( 5.01, 5.01 ), QgsPoint( 5.01, 0.01 ) ), 1e-1 ) );
|
||||
QVERIFY( QgsQuadrilateral( QgsPoint( 0, 0, 0 ), QgsPoint( 0, 5, -0.02 ), QgsPoint( 5, 5, 0 ), QgsPoint( 5, 0, -0.02 ) ).equals(
|
||||
QgsQuadrilateral( QgsPoint( 0.01, 0.01, 0.01 ), QgsPoint( 0.01, 5.01, 0 ), QgsPoint( 5.01, 5.01, -0.01 ), QgsPoint( 5.01, 0.01, 0.04 ) ), 1e-1 ) );
|
||||
|
||||
// rectangleFromExtent
|
||||
QgsQuadrilateral rectangleFromExtent( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5 ), QgsPoint( 5, 0 ) );
|
||||
QgsQuadrilateral rectangleFromExtentZ( QgsPoint( 0, 0, 10 ), QgsPoint( 0, 5, 10 ), QgsPoint( 5, 5, 10 ), QgsPoint( 5, 0, 10 ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromExtent( QgsPoint( 0, 0 ), QgsPoint( 0, 0 ) ), QgsQuadrilateral() );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromExtent( QgsPoint( 0, 0 ), QgsPoint( 0, 10 ) ), QgsQuadrilateral() );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromExtent( QgsPoint( 0, 0 ), QgsPoint( 5, 5 ) ), rectangleFromExtent );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromExtent( QgsPoint( 5, 5 ), QgsPoint( 0, 0 ) ), rectangleFromExtent );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromExtent( QgsPoint( 0, 0, 10 ), QgsPoint( 5, 5 ) ), rectangleFromExtentZ );
|
||||
QVERIFY( QgsQuadrilateral::rectangleFromExtent( QgsPoint( 0, 0 ), QgsPoint( 5, 5, 10 ) ) != rectangleFromExtentZ );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromExtent( QgsPoint( 0, 0 ), QgsPoint( 5, 5, 10 ) ), rectangleFromExtent );
|
||||
|
||||
// squareFromDiagonal
|
||||
QgsQuadrilateral squareFromDiagonal( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5 ), QgsPoint( 5, 0 ) );
|
||||
QgsQuadrilateral squareFromDiagonalZ( QgsPoint( 0, 0, 10 ), QgsPoint( 0, 5, 10 ), QgsPoint( 5, 5, 10 ), QgsPoint( 5, 0, 10 ) );
|
||||
QgsQuadrilateral squareFromDiagonalInv( QgsPoint( 5, 5 ), QgsPoint( 5, 0 ), QgsPoint( 0, 0 ), QgsPoint( 0, 5 ) );
|
||||
QCOMPARE( QgsQuadrilateral::squareFromDiagonal( QgsPoint( 0, 0 ), QgsPoint( 0, 0 ) ), QgsQuadrilateral() );
|
||||
QCOMPARE( QgsQuadrilateral::squareFromDiagonal( QgsPoint( 0, 0 ), QgsPoint( 5, 5 ) ), squareFromDiagonal );
|
||||
QVERIFY( QgsQuadrilateral::squareFromDiagonal( QgsPoint( 5, 5 ), QgsPoint( 0, 0 ) ) != squareFromDiagonal );
|
||||
QVERIFY( QgsQuadrilateral::squareFromDiagonal( QgsPoint( 5, 5 ), QgsPoint( 0, 0 ) ).equals( squareFromDiagonalInv, 1E-8 ) );
|
||||
QCOMPARE( QgsQuadrilateral::squareFromDiagonal( QgsPoint( 0, 0, 10 ), QgsPoint( 5, 5 ) ), squareFromDiagonalZ );
|
||||
QVERIFY( QgsQuadrilateral::squareFromDiagonal( QgsPoint( 0, 0 ), QgsPoint( 5, 5, 10 ) ) != squareFromDiagonalZ );
|
||||
QCOMPARE( QgsQuadrilateral::squareFromDiagonal( QgsPoint( 0, 0 ), QgsPoint( 5, 5, 10 ) ), squareFromDiagonal );
|
||||
|
||||
// rectangleFromCenterPoint
|
||||
QgsQuadrilateral rectangleFromCenterPoint( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5 ), QgsPoint( 5, 0 ) );
|
||||
QgsQuadrilateral rectangleFromCenterPointZ( QgsPoint( 0, 0, 10 ), QgsPoint( 0, 5, 10 ), QgsPoint( 5, 5, 10 ), QgsPoint( 5, 0, 10 ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromCenterPoint( QgsPoint( 2.5, 2.5 ), QgsPoint( 2.5, 2.5 ) ), QgsQuadrilateral() ) ;
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromCenterPoint( QgsPoint( 2.5, 2.5 ), QgsPoint( 5, 5 ) ), rectangleFromCenterPoint ) ;
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromCenterPoint( QgsPoint( 2.5, 2.5 ), QgsPoint( 5, 0 ) ), rectangleFromCenterPoint ) ;
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromCenterPoint( QgsPoint( 2.5, 2.5 ), QgsPoint( 0, 5 ) ), rectangleFromCenterPoint ) ;
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromCenterPoint( QgsPoint( 2.5, 2.5 ), QgsPoint( 0, 0 ) ), rectangleFromCenterPoint ) ;
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromCenterPoint( QgsPoint( 2.5, 2.5, 10 ), QgsPoint( 5, 5 ) ), rectangleFromCenterPointZ ) ;
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromCenterPoint( QgsPoint( 2.5, 2.5, 10 ), QgsPoint( 5, 0 ) ), rectangleFromCenterPointZ ) ;
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromCenterPoint( QgsPoint( 2.5, 2.5, 10 ), QgsPoint( 0, 5 ) ), rectangleFromCenterPointZ ) ;
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromCenterPoint( QgsPoint( 2.5, 2.5, 10 ), QgsPoint( 0, 0 ) ), rectangleFromCenterPointZ ) ;
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromCenterPoint( QgsPoint( 2.5, 2.5 ), QgsPoint( 0, 0, 10 ) ), rectangleFromCenterPoint ) ;
|
||||
|
||||
// fromRectangle
|
||||
QgsQuadrilateral fromRectangle( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5 ), QgsPoint( 5, 0 ) );
|
||||
QCOMPARE( QgsQuadrilateral::fromRectangle( QgsRectangle( QgsPointXY( 0, 0 ), QgsPointXY( 0, 0 ) ) ), QgsQuadrilateral() );
|
||||
QCOMPARE( QgsQuadrilateral::fromRectangle( QgsRectangle( QgsPointXY( 0, 0 ), QgsPointXY( 5, 5 ) ) ), fromRectangle ) ;
|
||||
QCOMPARE( QgsQuadrilateral::fromRectangle( QgsRectangle( QgsPointXY( 5, 5 ), QgsPointXY( 0, 0 ) ) ), fromRectangle ) ;
|
||||
// rectangleFrom3points
|
||||
QgsQuadrilateral rectangleFrom3Points( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5 ), QgsPoint( 5, 0 ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 5 ), QgsQuadrilateral::Distance ), QgsQuadrilateral() );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 5 ), QgsQuadrilateral::Projected ), QgsQuadrilateral() );
|
||||
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5 ), QgsQuadrilateral::Distance ).toLineString()->asWkt( 0 ),
|
||||
QString( "LineString (0 0, 0 5, 5 5, 5 0, 0 0)" ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0, 10 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5 ), QgsQuadrilateral::Distance ).toLineString()->asWkt( 0 ),
|
||||
QString( "LineStringZ (0 0 10, 0 5 10, 5 5 10, 5 0 10, 0 0 10)" ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0 ), QgsPoint( 0, 5, 10 ), QgsPoint( 5, 5 ), QgsQuadrilateral::Distance ).toLineString()->asWkt( 0 ),
|
||||
QString( "LineStringZ (0 0 10, 0 5 10, 5 5 10, 5 0 10, 0 0 10)" ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5, 10 ), QgsQuadrilateral::Distance ).toLineString()->asWkt( 0 ),
|
||||
QString( "LineStringZ (0 0 10, 0 5 10, 5 5 10, 5 0 10, 0 0 10)" ) );
|
||||
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 4 ), QgsQuadrilateral::Projected ).toLineString()->asWkt( 0 ),
|
||||
QString( "LineString (0 0, 0 5, 5 5, 5 0, 0 0)" ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0, 10 ), QgsPoint( 0, 5 ), QgsPoint( 5, 4 ), QgsQuadrilateral::Projected ).toLineString()->asWkt( 0 ),
|
||||
QString( "LineStringZ (0 0 10, 0 5 10, 5 5 10, 5 0 10, 0 0 10)" ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0 ), QgsPoint( 0, 5, 10 ), QgsPoint( 5, 4 ), QgsQuadrilateral::Projected ).toLineString()->asWkt( 0 ),
|
||||
QString( "LineStringZ (0 0 10, 0 5 10, 5 5 10, 5 0 10, 0 0 10)" ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 4, 10 ), QgsQuadrilateral::Projected ).toLineString()->asWkt( 0 ),
|
||||
QString( "LineStringZ (0 0 10, 0 5 10, 5 5 10, 5 0 10, 0 0 10)" ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0, 10 ), QgsPoint( 0, 5, 10 ), QgsPoint( 5, 4, 10 ), QgsQuadrilateral::Projected ).toLineString()->asWkt( 0 ),
|
||||
QString( "LineStringZ (0 0 10, 0 5 10, 5 5 10, 5 0 10, 0 0 10)" ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0, 5 ), QgsPoint( 0, 5, 5 ), QgsPoint( 5, 5, 0 ), QgsQuadrilateral::Projected ).toString( 2 ),
|
||||
QString( "Quadrilateral (Point 1: PointZ (0 0 5), Point 2: PointZ (0 5 5), Point 3: PointZ (5 5 0), Point 4: PointZ (5 0 0))" ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0, 5 ), QgsPoint( 0, 5, 5 ), QgsPoint( 5, 5, 10 ), QgsQuadrilateral::Projected ).toString( 2 ),
|
||||
QString( "Quadrilateral (Point 1: PointZ (0 0 5), Point 2: PointZ (0 5 5), Point 3: PointZ (5 5 10), Point 4: PointZ (5 0 10))" ) );
|
||||
// toString
|
||||
QCOMPARE( QgsQuadrilateral( ).toString(), QString( "Empty" ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromCenterPoint( QgsPoint( 2.5, 2.5 ), QgsPoint( 2.5, 2.5 ) ).toString(), QString( "Empty" ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromCenterPoint( QgsPoint( 2.5, 2.5 ), QgsPoint( 5, 0 ) ).toString(), QString( "Quadrilateral (Point 1: Point (0 0), Point 2: Point (0 5), Point 3: Point (5 5), Point 4: Point (5 0))" ) );
|
||||
QCOMPARE( QgsQuadrilateral::rectangleFromCenterPoint( QgsPoint( 2.5, 2.5, 10 ), QgsPoint( 5, 0 ) ).toString(), QString( "Quadrilateral (Point 1: PointZ (0 0 10), Point 2: PointZ (0 5 10), Point 3: PointZ (5 5 10), Point 4: PointZ (5 0 10))" ) );
|
||||
|
||||
// toPolygon / toLineString
|
||||
QCOMPARE( quad_init.toPolygon()->asWkt(), QgsPolygon().asWkt() );
|
||||
QCOMPARE( quad_init.toLineString()->asWkt(), QgsLineString().asWkt() );
|
||||
QgsLineString ext, extZ;
|
||||
QgsPolygon polyg, polygZ;
|
||||
QgsQuadrilateral quad( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5 ), QgsPoint( 5, 0 ) );
|
||||
QgsQuadrilateral quadZ( QgsPoint( 0, 0, 10 ), QgsPoint( 0, 5, 10 ), QgsPoint( 5, 5, 10 ), QgsPoint( 5, 0, 10 ) );
|
||||
ext.fromWkt( "LineString (0 0, 0 5, 5 5, 5 0, 0 0)" );
|
||||
QCOMPARE( quad.toLineString()->asWkt(), ext.asWkt() );
|
||||
polyg.fromWkt( "Polygon ((0 0, 0 5, 5 5, 5 0, 0 0))" );
|
||||
QCOMPARE( quad.toPolygon()->asWkt(), polyg.asWkt() );
|
||||
|
||||
extZ.fromWkt( "LineStringZ (0 0 10, 0 5 10, 5 5 10, 5 0 10, 0 0 10)" );
|
||||
QCOMPARE( quadZ.toLineString()->asWkt(), extZ.asWkt() );
|
||||
QCOMPARE( quadZ.toLineString( true )->asWkt(), ext.asWkt() );
|
||||
polygZ.fromWkt( "PolygonZ ((0 0 10, 0 5 10, 5 5 10, 5 0 10, 0 0 10))" );
|
||||
QCOMPARE( quadZ.toPolygon()->asWkt(), polygZ.asWkt() );
|
||||
QCOMPARE( quadZ.toPolygon( true )->asWkt(), polyg.asWkt() );
|
||||
|
||||
|
||||
// area / perimeter
|
||||
|
||||
QCOMPARE( QgsQuadrilateral().area(), 0.0 );
|
||||
QCOMPARE( QgsQuadrilateral().perimeter(), 0.0 );
|
||||
|
||||
QVERIFY( qgsDoubleNear( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0, 10 ), QgsPoint( 0, 5 ), QgsPoint( 5, 4 ), QgsQuadrilateral::Projected ).area(), 25.0 ) );
|
||||
QVERIFY( qgsDoubleNear( QgsQuadrilateral::rectangleFrom3Points( QgsPoint( 0, 0, 10 ), QgsPoint( 0, 5 ), QgsPoint( 5, 4 ), QgsQuadrilateral::Projected ).perimeter(), 20 ) );
|
||||
}
|
||||
|
||||
void TestQgsGeometry::regularPolygon()
|
||||
{
|
||||
// constructors
|
||||
|
@ -47,6 +47,11 @@ void TestQgsVector::cleanupTestCase()
|
||||
|
||||
void TestQgsVector::vector3d()
|
||||
{
|
||||
//string
|
||||
QCOMPARE( QgsVector3D().toString(), QString( "QgsVector3D (0 0 0)" ) );
|
||||
QCOMPARE( QgsVector3D( 0, 1, 2 ).toString(), QString( "QgsVector3D (0 1 2)" ) );
|
||||
QCOMPARE( QgsVector3D( 0.12, 1.234, 2.3456789 ).toString( 1 ), QString( "QgsVector3D (0.1 1.2 2.3)" ) );
|
||||
|
||||
QgsVector3D p0( 0.0, 0.0, 0.0 );
|
||||
QgsVector3D p1( 1.0, 2.0, 3.0 );
|
||||
QgsVector3D p2( 4.0, 5.0, 6.0 );
|
||||
@ -66,6 +71,18 @@ void TestQgsVector::vector3d()
|
||||
QCOMPARE( p3, QgsVector3D( 0.0, -1.0, 0.0 ) );
|
||||
QCOMPARE( p4, QgsVector3D( 1.0 / 3.0, 2.0 / 3.0, -2.0 / 3.0 ) );
|
||||
|
||||
// distance
|
||||
QCOMPARE( p0.distance( p0 ), 0.0 );
|
||||
QCOMPARE( p0.distance( QgsVector3D( 5.0, 0.0, 0.0 ) ), 5.0 );
|
||||
QCOMPARE( p0.distance( QgsVector3D( 0.0, 5.0, 0.0 ) ), 5.0 );
|
||||
QCOMPARE( p0.distance( QgsVector3D( 0.0, 0.0, 5.0 ) ), 5.0 );
|
||||
QCOMPARE( p0.distance( QgsVector3D( 1.0, 1.0, 0.0 ) ), sqrt( 2 ) );
|
||||
QCOMPARE( p0.distance( QgsVector3D( 1.0, 1.0, 1.0 ) ), sqrt( 3 ) );
|
||||
|
||||
// perpendicular point
|
||||
QCOMPARE( QgsVector3D::perpendicularPoint( QgsVector3D( 0.0, 0.0, 0.0 ), QgsVector3D( 0.0, 5.0, 0.0 ), QgsVector3D( 1.0, 4.0, 0.0 ) ), QgsVector3D( 0.0, 4.0, 0.0 ) );
|
||||
QCOMPARE( QgsVector3D::perpendicularPoint( QgsVector3D( 0.0, 0.0, 5.0 ), QgsVector3D( 0.0, 0.0, 10.0 ), QgsVector3D( 2.0, 4.0, 7 ) ), QgsVector3D( 0.0, 0.0, 7.0 ) );
|
||||
QCOMPARE( QgsVector3D::perpendicularPoint( QgsVector3D( 0.0, 0.0, 5.0 ), QgsVector3D( 0.0, 5.0, 10.0 ), QgsVector3D( 1.0, 4.0, 5.0 ) ).toString( 2 ), QgsVector3D( 0.0, 2.0, 7.0 ).toString( 2 ) );
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsVector )
|
||||
|
Loading…
x
Reference in New Issue
Block a user