Skip to content

Commit

Permalink
Add QgsScaleExpression subclass for sized based expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
vmora authored and nyalldawson committed Apr 29, 2015
1 parent 9b56618 commit 70b91b6
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -162,6 +162,7 @@ SET(QGIS_CORE_SRCS
qgsrectangle.cpp
qgsrunprocess.cpp
qgsscalecalculator.cpp
qgsscaleexpression.cpp
qgsscaleutils.cpp
qgssimplifymethod.cpp
qgssnapper.cpp
Expand Down Expand Up @@ -546,6 +547,7 @@ SET(QGIS_CORE_HDRS
qgsrenderchecker.h
qgsrendercontext.h
qgsscalecalculator.h
qgsscaleexpression.h
qgsscaleutils.h
qgssimplifymethod.h
qgssnapper.h
Expand Down
104 changes: 104 additions & 0 deletions src/core/qgsscaleexpression.cpp
@@ -0,0 +1,104 @@

/***************************************************************************
qgsscaleexpression.cpp
---------------------
begin : November 2015
copyright : (C) 2015 by Vincent Mora
email : vincent dor mora 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 "qgsscaleexpression.h"

#include <QStringList>
#include <qmath.h>

void QgsScaleExpression::init()
{
bool ok;
if ( rootNode() )
{
const NodeFunction * f = dynamic_cast<const NodeFunction*>( rootNode() );
if ( f )
{
QList<Node*> args = f->args()->list();
if ( "scale_linear" == Functions()[f->fnIndex()]->name() )
{
mType = Linear;
}
if ( "scale_exp" == Functions()[f->fnIndex()]->name() )
{
const double exp = QgsExpression( args[5]->dump() ).evaluate().toDouble( &ok );
if ( ! ok ) return;
if ( qAbs( exp - .57 ) < .001 ) mType = Flannery;
else if ( qAbs( exp - .5 ) < .001 ) mType = Area;
else return;
}
mMinValue = QgsExpression( args[1]->dump() ).evaluate().toDouble( &ok );
if ( ! ok ) return;
mMaxValue = QgsExpression( args[2]->dump() ).evaluate().toDouble( &ok );
if ( ! ok ) return;
mMinSize = QgsExpression( args[3]->dump() ).evaluate().toDouble( &ok );
if ( ! ok ) return;
mMaxSize = QgsExpression( args[4]->dump() ).evaluate().toDouble( &ok );
if ( ! ok ) return;
mExpression = args[0]->dump();
}
}
}

QgsScaleExpression::QgsScaleExpression( const QString & expr )
: QgsExpression( expr )
{
init();
}

QgsScaleExpression::QgsScaleExpression( Type type, const QString & baseExpr, double minValue, double maxValue, double minSize, double maxSize )
: QgsExpression( createExpression( type, baseExpr, minValue, maxValue, minSize, maxSize ) )
{
init();
}

QString QgsScaleExpression::createExpression( Type type, const QString & baseExpr, double minValue, double maxValue, double minSize, double maxSize )
{
switch ( type )
{
case Linear:
return "scale_linear(" + baseExpr
+ ", " + QString::number( minValue )
+ ", " + QString::number( maxValue )
+ ", " + QString::number( minSize )
+ ", " + QString::number( maxSize ) + ")";
case Area:
return "scale_exp(" + baseExpr
+ ", " + QString::number( minValue )
+ ", " + QString::number( maxValue )
+ ", " + QString::number( minSize )
+ ", " + QString::number( maxSize ) + ", .5)";
case Flannery:
return "scale_exp(" + baseExpr
+ ", " + QString::number( minValue )
+ ", " + QString::number( maxValue )
+ ", " + QString::number( minSize )
+ ", " + QString::number( maxSize ) + ", .57)";
}
return "";
}


double QgsScaleExpression::size( double value ) const
{
switch ( mType )
{
case Linear: return mMinSize + ( qBound( mMinValue, value, mMaxValue ) - mMinValue ) * ( mMaxSize - mMinSize ) / ( mMaxValue - mMinValue );
case Area: return qPow( qBound( mMinValue, value, mMaxValue ) - mMinValue, .5 ) * ( mMaxSize - mMinSize ) / qPow( mMaxValue - mMinValue, .5 );
case Flannery: return qPow( qBound( mMinValue, value, mMaxValue ) - mMinValue, .57 ) * ( mMaxSize - mMinSize ) / qPow( mMaxValue - mMinValue, .57 );
}
return 0;
}
63 changes: 63 additions & 0 deletions src/core/qgsscaleexpression.h
@@ -0,0 +1,63 @@

/***************************************************************************
qgsscaleexpression.h
---------------------
begin : November 2015
copyright : (C) 2015 by Vincent Mora
email : vincent dor mora 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 QGSSCALEEXPRESSION_H
#define QGSSCALEEXPRESSION_H

#include "qgsexpression.h"

//! Class to retrieve parameters of a
//! scale expression (size or width)
//!
//! It derives from QgsExpression,
class QgsScaleExpression: public QgsExpression
{
public:
enum Type {Linear, Area, Flannery};

//! parse string like an expression and
//! detemine if it's a size expression
QgsScaleExpression( const QString & expr );

//! setup expression from parameters
QgsScaleExpression( Type type, const QString & baseExpr, double minValue, double maxValue, double minSize, double maxSize );

operator bool() const { return ! mExpression.isEmpty(); }
double size( double value ) const;

double minSize() const { return mMinSize; }
double maxSize() const { return mMaxSize; }
double minValue() const { return mMinValue; }
double maxValue() const { return mMaxValue; }
QString baseExpression() const { return mExpression; }
Type type() const { return mType; }

private:
QString mExpression;
Type mType;
double mMinSize;
double mMaxSize;
double mMinValue;
double mMaxValue;

void init();
static QString createExpression( Type type, const QString & baseExpr, double minValue, double maxValue, double minSize, double maxSize );

};

#endif



1 change: 1 addition & 0 deletions tests/src/core/CMakeLists.txt
Expand Up @@ -83,6 +83,7 @@ ADD_QGIS_TEST(applicationtest testqgsapplication.cpp)
ADD_QGIS_TEST(diagramtest testqgsdiagram.cpp)
ADD_QGIS_TEST(diagramexpressiontest testqgsdiagramexpression.cpp)
ADD_QGIS_TEST(expressiontest testqgsexpression.cpp)
ADD_QGIS_TEST(scaleexpressiontest testqgsscaleexpression.cpp)
ADD_QGIS_TEST(filewritertest testqgsvectorfilewriter.cpp)
ADD_QGIS_TEST(projecttest testqgsproject.cpp)
ADD_QGIS_TEST(regression992 regression992.cpp)
Expand Down
105 changes: 105 additions & 0 deletions tests/src/core/testqgsscaleexpression.cpp
@@ -0,0 +1,105 @@
/***************************************************************************
test_template.cpp
--------------------------------------
Date : Sun Sep 16 12:22:23 AKDT 2007
Copyright : (C) 2007 by Gary E. Sherman
Email : sherman at mrcc 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 <QtTest/QtTest>
#include <QObject>
#include <QString>
#include <QObject>
#include <QtConcurrentMap>
#include <QSharedPointer>

#include <qgsapplication.h>
//header for class being tested
#include <qgsscaleexpression.h>
#include <qgsfeature.h>
#include <qgsfeaturerequest.h>
#include <qgsgeometry.h>
#include <qgsrenderchecker.h>

#if QT_VERSION < 0x40701
// See http://hub.qgis.org/issues/4284
Q_DECLARE_METATYPE( QVariant )
#endif

class TestQgsScaleExpression: public QObject
{
Q_OBJECT
private slots:

void initTestCase()
{
//
// Runs once before any tests are run
//
// init QGIS's paths - true means that all path will be inited from prefix
QgsApplication::init();
QgsApplication::initQgis();
// Will make sure the settings dir with the style file for color ramp is created
QgsApplication::createDB();
QgsApplication::showSettings();
}

void cleanupTestCase()
{
QgsApplication::exitQgis();
}

void parsing()
{
{
QgsScaleExpression exp( "scale_linear(column, 1, 7, 2, 10)" );
QCOMPARE( bool(exp), true );
QCOMPARE( exp.type(), QgsScaleExpression::Linear );
QCOMPARE( exp.baseExpression(), QString("column") );
QCOMPARE( exp.minValue(), 1. );
QCOMPARE( exp.maxValue(), 7. );
QCOMPARE( exp.minSize(), 2. );
QCOMPARE( exp.maxSize(), 10. );
}
{
QgsScaleExpression exp( "scale_exp(column, 1, 7, 2, 10, 0.5)" );
QCOMPARE( bool(exp), true );
QCOMPARE( exp.type(), QgsScaleExpression::Area );
}
{
QgsScaleExpression exp( "scale_exp(column, 1, 7, 2, 10, 0.57)" );
QCOMPARE( bool(exp), true );
QCOMPARE( exp.type(), QgsScaleExpression::Flannery );
}
{
QgsScaleExpression exp( "scale_exp(column, 1, 7, 2, 10, 0.51)" );
QCOMPARE( bool(exp), false );
}
{
QgsScaleExpression exp( "scale_exp(column, 1, 7, a, 10, 0.5)" );
QCOMPARE( bool(exp), false );
}
{
QgsScaleExpression exp( QgsScaleExpression::Linear, "column", 1, 7, 2, 10 );
QCOMPARE( bool(exp), true );
QCOMPARE( exp.type(), QgsScaleExpression::Linear );
QCOMPARE( exp.baseExpression(), QString("column") );
QCOMPARE( exp.minValue(), 1. );
QCOMPARE( exp.maxValue(), 7. );
QCOMPARE( exp.minSize(), 2. );
QCOMPARE( exp.maxSize(), 10. );
}

}
};

QTEST_MAIN( TestQgsScaleExpression )

#include "testqgsscaleexpression.moc"

0 comments on commit 70b91b6

Please sign in to comment.