394 changes: 226 additions & 168 deletions src/core/qgsexpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,15 @@ static QgsExpression::Interval getInterval( const QVariant& value, QgsExpression
return QgsExpression::Interval::invalidInterVal();
}

static QgsGeometry* getGeometry( const QVariant& value, QgsExpression* parent)
{
if ( value.canConvert<QgsGeometry*>() )
return value.value<QgsGeometry*>();

parent->setEvalErrorString( "Cannot convert to QgsGeometry" );
return 0;
}


// this handles also NULL values
static TVL getTVLValue( const QVariant& value, QgsExpression* parent )
Expand Down Expand Up @@ -782,7 +791,34 @@ static QVariant fcnGeomFromGML2( const QVariantList& values, QgsFeature*, QgsExp
if ( !doc.setContent( gml, true, &errorMsg ) )
return QVariant();

QgsGeometry* geom = QgsGeometry::fromGML2( doc.documentElement() );
QgsGeometry* geom = 0;
QDomElement elem = doc.documentElement();
if ( elem.tagName() == "Box" )
{
QDomElement bElem = elem.firstChild().toElement();
QString coordSeparator = ",";
QString tupelSeparator = " ";
if ( bElem.hasAttribute( "cs" ) )
{
coordSeparator = bElem.attribute( "cs" );
}
if ( bElem.hasAttribute( "ts" ) )
{
tupelSeparator = bElem.attribute( "ts" );
}

QString bString = bElem.text();
bool conversionSuccess;
double minx = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 0, 0 ).toDouble( &conversionSuccess );
double miny = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 1, 1 ).toDouble( &conversionSuccess );
double maxx = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 0, 0 ).toDouble( &conversionSuccess );
double maxy = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 1, 1 ).toDouble( &conversionSuccess );
QgsRectangle* rect = new QgsRectangle( minx, miny, maxx, maxy );
geom = QgsGeometry::fromRect( *rect );
}
else
geom = QgsGeometry::fromGML2( doc.documentElement() );

if ( geom )
return QVariant::fromValue( geom );
else
Expand All @@ -808,6 +844,71 @@ static QVariant fcnGeomPerimeter( const QVariantList& , QgsFeature* f, QgsExpres
return QVariant( calc->measurePerimeter( f->geometry() ) );
}

static QVariant fcnBbox( const QVariantList& values, QgsFeature* , QgsExpression* parent )
{
QgsGeometry* fGeom = getGeometry( values.at( 0 ), parent );
QgsGeometry* sGeom = getGeometry( values.at( 1 ), parent );
if ( fGeom && sGeom )
return fGeom->intersects( sGeom->boundingBox() ) ? TVL_True : TVL_False;
return QVariant();
}
static QVariant fcnDisjoint( const QVariantList& values, QgsFeature* , QgsExpression* parent )
{
QgsGeometry* fGeom = getGeometry( values.at( 0 ), parent );
QgsGeometry* sGeom = getGeometry( values.at( 1 ), parent );
if ( fGeom && sGeom )
return fGeom->disjoint( sGeom ) ? TVL_True : TVL_False;
return QVariant();
}
static QVariant fcnIntersects( const QVariantList& values, QgsFeature* , QgsExpression* parent )
{
QgsGeometry* fGeom = getGeometry( values.at( 0 ), parent );
QgsGeometry* sGeom = getGeometry( values.at( 1 ), parent );
if ( fGeom && sGeom )
return fGeom->intersects( sGeom ) ? TVL_True : TVL_False;
return QVariant();
}
static QVariant fcnTouches( const QVariantList& values, QgsFeature* , QgsExpression* parent )
{
QgsGeometry* fGeom = getGeometry( values.at( 0 ), parent );
QgsGeometry* sGeom = getGeometry( values.at( 1 ), parent );
if ( fGeom && sGeom )
return fGeom->touches( sGeom ) ? TVL_True : TVL_False;
return QVariant();
}
static QVariant fcnCrosses( const QVariantList& values, QgsFeature* , QgsExpression* parent )
{
QgsGeometry* fGeom = getGeometry( values.at( 0 ), parent );
QgsGeometry* sGeom = getGeometry( values.at( 1 ), parent );
if ( fGeom && sGeom )
return fGeom->crosses( sGeom ) ? TVL_True : TVL_False;
return QVariant();
}
static QVariant fcnContains( const QVariantList& values, QgsFeature* , QgsExpression* parent )
{
QgsGeometry* fGeom = getGeometry( values.at( 0 ), parent );
QgsGeometry* sGeom = getGeometry( values.at( 1 ), parent );
if ( fGeom && sGeom )
return fGeom->contains( sGeom ) ? TVL_True : TVL_False;
return QVariant();
}
static QVariant fcnOverlaps( const QVariantList& values, QgsFeature* , QgsExpression* parent )
{
QgsGeometry* fGeom = getGeometry( values.at( 0 ), parent );
QgsGeometry* sGeom = getGeometry( values.at( 1 ), parent );
if ( fGeom && sGeom )
return fGeom->overlaps( sGeom ) ? TVL_True : TVL_False;
return QVariant();
}
static QVariant fcnWithin( const QVariantList& values, QgsFeature* , QgsExpression* parent )
{
QgsGeometry* fGeom = getGeometry( values.at( 0 ), parent );
QgsGeometry* sGeom = getGeometry( values.at( 1 ), parent );
if ( fGeom && sGeom )
return fGeom->within( sGeom ) ? TVL_True : TVL_False;
return QVariant();
}

static QVariant fcnRound( const QVariantList& values , QgsFeature *f, QgsExpression* parent )
{
Q_UNUSED( f );
Expand Down Expand Up @@ -966,6 +1067,14 @@ const QList<QgsExpression::Function*> &QgsExpression::Functions()
<< new StaticFunction( "$geometry", 0, fcnGeometry, QObject::tr( "Geometry" ), "" , true )
<< new StaticFunction( "GeomFromWKT", 1, fcnGeomFromWKT, QObject::tr( "Geometry" ) )
<< new StaticFunction( "GeomFromGML2", 1, fcnGeomFromGML2, QObject::tr( "Geometry" ) )
<< new StaticFunction( "bbox", 2, fcnBbox, QObject::tr( "Geometry" ) )
<< new StaticFunction( "disjoint", 2, fcnDisjoint, QObject::tr( "Geometry" ) )
<< new StaticFunction( "intersects", 2, fcnIntersects, QObject::tr( "Geometry" ) )
<< new StaticFunction( "touches", 2, fcnTouches, QObject::tr( "Geometry" ) )
<< new StaticFunction( "crosses", 2, fcnCrosses, QObject::tr( "Geometry" ) )
<< new StaticFunction( "contains", 2, fcnContains, QObject::tr( "Geometry" ) )
<< new StaticFunction( "overlaps", 2, fcnOverlaps, QObject::tr( "Geometry" ) )
<< new StaticFunction( "within", 2, fcnWithin, QObject::tr( "Geometry" ) )
<< new StaticFunction( "$rownum", 0, fcnRowNumber, QObject::tr( "Record" ) )
<< new StaticFunction( "$id", 0, fcnFeatureId, QObject::tr( "Record" ) )
<< new StaticFunction( "$scale", 0, fcnScale, QObject::tr( "Record" ) )
Expand Down Expand Up @@ -1331,7 +1440,7 @@ QgsExpression::Node* QgsExpression::Node::createFromOgcFilter( QDomElement &elem

if ( element.tagName() == ogcOperatorName )
{
QgsExpression::Node *node = QgsExpression::NodeSpatialOperator::createFromOgcFilter( element, errorMessage );
QgsExpression::Node *node = QgsExpression::NodeFunction::createFromOgcFilter( element, errorMessage );
if ( node )
return node;

Expand Down Expand Up @@ -1929,168 +2038,6 @@ QgsExpression::Node* QgsExpression::NodeBinaryOperator::createFromOgcFilter( QDo

return NULL;
}

//

QVariant QgsExpression::NodeSpatialOperator::eval( QgsExpression* parent, QgsFeature* f )
{
QgsGeometry* geom = f->geometry();

switch ( mOp )
{
case soBbox:
return geom->intersects( mOpGeometry->boundingBox() ) ? TVL_True : TVL_False;
case soIntersects:
return geom->intersects( mOpGeometry ) ? TVL_True : TVL_False;
case soContains:
return geom->contains( mOpGeometry ) ? TVL_True : TVL_False;
case soCrosses:
return geom->crosses( mOpGeometry ) ? TVL_True : TVL_False;
case soEquals:
return geom->equals( mOpGeometry ) ? TVL_True : TVL_False;
case soDisjoint:
return geom->disjoint( mOpGeometry ) ? TVL_True : TVL_False;
case soOverlaps:
return geom->overlaps( mOpGeometry ) ? TVL_True : TVL_False;
case soTouches:
return geom->touches( mOpGeometry ) ? TVL_True : TVL_False;
case soWithin:
return geom->within( mOpGeometry ) ? TVL_True : TVL_False;
}
return TVL_False;
}

bool QgsExpression::NodeSpatialOperator::prepare( QgsExpression* /*parent*/, const QgsFieldMap& /*fields*/ )
{
return true;
}

QString QgsExpression::NodeSpatialOperator::dump() const
{
switch ( mOp )
{
case soBbox:
return QString( "Intersects('%1')" ).arg( mOpGeometry->boundingBox().asWktPolygon() );
case soIntersects:
return QString( "Intersects('%1')" ).arg( mOpGeometry->exportToWkt() );
}
return "";
}

QgsExpression::Node* QgsExpression::NodeSpatialOperator::createFromOgcFilter( QDomElement &element, QString &errorMessage )
{
if ( element.isNull() )
return NULL;

bool geomOk = false;
QgsGeometry* geom = new QgsGeometry();

QDomNodeList bNodes = element.elementsByTagName( "Box" );
if ( bNodes.size() > 0 ) {
QDomElement bElem = bNodes.at( 0 ).toElement().firstChild().toElement();
QString coordSeparator = ",";
QString tupelSeparator = " ";
if ( bElem.hasAttribute( "cs" ) )
{
coordSeparator = bElem.attribute( "cs" );
}
if ( bElem.hasAttribute( "ts" ) )
{
tupelSeparator = bElem.attribute( "ts" );
}

QString bString = bElem.text();
bool conversionSuccess;
double minx = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 0, 0 ).toDouble( &conversionSuccess );
double miny = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 1, 1 ).toDouble( &conversionSuccess );
double maxx = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 0, 0 ).toDouble( &conversionSuccess );
double maxy = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 1, 1 ).toDouble( &conversionSuccess );
QgsRectangle* rect = new QgsRectangle( minx, miny, maxx, maxy );
geom = QgsGeometry::fromRect( *rect );
geomOk = true;
}

if ( !geomOk )
{
QDomNodeList gNodes = element.elementsByTagName( "MultiPolygon" );
if ( gNodes.size() > 0 ) {
QDomElement gElem = gNodes.at( 0 ).toElement();
geom = QgsGeometry::fromGML2( gElem );
geomOk = true;
}
}

if ( !geomOk )
{
QDomNodeList gNodes = element.elementsByTagName( "MultiLineString" );
if ( gNodes.size() > 0 ) {
QDomElement gElem = gNodes.at( 0 ).toElement();
geom = QgsGeometry::fromGML2( gElem );
geomOk = true;
}
}

if ( !geomOk )
{
QDomNodeList gNodes = element.elementsByTagName( "MultiPoint" );
if ( gNodes.size() > 0 ) {
QDomElement gElem = gNodes.at( 0 ).toElement();
geom = QgsGeometry::fromGML2( gElem );
geomOk = true;
}
}

if ( !geomOk )
{
QDomNodeList gNodes = element.elementsByTagName( "Polygon" );
if ( gNodes.size() > 0 ) {
QDomElement gElem = gNodes.at( 0 ).toElement();
geom = QgsGeometry::fromGML2( gElem );
geomOk = true;
}
}

if ( !geomOk )
{
QDomNodeList gNodes = element.elementsByTagName( "LineString" );
if ( gNodes.size() > 0 ) {
QDomElement gElem = gNodes.at( 0 ).toElement();
geom = QgsGeometry::fromGML2( gElem );
geomOk = true;
}
}

if ( !geomOk )
{
QDomNodeList gNodes = element.elementsByTagName( "Point" );
if ( gNodes.size() > 0 ) {
QDomElement gElem = gNodes.at( 0 ).toElement();
geom = QgsGeometry::fromGML2( gElem );
geomOk = true;
}
}

if ( !geomOk )
{
errorMessage = QString( "invalid geometry" );
return NULL;
}

int spatialOpCount = sizeof( SpatialOgcOperatorText ) / sizeof( SpatialOgcOperatorText[0] );
for ( int i = 0; i < spatialOpCount; i++ )
{
QString ogcOperatorName = SpatialOgcOperatorText[ i ];
if ( ogcOperatorName.isEmpty() )
continue;

if ( element.tagName() != ogcOperatorName )
continue;

return new QgsExpression::NodeSpatialOperator(( SpatialOperator ) i, geom );
}
return NULL;
}

//

QVariant QgsExpression::NodeInOperator::eval( QgsExpression* parent, QgsFeature* f )
Expand Down Expand Up @@ -2235,17 +2182,128 @@ void QgsExpression::NodeFunction::toOgcFilter( QDomDocument &doc, QDomElement &e
if ( fd->params() == 0 )
return; // TODO: special column

QDomElement funcElem = doc.createElement( "ogc:Function" );
funcElem.setAttribute( "name", fd->name() );
mArgs->toOgcFilter( doc, funcElem );
element.appendChild( funcElem );
bool isSpatial = false;
// check for spatial operators
int spatialOpCount = sizeof( SpatialOgcOperatorText ) / sizeof( SpatialOgcOperatorText[0] );
for ( int i = 0; i < spatialOpCount; i++ )
{
QString ogcOperatorName = SpatialOgcOperatorText[ i ];
if ( ogcOperatorName.isEmpty() )
continue;

if ( fd.mName == ogcOperatorName.toLower() )
{
isSpatial = true;
QDomElement funcElem = doc.createElement( "ogc:"+ogcOperatorName );
QDomElement geomProperty = doc.createElement( "ogc:PropertyName" );
geomProperty.appendChild( doc.createTextNode( "geometry" ) );
funcElem.appendChild( geomProperty );

QDomDocument tmpDoc;
QDomElement tmpElem = tmpDoc.createElement( "tmp" );
mArgs->toOgcFilter( tmpDoc, tmpElem );
QDomElement childElem = funcElem.firstChildElement();
while ( !childElem.isNull() )
{
if ( childElem.tagName() == "Function" )
{
if ( childElem.attribute( "name" ) == "geomFromWKT" )
{
QgsGeometry* geom = 0;
geom = QgsGeometry::fromWkt( childElem.firstChildElement().text() );
if ( geom )
funcElem.appendChild( geom->exportToGML2( doc ) );

}
else if ( childElem.attribute( "name" ) == "geomFromGML2" )
{
QDomDocument geomDoc;
QString errorMsg;
QString gml = childElem.firstChildElement().text();
if ( geomDoc.setContent( gml, true, &errorMsg ) )
funcElem.appendChild( doc.documentElement() );
}
}
childElem = childElem.nextSiblingElement();
}

element.appendChild( funcElem );
}
}

if ( !isSpatial )
{
QDomElement funcElem = doc.createElement( "ogc:Function" );
funcElem.setAttribute( "name", fd.mName );
mArgs->toOgcFilter( doc, funcElem );
element.appendChild( funcElem );
}
}

QgsExpression::Node* QgsExpression::NodeFunction::createFromOgcFilter( QDomElement &element, QString &errorMessage )
{
if ( element.isNull() )
return NULL;

// check for spatial operators
int spatialOpCount = sizeof( SpatialOgcOperatorText ) / sizeof( SpatialOgcOperatorText[0] );
for ( int i = 0; i < spatialOpCount; i++ )
{
QString ogcOperatorName = SpatialOgcOperatorText[ i ];
if ( ogcOperatorName.isEmpty() )
continue;

if ( element.tagName() == ogcOperatorName )
{
int geomIdx = 0;
int gml2Idx = 0;
int opeIdx = 0;
for ( int j = 0; j < BuiltinFunctions().size(); j++ )
{
QgsExpression::FunctionDef funcDef = BuiltinFunctions()[j];
if ( funcDef.mName == "$geometry" )
geomIdx = j;
else if ( funcDef.mName == "geomFromGML2" )
gml2Idx = j;
else if ( funcDef.mName == ogcOperatorName.toLower() )
opeIdx = j;
}

if (geomIdx == 0 || gml2Idx == 0 || opeIdx == 0 )
{
errorMessage = QString( "spatial functions not find, got %1, %2, %3" ).arg( geomIdx ).arg( gml2Idx ).arg( opeIdx );
return NULL;
}

QgsExpression::NodeList *gml2Args = new QgsExpression::NodeList();
QDomElement childElem = element.firstChildElement();
QString gml2Str = "";
while ( !childElem.isNull() && gml2Str == "" ) {
if ( childElem.tagName() != "PropertyName" )
{
QTextStream gml2Stream( &gml2Str );
childElem.save( gml2Stream, 0 );
}
childElem = childElem.nextSiblingElement();
}
if ( gml2Str != "" )
{
gml2Args->append( new QgsExpression::NodeLiteral( QVariant( gml2Str.remove( "\n" ) ) ) );
}
else
{
errorMessage = QString( "No OGC Geometry found" );
return NULL;
}

QgsExpression::NodeList *opeArgs = new QgsExpression::NodeList();
opeArgs->append( new QgsExpression::NodeFunction( geomIdx, new QgsExpression::NodeList() ) );
opeArgs->append( new QgsExpression::NodeFunction( gml2Idx, gml2Args ) );

return new QgsExpression::NodeFunction( opeIdx, opeArgs );
}
}

if ( element.tagName() != "Function" )
{
errorMessage = QString( "ogc:Function expected, got %1" ).arg( element.tagName() );
Expand Down
25 changes: 0 additions & 25 deletions src/core/qgsexpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,31 +430,6 @@ class CORE_EXPORT QgsExpression
Node* mOpRight;
};

class CORE_EXPORT NodeSpatialOperator : public Node
{
public:
NodeSpatialOperator( SpatialOperator op, QgsGeometry* opGeom ) : mOp( op ), mOpGeometry( 0 ) { if ( opGeom ) mOpGeometry = opGeom; }
~NodeSpatialOperator() { }

virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual QString dump() const;

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

SpatialOperator op() { return mOp; }
QgsGeometry* opGeometry() { return mOpGeometry; }

virtual QStringList referencedColumns() const { QStringList lst; return lst; }
virtual bool needsGeometry() const { return true; }
virtual void accept( Visitor& /*v*/ ) { }

protected:
SpatialOperator mOp;
QgsGeometry* mOpGeometry;
};

class CORE_EXPORT NodeInOperator : public Node
{
public:
Expand Down
42 changes: 20 additions & 22 deletions src/mapserver/qgswfsserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,14 @@ QDomDocument QgsWFSServer::getCapabilities()
spatialOperatorsElement.appendChild( doc.createElement( "ogc:Crosses"/*ogc:Crosses*/ ) );
spatialOperatorsElement.appendChild( doc.createElement( "ogc:Contains"/*ogc:Contains*/ ) );
spatialOperatorsElement.appendChild( doc.createElement( "ogc:Overlaps"/*ogc:Overlaps*/ ) );
spatialOperatorsElement.appendChild( doc.createElement( "ogc:Within"/*ogc:Within*/ ) );
QDomElement scalarCapabilitiesElement = doc.createElement( "ogc:Scalar_Capabilities"/*ogc:Scalar_Capabilities*/ );
filterCapabilitiesElement.appendChild( scalarCapabilitiesElement );
QDomElement comparisonOperatorsElement = doc.createElement( "ogc:Comparison_Operators"/*ogc:Comparison_Operators*/ );
scalarCapabilitiesElement.appendChild( comparisonOperatorsElement );
comparisonOperatorsElement.appendChild( doc.createElement( "ogc:Simple_Comparisons"/*ogc:Simple_Comparisons*/ ) );
comparisonOperatorsElement.appendChild( doc.createElement( "ogc:Between"/*ogc:Simple_Comparisons*/ ) );
comparisonOperatorsElement.appendChild( doc.createElement( "ogc:Between"/*ogc:Between*/ ) );
comparisonOperatorsElement.appendChild( doc.createElement( "ogc:Like"/*ogc:Like*/ ) );
return doc;
}

Expand Down Expand Up @@ -452,20 +454,6 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
++featCounter;
}
}
/*
QgsFilter* mFilter = QgsFilter::createFilterFromXml( filterElem.firstChild().toElement(), layer );
if ( mFilter )
{
while ( provider->nextFeature( feature ) && featureCounter < maxFeat )
{
if ( mFilter->evaluate( feature ) )
{
sendGetFeature( request, format, &feature, featCounter, layerCrs, fields, layerExcludedAttributes );
++featCounter;
++featureCounter;
}
}
*/
}
}
}
Expand Down Expand Up @@ -1255,12 +1243,12 @@ QDomDocument QgsWFSServer::transaction( const QString& requestBody )
return resp;
}

QgsFeatureIds QgsWFSServer::getFeatureIdsFromFilter( QDomElement filter, QgsVectorLayer* layer )
QgsFeatureIds QgsWFSServer::getFeatureIdsFromFilter( QDomElement filterElem, QgsVectorLayer* layer )
{
QgsFeatureIds fids;

QgsVectorDataProvider* provider = layer->dataProvider();
QDomNodeList fidNodes = filter.elementsByTagName( "FeatureId" );
QDomNodeList fidNodes = filterElem.elementsByTagName( "FeatureId" );

if ( fidNodes.size() != 0 )
{
Expand All @@ -1274,13 +1262,23 @@ QgsFeatureIds QgsWFSServer::getFeatureIdsFromFilter( QDomElement filter, QgsVect
}
else
{
QgsFeature feature;
QgsFilter* mFilter = QgsFilter::createFilterFromXml( filter, layer );
while ( provider->nextFeature( feature ) )
QgsExpression *mFilter = QgsExpression::createFromOgcFilter( filterElem );
if (mFilter->hasParserError())
{
if ( mFilter )
throw QgsMapServiceException( "RequestNotWellFormed", mFilter->parserErrorString() );
}
if ( mFilter )
{
QgsFeature feature;
const QgsFieldMap& fields = provider->fields();
while ( provider->nextFeature( feature ) )
{
if ( mFilter->evaluate( feature ) )
QVariant res = mFilter->evaluate( &feature, fields );
if (mFilter->hasEvalError())
{
throw QgsMapServiceException( "RequestNotWellFormed", mFilter->evalErrorString() );
}
if ( res.toInt() != 0 )
{
fids.insert( feature.id() );
}
Expand Down