446 changes: 1 addition & 445 deletions src/core/qgsexpression.cpp

Large diffs are not rendered by default.

14 changes: 5 additions & 9 deletions src/core/qgsexpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@
#include <QDomDocument>

#include "qgsfield.h"
#include "qgsvectorlayer.h"
#include "qgsdistancearea.h"
#include "qgsgeometry.h"

class QgsFeature;
class QgsGeometry;
class QgsOgcUtils;
class QgsVectorLayer;

class QDomElement;

/**
Expand Down Expand Up @@ -57,6 +58,7 @@ Possible QVariant value types:
- int
- double
- string
- geometry
Similarly to SQL, this class supports three-value logic: true/false/unknown.
Unknown value may be a result of operations with missing data (NULL). Please note
Expand Down Expand Up @@ -322,7 +324,6 @@ class CORE_EXPORT QgsExpression
virtual QString dump() const = 0;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const { Q_UNUSED( doc ); Q_UNUSED( element ); }
static QgsExpression::Node* createFromOgcFilter( QDomElement &element, QString &errorMessage );

virtual QStringList referencedColumns() const = 0;
virtual bool needsGeometry() const = 0;
Expand Down Expand Up @@ -391,7 +392,6 @@ class CORE_EXPORT QgsExpression
virtual QString dump() const;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;
static QgsExpression::Node* createFromOgcFilter( QDomElement &element, QString &errorMessage );

virtual QStringList referencedColumns() const { return mOperand->referencedColumns(); }
virtual bool needsGeometry() const { return mOperand->needsGeometry(); }
Expand All @@ -417,7 +417,6 @@ class CORE_EXPORT QgsExpression
virtual QString dump() const;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;
static QgsExpression::Node* createFromOgcFilter( QDomElement &element, QString &errorMessage );

virtual QStringList referencedColumns() const { return mOpLeft->referencedColumns() + mOpRight->referencedColumns(); }
virtual bool needsGeometry() const { return mOpLeft->needsGeometry() || mOpRight->needsGeometry(); }
Expand Down Expand Up @@ -475,7 +474,6 @@ class CORE_EXPORT QgsExpression
virtual QString dump() const;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;
static QgsExpression::Node* createFromOgcFilter( QDomElement &element, QString &errorMessage );

virtual QStringList referencedColumns() const { QStringList lst; if ( !mArgs ) return lst; foreach ( Node* n, mArgs->list() ) lst.append( n->referencedColumns() ); return lst; }
virtual bool needsGeometry() const { bool needs = Functions()[mFnIndex]->usesgeometry(); if ( mArgs ) { foreach ( Node* n, mArgs->list() ) needs |= n->needsGeometry(); } return needs; }
Expand All @@ -499,7 +497,6 @@ class CORE_EXPORT QgsExpression
virtual QString dump() const;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;
static QgsExpression::Node* createFromOgcFilter( QDomElement &element, QString &errorMessage );

virtual QStringList referencedColumns() const { return QStringList(); }
virtual bool needsGeometry() const { return false; }
Expand All @@ -521,7 +518,6 @@ class CORE_EXPORT QgsExpression
virtual QString dump() const;

virtual void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;
static QgsExpression::Node* createFromOgcFilter( QDomElement &element, QString &errorMessage );

virtual QStringList referencedColumns() const { return QStringList( mName ); }
virtual bool needsGeometry() const { return false; }
Expand Down Expand Up @@ -587,7 +583,6 @@ class CORE_EXPORT QgsExpression

// convert from/to OGC Filter
void toOgcFilter( QDomDocument &doc, QDomElement &element ) const;
static QgsExpression* createFromOgcFilter( QDomElement &element );

protected:
// internally used to create an empty expression
Expand All @@ -607,6 +602,7 @@ class CORE_EXPORT QgsExpression
static QMap<QString, QVariant> gmSpecialColumns;
QgsDistanceArea mCalc;

friend class QgsOgcUtils;
};

Q_DECLARE_METATYPE( QgsExpression::Interval );
Expand Down
423 changes: 421 additions & 2 deletions src/core/qgsogcutils.cpp

Large diffs are not rendered by default.

30 changes: 28 additions & 2 deletions src/core/qgsogcutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ class QString;
#include <list>
#include <QVector>

class QgsExpression;
class QgsGeometry;
class QgsPoint;
class QgsRectangle;

#include "qgsexpression.h"

/**
* @brief The QgsOgcUtils class provides various utility functions for conversion between
* OGC (Open Geospatial Consortium) standards and QGIS internal representations.
Expand All @@ -24,8 +27,7 @@ class QgsRectangle;
*/
class CORE_EXPORT QgsOgcUtils
{
public:

public:

/** static method that creates geometry from GML
@param XML representation of the geometry. GML elements are expected to be
Expand Down Expand Up @@ -63,6 +65,11 @@ class CORE_EXPORT QgsOgcUtils
*/
static QDomElement rectangleToGMLEnvelope( QgsRectangle* env, QDomDocument& doc );


/** Parse XML with OGC filter into QGIS expression */
static QgsExpression* expressionFromOgcFilter( const QDomElement& element );


private:
/** static method that creates geometry from GML Point */
static QgsGeometry* geometryFromGMLPoint( const QDomElement& geometryElement );
Expand Down Expand Up @@ -99,6 +106,25 @@ class CORE_EXPORT QgsOgcUtils
@param the GML document
@return QDomElement */
static QDomElement createGMLPositions( const QVector<QgsPoint> points, QDomDocument& doc );

//! handle a generic sub-expression
static QgsExpression::Node* nodeFromOgcFilter( QDomElement &element, QString &errorMessage );
//! handle a generic binary operator
static QgsExpression::NodeBinaryOperator* nodeBinaryOperatorFromOgcFilter( QDomElement &element, QString &errorMessage );
//! handles various spatial operation tags (<Intersects>, <Touches> etc)
static QgsExpression::NodeFunction* nodeSpatialOperatorFromOgcFilter( QDomElement& element, QString& errorMessage );
//! handle <Not> tag
static QgsExpression::NodeUnaryOperator* nodeNotFromOgcFilter( QDomElement &element, QString &errorMessage );
//! handles <Function> tag
static QgsExpression::NodeFunction* nodeFunctionFromOgcFilter( QDomElement &element, QString &errorMessage );
//! handles <Literal> tag
static QgsExpression::Node* nodeLiteralFromOgcFilter( QDomElement &element, QString &errorMessage );
//! handles <PropertyName> tag
static QgsExpression::NodeColumnRef* nodeColumnRefFromOgcFilter( QDomElement &element, QString &errorMessage );
//! handles <PropertyIsBetween> tag
static QgsExpression::Node* nodeIsBetweenFromOgcFilter( QDomElement& element, QString& errorMessage );
//! handles <PropertyIsNull> tag
static QgsExpression::NodeBinaryOperator* nodePropertyIsNullFromOgcFilter( QDomElement& element, QString& errorMessage );
};

#endif // QGSOGCUTILS_H
3 changes: 2 additions & 1 deletion src/core/symbology-ng/qgsrulebasedrendererv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "qgsrendercontext.h"
#include "qgsvectorlayer.h"
#include "qgslogger.h"
#include "qgsogcutils.h"

#include <QSet>

Expand Down Expand Up @@ -538,7 +539,7 @@ QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2::Rule::createFromSld( QDomE
}
else if ( childElem.localName() == "Filter" )
{
QgsExpression *filter = QgsExpression::createFromOgcFilter( childElem );
QgsExpression *filter = QgsOgcUtils::expressionFromOgcFilter( childElem );
if ( filter )
{
if ( filter->hasParserError() )
Expand Down
4 changes: 2 additions & 2 deletions src/core/symbology-ng/qgssymbollayerv2utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#include "qgsexpression.h"
#include "qgsapplication.h"
#include "qgsproject.h"

#include "qgsogcutils.h"

#include "qgsapplication.h"
#include "qgsproject.h"
Expand Down Expand Up @@ -2239,7 +2239,7 @@ bool QgsSymbolLayerV2Utils::functionFromSldElement( QDomElement &element, QStrin
{
QgsDebugMsg( "Entered." );

QgsExpression *expr = QgsExpression::createFromOgcFilter( element );
QgsExpression *expr = QgsOgcUtils::expressionFromOgcFilter( element );
if ( !expr )
return false;

Expand Down
6 changes: 3 additions & 3 deletions src/mapserver/qgswfsserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
}
else
{
QgsExpression *mFilter = QgsExpression::createFromOgcFilter( filterElem );
QgsExpression *mFilter = QgsOgcUtils::expressionFromOgcFilter( filterElem );
if ( mFilter->hasParserError() )
{
throw QgsMapServiceException( "RequestNotWellFormed", mFilter->parserErrorString() );
Expand Down Expand Up @@ -920,7 +920,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
}
else
{
QgsExpression *mFilter = QgsExpression::createFromOgcFilter( filterElem );
QgsExpression *mFilter = QgsOgcUtils::expressionFromOgcFilter( filterElem );
if ( mFilter->hasParserError() )
{
throw QgsMapServiceException( "RequestNotWellFormed", mFilter->parserErrorString() );
Expand Down Expand Up @@ -1573,7 +1573,7 @@ QgsFeatureIds QgsWFSServer::getFeatureIdsFromFilter( QDomElement filterElem, Qgs
}
else
{
QgsExpression *mFilter = QgsExpression::createFromOgcFilter( filterElem );
QgsExpression *mFilter = QgsOgcUtils::expressionFromOgcFilter( filterElem );
if ( mFilter->hasParserError() )
{
throw QgsMapServiceException( "RequestNotWellFormed", mFilter->parserErrorString() );
Expand Down
107 changes: 0 additions & 107 deletions tests/src/core/testqgsexpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -758,113 +758,6 @@ class TestQgsExpression: public QObject
QgsExpression::unsetSpecialColumn( "$var1" );
}


void test_from_ogc_filter_data()
{
QTest::addColumn<QString>( "xmlText" );
QTest::addColumn<QString>( "dumpText" );

QTest::newRow( "=" ) << QString(
"<Filter><PropertyIsEqualTo>"
"<PropertyName>NAME</PropertyName>"
"<Literal>New York</Literal>"
"</PropertyIsEqualTo></Filter>" )
<< QString( "NAME = 'New York'" );

QTest::newRow( ">" ) << QString(
"<Filter><PropertyIsGreaterThan>"
"<PropertyName>COUNT</PropertyName>"
"<Literal>3</Literal>"
"</PropertyIsGreaterThan></Filter>" )
<< QString( "COUNT > 3" );

QTest::newRow( "AND" ) << QString(
"<ogc:Filter>"
"<ogc:And>"
"<ogc:PropertyIsGreaterThanOrEqualTo>"
"<ogc:PropertyName>pop</ogc:PropertyName>"
"<ogc:Literal>50000</ogc:Literal>"
"</ogc:PropertyIsGreaterThanOrEqualTo>"
"<ogc:PropertyIsLessThan>"
"<ogc:PropertyName>pop</ogc:PropertyName>"
"<ogc:Literal>100000</ogc:Literal>"
"</ogc:PropertyIsLessThan>"
"</ogc:And>"
"</ogc:Filter>" )
<< QString( "pop >= 50000 AND pop < 100000" );

// TODO: should work also without <Literal> tags in Lower/Upper-Boundary tags?
QTest::newRow( "between" ) << QString(
"<Filter>"
"<PropertyIsBetween><PropertyName>POPULATION</PropertyName>"
"<LowerBoundary><Literal>100</Literal></LowerBoundary>"
"<UpperBoundary><Literal>200</Literal></UpperBoundary></PropertyIsBetween>"
"</Filter>" )
<< QString( "POPULATION >= 100 AND POPULATION <= 200" );

// TODO: needs to handle different wildcards, single chars, escape chars
QTest::newRow( "like" ) << QString(
"<Filter>"
"<PropertyIsLike wildcard='*' singleChar='.' escape='!'>"
"<PropertyName>NAME</PropertyName><Literal>*QGIS*</Literal></PropertyIsLike>"
"</Filter>" )
<< QString( "NAME LIKE '*QGIS*'" );

QTest::newRow( "is null" ) << QString(
"<Filter>"
"<ogc:PropertyIsNull>"
"<ogc:PropertyName>FIRST_NAME</ogc:PropertyName>"
"</ogc:PropertyIsNull>"
"</Filter>" )
<< QString( "FIRST_NAME IS NULL" );

QTest::newRow( "bbox" ) << QString(
"<Filter>"
"<BBOX><PropertyName>Name>NAME</PropertyName><gml:Box srsName='foo'>"
"<gml:coordinates>135.2239,34.4879 135.8578,34.8471</gml:coordinates></gml:Box></BBOX>"
"</Filter>" )
<< QString( "bbox($geometry, geomFromGML2('<Box srsName=\"foo\"><coordinates>135.2239,34.4879 135.8578,34.8471</coordinates></Box>'))" );

QTest::newRow( "Intersects" ) << QString(
"<Filter>"
"<Intersects>"
"<PropertyName>GEOMETRY</PropertyName>"
"<gml:Point>"
"<gml:coordinates>123,456</gml:coordinates>"
"</gml:Point>"
"</Intersects>"
"</Filter>" )
<< QString( "intersects($geometry, geomFromGML2('<Point><coordinates>123,456</coordinates></Point>'))" );
}

void test_from_ogc_filter()
{
QFETCH( QString, xmlText );
QFETCH( QString, dumpText );

QDomDocument doc;
QVERIFY(doc.setContent(xmlText, true));
QDomElement rootElem = doc.documentElement();

QgsExpression* expr = QgsExpression::createFromOgcFilter( rootElem );
QVERIFY( expr );

qDebug("OGC XML : %s", xmlText.toAscii().data() );
qDebug("EXPR-DUMP: %s", expr->dump().toAscii().data() );

if ( expr->hasParserError() )
qDebug( "ERROR: %s ", expr->parserErrorString().toAscii().data() );
QVERIFY( !expr->hasParserError() );

QCOMPARE( dumpText, expr->dump() );

delete expr;
}

void test_to_ogc_filter()
{
// TODO
}
};

QTEST_MAIN( TestQgsExpression )
Expand Down
106 changes: 106 additions & 0 deletions tests/src/core/testqgsogcutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class TestQgsOgcUtils : public QObject

void testGeometryFromGML();
void testGeometryToGML();

void testExpressionFromOgcFilter();
void testExpressionFromOgcFilter_data();
};


Expand Down Expand Up @@ -107,5 +110,108 @@ void TestQgsOgcUtils::testGeometryToGML()
}


void TestQgsOgcUtils::testExpressionFromOgcFilter_data()
{
QTest::addColumn<QString>( "xmlText" );
QTest::addColumn<QString>( "dumpText" );

QTest::newRow( "=" ) << QString(
"<Filter><PropertyIsEqualTo>"
"<PropertyName>NAME</PropertyName>"
"<Literal>New York</Literal>"
"</PropertyIsEqualTo></Filter>" )
<< QString( "NAME = 'New York'" );

QTest::newRow( ">" ) << QString(
"<Filter><PropertyIsGreaterThan>"
"<PropertyName>COUNT</PropertyName>"
"<Literal>3</Literal>"
"</PropertyIsGreaterThan></Filter>" )
<< QString( "COUNT > 3" );

QTest::newRow( "AND" ) << QString(
"<ogc:Filter>"
"<ogc:And>"
"<ogc:PropertyIsGreaterThanOrEqualTo>"
"<ogc:PropertyName>pop</ogc:PropertyName>"
"<ogc:Literal>50000</ogc:Literal>"
"</ogc:PropertyIsGreaterThanOrEqualTo>"
"<ogc:PropertyIsLessThan>"
"<ogc:PropertyName>pop</ogc:PropertyName>"
"<ogc:Literal>100000</ogc:Literal>"
"</ogc:PropertyIsLessThan>"
"</ogc:And>"
"</ogc:Filter>" )
<< QString( "pop >= 50000 AND pop < 100000" );

// TODO: should work also without <Literal> tags in Lower/Upper-Boundary tags?
QTest::newRow( "between" ) << QString(
"<Filter>"
"<PropertyIsBetween><PropertyName>POPULATION</PropertyName>"
"<LowerBoundary><Literal>100</Literal></LowerBoundary>"
"<UpperBoundary><Literal>200</Literal></UpperBoundary></PropertyIsBetween>"
"</Filter>" )
<< QString( "POPULATION >= 100 AND POPULATION <= 200" );

// TODO: needs to handle different wildcards, single chars, escape chars
QTest::newRow( "like" ) << QString(
"<Filter>"
"<PropertyIsLike wildcard='*' singleChar='.' escape='!'>"
"<PropertyName>NAME</PropertyName><Literal>*QGIS*</Literal></PropertyIsLike>"
"</Filter>" )
<< QString( "NAME LIKE '*QGIS*'" );

QTest::newRow( "is null" ) << QString(
"<Filter>"
"<ogc:PropertyIsNull>"
"<ogc:PropertyName>FIRST_NAME</ogc:PropertyName>"
"</ogc:PropertyIsNull>"
"</Filter>" )
<< QString( "FIRST_NAME IS NULL" );

QTest::newRow( "bbox" ) << QString(
"<Filter>"
"<BBOX><PropertyName>Name>NAME</PropertyName><gml:Box srsName='foo'>"
"<gml:coordinates>135.2239,34.4879 135.8578,34.8471</gml:coordinates></gml:Box></BBOX>"
"</Filter>" )
<< QString( "bbox($geometry, geomFromGML2('<Box srsName=\"foo\"><coordinates>135.2239,34.4879 135.8578,34.8471</coordinates></Box>'))" );

QTest::newRow( "Intersects" ) << QString(
"<Filter>"
"<Intersects>"
"<PropertyName>GEOMETRY</PropertyName>"
"<gml:Point>"
"<gml:coordinates>123,456</gml:coordinates>"
"</gml:Point>"
"</Intersects>"
"</Filter>" )
<< QString( "intersects($geometry, geomFromGML2('<Point><coordinates>123,456</coordinates></Point>'))" );
}

void TestQgsOgcUtils::testExpressionFromOgcFilter()
{
QFETCH( QString, xmlText );
QFETCH( QString, dumpText );

QDomDocument doc;
QVERIFY( doc.setContent( xmlText, true ) );
QDomElement rootElem = doc.documentElement();

QgsExpression* expr = QgsOgcUtils::expressionFromOgcFilter( rootElem );
QVERIFY( expr );

qDebug( "OGC XML : %s", xmlText.toAscii().data() );
qDebug( "EXPR-DUMP: %s", expr->dump().toAscii().data() );

if ( expr->hasParserError() )
qDebug( "ERROR: %s ", expr->parserErrorString().toAscii().data() );
QVERIFY( !expr->hasParserError() );

QCOMPARE( dumpText, expr->dump() );

delete expr;
}


QTEST_MAIN( TestQgsOgcUtils )
#include "moc_testqgsogcutils.cxx"