Skip to content

Commit 4ca2160

Browse files
committed
Add Spatial Operator to QgsExpression and use it in QGIS WFS Server
1 parent 149b24c commit 4ca2160

File tree

4 files changed

+293
-33
lines changed

4 files changed

+293
-33
lines changed

src/core/qgsexpression.cpp

Lines changed: 203 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,15 @@ const char* QgsExpression::UnaryOgcOperatorText[] =
226226
"Not", ""
227227
};
228228

229+
const char* QgsExpression::SpatialOgcOperatorText[] =
230+
{
231+
"BBOX", "Intersects",
232+
"Contians", "Crosses",
233+
"Equals", "Disjoint",
234+
"Overlaps", "Touches",
235+
"Within"
236+
};
237+
229238
///////////////////////////////////////////////
230239
// functions
231240

@@ -1250,7 +1259,7 @@ QgsExpression::Node* QgsExpression::Node::createFromOgcFilter( QDomElement &elem
12501259
if ( ogcOperatorName.isEmpty() )
12511260
continue;
12521261

1253-
if ( element.localName() == ogcOperatorName )
1262+
if ( element.tagName() == ogcOperatorName )
12541263
{
12551264
QgsExpression::Node *node = QgsExpression::NodeUnaryOperator::createFromOgcFilter( element, errorMessage );
12561265
if ( node )
@@ -1268,7 +1277,7 @@ QgsExpression::Node* QgsExpression::Node::createFromOgcFilter( QDomElement &elem
12681277
if ( ogcOperatorName.isEmpty() )
12691278
continue;
12701279

1271-
if ( element.localName() == ogcOperatorName )
1280+
if ( element.tagName() == ogcOperatorName )
12721281
{
12731282
QgsExpression::Node *node = QgsExpression::NodeBinaryOperator::createFromOgcFilter( element, errorMessage );
12741283
if ( node )
@@ -1278,25 +1287,43 @@ QgsExpression::Node* QgsExpression::Node::createFromOgcFilter( QDomElement &elem
12781287
}
12791288
}
12801289

1290+
// check for spatial operators
1291+
int spatialOpCount = sizeof( SpatialOgcOperatorText ) / sizeof( SpatialOgcOperatorText[0] );
1292+
for ( int i = 0; i < spatialOpCount; i++ )
1293+
{
1294+
QString ogcOperatorName = SpatialOgcOperatorText[ i ];
1295+
if ( ogcOperatorName.isEmpty() )
1296+
continue;
1297+
1298+
if ( element.tagName() == ogcOperatorName )
1299+
{
1300+
QgsExpression::Node *node = QgsExpression::NodeSpatialOperator::createFromOgcFilter( element, errorMessage );
1301+
if ( node )
1302+
return node;
1303+
1304+
return NULL;
1305+
}
1306+
}
1307+
12811308
// check for other OGC operators, convert them to expressions
12821309

1283-
if ( element.localName() == "PropertyIsNull" )
1310+
if ( element.tagName() == "PropertyIsNull" )
12841311
{
12851312
return QgsExpression::NodeBinaryOperator::createFromOgcFilter( element, errorMessage );
12861313
}
1287-
else if ( element.localName() == "Literal" )
1314+
else if ( element.tagName() == "Literal" )
12881315
{
12891316
return QgsExpression::NodeLiteral::createFromOgcFilter( element, errorMessage );
12901317
}
1291-
else if ( element.localName() == "Function" )
1318+
else if ( element.tagName() == "Function" )
12921319
{
12931320
return QgsExpression::NodeFunction::createFromOgcFilter( element, errorMessage );
12941321
}
1295-
else if ( element.localName() == "PropertyName" )
1322+
else if ( element.tagName() == "PropertyName" )
12961323
{
12971324
return QgsExpression::NodeColumnRef::createFromOgcFilter( element, errorMessage );
12981325
}
1299-
else if ( element.localName() == "PropertyIsBetween" )
1326+
else if ( element.tagName() == "PropertyIsBetween" )
13001327
{
13011328
// <ogc:PropertyIsBetween> encode a Range check
13021329
QgsExpression::Node *operand = 0, *lowerBound = 0;
@@ -1305,12 +1332,12 @@ QgsExpression::Node* QgsExpression::Node::createFromOgcFilter( QDomElement &elem
13051332
QDomElement operandElem = element.firstChildElement();
13061333
while ( !operandElem.isNull() )
13071334
{
1308-
if ( operandElem.localName() == "LowerBoundary" )
1335+
if ( operandElem.tagName() == "LowerBoundary" )
13091336
{
13101337
QDomElement lowerBoundElem = operandElem.firstChildElement();
13111338
lowerBound = createFromOgcFilter( lowerBoundElem, errorMessage );
13121339
}
1313-
else if ( operandElem.localName() == "UpperBoundary" )
1340+
else if ( operandElem.tagName() == "UpperBoundary" )
13141341
{
13151342
QDomElement upperBoundElem = operandElem.firstChildElement();
13161343
upperBound = createFromOgcFilter( upperBoundElem, errorMessage );
@@ -1449,7 +1476,7 @@ QgsExpression::Node* QgsExpression::NodeUnaryOperator::createFromOgcFilter( QDom
14491476
if ( ogcOperatorName.isEmpty() )
14501477
continue;
14511478

1452-
if ( element.localName() != ogcOperatorName )
1479+
if ( element.tagName() != ogcOperatorName )
14531480
continue;
14541481

14551482
QDomElement operandElem = element.firstChildElement();
@@ -1811,7 +1838,7 @@ QgsExpression::Node* QgsExpression::NodeBinaryOperator::createFromOgcFilter( QDo
18111838
QgsExpression::Node* opRight = 0;
18121839

18131840
// convert ogc:PropertyIsNull to IS operator with NULL right operand
1814-
if ( element.localName() == "PropertyIsNull" )
1841+
if ( element.tagName() == "PropertyIsNull" )
18151842
{
18161843
QDomElement operandElem = element.firstChildElement();
18171844
opLeft = QgsExpression::Node::createFromOgcFilter( operandElem, errorMessage );
@@ -1830,7 +1857,7 @@ QgsExpression::Node* QgsExpression::NodeBinaryOperator::createFromOgcFilter( QDo
18301857
if ( ogcOperatorName.isEmpty() )
18311858
continue;
18321859

1833-
if ( element.localName() != ogcOperatorName )
1860+
if ( element.tagName() != ogcOperatorName )
18341861
continue;
18351862

18361863
QDomElement operandElem = element.firstChildElement();
@@ -1871,6 +1898,167 @@ QgsExpression::Node* QgsExpression::NodeBinaryOperator::createFromOgcFilter( QDo
18711898

18721899
//
18731900

1901+
QVariant QgsExpression::NodeSpatialOperator::eval( QgsExpression* parent, QgsFeature* f )
1902+
{
1903+
QgsGeometry* geom = f->geometry();
1904+
1905+
switch ( mOp )
1906+
{
1907+
case soBbox:
1908+
return geom->intersects( mOpGeometry->boundingBox() ) ? TVL_True : TVL_False;
1909+
case soIntersects:
1910+
return geom->intersects( mOpGeometry ) ? TVL_True : TVL_False;
1911+
case soContains:
1912+
return geom->contains( mOpGeometry ) ? TVL_True : TVL_False;
1913+
case soCrosses:
1914+
return geom->crosses( mOpGeometry ) ? TVL_True : TVL_False;
1915+
case soEquals:
1916+
return geom->equals( mOpGeometry ) ? TVL_True : TVL_False;
1917+
case soDisjoint:
1918+
return geom->disjoint( mOpGeometry ) ? TVL_True : TVL_False;
1919+
case soOverlaps:
1920+
return geom->overlaps( mOpGeometry ) ? TVL_True : TVL_False;
1921+
case soTouches:
1922+
return geom->touches( mOpGeometry ) ? TVL_True : TVL_False;
1923+
case soWithin:
1924+
return geom->within( mOpGeometry ) ? TVL_True : TVL_False;
1925+
}
1926+
return TVL_False;
1927+
}
1928+
1929+
bool QgsExpression::NodeSpatialOperator::prepare( QgsExpression* /*parent*/, const QgsFieldMap& /*fields*/ )
1930+
{
1931+
return true;
1932+
}
1933+
1934+
QString QgsExpression::NodeSpatialOperator::dump() const
1935+
{
1936+
switch ( mOp )
1937+
{
1938+
case soBbox:
1939+
return QString( "Intersects('%1')" ).arg( mOpGeometry->boundingBox().asWktPolygon() );
1940+
case soIntersects:
1941+
return QString( "Intersects('%1')" ).arg( mOpGeometry->exportToWkt() );
1942+
}
1943+
return "";
1944+
}
1945+
1946+
QgsExpression::Node* QgsExpression::NodeSpatialOperator::createFromOgcFilter( QDomElement &element, QString &errorMessage )
1947+
{
1948+
if ( element.isNull() )
1949+
return NULL;
1950+
1951+
bool geomOk = false;
1952+
QgsGeometry* geom = new QgsGeometry();
1953+
1954+
QDomNodeList bNodes = element.elementsByTagName( "Box" );
1955+
if ( bNodes.size() > 0 ) {
1956+
QDomElement bElem = bNodes.at( 0 ).toElement().firstChild().toElement();
1957+
QString coordSeparator = ",";
1958+
QString tupelSeparator = " ";
1959+
if ( bElem.hasAttribute( "cs" ) )
1960+
{
1961+
coordSeparator = bElem.attribute( "cs" );
1962+
}
1963+
if ( bElem.hasAttribute( "ts" ) )
1964+
{
1965+
tupelSeparator = bElem.attribute( "ts" );
1966+
}
1967+
1968+
QString bString = bElem.text();
1969+
bool conversionSuccess;
1970+
double minx = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 0, 0 ).toDouble( &conversionSuccess );
1971+
double miny = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 1, 1 ).toDouble( &conversionSuccess );
1972+
double maxx = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 0, 0 ).toDouble( &conversionSuccess );
1973+
double maxy = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 1, 1 ).toDouble( &conversionSuccess );
1974+
QgsRectangle* rect = new QgsRectangle( minx, miny, maxx, maxy );
1975+
geom = QgsGeometry::fromRect( *rect );
1976+
geomOk = true;
1977+
}
1978+
1979+
if ( !geomOk )
1980+
{
1981+
QDomNodeList gNodes = element.elementsByTagName( "MultiPolygon" );
1982+
if ( gNodes.size() > 0 ) {
1983+
QDomElement gElem = gNodes.at( 0 ).toElement();
1984+
geom = QgsGeometry::fromGML2( gElem );
1985+
geomOk = true;
1986+
}
1987+
}
1988+
1989+
if ( !geomOk )
1990+
{
1991+
QDomNodeList gNodes = element.elementsByTagName( "MultiLineString" );
1992+
if ( gNodes.size() > 0 ) {
1993+
QDomElement gElem = gNodes.at( 0 ).toElement();
1994+
geom = QgsGeometry::fromGML2( gElem );
1995+
geomOk = true;
1996+
}
1997+
}
1998+
1999+
if ( !geomOk )
2000+
{
2001+
QDomNodeList gNodes = element.elementsByTagName( "MultiPoint" );
2002+
if ( gNodes.size() > 0 ) {
2003+
QDomElement gElem = gNodes.at( 0 ).toElement();
2004+
geom = QgsGeometry::fromGML2( gElem );
2005+
geomOk = true;
2006+
}
2007+
}
2008+
2009+
if ( !geomOk )
2010+
{
2011+
QDomNodeList gNodes = element.elementsByTagName( "Polygon" );
2012+
if ( gNodes.size() > 0 ) {
2013+
QDomElement gElem = gNodes.at( 0 ).toElement();
2014+
geom = QgsGeometry::fromGML2( gElem );
2015+
geomOk = true;
2016+
}
2017+
}
2018+
2019+
if ( !geomOk )
2020+
{
2021+
QDomNodeList gNodes = element.elementsByTagName( "LineString" );
2022+
if ( gNodes.size() > 0 ) {
2023+
QDomElement gElem = gNodes.at( 0 ).toElement();
2024+
geom = QgsGeometry::fromGML2( gElem );
2025+
geomOk = true;
2026+
}
2027+
}
2028+
2029+
if ( !geomOk )
2030+
{
2031+
QDomNodeList gNodes = element.elementsByTagName( "Point" );
2032+
if ( gNodes.size() > 0 ) {
2033+
QDomElement gElem = gNodes.at( 0 ).toElement();
2034+
geom = QgsGeometry::fromGML2( gElem );
2035+
geomOk = true;
2036+
}
2037+
}
2038+
2039+
if ( !geomOk )
2040+
{
2041+
errorMessage = QString( "invalid geometry" );
2042+
return NULL;
2043+
}
2044+
2045+
int spatialOpCount = sizeof( SpatialOgcOperatorText ) / sizeof( SpatialOgcOperatorText[0] );
2046+
for ( int i = 0; i < spatialOpCount; i++ )
2047+
{
2048+
QString ogcOperatorName = SpatialOgcOperatorText[ i ];
2049+
if ( ogcOperatorName.isEmpty() )
2050+
continue;
2051+
2052+
if ( element.tagName() != ogcOperatorName )
2053+
continue;
2054+
2055+
return new QgsExpression::NodeSpatialOperator(( SpatialOperator ) i, geom );
2056+
}
2057+
return NULL;
2058+
}
2059+
2060+
//
2061+
18742062
QVariant QgsExpression::NodeInOperator::eval( QgsExpression* parent, QgsFeature* f )
18752063
{
18762064
if ( mList->count() == 0 )
@@ -2024,7 +2212,7 @@ QgsExpression::Node* QgsExpression::NodeFunction::createFromOgcFilter( QDomEleme
20242212
if ( element.isNull() )
20252213
return NULL;
20262214

2027-
if ( element.localName() != "Function" )
2215+
if ( element.tagName() != "Function" )
20282216
{
20292217
errorMessage = QString( "ogc:Function expected, got %1" ).arg( element.tagName() );
20302218
return NULL;
@@ -2116,7 +2304,7 @@ QgsExpression::Node* QgsExpression::NodeLiteral::createFromOgcFilter( QDomElemen
21162304
if ( element.isNull() )
21172305
return NULL;
21182306

2119-
if ( element.localName() != "Literal" )
2307+
if ( element.tagName() != "Literal" )
21202308
{
21212309
errorMessage = QString( "ogc:Literal expected, got %1" ).arg( element.tagName() );
21222310
return NULL;
@@ -2223,7 +2411,7 @@ QgsExpression::Node* QgsExpression::NodeColumnRef::createFromOgcFilter( QDomElem
22232411
if ( element.isNull() )
22242412
return NULL;
22252413

2226-
if ( element.localName() != "PropertyName" )
2414+
if ( element.tagName() != "PropertyName" )
22272415
{
22282416
errorMessage = QString( "ogc:PropertyName expected, got %1" ).arg( element.tagName() );
22292417
return NULL;

src/core/qgsexpression.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,25 @@ class CORE_EXPORT QgsExpression
194194
// strings
195195
boConcat,
196196
};
197+
enum SpatialOperator
198+
{
199+
soBbox,
200+
soIntersects,
201+
soContains,
202+
soCrosses,
203+
soEquals,
204+
soDisjoint,
205+
soOverlaps,
206+
soTouches,
207+
soWithin,
208+
};
197209

198210
static const char* BinaryOperatorText[];
199211
static const char* UnaryOperatorText[];
200212

201213
static const char* BinaryOgcOperatorText[];
202214
static const char* UnaryOgcOperatorText[];
215+
static const char* SpatialOgcOperatorText[];
203216

204217
typedef QVariant( *FcnEval )( const QVariantList& values, QgsFeature* f, QgsExpression* parent );
205218

@@ -417,6 +430,31 @@ class CORE_EXPORT QgsExpression
417430
Node* mOpRight;
418431
};
419432

433+
class CORE_EXPORT NodeSpatialOperator : public Node
434+
{
435+
public:
436+
NodeSpatialOperator( SpatialOperator op, QgsGeometry* opGeom ) : mOp( op ), mOpGeometry( 0 ) { if ( opGeom ) mOpGeometry = opGeom; }
437+
~NodeSpatialOperator() { delete mOpGeometry; }
438+
439+
virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
440+
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
441+
virtual QString dump() const;
442+
443+
virtual void toOgcFilter( QDomDocument& /*doc*/, QDomElement& /*element*/ ) const {};
444+
static QgsExpression::Node* createFromOgcFilter( QDomElement &element, QString &errorMessage );
445+
446+
SpatialOperator op() { return mOp; }
447+
QgsGeometry* opGeometry() { return mOpGeometry; }
448+
449+
virtual QStringList referencedColumns() const { QStringList lst; return lst; }
450+
virtual bool needsGeometry() const { return true; }
451+
virtual void accept( Visitor& /*v*/ ) { }
452+
453+
protected:
454+
SpatialOperator mOp;
455+
QgsGeometry* mOpGeometry;
456+
};
457+
420458
class CORE_EXPORT NodeInOperator : public Node
421459
{
422460
public:

0 commit comments

Comments
 (0)