Merge pull request #4720 from nyalldawson/ref_geom

Template based referenced geometry class
This commit is contained in:
Nyall Dawson 2017-09-06 13:51:31 +10:00 committed by GitHub
commit 2e20b6f69b
13 changed files with 492 additions and 2 deletions

View File

@ -268,6 +268,7 @@
%Include geometry/qgsmultisurface.sip
%Include geometry/qgspolygon.sip
%Include geometry/qgsrectangle.sip
%Include geometry/qgsreferencedgeometry.sip
%Include geometry/qgsregularpolygon.sip
%Include geometry/qgstriangle.sip
%Include geometry/qgssurface.sip

View File

@ -42,6 +42,8 @@ Construct a rectangle from a QRectF. The rectangle is normalized after construct
Copy constructor
%End
~QgsRectangle();
void set( const QgsPointXY &p1, const QgsPointXY &p2 );
%Docstring
Sets the rectangle from two QgsPoints. The rectangle is
@ -315,9 +317,15 @@ Copy constructor
:rtype: QgsBox3d
%End
operator QVariant() const;
%Docstring
Allows direct construction of QVariants from rectangles.
%End
};
/************************************************************************
* This file has been generated automatically from *
* *

View File

@ -0,0 +1,120 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/geometry/qgsreferencedgeometry.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsReferencedGeometryBase
{
%Docstring
A base class for geometry primitives which are stored with an associated reference system.
QgsReferencedGeometryBase classes represent some form of geometry primitive
(such as rectangles) which have an optional coordinate reference system
associated with them.
.. versionadded:: 3.0
.. seealso:: QgsReferencedRectangle
%End
%TypeHeaderCode
#include "qgsreferencedgeometry.h"
%End
public:
QgsReferencedGeometryBase( const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() );
%Docstring
Constructor for QgsReferencedGeometryBase, with the specified ``crs``.
%End
QgsCoordinateReferenceSystem crs() const;
%Docstring
Returns the associated coordinate reference system, or an invalid CRS if
no reference system is set.
.. seealso:: setCrs()
:rtype: QgsCoordinateReferenceSystem
%End
void setCrs( const QgsCoordinateReferenceSystem &crs );
%Docstring
Sets the associated ``crs``. Set to an invalid CRS if
no reference system is required.
.. seealso:: crs()
%End
};
class QgsReferencedRectangle : QgsRectangle, QgsReferencedGeometryBase
{
%Docstring
A QgsRectangle with associated coordinate reference system.
.. versionadded:: 3.0
%End
%TypeHeaderCode
#include "qgsreferencedgeometry.h"
%End
public:
QgsReferencedRectangle( const QgsRectangle &rectangle, const QgsCoordinateReferenceSystem &crs );
%Docstring
Constructor for QgsReferencedRectangle, with the specified initial ``rectangle``
and ``crs``.
%End
QgsReferencedRectangle();
%Docstring
Constructor for QgsReferencedRectangle.
%End
operator QVariant() const;
%Docstring
Allows direct construction of QVariants from rectangle.
%End
};
class QgsReferencedPointXY : QgsPointXY, QgsReferencedGeometryBase
{
%Docstring
A QgsPointXY with associated coordinate reference system.
.. versionadded:: 3.0
%End
%TypeHeaderCode
#include "qgsreferencedgeometry.h"
%End
public:
QgsReferencedPointXY( const QgsPointXY &point, const QgsCoordinateReferenceSystem &crs );
%Docstring
Constructor for QgsReferencedPointXY, with the specified initial ``point``
and ``crs``.
%End
QgsReferencedPointXY();
%Docstring
Constructor for QgsReferencedPointXY.
%End
operator QVariant() const;
%Docstring
Allows direct construction of QVariants from point.
%End
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/geometry/qgsreferencedgeometry.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -285,6 +285,11 @@ Divides the coordinates in this point by a scalar quantity in place
:rtype: QgsPointXY
%End
operator QVariant() const;
%Docstring
Allows direct construction of QVariants from points.
%End
SIP_PYOBJECT __repr__();
%MethodCode
QString str = "(" + QString::number( sipCpp->x() ) + "," + QString::number( sipCpp->y() ) + ")";

View File

@ -447,6 +447,7 @@ SET(QGIS_CORE_SRCS
geometry/qgspoint.cpp
geometry/qgspolygon.cpp
geometry/qgsrectangle.cpp
geometry/qgsreferencedgeometry.cpp
geometry/qgsregularpolygon.cpp
geometry/qgstriangle.cpp
geometry/qgswkbptr.cpp
@ -1059,6 +1060,7 @@ SET(QGIS_CORE_HDRS
geometry/qgsmultisurface.h
geometry/qgspolygon.h
geometry/qgsrectangle.h
geometry/qgsreferencedgeometry.h
geometry/qgsregularpolygon.h
geometry/qgstriangle.h
geometry/qgssurface.h

View File

@ -47,6 +47,11 @@ class CORE_EXPORT QgsRectangle
//! Copy constructor
QgsRectangle( const QgsRectangle &other );
// IMPORTANT - while QgsRectangle is inherited by QgsReferencedRectangle, we do NOT want a virtual destructor here
// because this class MUST be lightweight and we don't want the cost of the vtable here.
// see https://github.com/qgis/QGIS/pull/4720#issuecomment-308652392
~QgsRectangle() = default;
/**
* Sets the rectangle from two QgsPoints. The rectangle is
* normalised after construction.
@ -301,6 +306,12 @@ class CORE_EXPORT QgsRectangle
*/
QgsBox3d toBox3d( double zMin, double zMax ) const;
//! Allows direct construction of QVariants from rectangles.
operator QVariant() const
{
return QVariant::fromValue( *this );
}
private:
double mXmin;
@ -310,6 +321,8 @@ class CORE_EXPORT QgsRectangle
};
Q_DECLARE_METATYPE( QgsRectangle )
#ifndef SIP_RUN
/**

View File

@ -0,0 +1,32 @@
/***************************************************************************
qgsreferencedgeometry.cpp
------------------------
begin : June 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail 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 "qgsreferencedgeometry.h"
QgsReferencedGeometryBase::QgsReferencedGeometryBase( const QgsCoordinateReferenceSystem &crs )
: mCrs( crs )
{}
QgsReferencedRectangle::QgsReferencedRectangle( const QgsRectangle &rect, const QgsCoordinateReferenceSystem &crs )
: QgsRectangle( rect )
, QgsReferencedGeometryBase( crs )
{}
QgsReferencedPointXY::QgsReferencedPointXY( const QgsPointXY &point, const QgsCoordinateReferenceSystem &crs )
: QgsPointXY( point )
, QgsReferencedGeometryBase( crs )
{}

View File

@ -0,0 +1,128 @@
/***************************************************************************
qgsreferencedgeometry.h
----------------------
begin : June 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail 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 QGSREFERENCEDGEOMETRY_H
#define QGSREFERENCEDGEOMETRY_H
#include "qgis.h"
#include "qgis_sip.h"
#include "qgis_core.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsrectangle.h"
/**
* \class QgsReferencedGeometryBase
* \ingroup core
* A base class for geometry primitives which are stored with an associated reference system.
*
* QgsReferencedGeometryBase classes represent some form of geometry primitive
* (such as rectangles) which have an optional coordinate reference system
* associated with them.
*
* \since QGIS 3.0
* \see QgsReferencedRectangle
*/
class CORE_EXPORT QgsReferencedGeometryBase
{
public:
/**
* Constructor for QgsReferencedGeometryBase, with the specified \a crs.
*/
QgsReferencedGeometryBase( const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() );
/**
* Returns the associated coordinate reference system, or an invalid CRS if
* no reference system is set.
* \see setCrs()
*/
QgsCoordinateReferenceSystem crs() const { return mCrs; }
/**
* Sets the associated \a crs. Set to an invalid CRS if
* no reference system is required.
* \see crs()
*/
void setCrs( const QgsCoordinateReferenceSystem &crs ) { mCrs = crs; }
private:
QgsCoordinateReferenceSystem mCrs;
};
/**
* \ingroup core
* A QgsRectangle with associated coordinate reference system.
* \since QGIS 3.0
*/
class CORE_EXPORT QgsReferencedRectangle : public QgsRectangle, public QgsReferencedGeometryBase
{
public:
/**
* Constructor for QgsReferencedRectangle, with the specified initial \a rectangle
* and \a crs.
*/
QgsReferencedRectangle( const QgsRectangle &rectangle, const QgsCoordinateReferenceSystem &crs );
/**
* Constructor for QgsReferencedRectangle.
*/
QgsReferencedRectangle() = default;
//! Allows direct construction of QVariants from rectangle.
operator QVariant() const
{
return QVariant::fromValue( *this );
}
};
Q_DECLARE_METATYPE( QgsReferencedRectangle )
/**
* \ingroup core
* A QgsPointXY with associated coordinate reference system.
* \since QGIS 3.0
*/
class CORE_EXPORT QgsReferencedPointXY : public QgsPointXY, public QgsReferencedGeometryBase
{
public:
/**
* Constructor for QgsReferencedPointXY, with the specified initial \a point
* and \a crs.
*/
QgsReferencedPointXY( const QgsPointXY &point, const QgsCoordinateReferenceSystem &crs );
/**
* Constructor for QgsReferencedPointXY.
*/
QgsReferencedPointXY() = default;
//! Allows direct construction of QVariants from point.
operator QVariant() const
{
return QVariant::fromValue( *this );
}
};
Q_DECLARE_METATYPE( QgsReferencedPointXY )
#endif // QGSREFERENCEDGEOMETRY_H

View File

@ -91,8 +91,10 @@ class CORE_EXPORT QgsPointXY
*/
QgsPointXY( const QgsPoint &point );
~QgsPointXY()
{}
// IMPORTANT - while QgsPointXY is inherited by QgsReferencedPointXY, we do NOT want a virtual destructor here
// because this class MUST be lightweight and we don't want the cost of the vtable here.
// see https://github.com/qgis/QGIS/pull/4720#issuecomment-308652392
~QgsPointXY() = default;
/** Sets the x value of the point
* \param x x coordinate
@ -260,6 +262,12 @@ class CORE_EXPORT QgsPointXY
//! Divides the coordinates in this point by a scalar quantity in place
QgsPointXY &operator/=( double scalar ) { mX /= scalar; mY /= scalar; return *this; }
//! Allows direct construction of QVariants from points.
operator QVariant() const
{
return QVariant::fromValue( *this );
}
#ifdef SIP_RUN
SIP_PYOBJECT __repr__();
% MethodCode
@ -309,6 +317,7 @@ class CORE_EXPORT QgsPointXY
}; // class QgsPoint
Q_DECLARE_METATYPE( QgsPointXY )
inline bool operator==( const QgsPointXY &p1, const QgsPointXY &p2 ) SIP_SKIP
{

View File

@ -25,6 +25,7 @@
#include <qgsgeometry.h>
//header for class being tested
#include <qgspoint.h>
#include "qgsreferencedgeometry.h"
class TestQgsPointXY: public QObject
{
@ -51,6 +52,8 @@ class TestQgsPointXY: public QObject
void compare();
void project();
void vector(); //tests for QgsVector
void asVariant();
void referenced();
private:
QgsPointXY mPoint1;
@ -760,5 +763,42 @@ void TestQgsPointXY::vector()
QCOMPARE( v1.y(), 3.0 );
}
void TestQgsPointXY::asVariant()
{
QgsPointXY p1 = QgsPointXY( 10.0, 20.0 );
//convert to and from a QVariant
QVariant var = QVariant::fromValue( p1 );
QVERIFY( var.isValid() );
QVERIFY( var.canConvert< QgsPointXY >() );
QVERIFY( !var.canConvert< QgsReferencedPointXY >() );
QgsPointXY p2 = qvariant_cast<QgsPointXY>( var );
QCOMPARE( p2.x(), p1.x() );
QCOMPARE( p2.y(), p1.y() );
}
void TestQgsPointXY::referenced()
{
QgsReferencedPointXY p1 = QgsReferencedPointXY( QgsPointXY( 10.0, 20.0 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) );
QCOMPARE( p1.crs().authid(), QStringLiteral( "EPSG:3111" ) );
p1.setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ) );
QCOMPARE( p1.crs().authid(), QStringLiteral( "EPSG:28356" ) );
//convert to and from a QVariant
QVariant var = QVariant::fromValue( p1 );
QVERIFY( var.isValid() );
// not great - we'd ideally like this to pass, but it doesn't:
// QVERIFY( !var.canConvert< QgsPointXY >() );
QVERIFY( var.canConvert< QgsReferencedPointXY >() );
QgsReferencedPointXY p2 = qvariant_cast<QgsReferencedPointXY>( var );
QCOMPARE( p2.x(), p1.x() );
QCOMPARE( p2.y(), p1.y() );
QCOMPARE( p2.crs().authid(), QStringLiteral( "EPSG:28356" ) );
}
QGSTEST_MAIN( TestQgsPointXY )
#include "testqgspoint.moc"

View File

@ -19,6 +19,7 @@
#include <qgsrectangle.h>
#include <qgspoint.h>
#include "qgslogger.h"
#include "qgsreferencedgeometry.h"
class TestQgsRectangle: public QObject
{
@ -27,6 +28,8 @@ class TestQgsRectangle: public QObject
void manipulate();
void regression6194();
void operators();
void asVariant();
void referenced();
};
void TestQgsRectangle::manipulate()
@ -111,5 +114,46 @@ void TestQgsRectangle::operators()
QCOMPARE( rect2.width(), rect1.width() );
}
void TestQgsRectangle::asVariant()
{
QgsRectangle rect1 = QgsRectangle( 10.0, 20.0, 110.0, 220.0 );
//convert to and from a QVariant
QVariant var = QVariant::fromValue( rect1 );
QVERIFY( var.isValid() );
QVERIFY( var.canConvert< QgsRectangle >() );
QVERIFY( !var.canConvert< QgsReferencedRectangle >() );
QgsRectangle rect2 = qvariant_cast<QgsRectangle>( var );
QCOMPARE( rect2.xMinimum(), rect1.xMinimum() );
QCOMPARE( rect2.yMinimum(), rect1.yMinimum() );
QCOMPARE( rect2.height(), rect1.height() );
QCOMPARE( rect2.width(), rect1.width() );
}
void TestQgsRectangle::referenced()
{
QgsReferencedRectangle rect1 = QgsReferencedRectangle( QgsRectangle( 10.0, 20.0, 110.0, 220.0 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) );
QCOMPARE( rect1.crs().authid(), QStringLiteral( "EPSG:3111" ) );
rect1.setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ) );
QCOMPARE( rect1.crs().authid(), QStringLiteral( "EPSG:28356" ) );
//convert to and from a QVariant
QVariant var = QVariant::fromValue( rect1 );
QVERIFY( var.isValid() );
// not great - we'd ideally like this to pass, but it doesn't:
// QVERIFY( !var.canConvert< QgsRectangle >() );
QVERIFY( var.canConvert< QgsReferencedRectangle >() );
QgsReferencedRectangle rect2 = qvariant_cast<QgsReferencedRectangle>( var );
QCOMPARE( rect2.xMinimum(), rect1.xMinimum() );
QCOMPARE( rect2.yMinimum(), rect1.yMinimum() );
QCOMPARE( rect2.height(), rect1.height() );
QCOMPARE( rect2.width(), rect1.width() );
QCOMPARE( rect2.crs().authid(), QStringLiteral( "EPSG:28356" ) );
}
QGSTEST_MAIN( TestQgsRectangle )
#include "testqgsrectangle.moc"

View File

@ -124,6 +124,7 @@ ADD_PYTHON_TEST(PyQgsRasterLayer test_qgsrasterlayer.py)
ADD_PYTHON_TEST(PyQgsRasterColorRampShader test_qgsrastercolorrampshader.py)
ADD_PYTHON_TEST(PyQgsRatioLockButton test_qgsratiolockbutton.py)
ADD_PYTHON_TEST(PyQgsRectangle test_qgsrectangle.py)
ADD_PYTHON_TEST(PyQgsReferencedGeometry test_qgsreferencedgeometry.py)
ADD_PYTHON_TEST(PyQgsRelation test_qgsrelation.py)
ADD_PYTHON_TEST(PyQgsRelationManager test_qgsrelationmanager.py)
ADD_PYTHON_TEST(PyQgsRenderContext test_qgsrendercontext.py)

View File

@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsReferencedGeometry.
.. note:: 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.
"""
__author__ = 'Nyall Dawson'
__date__ = '31/08/2017'
__copyright__ = 'Copyright 2017, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
import qgis # NOQA
from qgis.core import (QgsRectangle,
QgsPointXY,
QgsReferencedRectangle,
QgsReferencedPointXY,
QgsCoordinateReferenceSystem)
from qgis.PyQt.QtCore import QVariant
from qgis.testing import start_app, unittest
from utilities import compareWkt
start_app()
class TestQgsReferencedGeometry(unittest.TestCase):
def testRectangle(self):
rect = QgsReferencedRectangle(QgsRectangle(0.0, 1.0, 20.0, 10.0), QgsCoordinateReferenceSystem('epsg:3111'))
self.assertEqual(rect.xMinimum(), 0.0)
self.assertEqual(rect.yMinimum(), 1.0)
self.assertEqual(rect.xMaximum(), 20.0)
self.assertEqual(rect.yMaximum(), 10.0)
self.assertEqual(rect.crs().authid(), 'EPSG:3111')
rect.setCrs(QgsCoordinateReferenceSystem('epsg:28356'))
self.assertEqual(rect.crs().authid(), 'EPSG:28356')
# in variant
v = QVariant(QgsReferencedRectangle(QgsRectangle(1.0, 2.0, 3.0, 4.0), QgsCoordinateReferenceSystem('epsg:3111')))
self.assertEqual(v.value().xMinimum(), 1.0)
self.assertEqual(v.value().yMinimum(), 2.0)
self.assertEqual(v.value().xMaximum(), 3.0)
self.assertEqual(v.value().yMaximum(), 4.0)
self.assertEqual(v.value().crs().authid(), 'EPSG:3111')
# to rectangle
r = QgsRectangle(rect)
self.assertEqual(r.xMinimum(), 0.0)
self.assertEqual(r.yMinimum(), 1.0)
self.assertEqual(r.xMaximum(), 20.0)
self.assertEqual(r.yMaximum(), 10.0)
# test that QgsReferencedRectangle IS a QgsRectangle
r2 = QgsRectangle(5, 6, 30, 40)
r2.combineExtentWith(rect)
self.assertEqual(r2.xMinimum(), 0.0)
self.assertEqual(r2.yMinimum(), 1.0)
self.assertEqual(r2.xMaximum(), 30.0)
self.assertEqual(r2.yMaximum(), 40.0)
def testPoint(self):
point = QgsReferencedPointXY(QgsPointXY(1.0, 2.0), QgsCoordinateReferenceSystem('epsg:3111'))
self.assertEqual(point.x(), 1.0)
self.assertEqual(point.y(), 2.0)
self.assertEqual(point.crs().authid(), 'EPSG:3111')
point.setCrs(QgsCoordinateReferenceSystem('epsg:28356'))
self.assertEqual(point.crs().authid(), 'EPSG:28356')
# in variant
v = QVariant(QgsReferencedPointXY(QgsPointXY(3.0, 4.0), QgsCoordinateReferenceSystem('epsg:3111')))
self.assertEqual(v.value().x(), 3.0)
self.assertEqual(v.value().y(), 4.0)
self.assertEqual(v.value().crs().authid(), 'EPSG:3111')
# to QgsPointXY
p = QgsPointXY(point)
self.assertEqual(p.x(), 1.0)
self.assertEqual(p.y(), 2.0)
if __name__ == '__main__':
unittest.main()