diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index 8af0c5dfa5b..e958891c4a9 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -153,12 +153,13 @@ %Include composer/qgscomposermultiframecommand.sip %Include composer/qgscomposertexttable.sip %Include composer/qgspaperitem.sip -%Include layout/qgslayout.sip +%Include layout/qgslayoutcontext.sip %Include layout/qgslayoutmeasurement.sip %Include layout/qgslayoutmeasurementconverter.sip %Include layout/qgspagesizeregistry.sip %Include layout/qgslayoutpoint.sip %Include layout/qgslayoutsize.sip +%Include layout/qgslayoututils.sip %Include metadata/qgslayermetadata.sip %Include metadata/qgslayermetadatavalidator.sip %Include processing/qgsprocessing.sip @@ -378,7 +379,7 @@ %Include gps/qgsgpsdetector.sip %Include gps/qgsnmeaconnection.sip %Include gps/qgsgpsdconnection.sip -%Include layout/qgslayoutcontext.sip +%Include layout/qgslayout.sip %Include layout/qgslayoutitem.sip %Include layout/qgslayoutitemregistry.sip %Include layout/qgslayoutobject.sip diff --git a/python/core/layout/qgslayoututils.sip b/python/core/layout/qgslayoututils.sip new file mode 100644 index 00000000000..a8a0346f88b --- /dev/null +++ b/python/core/layout/qgslayoututils.sip @@ -0,0 +1,39 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/layout/qgslayoututils.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + +class QgsLayoutUtils +{ +%Docstring + Utilities for layouts. +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgslayoututils.h" +%End + public: + + static double normalizedAngle( const double angle, const bool allowNegative = false ); +%Docstring + Ensures that an ``angle`` (in degrees) is in the range 0 <= angle < 360. + If ``allowNegative`` is true then angles between (-360, 360) are allowed. If false, + angles are converted to positive angles in the range [0, 360). + :rtype: float +%End + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/layout/qgslayoututils.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3f3e49ba55a..9c1ea975222 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -356,6 +356,7 @@ SET(QGIS_CORE_SRCS layout/qgslayoutmeasurement.cpp layout/qgslayoutmeasurementconverter.cpp layout/qgslayoutobject.cpp + layout/qgslayoututils.cpp layout/qgspagesizeregistry.cpp layout/qgslayoutpoint.cpp layout/qgslayoutsize.cpp @@ -923,6 +924,7 @@ SET(QGIS_CORE_HDRS layout/qgspagesizeregistry.h layout/qgslayoutpoint.h layout/qgslayoutsize.h + layout/qgslayoututils.h metadata/qgslayermetadata.h metadata/qgslayermetadatavalidator.h diff --git a/src/core/layout/qgslayoutitem.cpp b/src/core/layout/qgslayoutitem.cpp index b9217fad4dc..31f644f4703 100644 --- a/src/core/layout/qgslayoutitem.cpp +++ b/src/core/layout/qgslayoutitem.cpp @@ -16,6 +16,7 @@ #include "qgslayoutitem.h" #include "qgslayout.h" +#include "qgslayoututils.h" #include QgsLayoutItem::QgsLayoutItem( QgsLayout *layout ) diff --git a/src/core/layout/qgslayoututils.cpp b/src/core/layout/qgslayoututils.cpp new file mode 100644 index 00000000000..18ecc20ecd0 --- /dev/null +++ b/src/core/layout/qgslayoututils.cpp @@ -0,0 +1,33 @@ +/*************************************************************************** + qgslayoututils.cpp + ------------------ + begin : July 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 "qgslayoututils.h" +#include + +double QgsLayoutUtils::normalizedAngle( const double angle, const bool allowNegative ) +{ + double clippedAngle = angle; + if ( clippedAngle >= 360.0 || clippedAngle <= -360.0 ) + { + clippedAngle = fmod( clippedAngle, 360.0 ); + } + if ( !allowNegative && clippedAngle < 0.0 ) + { + clippedAngle += 360.0; + } + return clippedAngle; +} diff --git a/src/core/layout/qgslayoututils.h b/src/core/layout/qgslayoututils.h new file mode 100644 index 00000000000..4cd822c03a4 --- /dev/null +++ b/src/core/layout/qgslayoututils.h @@ -0,0 +1,40 @@ +/*************************************************************************** + qgslayoututils.h + ------------------- + begin : July 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 QGSLAYOUTUTILS_H +#define QGSLAYOUTUTILS_H + +#include "qgis_core.h" + +/** + * \ingroup core + * Utilities for layouts. + * \since QGIS 3.0 + */ +class CORE_EXPORT QgsLayoutUtils +{ + public: + + /** + * Ensures that an \a angle (in degrees) is in the range 0 <= angle < 360. + * If \a allowNegative is true then angles between (-360, 360) are allowed. If false, + * angles are converted to positive angles in the range [0, 360). + */ + static double normalizedAngle( const double angle, const bool allowNegative = false ); + +}; + +#endif //QGSLAYOUTUTILS_H diff --git a/tests/src/core/CMakeLists.txt b/tests/src/core/CMakeLists.txt index 32c62b090ff..50c9693bc8a 100755 --- a/tests/src/core/CMakeLists.txt +++ b/tests/src/core/CMakeLists.txt @@ -131,6 +131,7 @@ SET(TESTS testqgslayoutitem.cpp testqgslayoutobject.cpp testqgslayoutunits.cpp + testqgslayoututils.cpp testqgslegendrenderer.cpp testqgscentroidfillsymbol.cpp testqgslinefillsymbol.cpp diff --git a/tests/src/core/testqgslayoututils.cpp b/tests/src/core/testqgslayoututils.cpp new file mode 100644 index 00000000000..b2d69e15269 --- /dev/null +++ b/tests/src/core/testqgslayoututils.cpp @@ -0,0 +1,116 @@ +/*************************************************************************** + testqgslayoututils.cpp + --------------------- + begin : July 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 "qgslayout.h" +#include "qgstest.h" +#include "qgslayoututils.h" + +class TestQgsLayoutUtils: public QObject +{ + Q_OBJECT + + private slots: + void initTestCase();// will be called before the first testfunction is executed. + void cleanupTestCase();// will be called after the last testfunction was executed. + void init();// will be called before each testfunction is executed. + void cleanup();// will be called after every testfunction. + void normalizedAngle(); //test normalised angle function + + private: + QString mReport; + +}; + +void TestQgsLayoutUtils::initTestCase() +{ + mReport = "

Layout Utils Tests

\n"; +} + +void TestQgsLayoutUtils::cleanupTestCase() +{ + QString myReportFile = QDir::tempPath() + QDir::separator() + "qgistest.html"; + QFile myFile( myReportFile ); + if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) + { + QTextStream myQTextStream( &myFile ); + myQTextStream << mReport; + myFile.close(); + } +} + +void TestQgsLayoutUtils::init() +{ + +} + +void TestQgsLayoutUtils::cleanup() +{ + +} + +void TestQgsLayoutUtils::normalizedAngle() +{ + QList< QPair< double, double > > testVals; + testVals << qMakePair( 0.0, 0.0 ); + testVals << qMakePair( 90.0, 90.0 ); + testVals << qMakePair( 180.0, 180.0 ); + testVals << qMakePair( 270.0, 270.0 ); + testVals << qMakePair( 360.0, 0.0 ); + testVals << qMakePair( 390.0, 30.0 ); + testVals << qMakePair( 720.0, 0.0 ); + testVals << qMakePair( 730.0, 10.0 ); + testVals << qMakePair( -10.0, 350.0 ); + testVals << qMakePair( -360.0, 0.0 ); + testVals << qMakePair( -370.0, 350.0 ); + testVals << qMakePair( -760.0, 320.0 ); + + //test normalized angle helper function + QList< QPair< double, double > >::const_iterator it = testVals.constBegin(); + for ( ; it != testVals.constEnd(); ++it ) + + { + double result = QgsLayoutUtils::normalizedAngle( ( *it ).first ); + qDebug() << QString( "actual: %1 expected: %2" ).arg( result ).arg( ( *it ).second ); + QVERIFY( qgsDoubleNear( result, ( *it ).second ) ); + + } + + //test with allowing negative angles + QList< QPair< double, double > > negativeTestVals; + negativeTestVals << qMakePair( 0.0, 0.0 ); + negativeTestVals << qMakePair( 90.0, 90.0 ); + negativeTestVals << qMakePair( 360.0, 0.0 ); + negativeTestVals << qMakePair( -10.0, -10.0 ); + negativeTestVals << qMakePair( -359.0, -359.0 ); + negativeTestVals << qMakePair( -360.0, 0.0 ); + negativeTestVals << qMakePair( -361.0, -1.0 ); + negativeTestVals << qMakePair( -370.0, -10.0 ); + negativeTestVals << qMakePair( -760.0, -40.0 ); + it = negativeTestVals.constBegin(); + for ( ; it != negativeTestVals.constEnd(); ++it ) + + { + double result = QgsLayoutUtils::normalizedAngle( ( *it ).first, true ); + qDebug() << QString( "actual: %1 expected: %2" ).arg( result ).arg( ( *it ).second ); + QVERIFY( qgsDoubleNear( result, ( *it ).second ) ); + + } +} + + +QGSTEST_MAIN( TestQgsLayoutUtils ) +#include "testqgslayoututils.moc"