QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsogcutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsogcutils.cpp
3  ---------------------
4  begin : March 2013
5  copyright : (C) 2013 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include "qgsogcutils.h"
16 
17 #include "qgsexpression.h"
18 #include "qgsexpressionnodeimpl.h"
19 #include "qgsexpressionfunction.h"
20 #include "qgsexpression_p.h"
21 #include "qgsgeometry.h"
22 #include "qgswkbptr.h"
24 #include "qgsrectangle.h"
25 #include "qgsvectorlayer.h"
27 #include "qgslogger.h"
28 #include "qgsstringutils.h"
29 
30 #include <QColor>
31 #include <QStringList>
32 #include <QTextStream>
33 #include <QObject>
34 #include <QRegularExpression>
35 
36 #ifndef Q_OS_WIN
37 #include <netinet/in.h>
38 #else
39 #include <winsock.h>
40 #endif
41 
42 
43 #define GML_NAMESPACE QStringLiteral( "http://www.opengis.net/gml" )
44 #define GML32_NAMESPACE QStringLiteral( "http://www.opengis.net/gml/3.2" )
45 #define OGC_NAMESPACE QStringLiteral( "http://www.opengis.net/ogc" )
46 #define FES_NAMESPACE QStringLiteral( "http://www.opengis.net/fes/2.0" )
47 
49  QgsOgcUtils::GMLVersion gmlVersion,
50  QgsOgcUtils::FilterVersion filterVersion,
51  const QString &geometryName,
52  const QString &srsName,
53  bool honourAxisOrientation,
54  bool invertAxisOrientation )
55  : mDoc( doc )
56  , mGMLUsed( false )
57  , mGMLVersion( gmlVersion )
58  , mFilterVersion( filterVersion )
59  , mGeometryName( geometryName )
60  , mSrsName( srsName )
61  , mInvertAxisOrientation( invertAxisOrientation )
62  , mFilterPrefix( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "fes" : "ogc" )
63  , mPropertyName( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "ValueReference" : "PropertyName" )
64  , mGeomId( 1 )
65 {
67  if ( !mSrsName.isEmpty() )
69  if ( crs.isValid() )
70  {
71  if ( honourAxisOrientation && crs.hasAxisInverted() )
72  {
73  mInvertAxisOrientation = !mInvertAxisOrientation;
74  }
75  }
76 }
77 
78 QgsGeometry QgsOgcUtils::geometryFromGML( const QDomNode &geometryNode, const Context &context )
79 {
80  QDomElement geometryTypeElement = geometryNode.toElement();
81  QString geomType = geometryTypeElement.tagName();
82  QgsGeometry geometry;
83 
84  if ( !( geomType == QLatin1String( "Point" ) || geomType == QLatin1String( "LineString" ) || geomType == QLatin1String( "Polygon" ) ||
85  geomType == QLatin1String( "MultiPoint" ) || geomType == QLatin1String( "MultiLineString" ) || geomType == QLatin1String( "MultiPolygon" ) ||
86  geomType == QLatin1String( "Box" ) || geomType == QLatin1String( "Envelope" ) ) )
87  {
88  const QDomNode geometryChild = geometryNode.firstChild();
89  if ( geometryChild.isNull() )
90  {
91  return geometry;
92  }
93  geometryTypeElement = geometryChild.toElement();
94  geomType = geometryTypeElement.tagName();
95  }
96 
97  if ( !( geomType == QLatin1String( "Point" ) || geomType == QLatin1String( "LineString" ) || geomType == QLatin1String( "Polygon" ) ||
98  geomType == QLatin1String( "MultiPoint" ) || geomType == QLatin1String( "MultiLineString" ) || geomType == QLatin1String( "MultiPolygon" ) ||
99  geomType == QLatin1String( "Box" ) || geomType == QLatin1String( "Envelope" ) ) )
100  return QgsGeometry();
101 
102  if ( geomType == QLatin1String( "Point" ) )
103  {
104  geometry = geometryFromGMLPoint( geometryTypeElement );
105  }
106  else if ( geomType == QLatin1String( "LineString" ) )
107  {
108  geometry = geometryFromGMLLineString( geometryTypeElement );
109  }
110  else if ( geomType == QLatin1String( "Polygon" ) )
111  {
112  geometry = geometryFromGMLPolygon( geometryTypeElement );
113  }
114  else if ( geomType == QLatin1String( "MultiPoint" ) )
115  {
116  geometry = geometryFromGMLMultiPoint( geometryTypeElement );
117  }
118  else if ( geomType == QLatin1String( "MultiLineString" ) )
119  {
120  geometry = geometryFromGMLMultiLineString( geometryTypeElement );
121  }
122  else if ( geomType == QLatin1String( "MultiPolygon" ) )
123  {
124  geometry = geometryFromGMLMultiPolygon( geometryTypeElement );
125  }
126  else if ( geomType == QLatin1String( "Box" ) )
127  {
128  geometry = QgsGeometry::fromRect( rectangleFromGMLBox( geometryTypeElement ) );
129  }
130  else if ( geomType == QLatin1String( "Envelope" ) )
131  {
132  geometry = QgsGeometry::fromRect( rectangleFromGMLEnvelope( geometryTypeElement ) );
133  }
134  else //unknown type
135  {
136  return geometry;
137  }
138 
139  // Handle srsName if context has information about the layer and the transformation context
140  if ( context.layer )
141  {
143 
144  if ( geometryTypeElement.hasAttribute( QStringLiteral( "srsName" ) ) )
145  {
146  QString srsName { geometryTypeElement.attribute( QStringLiteral( "srsName" ) ) };
147 
148  // The logic here follows WFS GeoServer conventions from https://docs.geoserver.org/latest/en/user/services/wfs/axis_order.html
149  const bool ignoreAxisOrientation { srsName.startsWith( QLatin1String( "http://www.opengis.net/gml/srs/" ) ) || srsName.startsWith( QLatin1String( "EPSG:" ) ) };
150 
151  // GDAL does not recognise http://www.opengis.net/gml/srs/epsg.xml#4326 but it does
152  // http://www.opengis.net/def/crs/EPSG/0/4326 so, let's try that
153  if ( srsName.startsWith( QLatin1String( "http://www.opengis.net/gml/srs/" ) ) )
154  {
155  const auto parts { srsName.split( QRegularExpression( QStringLiteral( R"raw(/|#|\.)raw" ) ) ) };
156  if ( parts.length() == 10 )
157  {
158  srsName = QStringLiteral( "http://www.opengis.net/def/crs/%1/0/%2" ).arg( parts[ 7 ].toUpper(), parts[ 9 ] );
159  }
160  }
161  geomSrs.createFromUserInput( srsName );
162  if ( geomSrs.isValid() && geomSrs != context.layer->crs() )
163  {
164  if ( geomSrs.hasAxisInverted() && ! ignoreAxisOrientation )
165  {
166  geometry.get()->swapXy();
167  }
168  const QgsCoordinateTransform transformer { geomSrs, context.layer->crs(), context.transformContext };
169  try
170  {
171  const Qgis::GeometryOperationResult result = geometry.transform( transformer );
173  {
174  QgsDebugMsgLevel( QStringLiteral( "Error transforming geometry: %1" ).arg( qgsEnumValueToKey( result ) ), 2 );
175  }
176  }
177  catch ( QgsCsException & )
178  {
179  QgsDebugMsgLevel( QStringLiteral( "CS error transforming geometry" ), 2 );
180  }
181  }
182  }
183  }
184 
185  return geometry;
186 }
187 
188 QgsGeometry QgsOgcUtils::geometryFromGML( const QString &xmlString, const Context &context )
189 {
190  // wrap the string into a root tag to have "gml" namespace (and also as a default namespace)
191  const QString xml = QStringLiteral( "<tmp xmlns=\"%1\" xmlns:gml=\"%1\">%2</tmp>" ).arg( GML_NAMESPACE, xmlString );
192  QDomDocument doc;
193  if ( !doc.setContent( xml, true ) )
194  return QgsGeometry();
195 
196  return geometryFromGML( doc.documentElement().firstChildElement(), context );
197 }
198 
199 
200 QgsGeometry QgsOgcUtils::geometryFromGMLPoint( const QDomElement &geometryElement )
201 {
202  QgsPolylineXY pointCoordinate;
203 
204  const QDomNodeList coordList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
205  if ( !coordList.isEmpty() )
206  {
207  const QDomElement coordElement = coordList.at( 0 ).toElement();
208  if ( readGMLCoordinates( pointCoordinate, coordElement ) != 0 )
209  {
210  return QgsGeometry();
211  }
212  }
213  else
214  {
215  const QDomNodeList posList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "pos" ) );
216  if ( posList.size() < 1 )
217  {
218  return QgsGeometry();
219  }
220  const QDomElement posElement = posList.at( 0 ).toElement();
221  if ( readGMLPositions( pointCoordinate, posElement ) != 0 )
222  {
223  return QgsGeometry();
224  }
225  }
226 
227  if ( pointCoordinate.empty() )
228  {
229  return QgsGeometry();
230  }
231 
232  QgsPolylineXY::const_iterator point_it = pointCoordinate.constBegin();
233  char e = htonl( 1 ) != 1;
234  double x = point_it->x();
235  double y = point_it->y();
236  const int size = 1 + sizeof( int ) + 2 * sizeof( double );
237 
239  unsigned char *wkb = new unsigned char[size];
240 
241  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
242  memcpy( &( wkb )[wkbPosition], &e, 1 );
243  wkbPosition += 1;
244  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
245  wkbPosition += sizeof( int );
246  memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
247  wkbPosition += sizeof( double );
248  memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
249 
250  QgsGeometry g;
251  g.fromWkb( wkb, size );
252  return g;
253 }
254 
255 QgsGeometry QgsOgcUtils::geometryFromGMLLineString( const QDomElement &geometryElement )
256 {
257  QgsPolylineXY lineCoordinates;
258 
259  const QDomNodeList coordList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
260  if ( !coordList.isEmpty() )
261  {
262  const QDomElement coordElement = coordList.at( 0 ).toElement();
263  if ( readGMLCoordinates( lineCoordinates, coordElement ) != 0 )
264  {
265  return QgsGeometry();
266  }
267  }
268  else
269  {
270  const QDomNodeList posList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
271  if ( posList.size() < 1 )
272  {
273  return QgsGeometry();
274  }
275  const QDomElement posElement = posList.at( 0 ).toElement();
276  if ( readGMLPositions( lineCoordinates, posElement ) != 0 )
277  {
278  return QgsGeometry();
279  }
280  }
281 
282  char e = htonl( 1 ) != 1;
283  const int size = 1 + 2 * sizeof( int ) + lineCoordinates.size() * 2 * sizeof( double );
284 
286  unsigned char *wkb = new unsigned char[size];
287 
288  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
289  double x, y;
290  int nPoints = lineCoordinates.size();
291 
292  //fill the contents into *wkb
293  memcpy( &( wkb )[wkbPosition], &e, 1 );
294  wkbPosition += 1;
295  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
296  wkbPosition += sizeof( int );
297  memcpy( &( wkb )[wkbPosition], &nPoints, sizeof( int ) );
298  wkbPosition += sizeof( int );
299 
300  QgsPolylineXY::const_iterator iter;
301  for ( iter = lineCoordinates.constBegin(); iter != lineCoordinates.constEnd(); ++iter )
302  {
303  x = iter->x();
304  y = iter->y();
305  memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
306  wkbPosition += sizeof( double );
307  memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
308  wkbPosition += sizeof( double );
309  }
310 
311  QgsGeometry g;
312  g.fromWkb( wkb, size );
313  return g;
314 }
315 
316 QgsGeometry QgsOgcUtils::geometryFromGMLPolygon( const QDomElement &geometryElement )
317 {
318  //read all the coordinates (as QgsPoint) into memory. Each linear ring has an entry in the vector
319  QgsMultiPolylineXY ringCoordinates;
320 
321  //read coordinates for outer boundary
322  QgsPolylineXY exteriorPointList;
323  const QDomNodeList outerBoundaryList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "outerBoundaryIs" ) );
324  if ( !outerBoundaryList.isEmpty() ) //outer ring is necessary
325  {
326  QDomElement coordinatesElement = outerBoundaryList.at( 0 ).firstChild().firstChild().toElement();
327  if ( coordinatesElement.isNull() )
328  {
329  return QgsGeometry();
330  }
331  if ( readGMLCoordinates( exteriorPointList, coordinatesElement ) != 0 )
332  {
333  return QgsGeometry();
334  }
335  ringCoordinates.push_back( exteriorPointList );
336 
337  //read coordinates for inner boundary
338  const QDomNodeList innerBoundaryList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "innerBoundaryIs" ) );
339  for ( int i = 0; i < innerBoundaryList.size(); ++i )
340  {
341  QgsPolylineXY interiorPointList;
342  coordinatesElement = innerBoundaryList.at( i ).firstChild().firstChild().toElement();
343  if ( coordinatesElement.isNull() )
344  {
345  return QgsGeometry();
346  }
347  if ( readGMLCoordinates( interiorPointList, coordinatesElement ) != 0 )
348  {
349  return QgsGeometry();
350  }
351  ringCoordinates.push_back( interiorPointList );
352  }
353  }
354  else
355  {
356  //read coordinates for exterior
357  const QDomNodeList exteriorList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "exterior" ) );
358  if ( exteriorList.size() < 1 ) //outer ring is necessary
359  {
360  return QgsGeometry();
361  }
362  const QDomElement posElement = exteriorList.at( 0 ).firstChild().firstChild().toElement();
363  if ( posElement.isNull() )
364  {
365  return QgsGeometry();
366  }
367  if ( readGMLPositions( exteriorPointList, posElement ) != 0 )
368  {
369  return QgsGeometry();
370  }
371  ringCoordinates.push_back( exteriorPointList );
372 
373  //read coordinates for inner boundary
374  const QDomNodeList interiorList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "interior" ) );
375  for ( int i = 0; i < interiorList.size(); ++i )
376  {
377  QgsPolylineXY interiorPointList;
378  const QDomElement posElement = interiorList.at( i ).firstChild().firstChild().toElement();
379  if ( posElement.isNull() )
380  {
381  return QgsGeometry();
382  }
383  // Note: readGMLPositions returns true on errors and false on success
384  if ( readGMLPositions( interiorPointList, posElement ) )
385  {
386  return QgsGeometry();
387  }
388  ringCoordinates.push_back( interiorPointList );
389  }
390  }
391 
392  //calculate number of bytes to allocate
393  int nrings = ringCoordinates.size();
394  if ( nrings < 1 )
395  return QgsGeometry();
396 
397  int npoints = 0;//total number of points
398  for ( QgsMultiPolylineXY::const_iterator it = ringCoordinates.constBegin(); it != ringCoordinates.constEnd(); ++it )
399  {
400  npoints += it->size();
401  }
402  const int size = 1 + 2 * sizeof( int ) + nrings * sizeof( int ) + 2 * npoints * sizeof( double );
403 
405  unsigned char *wkb = new unsigned char[size];
406 
407  //char e = QgsApplication::endian();
408  char e = htonl( 1 ) != 1;
409  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
410  int nPointsInRing = 0;
411  double x, y;
412 
413  //fill the contents into *wkb
414  memcpy( &( wkb )[wkbPosition], &e, 1 );
415  wkbPosition += 1;
416  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
417  wkbPosition += sizeof( int );
418  memcpy( &( wkb )[wkbPosition], &nrings, sizeof( int ) );
419  wkbPosition += sizeof( int );
420  for ( QgsMultiPolylineXY::const_iterator it = ringCoordinates.constBegin(); it != ringCoordinates.constEnd(); ++it )
421  {
422  nPointsInRing = it->size();
423  memcpy( &( wkb )[wkbPosition], &nPointsInRing, sizeof( int ) );
424  wkbPosition += sizeof( int );
425  //iterate through the string list converting the strings to x-/y- doubles
426  QgsPolylineXY::const_iterator iter;
427  for ( iter = it->begin(); iter != it->end(); ++iter )
428  {
429  x = iter->x();
430  y = iter->y();
431  //qWarning("currentCoordinate: " + QString::number(x) + " // " + QString::number(y));
432  memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
433  wkbPosition += sizeof( double );
434  memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
435  wkbPosition += sizeof( double );
436  }
437  }
438 
439  QgsGeometry g;
440  g.fromWkb( wkb, size );
441  return g;
442 }
443 
444 QgsGeometry QgsOgcUtils::geometryFromGMLMultiPoint( const QDomElement &geometryElement )
445 {
446  QgsPolylineXY pointList;
447  QgsPolylineXY currentPoint;
448  const QDomNodeList pointMemberList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "pointMember" ) );
449  if ( pointMemberList.size() < 1 )
450  {
451  return QgsGeometry();
452  }
453  QDomNodeList pointNodeList;
454  // coordinates or pos element
455  QDomNodeList coordinatesList;
456  QDomNodeList posList;
457  for ( int i = 0; i < pointMemberList.size(); ++i )
458  {
459  //<Point> element
460  pointNodeList = pointMemberList.at( i ).toElement().elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "Point" ) );
461  if ( pointNodeList.size() < 1 )
462  {
463  continue;
464  }
465  //<coordinates> element
466  coordinatesList = pointNodeList.at( 0 ).toElement().elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
467  if ( !coordinatesList.isEmpty() )
468  {
469  currentPoint.clear();
470  if ( readGMLCoordinates( currentPoint, coordinatesList.at( 0 ).toElement() ) != 0 )
471  {
472  continue;
473  }
474  if ( currentPoint.empty() )
475  {
476  continue;
477  }
478  pointList.push_back( ( *currentPoint.begin() ) );
479  continue;
480  }
481  else
482  {
483  //<pos> element
484  posList = pointNodeList.at( 0 ).toElement().elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "pos" ) );
485  if ( posList.size() < 1 )
486  {
487  continue;
488  }
489  currentPoint.clear();
490  if ( readGMLPositions( currentPoint, posList.at( 0 ).toElement() ) != 0 )
491  {
492  continue;
493  }
494  if ( currentPoint.empty() )
495  {
496  continue;
497  }
498  pointList.push_back( ( *currentPoint.begin() ) );
499  }
500  }
501 
502  int nPoints = pointList.size(); //number of points
503  if ( nPoints < 1 )
504  return QgsGeometry();
505 
506  //calculate the required wkb size
507  const int size = 1 + 2 * sizeof( int ) + pointList.size() * ( 2 * sizeof( double ) + 1 + sizeof( int ) );
508 
510  unsigned char *wkb = new unsigned char[size];
511 
512  //fill the wkb content
513  char e = htonl( 1 ) != 1;
514  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
515  double x, y;
516  memcpy( &( wkb )[wkbPosition], &e, 1 );
517  wkbPosition += 1;
518  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
519  wkbPosition += sizeof( int );
520  memcpy( &( wkb )[wkbPosition], &nPoints, sizeof( int ) );
521  wkbPosition += sizeof( int );
522  type = QgsWkbTypes::Point;
523  for ( QgsPolylineXY::const_iterator it = pointList.constBegin(); it != pointList.constEnd(); ++it )
524  {
525  memcpy( &( wkb )[wkbPosition], &e, 1 );
526  wkbPosition += 1;
527  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
528  wkbPosition += sizeof( int );
529  x = it->x();
530  memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
531  wkbPosition += sizeof( double );
532  y = it->y();
533  memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
534  wkbPosition += sizeof( double );
535  }
536 
537  QgsGeometry g;
538  g.fromWkb( wkb, size );
539  return g;
540 }
541 
542 QgsGeometry QgsOgcUtils::geometryFromGMLMultiLineString( const QDomElement &geometryElement )
543 {
544  //geoserver has
545  //<gml:MultiLineString>
546  //<gml:lineStringMember>
547  //<gml:LineString>
548 
549  //mapserver has directly
550  //<gml:MultiLineString
551  //<gml:LineString
552 
553  QList< QgsPolylineXY > lineCoordinates; //first list: lines, second list: points of one line
554  QDomElement currentLineStringElement;
555  QDomNodeList currentCoordList;
556  QDomNodeList currentPosList;
557 
558  const QDomNodeList lineStringMemberList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "lineStringMember" ) );
559  if ( !lineStringMemberList.isEmpty() ) //geoserver
560  {
561  for ( int i = 0; i < lineStringMemberList.size(); ++i )
562  {
563  const QDomNodeList lineStringNodeList = lineStringMemberList.at( i ).toElement().elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LineString" ) );
564  if ( lineStringNodeList.size() < 1 )
565  {
566  return QgsGeometry();
567  }
568  currentLineStringElement = lineStringNodeList.at( 0 ).toElement();
569  currentCoordList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
570  if ( !currentCoordList.isEmpty() )
571  {
572  QgsPolylineXY currentPointList;
573  if ( readGMLCoordinates( currentPointList, currentCoordList.at( 0 ).toElement() ) != 0 )
574  {
575  return QgsGeometry();
576  }
577  lineCoordinates.push_back( currentPointList );
578  }
579  else
580  {
581  currentPosList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
582  if ( currentPosList.size() < 1 )
583  {
584  return QgsGeometry();
585  }
586  QgsPolylineXY currentPointList;
587  if ( readGMLPositions( currentPointList, currentPosList.at( 0 ).toElement() ) != 0 )
588  {
589  return QgsGeometry();
590  }
591  lineCoordinates.push_back( currentPointList );
592  }
593  }
594  }
595  else
596  {
597  const QDomNodeList lineStringList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LineString" ) );
598  if ( !lineStringList.isEmpty() ) //mapserver
599  {
600  for ( int i = 0; i < lineStringList.size(); ++i )
601  {
602  currentLineStringElement = lineStringList.at( i ).toElement();
603  currentCoordList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
604  if ( !currentCoordList.isEmpty() )
605  {
606  QgsPolylineXY currentPointList;
607  if ( readGMLCoordinates( currentPointList, currentCoordList.at( 0 ).toElement() ) != 0 )
608  {
609  return QgsGeometry();
610  }
611  lineCoordinates.push_back( currentPointList );
612  return QgsGeometry();
613  }
614  else
615  {
616  currentPosList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
617  if ( currentPosList.size() < 1 )
618  {
619  return QgsGeometry();
620  }
621  QgsPolylineXY currentPointList;
622  if ( readGMLPositions( currentPointList, currentPosList.at( 0 ).toElement() ) != 0 )
623  {
624  return QgsGeometry();
625  }
626  lineCoordinates.push_back( currentPointList );
627  }
628  }
629  }
630  else
631  {
632  return QgsGeometry();
633  }
634  }
635 
636  int nLines = lineCoordinates.size();
637  if ( nLines < 1 )
638  return QgsGeometry();
639 
640  //calculate the required wkb size
641  int size = ( lineCoordinates.size() + 1 ) * ( 1 + 2 * sizeof( int ) );
642  for ( QList< QgsPolylineXY >::const_iterator it = lineCoordinates.constBegin(); it != lineCoordinates.constEnd(); ++it )
643  {
644  size += it->size() * 2 * sizeof( double );
645  }
646 
648  unsigned char *wkb = new unsigned char[size];
649 
650  //fill the wkb content
651  char e = htonl( 1 ) != 1;
652  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
653  int nPoints; //number of points in a line
654  double x, y;
655  memcpy( &( wkb )[wkbPosition], &e, 1 );
656  wkbPosition += 1;
657  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
658  wkbPosition += sizeof( int );
659  memcpy( &( wkb )[wkbPosition], &nLines, sizeof( int ) );
660  wkbPosition += sizeof( int );
662  for ( QList< QgsPolylineXY >::const_iterator it = lineCoordinates.constBegin(); it != lineCoordinates.constEnd(); ++it )
663  {
664  memcpy( &( wkb )[wkbPosition], &e, 1 );
665  wkbPosition += 1;
666  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
667  wkbPosition += sizeof( int );
668  nPoints = it->size();
669  memcpy( &( wkb )[wkbPosition], &nPoints, sizeof( int ) );
670  wkbPosition += sizeof( int );
671  for ( QgsPolylineXY::const_iterator iter = it->begin(); iter != it->end(); ++iter )
672  {
673  x = iter->x();
674  y = iter->y();
675  // QgsDebugMsg( QStringLiteral( "x, y is %1,%2" ).arg( x, 'f' ).arg( y, 'f' ) );
676  memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
677  wkbPosition += sizeof( double );
678  memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
679  wkbPosition += sizeof( double );
680  }
681  }
682 
683  QgsGeometry g;
684  g.fromWkb( wkb, size );
685  return g;
686 }
687 
688 QgsGeometry QgsOgcUtils::geometryFromGMLMultiPolygon( const QDomElement &geometryElement )
689 {
690  //first list: different polygons, second list: different rings, third list: different points
691  QgsMultiPolygonXY multiPolygonPoints;
692  QDomElement currentPolygonMemberElement;
693  QDomNodeList polygonList;
694  QDomElement currentPolygonElement;
695  // rings in GML2
696  QDomNodeList outerBoundaryList;
697  QDomElement currentOuterBoundaryElement;
698  const QDomNodeList innerBoundaryList;
699  QDomElement currentInnerBoundaryElement;
700  // rings in GML3
701  QDomNodeList exteriorList;
702  QDomElement currentExteriorElement;
703  QDomElement currentInteriorElement;
704  const QDomNodeList interiorList;
705  // lienar ring
706  QDomNodeList linearRingNodeList;
707  QDomElement currentLinearRingElement;
708  // Coordinates or position list
709  QDomNodeList currentCoordinateList;
710  QDomNodeList currentPosList;
711 
712  const QDomNodeList polygonMemberList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "polygonMember" ) );
713  QgsPolygonXY currentPolygonList;
714  for ( int i = 0; i < polygonMemberList.size(); ++i )
715  {
716  currentPolygonList.resize( 0 ); // preserve capacity - don't use clear
717  currentPolygonMemberElement = polygonMemberList.at( i ).toElement();
718  polygonList = currentPolygonMemberElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "Polygon" ) );
719  if ( polygonList.size() < 1 )
720  {
721  continue;
722  }
723  currentPolygonElement = polygonList.at( 0 ).toElement();
724 
725  //find exterior ring
726  outerBoundaryList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "outerBoundaryIs" ) );
727  if ( !outerBoundaryList.isEmpty() )
728  {
729  currentOuterBoundaryElement = outerBoundaryList.at( 0 ).toElement();
730  QgsPolylineXY ringCoordinates;
731 
732  linearRingNodeList = currentOuterBoundaryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LinearRing" ) );
733  if ( linearRingNodeList.size() < 1 )
734  {
735  continue;
736  }
737  currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
738  currentCoordinateList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
739  if ( currentCoordinateList.size() < 1 )
740  {
741  continue;
742  }
743  if ( readGMLCoordinates( ringCoordinates, currentCoordinateList.at( 0 ).toElement() ) != 0 )
744  {
745  continue;
746  }
747  currentPolygonList.push_back( ringCoordinates );
748 
749  //find interior rings
750  const QDomNodeList innerBoundaryList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "innerBoundaryIs" ) );
751  for ( int j = 0; j < innerBoundaryList.size(); ++j )
752  {
753  QgsPolylineXY ringCoordinates;
754  currentInnerBoundaryElement = innerBoundaryList.at( j ).toElement();
755  linearRingNodeList = currentInnerBoundaryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LinearRing" ) );
756  if ( linearRingNodeList.size() < 1 )
757  {
758  continue;
759  }
760  currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
761  currentCoordinateList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
762  if ( currentCoordinateList.size() < 1 )
763  {
764  continue;
765  }
766  if ( readGMLCoordinates( ringCoordinates, currentCoordinateList.at( 0 ).toElement() ) != 0 )
767  {
768  continue;
769  }
770  currentPolygonList.push_back( ringCoordinates );
771  }
772  }
773  else
774  {
775  //find exterior ring
776  exteriorList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "exterior" ) );
777  if ( exteriorList.size() < 1 )
778  {
779  continue;
780  }
781 
782  currentExteriorElement = exteriorList.at( 0 ).toElement();
783  QgsPolylineXY ringPositions;
784 
785  linearRingNodeList = currentExteriorElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LinearRing" ) );
786  if ( linearRingNodeList.size() < 1 )
787  {
788  continue;
789  }
790  currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
791  currentPosList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
792  if ( currentPosList.size() < 1 )
793  {
794  continue;
795  }
796  if ( readGMLPositions( ringPositions, currentPosList.at( 0 ).toElement() ) != 0 )
797  {
798  continue;
799  }
800  currentPolygonList.push_back( ringPositions );
801 
802  //find interior rings
803  const QDomNodeList interiorList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "interior" ) );
804  for ( int j = 0; j < interiorList.size(); ++j )
805  {
806  QgsPolylineXY ringPositions;
807  currentInteriorElement = interiorList.at( j ).toElement();
808  linearRingNodeList = currentInteriorElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LinearRing" ) );
809  if ( linearRingNodeList.size() < 1 )
810  {
811  continue;
812  }
813  currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
814  currentPosList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
815  if ( currentPosList.size() < 1 )
816  {
817  continue;
818  }
819  if ( readGMLPositions( ringPositions, currentPosList.at( 0 ).toElement() ) != 0 )
820  {
821  continue;
822  }
823  currentPolygonList.push_back( ringPositions );
824  }
825  }
826  multiPolygonPoints.push_back( currentPolygonList );
827  }
828 
829  int nPolygons = multiPolygonPoints.size();
830  if ( nPolygons < 1 )
831  return QgsGeometry();
832 
833  int size = 1 + 2 * sizeof( int );
834  //calculate the wkb size
835  for ( QgsMultiPolygonXY::const_iterator it = multiPolygonPoints.constBegin(); it != multiPolygonPoints.constEnd(); ++it )
836  {
837  size += 1 + 2 * sizeof( int );
838  for ( QgsPolygonXY::const_iterator iter = it->begin(); iter != it->end(); ++iter )
839  {
840  size += sizeof( int ) + 2 * iter->size() * sizeof( double );
841  }
842  }
843 
845  unsigned char *wkb = new unsigned char[size];
846 
847  char e = htonl( 1 ) != 1;
848  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
849  double x, y;
850  int nRings;
851  int nPointsInRing;
852 
853  //fill the contents into *wkb
854  memcpy( &( wkb )[wkbPosition], &e, 1 );
855  wkbPosition += 1;
856  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
857  wkbPosition += sizeof( int );
858  memcpy( &( wkb )[wkbPosition], &nPolygons, sizeof( int ) );
859  wkbPosition += sizeof( int );
860 
861  type = QgsWkbTypes::Polygon;
862 
863  for ( QgsMultiPolygonXY::const_iterator it = multiPolygonPoints.constBegin(); it != multiPolygonPoints.constEnd(); ++it )
864  {
865  memcpy( &( wkb )[wkbPosition], &e, 1 );
866  wkbPosition += 1;
867  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
868  wkbPosition += sizeof( int );
869  nRings = it->size();
870  memcpy( &( wkb )[wkbPosition], &nRings, sizeof( int ) );
871  wkbPosition += sizeof( int );
872  for ( QgsPolygonXY::const_iterator iter = it->begin(); iter != it->end(); ++iter )
873  {
874  nPointsInRing = iter->size();
875  memcpy( &( wkb )[wkbPosition], &nPointsInRing, sizeof( int ) );
876  wkbPosition += sizeof( int );
877  for ( QgsPolylineXY::const_iterator iterator = iter->begin(); iterator != iter->end(); ++iterator )
878  {
879  x = iterator->x();
880  y = iterator->y();
881  memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
882  wkbPosition += sizeof( double );
883  memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
884  wkbPosition += sizeof( double );
885  }
886  }
887  }
888 
889  QgsGeometry g;
890  g.fromWkb( wkb, size );
891  return g;
892 }
893 
894 bool QgsOgcUtils::readGMLCoordinates( QgsPolylineXY &coords, const QDomElement &elem )
895 {
896  QString coordSeparator = QStringLiteral( "," );
897  QString tupelSeparator = QStringLiteral( " " );
898  //"decimal" has to be "."
899 
900  coords.clear();
901 
902  if ( elem.hasAttribute( QStringLiteral( "cs" ) ) )
903  {
904  coordSeparator = elem.attribute( QStringLiteral( "cs" ) );
905  }
906  if ( elem.hasAttribute( QStringLiteral( "ts" ) ) )
907  {
908  tupelSeparator = elem.attribute( QStringLiteral( "ts" ) );
909  }
910 
911 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
912  QStringList tupels = elem.text().split( tupelSeparator, QString::SkipEmptyParts );
913 #else
914  const QStringList tupels = elem.text().split( tupelSeparator, Qt::SkipEmptyParts );
915 #endif
916  QStringList tuple_coords;
917  double x, y;
918  bool conversionSuccess;
919 
920  QStringList::const_iterator it;
921  for ( it = tupels.constBegin(); it != tupels.constEnd(); ++it )
922  {
923 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
924  tuple_coords = ( *it ).split( coordSeparator, QString::SkipEmptyParts );
925 #else
926  tuple_coords = ( *it ).split( coordSeparator, Qt::SkipEmptyParts );
927 #endif
928  if ( tuple_coords.size() < 2 )
929  {
930  continue;
931  }
932  x = tuple_coords.at( 0 ).toDouble( &conversionSuccess );
933  if ( !conversionSuccess )
934  {
935  return true;
936  }
937  y = tuple_coords.at( 1 ).toDouble( &conversionSuccess );
938  if ( !conversionSuccess )
939  {
940  return true;
941  }
942  coords.push_back( QgsPointXY( x, y ) );
943  }
944  return false;
945 }
946 
948 {
949  QgsRectangle rect;
950 
951  const QDomElement boxElem = boxNode.toElement();
952  if ( boxElem.tagName() != QLatin1String( "Box" ) )
953  return rect;
954 
955  const QDomElement bElem = boxElem.firstChild().toElement();
956  QString coordSeparator = QStringLiteral( "," );
957  QString tupelSeparator = QStringLiteral( " " );
958  if ( bElem.hasAttribute( QStringLiteral( "cs" ) ) )
959  {
960  coordSeparator = bElem.attribute( QStringLiteral( "cs" ) );
961  }
962  if ( bElem.hasAttribute( QStringLiteral( "ts" ) ) )
963  {
964  tupelSeparator = bElem.attribute( QStringLiteral( "ts" ) );
965  }
966 
967  const QString bString = bElem.text();
968  bool ok1, ok2, ok3, ok4;
969  const double xmin = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 0, 0 ).toDouble( &ok1 );
970  const double ymin = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 1, 1 ).toDouble( &ok2 );
971  const double xmax = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 0, 0 ).toDouble( &ok3 );
972  const double ymax = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 1, 1 ).toDouble( &ok4 );
973 
974  if ( ok1 && ok2 && ok3 && ok4 )
975  {
976  rect = QgsRectangle( xmin, ymin, xmax, ymax );
977  rect.normalize();
978  }
979 
980  return rect;
981 }
982 
983 bool QgsOgcUtils::readGMLPositions( QgsPolylineXY &coords, const QDomElement &elem )
984 {
985  coords.clear();
986 
987 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
988  QStringList pos = elem.text().split( ' ', QString::SkipEmptyParts );
989 #else
990  const QStringList pos = elem.text().split( ' ', Qt::SkipEmptyParts );
991 #endif
992  double x, y;
993  bool conversionSuccess;
994  const int posSize = pos.size();
995 
996  int srsDimension = 2;
997  if ( elem.hasAttribute( QStringLiteral( "srsDimension" ) ) )
998  {
999  srsDimension = elem.attribute( QStringLiteral( "srsDimension" ) ).toInt( &conversionSuccess );
1000  if ( !conversionSuccess )
1001  {
1002  srsDimension = 2;
1003  }
1004  }
1005  else if ( elem.hasAttribute( QStringLiteral( "dimension" ) ) )
1006  {
1007  srsDimension = elem.attribute( QStringLiteral( "dimension" ) ).toInt( &conversionSuccess );
1008  if ( !conversionSuccess )
1009  {
1010  srsDimension = 2;
1011  }
1012  }
1013 
1014  for ( int i = 0; i < posSize / srsDimension; i++ )
1015  {
1016  x = pos.at( i * srsDimension ).toDouble( &conversionSuccess );
1017  if ( !conversionSuccess )
1018  {
1019  return true;
1020  }
1021  y = pos.at( i * srsDimension + 1 ).toDouble( &conversionSuccess );
1022  if ( !conversionSuccess )
1023  {
1024  return true;
1025  }
1026  coords.push_back( QgsPointXY( x, y ) );
1027  }
1028  return false;
1029 }
1030 
1031 
1033 {
1034  QgsRectangle rect;
1035 
1036  const QDomElement envelopeElem = envelopeNode.toElement();
1037  if ( envelopeElem.tagName() != QLatin1String( "Envelope" ) )
1038  return rect;
1039 
1040  const QDomNodeList lowerCornerList = envelopeElem.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "lowerCorner" ) );
1041  if ( lowerCornerList.size() < 1 )
1042  return rect;
1043 
1044  const QDomNodeList upperCornerList = envelopeElem.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "upperCorner" ) );
1045  if ( upperCornerList.size() < 1 )
1046  return rect;
1047 
1048  bool conversionSuccess;
1049  int srsDimension = 2;
1050 
1051  QDomElement elem = lowerCornerList.at( 0 ).toElement();
1052  if ( elem.hasAttribute( QStringLiteral( "srsDimension" ) ) )
1053  {
1054  srsDimension = elem.attribute( QStringLiteral( "srsDimension" ) ).toInt( &conversionSuccess );
1055  if ( !conversionSuccess )
1056  {
1057  srsDimension = 2;
1058  }
1059  }
1060  else if ( elem.hasAttribute( QStringLiteral( "dimension" ) ) )
1061  {
1062  srsDimension = elem.attribute( QStringLiteral( "dimension" ) ).toInt( &conversionSuccess );
1063  if ( !conversionSuccess )
1064  {
1065  srsDimension = 2;
1066  }
1067  }
1068  QString bString = elem.text();
1069 
1070  const double xmin = bString.section( ' ', 0, 0 ).toDouble( &conversionSuccess );
1071  if ( !conversionSuccess )
1072  return rect;
1073  const double ymin = bString.section( ' ', 1, 1 ).toDouble( &conversionSuccess );
1074  if ( !conversionSuccess )
1075  return rect;
1076 
1077  elem = upperCornerList.at( 0 ).toElement();
1078  if ( elem.hasAttribute( QStringLiteral( "srsDimension" ) ) )
1079  {
1080  srsDimension = elem.attribute( QStringLiteral( "srsDimension" ) ).toInt( &conversionSuccess );
1081  if ( !conversionSuccess )
1082  {
1083  srsDimension = 2;
1084  }
1085  }
1086  else if ( elem.hasAttribute( QStringLiteral( "dimension" ) ) )
1087  {
1088  srsDimension = elem.attribute( QStringLiteral( "dimension" ) ).toInt( &conversionSuccess );
1089  if ( !conversionSuccess )
1090  {
1091  srsDimension = 2;
1092  }
1093  }
1094 
1095  Q_UNUSED( srsDimension )
1096 
1097  bString = elem.text();
1098  const double xmax = bString.section( ' ', 0, 0 ).toDouble( &conversionSuccess );
1099  if ( !conversionSuccess )
1100  return rect;
1101  const double ymax = bString.section( ' ', 1, 1 ).toDouble( &conversionSuccess );
1102  if ( !conversionSuccess )
1103  return rect;
1104 
1105  rect = QgsRectangle( xmin, ymin, xmax, ymax );
1106  rect.normalize();
1107 
1108  return rect;
1109 }
1110 
1111 QDomElement QgsOgcUtils::rectangleToGMLBox( QgsRectangle *box, QDomDocument &doc, int precision )
1112 {
1113  return rectangleToGMLBox( box, doc, QString(), false, precision );
1114 }
1115 
1116 QDomElement QgsOgcUtils::rectangleToGMLBox( QgsRectangle *box, QDomDocument &doc,
1117  const QString &srsName,
1118  bool invertAxisOrientation,
1119  int precision )
1120 {
1121  if ( !box )
1122  {
1123  return QDomElement();
1124  }
1125 
1126  QDomElement boxElem = doc.createElement( QStringLiteral( "gml:Box" ) );
1127  if ( !srsName.isEmpty() )
1128  {
1129  boxElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1130  }
1131  QDomElement coordElem = doc.createElement( QStringLiteral( "gml:coordinates" ) );
1132  coordElem.setAttribute( QStringLiteral( "cs" ), QStringLiteral( "," ) );
1133  coordElem.setAttribute( QStringLiteral( "ts" ), QStringLiteral( " " ) );
1134 
1135  QString coordString;
1136  coordString += qgsDoubleToString( invertAxisOrientation ? box->yMinimum() : box->xMinimum(), precision );
1137  coordString += ',';
1138  coordString += qgsDoubleToString( invertAxisOrientation ? box->xMinimum() : box->yMinimum(), precision );
1139  coordString += ' ';
1140  coordString += qgsDoubleToString( invertAxisOrientation ? box->yMaximum() : box->xMaximum(), precision );
1141  coordString += ',';
1142  coordString += qgsDoubleToString( invertAxisOrientation ? box->xMaximum() : box->yMaximum(), precision );
1143 
1144  const QDomText coordText = doc.createTextNode( coordString );
1145  coordElem.appendChild( coordText );
1146  boxElem.appendChild( coordElem );
1147 
1148  return boxElem;
1149 }
1150 
1151 QDomElement QgsOgcUtils::rectangleToGMLEnvelope( QgsRectangle *env, QDomDocument &doc, int precision )
1152 {
1153  return rectangleToGMLEnvelope( env, doc, QString(), false, precision );
1154 }
1155 
1156 QDomElement QgsOgcUtils::rectangleToGMLEnvelope( QgsRectangle *env, QDomDocument &doc,
1157  const QString &srsName,
1158  bool invertAxisOrientation,
1159  int precision )
1160 {
1161  if ( !env )
1162  {
1163  return QDomElement();
1164  }
1165 
1166  QDomElement envElem = doc.createElement( QStringLiteral( "gml:Envelope" ) );
1167  if ( !srsName.isEmpty() )
1168  {
1169  envElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1170  }
1171  QString posList;
1172 
1173  QDomElement lowerCornerElem = doc.createElement( QStringLiteral( "gml:lowerCorner" ) );
1174  posList = qgsDoubleToString( invertAxisOrientation ? env->yMinimum() : env->xMinimum(), precision );
1175  posList += ' ';
1176  posList += qgsDoubleToString( invertAxisOrientation ? env->xMinimum() : env->yMinimum(), precision );
1177  const QDomText lowerCornerText = doc.createTextNode( posList );
1178  lowerCornerElem.appendChild( lowerCornerText );
1179  envElem.appendChild( lowerCornerElem );
1180 
1181  QDomElement upperCornerElem = doc.createElement( QStringLiteral( "gml:upperCorner" ) );
1182  posList = qgsDoubleToString( invertAxisOrientation ? env->yMaximum() : env->xMaximum(), precision );
1183  posList += ' ';
1184  posList += qgsDoubleToString( invertAxisOrientation ? env->xMaximum() : env->yMaximum(), precision );
1185  const QDomText upperCornerText = doc.createTextNode( posList );
1186  upperCornerElem.appendChild( upperCornerText );
1187  envElem.appendChild( upperCornerElem );
1188 
1189  return envElem;
1190 }
1191 
1192 QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry &geometry, QDomDocument &doc, const QString &format, int precision )
1193 {
1194  return geometryToGML( geometry, doc, ( format == QLatin1String( "GML2" ) ) ? GML_2_1_2 : GML_3_2_1, QString(), false, QString(), precision );
1195 }
1196 
1197 QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry &geometry,
1198  QDomDocument &doc,
1199  GMLVersion gmlVersion,
1200  const QString &srsName,
1201  bool invertAxisOrientation,
1202  const QString &gmlIdBase,
1203  int precision )
1204 {
1205  if ( geometry.isNull() )
1206  return QDomElement();
1207 
1208  // coordinate separator
1209  QString cs = QStringLiteral( "," );
1210  // tuple separator
1211  const QString ts = QStringLiteral( " " );
1212  // coord element tagname
1213  QDomElement baseCoordElem;
1214 
1215  bool hasZValue = false;
1216 
1217  const QByteArray wkb( geometry.asWkb() );
1218  QgsConstWkbPtr wkbPtr( wkb );
1219  try
1220  {
1221  wkbPtr.readHeader();
1222  }
1223  catch ( const QgsWkbException &e )
1224  {
1225  Q_UNUSED( e )
1226  // WKB exception while reading header
1227  return QDomElement();
1228  }
1229 
1230  if ( gmlVersion != GML_2_1_2 )
1231  {
1232  switch ( geometry.wkbType() )
1233  {
1234  case QgsWkbTypes::Point25D:
1235  case QgsWkbTypes::Point:
1238  baseCoordElem = doc.createElement( QStringLiteral( "gml:pos" ) );
1239  break;
1240  default:
1241  baseCoordElem = doc.createElement( QStringLiteral( "gml:posList" ) );
1242  break;
1243  }
1244  baseCoordElem.setAttribute( QStringLiteral( "srsDimension" ), QStringLiteral( "2" ) );
1245  cs = ' ';
1246  }
1247  else
1248  {
1249  baseCoordElem = doc.createElement( QStringLiteral( "gml:coordinates" ) );
1250  baseCoordElem.setAttribute( QStringLiteral( "cs" ), cs );
1251  baseCoordElem.setAttribute( QStringLiteral( "ts" ), ts );
1252  }
1253 
1254  try
1255  {
1256  switch ( geometry.wkbType() )
1257  {
1258  case QgsWkbTypes::Point25D:
1259  case QgsWkbTypes::Point:
1260  {
1261  QDomElement pointElem = doc.createElement( QStringLiteral( "gml:Point" ) );
1262  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1263  pointElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1264  if ( !srsName.isEmpty() )
1265  pointElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1266  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1267 
1268  double x, y;
1269 
1270  if ( invertAxisOrientation )
1271  wkbPtr >> y >> x;
1272  else
1273  wkbPtr >> x >> y;
1274  const QDomText coordText = doc.createTextNode( qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision ) );
1275 
1276  coordElem.appendChild( coordText );
1277  pointElem.appendChild( coordElem );
1278  return pointElem;
1279  }
1281  hasZValue = true;
1282  //intentional fall-through
1283  FALLTHROUGH
1285  {
1286  QDomElement multiPointElem = doc.createElement( QStringLiteral( "gml:MultiPoint" ) );
1287  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1288  multiPointElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1289  if ( !srsName.isEmpty() )
1290  multiPointElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1291 
1292  int nPoints;
1293  wkbPtr >> nPoints;
1294 
1295  for ( int idx = 0; idx < nPoints; ++idx )
1296  {
1297  QDomElement pointMemberElem = doc.createElement( QStringLiteral( "gml:pointMember" ) );
1298  QDomElement pointElem = doc.createElement( QStringLiteral( "gml:Point" ) );
1299  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1300  pointElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase + QStringLiteral( ".%1" ).arg( idx + 1 ) );
1301  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1302 
1303  wkbPtr.readHeader();
1304 
1305  double x = 0;
1306  double y = 0;
1307  if ( invertAxisOrientation )
1308  wkbPtr >> y >> x;
1309  else
1310  wkbPtr >> x >> y;
1311  const QDomText coordText = doc.createTextNode( qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision ) );
1312 
1313  coordElem.appendChild( coordText );
1314  pointElem.appendChild( coordElem );
1315 
1316  if ( hasZValue )
1317  {
1318  wkbPtr += sizeof( double );
1319  }
1320  pointMemberElem.appendChild( pointElem );
1321  multiPointElem.appendChild( pointMemberElem );
1322  }
1323  return multiPointElem;
1324  }
1326  hasZValue = true;
1327  //intentional fall-through
1328  FALLTHROUGH
1330  {
1331  QDomElement lineStringElem = doc.createElement( QStringLiteral( "gml:LineString" ) );
1332  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1333  lineStringElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1334  if ( !srsName.isEmpty() )
1335  lineStringElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1336  // get number of points in the line
1337 
1338  int nPoints;
1339  wkbPtr >> nPoints;
1340 
1341  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1342  QString coordString;
1343  for ( int idx = 0; idx < nPoints; ++idx )
1344  {
1345  if ( idx != 0 )
1346  {
1347  coordString += ts;
1348  }
1349 
1350  double x = 0;
1351  double y = 0;
1352  if ( invertAxisOrientation )
1353  wkbPtr >> y >> x;
1354  else
1355  wkbPtr >> x >> y;
1356  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1357 
1358  if ( hasZValue )
1359  {
1360  wkbPtr += sizeof( double );
1361  }
1362  }
1363  const QDomText coordText = doc.createTextNode( coordString );
1364  coordElem.appendChild( coordText );
1365  lineStringElem.appendChild( coordElem );
1366  return lineStringElem;
1367  }
1369  hasZValue = true;
1370  //intentional fall-through
1371  FALLTHROUGH
1373  {
1374  QDomElement multiLineStringElem = doc.createElement( QStringLiteral( "gml:MultiLineString" ) );
1375  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1376  multiLineStringElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1377  if ( !srsName.isEmpty() )
1378  multiLineStringElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1379 
1380  int nLines;
1381  wkbPtr >> nLines;
1382 
1383  for ( int jdx = 0; jdx < nLines; jdx++ )
1384  {
1385  QDomElement lineStringMemberElem = doc.createElement( QStringLiteral( "gml:lineStringMember" ) );
1386  QDomElement lineStringElem = doc.createElement( QStringLiteral( "gml:LineString" ) );
1387  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1388  lineStringElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase + QStringLiteral( ".%1" ).arg( jdx + 1 ) );
1389 
1390  wkbPtr.readHeader();
1391 
1392  int nPoints;
1393  wkbPtr >> nPoints;
1394 
1395  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1396  QString coordString;
1397  for ( int idx = 0; idx < nPoints; idx++ )
1398  {
1399  if ( idx != 0 )
1400  {
1401  coordString += ts;
1402  }
1403 
1404  double x = 0;
1405  double y = 0;
1406  if ( invertAxisOrientation )
1407  wkbPtr >> y >> x;
1408  else
1409  wkbPtr >> x >> y;
1410 
1411  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1412 
1413  if ( hasZValue )
1414  {
1415  wkbPtr += sizeof( double );
1416  }
1417  }
1418  const QDomText coordText = doc.createTextNode( coordString );
1419  coordElem.appendChild( coordText );
1420  lineStringElem.appendChild( coordElem );
1421  lineStringMemberElem.appendChild( lineStringElem );
1422  multiLineStringElem.appendChild( lineStringMemberElem );
1423  }
1424  return multiLineStringElem;
1425  }
1427  hasZValue = true;
1428  //intentional fall-through
1429  FALLTHROUGH
1430  case QgsWkbTypes::Polygon:
1431  {
1432  QDomElement polygonElem = doc.createElement( QStringLiteral( "gml:Polygon" ) );
1433  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1434  polygonElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1435  if ( !srsName.isEmpty() )
1436  polygonElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1437 
1438  // get number of rings in the polygon
1439  int numRings;
1440  wkbPtr >> numRings;
1441 
1442  if ( numRings == 0 ) // sanity check for zero rings in polygon
1443  return QDomElement();
1444 
1445  for ( int idx = 0; idx < numRings; idx++ )
1446  {
1447  QString boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:outerBoundaryIs" : "gml:exterior";
1448  if ( idx != 0 )
1449  {
1450  boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:innerBoundaryIs" : "gml:interior";
1451  }
1452  QDomElement boundaryElem = doc.createElement( boundaryName );
1453  QDomElement ringElem = doc.createElement( QStringLiteral( "gml:LinearRing" ) );
1454  // get number of points in the ring
1455  int nPoints = 0;
1456  wkbPtr >> nPoints;
1457 
1458  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1459  QString coordString;
1460  for ( int jdx = 0; jdx < nPoints; jdx++ )
1461  {
1462  if ( jdx != 0 )
1463  {
1464  coordString += ts;
1465  }
1466 
1467  double x = 0;
1468  double y = 0;
1469  if ( invertAxisOrientation )
1470  wkbPtr >> y >> x;
1471  else
1472  wkbPtr >> x >> y;
1473 
1474  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1475  if ( hasZValue )
1476  {
1477  wkbPtr += sizeof( double );
1478  }
1479  }
1480  const QDomText coordText = doc.createTextNode( coordString );
1481  coordElem.appendChild( coordText );
1482  ringElem.appendChild( coordElem );
1483  boundaryElem.appendChild( ringElem );
1484  polygonElem.appendChild( boundaryElem );
1485  }
1486  return polygonElem;
1487  }
1489  hasZValue = true;
1490  //intentional fall-through
1491  FALLTHROUGH
1493  {
1494  QDomElement multiPolygonElem = doc.createElement( QStringLiteral( "gml:MultiPolygon" ) );
1495  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1496  multiPolygonElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1497  if ( !srsName.isEmpty() )
1498  multiPolygonElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1499 
1500  int numPolygons;
1501  wkbPtr >> numPolygons;
1502 
1503  for ( int kdx = 0; kdx < numPolygons; kdx++ )
1504  {
1505  QDomElement polygonMemberElem = doc.createElement( QStringLiteral( "gml:polygonMember" ) );
1506  QDomElement polygonElem = doc.createElement( QStringLiteral( "gml:Polygon" ) );
1507  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1508  polygonElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase + QStringLiteral( ".%1" ).arg( kdx + 1 ) );
1509 
1510  wkbPtr.readHeader();
1511 
1512  int numRings;
1513  wkbPtr >> numRings;
1514 
1515  for ( int idx = 0; idx < numRings; idx++ )
1516  {
1517  QString boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:outerBoundaryIs" : "gml:exterior";
1518  if ( idx != 0 )
1519  {
1520  boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:innerBoundaryIs" : "gml:interior";
1521  }
1522  QDomElement boundaryElem = doc.createElement( boundaryName );
1523  QDomElement ringElem = doc.createElement( QStringLiteral( "gml:LinearRing" ) );
1524 
1525  int nPoints;
1526  wkbPtr >> nPoints;
1527 
1528  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1529  QString coordString;
1530  for ( int jdx = 0; jdx < nPoints; jdx++ )
1531  {
1532  if ( jdx != 0 )
1533  {
1534  coordString += ts;
1535  }
1536 
1537  double x = 0;
1538  double y = 0;
1539  if ( invertAxisOrientation )
1540  wkbPtr >> y >> x;
1541  else
1542  wkbPtr >> x >> y;
1543 
1544  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1545 
1546  if ( hasZValue )
1547  {
1548  wkbPtr += sizeof( double );
1549  }
1550  }
1551  const QDomText coordText = doc.createTextNode( coordString );
1552  coordElem.appendChild( coordText );
1553  ringElem.appendChild( coordElem );
1554  boundaryElem.appendChild( ringElem );
1555  polygonElem.appendChild( boundaryElem );
1556  polygonMemberElem.appendChild( polygonElem );
1557  multiPolygonElem.appendChild( polygonMemberElem );
1558  }
1559  }
1560  return multiPolygonElem;
1561  }
1562  default:
1563  return QDomElement();
1564  }
1565  }
1566  catch ( const QgsWkbException &e )
1567  {
1568  Q_UNUSED( e )
1569  return QDomElement();
1570  }
1571 }
1572 
1573 QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry &geometry, QDomDocument &doc, int precision )
1574 {
1575  return geometryToGML( geometry, doc, QStringLiteral( "GML2" ), precision );
1576 }
1577 
1578 QDomElement QgsOgcUtils::createGMLCoordinates( const QgsPolylineXY &points, QDomDocument &doc )
1579 {
1580  QDomElement coordElem = doc.createElement( QStringLiteral( "gml:coordinates" ) );
1581  coordElem.setAttribute( QStringLiteral( "cs" ), QStringLiteral( "," ) );
1582  coordElem.setAttribute( QStringLiteral( "ts" ), QStringLiteral( " " ) );
1583 
1584  QString coordString;
1585  QVector<QgsPointXY>::const_iterator pointIt = points.constBegin();
1586  for ( ; pointIt != points.constEnd(); ++pointIt )
1587  {
1588  if ( pointIt != points.constBegin() )
1589  {
1590  coordString += ' ';
1591  }
1592  coordString += qgsDoubleToString( pointIt->x() );
1593  coordString += ',';
1594  coordString += qgsDoubleToString( pointIt->y() );
1595  }
1596 
1597  const QDomText coordText = doc.createTextNode( coordString );
1598  coordElem.appendChild( coordText );
1599  return coordElem;
1600 }
1601 
1602 QDomElement QgsOgcUtils::createGMLPositions( const QgsPolylineXY &points, QDomDocument &doc )
1603 {
1604  QDomElement posElem = doc.createElement( QStringLiteral( "gml:pos" ) );
1605  if ( points.size() > 1 )
1606  posElem = doc.createElement( QStringLiteral( "gml:posList" ) );
1607  posElem.setAttribute( QStringLiteral( "srsDimension" ), QStringLiteral( "2" ) );
1608 
1609  QString coordString;
1610  QVector<QgsPointXY>::const_iterator pointIt = points.constBegin();
1611  for ( ; pointIt != points.constEnd(); ++pointIt )
1612  {
1613  if ( pointIt != points.constBegin() )
1614  {
1615  coordString += ' ';
1616  }
1617  coordString += qgsDoubleToString( pointIt->x() );
1618  coordString += ' ';
1619  coordString += qgsDoubleToString( pointIt->y() );
1620  }
1621 
1622  const QDomText coordText = doc.createTextNode( coordString );
1623  posElem.appendChild( coordText );
1624  return posElem;
1625 }
1626 
1627 
1628 
1629 // -----------------------------------------
1630 
1631 QColor QgsOgcUtils::colorFromOgcFill( const QDomElement &fillElement )
1632 {
1633  if ( fillElement.isNull() || !fillElement.hasChildNodes() )
1634  {
1635  return QColor();
1636  }
1637 
1638  QString cssName;
1639  QString elemText;
1640  QColor color;
1641  QDomElement cssElem = fillElement.firstChildElement( QStringLiteral( "CssParameter" ) );
1642  while ( !cssElem.isNull() )
1643  {
1644  cssName = cssElem.attribute( QStringLiteral( "name" ), QStringLiteral( "not_found" ) );
1645  if ( cssName != QLatin1String( "not_found" ) )
1646  {
1647  elemText = cssElem.text();
1648  if ( cssName == QLatin1String( "fill" ) )
1649  {
1650  color.setNamedColor( elemText );
1651  }
1652  else if ( cssName == QLatin1String( "fill-opacity" ) )
1653  {
1654  bool ok;
1655  const double opacity = elemText.toDouble( &ok );
1656  if ( ok )
1657  {
1658  color.setAlphaF( opacity );
1659  }
1660  }
1661  }
1662 
1663  cssElem = cssElem.nextSiblingElement( QStringLiteral( "CssParameter" ) );
1664  }
1665 
1666  return color;
1667 }
1668 
1669 
1671 {
1672  return expressionFromOgcFilter( element, QgsOgcUtils::FILTER_OGC_1_0, layer );
1673 }
1674 
1675 QgsExpression *QgsOgcUtils::expressionFromOgcFilter( const QDomElement &element, const FilterVersion version, QgsVectorLayer *layer )
1676 {
1677  if ( element.isNull() || !element.hasChildNodes() )
1678  return nullptr;
1679 
1680  QgsExpression *expr = new QgsExpression();
1681 
1682  // check if it is a single string value not having DOM elements
1683  // that express OGC operators
1684  if ( element.firstChild().nodeType() == QDomNode::TextNode )
1685  {
1686  expr->setExpression( element.firstChild().nodeValue() );
1687  return expr;
1688  }
1689 
1690  QgsOgcUtilsExpressionFromFilter utils( version, layer );
1691 
1692  // then check OGC DOM elements that contain OGC tags specifying
1693  // OGC operators.
1694  QDomElement childElem = element.firstChildElement();
1695  while ( !childElem.isNull() )
1696  {
1697  QgsExpressionNode *node = utils.nodeFromOgcFilter( childElem );
1698 
1699  if ( !node )
1700  {
1701  // invalid expression, parser error
1702  expr->d->mParserErrorString = utils.errorMessage();
1703  return expr;
1704  }
1705 
1706  // use the concat binary operator to append to the root node
1707  if ( !expr->d->mRootNode )
1708  {
1709  expr->d->mRootNode = node;
1710  }
1711  else
1712  {
1713  expr->d->mRootNode = new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boConcat, expr->d->mRootNode, node );
1714  }
1715 
1716  childElem = childElem.nextSiblingElement();
1717  }
1718 
1719  // update expression string
1720  expr->d->mExp = expr->dump();
1721 
1722  return expr;
1723 }
1724 
1725 typedef QMap<QString, int> IntMap;
1726 Q_GLOBAL_STATIC_WITH_ARGS( IntMap, BINARY_OPERATORS_TAG_NAMES_MAP, (
1727 {
1728  // logical
1729  { QLatin1String( "Or" ), QgsExpressionNodeBinaryOperator::boOr },
1730  { QLatin1String( "And" ), QgsExpressionNodeBinaryOperator::boAnd },
1731  // comparison
1732  { QLatin1String( "PropertyIsEqualTo" ), QgsExpressionNodeBinaryOperator::boEQ },
1733  { QLatin1String( "PropertyIsNotEqualTo" ), QgsExpressionNodeBinaryOperator::boNE },
1734  { QLatin1String( "PropertyIsLessThanOrEqualTo" ), QgsExpressionNodeBinaryOperator::boLE },
1735  { QLatin1String( "PropertyIsGreaterThanOrEqualTo" ), QgsExpressionNodeBinaryOperator::boGE },
1736  { QLatin1String( "PropertyIsLessThan" ), QgsExpressionNodeBinaryOperator::boLT },
1737  { QLatin1String( "PropertyIsGreaterThan" ), QgsExpressionNodeBinaryOperator::boGT },
1738  { QLatin1String( "PropertyIsLike" ), QgsExpressionNodeBinaryOperator::boLike },
1739  // arithmetic
1740  { QLatin1String( "Add" ), QgsExpressionNodeBinaryOperator::boPlus },
1741  { QLatin1String( "Sub" ), QgsExpressionNodeBinaryOperator::boMinus },
1742  { QLatin1String( "Mul" ), QgsExpressionNodeBinaryOperator::boMul },
1743  { QLatin1String( "Div" ), QgsExpressionNodeBinaryOperator::boDiv },
1744 } ) )
1745 
1746 static int binaryOperatorFromTagName( const QString &tagName )
1747 {
1748 
1749  return BINARY_OPERATORS_TAG_NAMES_MAP()->value( tagName, -1 );
1750 }
1751 
1752 static QString binaryOperatorToTagName( QgsExpressionNodeBinaryOperator::BinaryOperator op )
1753 {
1755  {
1756  return QStringLiteral( "PropertyIsLike" );
1757  }
1758  return BINARY_OPERATORS_TAG_NAMES_MAP()->key( op, QString() );
1759 }
1760 
1761 static bool isBinaryOperator( const QString &tagName )
1762 {
1763  return binaryOperatorFromTagName( tagName ) >= 0;
1764 }
1765 
1766 
1767 static bool isSpatialOperator( const QString &tagName )
1768 {
1769  static QStringList spatialOps;
1770  if ( spatialOps.isEmpty() )
1771  {
1772  spatialOps << QStringLiteral( "BBOX" ) << QStringLiteral( "Intersects" ) << QStringLiteral( "Contains" ) << QStringLiteral( "Crosses" ) << QStringLiteral( "Equals" )
1773  << QStringLiteral( "Disjoint" ) << QStringLiteral( "Overlaps" ) << QStringLiteral( "Touches" ) << QStringLiteral( "Within" );
1774  }
1775 
1776  return spatialOps.contains( tagName );
1777 }
1778 
1779 QgsExpressionNode *QgsOgcUtils::nodeFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer )
1780 {
1782  QgsExpressionNode *node = utils.nodeFromOgcFilter( element );
1783  errorMessage = utils.errorMessage();
1784  return node;
1785 }
1786 
1787 QgsExpressionNodeBinaryOperator *QgsOgcUtils::nodeBinaryOperatorFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer )
1788 {
1790  QgsExpressionNodeBinaryOperator *node = utils.nodeBinaryOperatorFromOgcFilter( element );
1791  errorMessage = utils.errorMessage();
1792  return node;
1793 }
1794 
1795 QgsExpressionNodeFunction *QgsOgcUtils::nodeSpatialOperatorFromOgcFilter( QDomElement &element, QString &errorMessage )
1796 {
1798  QgsExpressionNodeFunction *node = utils.nodeSpatialOperatorFromOgcFilter( element );
1799  errorMessage = utils.errorMessage();
1800  return node;
1801 }
1802 
1803 QgsExpressionNodeUnaryOperator *QgsOgcUtils::nodeNotFromOgcFilter( QDomElement &element, QString &errorMessage )
1804 {
1806  QgsExpressionNodeUnaryOperator *node = utils.nodeNotFromOgcFilter( element );
1807  errorMessage = utils.errorMessage();
1808  return node;
1809 }
1810 
1811 QgsExpressionNodeFunction *QgsOgcUtils::nodeFunctionFromOgcFilter( QDomElement &element, QString &errorMessage )
1812 {
1814  QgsExpressionNodeFunction *node = utils.nodeFunctionFromOgcFilter( element );
1815  errorMessage = utils.errorMessage();
1816  return node;
1817 }
1818 
1819 QgsExpressionNode *QgsOgcUtils::nodeLiteralFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer )
1820 {
1822  QgsExpressionNode *node = utils.nodeLiteralFromOgcFilter( element );
1823  errorMessage = utils.errorMessage();
1824  return node;
1825 }
1826 
1827 QgsExpressionNodeColumnRef *QgsOgcUtils::nodeColumnRefFromOgcFilter( QDomElement &element, QString &errorMessage )
1828 {
1830  QgsExpressionNodeColumnRef *node = utils.nodeColumnRefFromOgcFilter( element );
1831  errorMessage = utils.errorMessage();
1832  return node;
1833 }
1834 
1835 QgsExpressionNode *QgsOgcUtils::nodeIsBetweenFromOgcFilter( QDomElement &element, QString &errorMessage )
1836 {
1838  QgsExpressionNode *node = utils.nodeIsBetweenFromOgcFilter( element );
1839  errorMessage = utils.errorMessage();
1840  return node;
1841 }
1842 
1843 QgsExpressionNodeBinaryOperator *QgsOgcUtils::nodePropertyIsNullFromOgcFilter( QDomElement &element, QString &errorMessage )
1844 {
1846  QgsExpressionNodeBinaryOperator *node = utils.nodePropertyIsNullFromOgcFilter( element );
1847  errorMessage = utils.errorMessage();
1848  return node;
1849 }
1850 
1851 
1853 
1854 
1855 QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression &exp, QDomDocument &doc, QString *errorMessage )
1856 {
1857  return expressionToOgcFilter( exp, doc, GML_2_1_2, FILTER_OGC_1_0,
1858  QStringLiteral( "geometry" ), QString(), false, false, errorMessage );
1859 }
1860 
1861 QDomElement QgsOgcUtils::expressionToOgcExpression( const QgsExpression &exp, QDomDocument &doc, QString *errorMessage )
1862 {
1864  QStringLiteral( "geometry" ), QString(), false, false, errorMessage );
1865 }
1866 
1867 QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression &expression,
1868  QDomDocument &doc,
1869  GMLVersion gmlVersion,
1870  FilterVersion filterVersion,
1871  const QString &geometryName,
1872  const QString &srsName,
1873  bool honourAxisOrientation,
1874  bool invertAxisOrientation,
1875  QString *errorMessage )
1876 {
1877  if ( !expression.rootNode() )
1878  return QDomElement();
1879 
1880  QgsExpression exp = expression;
1881 
1882  QgsExpressionContext context;
1884  QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
1885  const QDomElement exprRootElem = utils.expressionNodeToOgcFilter( exp.rootNode(), &exp, &context );
1886  if ( errorMessage )
1887  *errorMessage = utils.errorMessage();
1888  if ( exprRootElem.isNull() )
1889  return QDomElement();
1890 
1891  QDomElement filterElem =
1892  ( filterVersion == FILTER_FES_2_0 ) ?
1893  doc.createElementNS( FES_NAMESPACE, QStringLiteral( "fes:Filter" ) ) :
1894  doc.createElementNS( OGC_NAMESPACE, QStringLiteral( "ogc:Filter" ) );
1895  if ( utils.GMLNamespaceUsed() )
1896  {
1897  QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:gml" ) );
1898  if ( gmlVersion == GML_3_2_1 )
1899  attr.setValue( GML32_NAMESPACE );
1900  else
1901  attr.setValue( GML_NAMESPACE );
1902  filterElem.setAttributeNode( attr );
1903  }
1904  filterElem.appendChild( exprRootElem );
1905  return filterElem;
1906 }
1907 
1909  QDomDocument &doc,
1910  GMLVersion gmlVersion,
1911  FilterVersion filterVersion,
1912  const QString &geometryName,
1913  const QString &srsName,
1914  bool honourAxisOrientation,
1915  bool invertAxisOrientation,
1916  QString *errorMessage )
1917 {
1918  QgsExpressionContext context;
1920 
1921  QgsExpression exp = expression;
1922 
1923  const QgsExpressionNode *node = exp.rootNode();
1924  if ( !node )
1925  return QDomElement();
1926 
1927  switch ( node->nodeType() )
1928  {
1932  {
1933  QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
1934  const QDomElement exprRootElem = utils.expressionNodeToOgcFilter( node, &exp, &context );
1935 
1936  if ( errorMessage )
1937  *errorMessage = utils.errorMessage();
1938 
1939  if ( !exprRootElem.isNull() )
1940  {
1941  return exprRootElem;
1942  }
1943  break;
1944  }
1945  default:
1946  {
1947  if ( errorMessage )
1948  *errorMessage = QObject::tr( "Node type not supported in expression translation: %1" ).arg( node->nodeType() );
1949  }
1950  }
1951  // got an error
1952  return QDomElement();
1953 }
1954 
1956  QDomDocument &doc,
1957  GMLVersion gmlVersion,
1958  FilterVersion filterVersion,
1959  const QList<LayerProperties> &layerProperties,
1960  bool honourAxisOrientation,
1961  bool invertAxisOrientation,
1962  const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename,
1963  QString *errorMessage )
1964 {
1965  if ( !statement.rootNode() )
1966  return QDomElement();
1967 
1968  QgsOgcUtilsSQLStatementToFilter utils( doc, gmlVersion, filterVersion,
1969  layerProperties, honourAxisOrientation, invertAxisOrientation,
1970  mapUnprefixedTypenameToPrefixedTypename );
1971  const QDomElement exprRootElem = utils.toOgcFilter( statement.rootNode() );
1972  if ( errorMessage )
1973  *errorMessage = utils.errorMessage();
1974  if ( exprRootElem.isNull() )
1975  return QDomElement();
1976 
1977  QDomElement filterElem =
1978  ( filterVersion == FILTER_FES_2_0 ) ?
1979  doc.createElementNS( FES_NAMESPACE, QStringLiteral( "fes:Filter" ) ) :
1980  doc.createElementNS( OGC_NAMESPACE, QStringLiteral( "ogc:Filter" ) );
1981  if ( utils.GMLNamespaceUsed() )
1982  {
1983  QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:gml" ) );
1984  if ( gmlVersion == GML_3_2_1 )
1985  attr.setValue( GML32_NAMESPACE );
1986  else
1987  attr.setValue( GML_NAMESPACE );
1988  filterElem.setAttributeNode( attr );
1989  }
1990 
1991  QSet<QString> setNamespaceURI;
1992  for ( const LayerProperties &props : layerProperties )
1993  {
1994  if ( !props.mNamespacePrefix.isEmpty() && !props.mNamespaceURI.isEmpty() &&
1995  !setNamespaceURI.contains( props.mNamespaceURI ) )
1996  {
1997  setNamespaceURI.insert( props.mNamespaceURI );
1998  QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:" ) + props.mNamespacePrefix );
1999  attr.setValue( props.mNamespaceURI );
2000  filterElem.setAttributeNode( attr );
2001  }
2002  }
2003 
2004  filterElem.appendChild( exprRootElem );
2005  return filterElem;
2006 }
2007 
2008 //
2009 
2010 
2012 {
2013  switch ( node->nodeType() )
2014  {
2016  return expressionUnaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeUnaryOperator *>( node ), expression, context );
2018  return expressionBinaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeBinaryOperator *>( node ), expression, context );
2020  return expressionInOperatorToOgcFilter( static_cast<const QgsExpressionNodeInOperator *>( node ), expression, context );
2022  return expressionFunctionToOgcFilter( static_cast<const QgsExpressionNodeFunction *>( node ), expression, context );
2024  return expressionLiteralToOgcFilter( static_cast<const QgsExpressionNodeLiteral *>( node ), expression, context );
2026  return expressionColumnRefToOgcFilter( static_cast<const QgsExpressionNodeColumnRef *>( node ), expression, context );
2027 
2028  default:
2029  mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
2030  return QDomElement();
2031  }
2032 }
2033 
2034 QDomElement QgsOgcUtilsExprToFilter::expressionUnaryOperatorToOgcFilter( const QgsExpressionNodeUnaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
2035 {
2036  const QDomElement operandElem = expressionNodeToOgcFilter( node->operand(), expression, context );
2037  if ( !mErrorMessage.isEmpty() )
2038  return QDomElement();
2039 
2040  QDomElement uoElem;
2041  switch ( node->op() )
2042  {
2044  uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2045  if ( node->operand()->nodeType() == QgsExpressionNode::ntLiteral )
2046  {
2047  // operand expression already created a Literal node:
2048  // take the literal value, prepend - and remove old literal node
2049  uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2050  mDoc.removeChild( operandElem );
2051  }
2052  else
2053  {
2054  mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2055  return QDomElement();
2056  }
2057  break;
2059  uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2060  uoElem.appendChild( operandElem );
2061  break;
2062 
2063  default:
2064  mErrorMessage = QObject::tr( "Unary operator '%1' not implemented yet" ).arg( node->text() );
2065  return QDomElement();
2066  }
2067 
2068  return uoElem;
2069 }
2070 
2071 
2072 QDomElement QgsOgcUtilsExprToFilter::expressionBinaryOperatorToOgcFilter( const QgsExpressionNodeBinaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
2073 {
2074  const QDomElement leftElem = expressionNodeToOgcFilter( node->opLeft(), expression, context );
2075  if ( !mErrorMessage.isEmpty() )
2076  return QDomElement();
2077 
2079 
2080  // before right operator is parsed: to allow NULL handling
2082  {
2083  if ( node->opRight()->nodeType() == QgsExpressionNode::ntLiteral )
2084  {
2085  const QgsExpressionNodeLiteral *rightLit = static_cast<const QgsExpressionNodeLiteral *>( node->opRight() );
2086  if ( rightLit->value().isNull() )
2087  {
2088 
2089  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2090  elem.appendChild( leftElem );
2091 
2093  {
2094  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2095  notElem.appendChild( elem );
2096  return notElem;
2097  }
2098 
2099  return elem;
2100  }
2101 
2102  // continue with equal / not equal operator once the null case is handled
2104  }
2105 
2106  }
2107 
2108  const QDomElement rightElem = expressionNodeToOgcFilter( node->opRight(), expression, context );
2109  if ( !mErrorMessage.isEmpty() )
2110  return QDomElement();
2111 
2112 
2113  const QString opText = binaryOperatorToTagName( op );
2114  if ( opText.isEmpty() )
2115  {
2116  // not implemented binary operators
2117  // TODO: regex, % (mod), ^ (pow) are not supported yet
2118  mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( node->text() );
2119  return QDomElement();
2120  }
2121 
2122  QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2123 
2125  {
2127  boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2128 
2129  // setup wildCards to <ogc:PropertyIsLike>
2130  boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2131  boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2132  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2133  boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2134  else
2135  boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2136  }
2137 
2138  boElem.appendChild( leftElem );
2139  boElem.appendChild( rightElem );
2140  return boElem;
2141 }
2142 
2143 
2144 QDomElement QgsOgcUtilsExprToFilter::expressionLiteralToOgcFilter( const QgsExpressionNodeLiteral *node, QgsExpression *expression, const QgsExpressionContext *context )
2145 {
2146  Q_UNUSED( expression )
2147  Q_UNUSED( context )
2148  QString value;
2149  switch ( node->value().type() )
2150  {
2151  case QVariant::Int:
2152  value = QString::number( node->value().toInt() );
2153  break;
2154  case QVariant::Double:
2155  value = qgsDoubleToString( node->value().toDouble() );
2156  break;
2157  case QVariant::String:
2158  value = node->value().toString();
2159  break;
2160  case QVariant::Date:
2161  value = node->value().toDate().toString( Qt::ISODate );
2162  break;
2163  case QVariant::DateTime:
2164  value = node->value().toDateTime().toString( Qt::ISODate );
2165  break;
2166 
2167  default:
2168  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( node->value().type() );
2169  return QDomElement();
2170  }
2171 
2172  QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2173  litElem.appendChild( mDoc.createTextNode( value ) );
2174  return litElem;
2175 }
2176 
2177 
2178 QDomElement QgsOgcUtilsExprToFilter::expressionColumnRefToOgcFilter( const QgsExpressionNodeColumnRef *node, QgsExpression *expression, const QgsExpressionContext *context )
2179 {
2180  Q_UNUSED( expression )
2181  Q_UNUSED( context )
2182  QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2183  propElem.appendChild( mDoc.createTextNode( node->name() ) );
2184  return propElem;
2185 }
2186 
2187 
2188 
2189 QDomElement QgsOgcUtilsExprToFilter::expressionInOperatorToOgcFilter( const QgsExpressionNodeInOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
2190 {
2191  if ( node->list()->list().size() == 1 )
2192  return expressionNodeToOgcFilter( node->list()->list()[0], expression, context );
2193 
2194  QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2195  const QDomElement leftNode = expressionNodeToOgcFilter( node->node(), expression, context );
2196 
2197  const auto constList = node->list()->list();
2198  for ( QgsExpressionNode *n : constList )
2199  {
2200  const QDomElement listNode = expressionNodeToOgcFilter( n, expression, context );
2201  if ( !mErrorMessage.isEmpty() )
2202  return QDomElement();
2203 
2204  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2205  eqElem.appendChild( leftNode.cloneNode() );
2206  eqElem.appendChild( listNode );
2207 
2208  orElem.appendChild( eqElem );
2209  }
2210 
2211  if ( node->isNotIn() )
2212  {
2213  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2214  notElem.appendChild( orElem );
2215  return notElem;
2216  }
2217 
2218  return orElem;
2219 }
2220 
2221 Q_GLOBAL_STATIC_WITH_ARGS( QgsStringMap, BINARY_SPATIAL_OPS_MAP, (
2222 {
2223  { QLatin1String( "disjoint" ), QLatin1String( "Disjoint" ) },
2224  { QLatin1String( "intersects" ), QLatin1String( "Intersects" )},
2225  { QLatin1String( "touches" ), QLatin1String( "Touches" ) },
2226  { QLatin1String( "crosses" ), QLatin1String( "Crosses" ) },
2227  { QLatin1String( "contains" ), QLatin1String( "Contains" ) },
2228  { QLatin1String( "overlaps" ), QLatin1String( "Overlaps" ) },
2229  { QLatin1String( "within" ), QLatin1String( "Within" ) }
2230 } ) )
2231 
2232 static bool isBinarySpatialOperator( const QString &fnName )
2233 {
2234  return BINARY_SPATIAL_OPS_MAP()->contains( fnName );
2235 }
2236 
2237 static QString tagNameForSpatialOperator( const QString &fnName )
2238 {
2239  return BINARY_SPATIAL_OPS_MAP()->value( fnName );
2240 }
2241 
2242 static bool isGeometryColumn( const QgsExpressionNode *node )
2243 {
2244  if ( node->nodeType() != QgsExpressionNode::ntFunction )
2245  return false;
2246 
2247  const QgsExpressionNodeFunction *fn = static_cast<const QgsExpressionNodeFunction *>( node );
2249  return fd->name() == QLatin1String( "$geometry" );
2250 }
2251 
2252 static QgsGeometry geometryFromConstExpr( const QgsExpressionNode *node )
2253 {
2254  // Right now we support only geomFromWKT(' ..... ')
2255  // Ideally we should support any constant sub-expression (not dependent on feature's geometry or attributes)
2256 
2257  if ( node->nodeType() == QgsExpressionNode::ntFunction )
2258  {
2259  const QgsExpressionNodeFunction *fnNode = static_cast<const QgsExpressionNodeFunction *>( node );
2261  if ( fnDef->name() == QLatin1String( "geom_from_wkt" ) )
2262  {
2263  const QList<QgsExpressionNode *> &args = fnNode->args()->list();
2264  if ( args[0]->nodeType() == QgsExpressionNode::ntLiteral )
2265  {
2266  const QString wkt = static_cast<const QgsExpressionNodeLiteral *>( args[0] )->value().toString();
2267  return QgsGeometry::fromWkt( wkt );
2268  }
2269  }
2270  }
2271  return QgsGeometry();
2272 }
2273 
2274 
2275 QDomElement QgsOgcUtilsExprToFilter::expressionFunctionToOgcFilter( const QgsExpressionNodeFunction *node, QgsExpression *expression, const QgsExpressionContext *context )
2276 {
2278 
2279  if ( fd->name() == QLatin1String( "intersects_bbox" ) )
2280  {
2281  QList<QgsExpressionNode *> argNodes = node->args()->list();
2282  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2283 
2284  const QgsGeometry geom = geometryFromConstExpr( argNodes[1] );
2285  if ( !geom.isNull() && isGeometryColumn( argNodes[0] ) )
2286  {
2287  QgsRectangle rect = geom.boundingBox();
2288 
2289  mGMLUsed = true;
2290 
2291  const QDomElement elemBox = ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
2292  QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, mSrsName, mInvertAxisOrientation ) :
2293  QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, mSrsName, mInvertAxisOrientation );
2294 
2295  QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2296  geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );
2297 
2298  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":BBOX" );
2299  funcElem.appendChild( geomProperty );
2300  funcElem.appendChild( elemBox );
2301  return funcElem;
2302  }
2303  else
2304  {
2305  mErrorMessage = QObject::tr( "<BBOX> is currently supported only in form: bbox($geometry, geomFromWKT('…'))" );
2306  return QDomElement();
2307  }
2308  }
2309 
2310  if ( isBinarySpatialOperator( fd->name() ) )
2311  {
2312  QList<QgsExpressionNode *> argNodes = node->args()->list();
2313  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2314 
2315  QgsExpressionNode *otherNode = nullptr;
2316  if ( isGeometryColumn( argNodes[0] ) )
2317  otherNode = argNodes[1];
2318  else if ( isGeometryColumn( argNodes[1] ) )
2319  otherNode = argNodes[0];
2320  else
2321  {
2322  mErrorMessage = QObject::tr( "Unable to translate spatial operator: at least one must refer to geometry." );
2323  return QDomElement();
2324  }
2325 
2326  QDomElement otherGeomElem;
2327 
2328  // the other node must be a geometry constructor
2329  if ( otherNode->nodeType() != QgsExpressionNode::ntFunction )
2330  {
2331  mErrorMessage = QObject::tr( "spatial operator: the other operator must be a geometry constructor function" );
2332  return QDomElement();
2333  }
2334 
2335  const QgsExpressionNodeFunction *otherFn = static_cast<const QgsExpressionNodeFunction *>( otherNode );
2336  QgsExpressionFunction *otherFnDef = QgsExpression::Functions()[otherFn->fnIndex()];
2337  if ( otherFnDef->name() == QLatin1String( "geom_from_wkt" ) )
2338  {
2339  QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2340  if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2341  {
2342  mErrorMessage = QObject::tr( "geom_from_wkt: argument must be string literal" );
2343  return QDomElement();
2344  }
2345  const QString wkt = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2346  const QgsGeometry geom = QgsGeometry::fromWkt( wkt );
2347  otherGeomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation,
2348  QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2349  mGeomId ++;
2350  }
2351  else if ( otherFnDef->name() == QLatin1String( "geom_from_gml" ) )
2352  {
2353  QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2354  if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2355  {
2356  mErrorMessage = QObject::tr( "geom_from_gml: argument must be string literal" );
2357  return QDomElement();
2358  }
2359 
2360  QDomDocument geomDoc;
2361  const QString gml = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2362  if ( !geomDoc.setContent( gml, true ) )
2363  {
2364  mErrorMessage = QObject::tr( "geom_from_gml: unable to parse XML" );
2365  return QDomElement();
2366  }
2367 
2368  const QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
2369  otherGeomElem = geomNode.toElement();
2370  }
2371  else
2372  {
2373  mErrorMessage = QObject::tr( "spatial operator: unknown geometry constructor function" );
2374  return QDomElement();
2375  }
2376 
2377  mGMLUsed = true;
2378 
2379  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + tagNameForSpatialOperator( fd->name() ) );
2380  QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2381  geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );
2382  funcElem.appendChild( geomProperty );
2383  funcElem.appendChild( otherGeomElem );
2384  return funcElem;
2385  }
2386 
2387  if ( fd->isStatic( node, expression, context ) )
2388  {
2389  const QVariant result = fd->run( node->args(), context, expression, node );
2390  const QgsExpressionNodeLiteral literal( result );
2391  return expressionLiteralToOgcFilter( &literal, expression, context );
2392  }
2393 
2394  if ( fd->params() == 0 )
2395  {
2396  mErrorMessage = QObject::tr( "Special columns/constants are not supported." );
2397  return QDomElement();
2398  }
2399 
2400  // this is somehow wrong - we are just hoping that the other side supports the same functions as we do...
2401  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
2402  funcElem.setAttribute( QStringLiteral( "name" ), fd->name() );
2403  const auto constList = node->args()->list();
2404  for ( QgsExpressionNode *n : constList )
2405  {
2406  const QDomElement childElem = expressionNodeToOgcFilter( n, expression, context );
2407  if ( !mErrorMessage.isEmpty() )
2408  return QDomElement();
2409 
2410  funcElem.appendChild( childElem );
2411  }
2412 
2413  return funcElem;
2414 }
2415 
2416 //
2417 
2419  QgsOgcUtils::GMLVersion gmlVersion,
2420  QgsOgcUtils::FilterVersion filterVersion,
2421  const QList<QgsOgcUtils::LayerProperties> &layerProperties,
2422  bool honourAxisOrientation,
2423  bool invertAxisOrientation,
2424  const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename )
2425  : mDoc( doc )
2426  , mGMLUsed( false )
2427  , mGMLVersion( gmlVersion )
2428  , mFilterVersion( filterVersion )
2429  , mLayerProperties( layerProperties )
2430  , mHonourAxisOrientation( honourAxisOrientation )
2431  , mInvertAxisOrientation( invertAxisOrientation )
2432  , mFilterPrefix( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "fes" : "ogc" )
2433  , mPropertyName( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "ValueReference" : "PropertyName" )
2434  , mGeomId( 1 )
2435  , mMapUnprefixedTypenameToPrefixedTypename( mapUnprefixedTypenameToPrefixedTypename )
2436 {
2437 }
2438 
2440 {
2441  switch ( node->nodeType() )
2442  {
2444  return toOgcFilter( static_cast<const QgsSQLStatement::NodeUnaryOperator *>( node ) );
2446  return toOgcFilter( static_cast<const QgsSQLStatement::NodeBinaryOperator *>( node ) );
2448  return toOgcFilter( static_cast<const QgsSQLStatement::NodeInOperator *>( node ) );
2450  return toOgcFilter( static_cast<const QgsSQLStatement::NodeBetweenOperator *>( node ) );
2452  return toOgcFilter( static_cast<const QgsSQLStatement::NodeFunction *>( node ) );
2454  return toOgcFilter( static_cast<const QgsSQLStatement::NodeLiteral *>( node ) );
2456  return toOgcFilter( static_cast<const QgsSQLStatement::NodeColumnRef *>( node ) );
2458  return toOgcFilter( static_cast<const QgsSQLStatement::NodeSelect *>( node ) );
2459 
2460  default:
2461  mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
2462  return QDomElement();
2463  }
2464 }
2465 
2466 
2468 {
2469 
2470  const QDomElement operandElem = toOgcFilter( node->operand() );
2471  if ( !mErrorMessage.isEmpty() )
2472  return QDomElement();
2473 
2474  QDomElement uoElem;
2475  switch ( node->op() )
2476  {
2478  uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2479  if ( node->operand()->nodeType() == QgsSQLStatement::ntLiteral )
2480  {
2481  // operand expression already created a Literal node:
2482  // take the literal value, prepend - and remove old literal node
2483  uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2484  mDoc.removeChild( operandElem );
2485  }
2486  else
2487  {
2488  mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2489  return QDomElement();
2490  }
2491  break;
2493  uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2494  uoElem.appendChild( operandElem );
2495  break;
2496 
2497  default:
2498  mErrorMessage = QObject::tr( "Unary operator %1 not implemented yet" ).arg( QgsSQLStatement::UNARY_OPERATOR_TEXT[node->op()] );
2499  return QDomElement();
2500  }
2501 
2502  return uoElem;
2503 }
2504 
2505 
2507 {
2508  const QDomElement leftElem = toOgcFilter( node->opLeft() );
2509  if ( !mErrorMessage.isEmpty() )
2510  return QDomElement();
2511 
2512  QgsSQLStatement::BinaryOperator op = node->op();
2513 
2514  // before right operator is parsed: to allow NULL handling
2515  if ( op == QgsSQLStatement::boIs || op == QgsSQLStatement::boIsNot )
2516  {
2517  if ( node->opRight()->nodeType() == QgsSQLStatement::ntLiteral )
2518  {
2519  const QgsSQLStatement::NodeLiteral *rightLit = static_cast<const QgsSQLStatement::NodeLiteral *>( node->opRight() );
2520  if ( rightLit->value().isNull() )
2521  {
2522 
2523  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2524  elem.appendChild( leftElem );
2525 
2526  if ( op == QgsSQLStatement::boIsNot )
2527  {
2528  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2529  notElem.appendChild( elem );
2530  return notElem;
2531  }
2532 
2533  return elem;
2534  }
2535 
2536  // continue with equal / not equal operator once the null case is handled
2538  }
2539 
2540  }
2541 
2542  const QDomElement rightElem = toOgcFilter( node->opRight() );
2543  if ( !mErrorMessage.isEmpty() )
2544  return QDomElement();
2545 
2546 
2547  QString opText;
2548  if ( op == QgsSQLStatement::boOr )
2549  opText = QStringLiteral( "Or" );
2550  else if ( op == QgsSQLStatement::boAnd )
2551  opText = QStringLiteral( "And" );
2552  else if ( op == QgsSQLStatement::boEQ )
2553  opText = QStringLiteral( "PropertyIsEqualTo" );
2554  else if ( op == QgsSQLStatement::boNE )
2555  opText = QStringLiteral( "PropertyIsNotEqualTo" );
2556  else if ( op == QgsSQLStatement::boLE )
2557  opText = QStringLiteral( "PropertyIsLessThanOrEqualTo" );
2558  else if ( op == QgsSQLStatement::boGE )
2559  opText = QStringLiteral( "PropertyIsGreaterThanOrEqualTo" );
2560  else if ( op == QgsSQLStatement::boLT )
2561  opText = QStringLiteral( "PropertyIsLessThan" );
2562  else if ( op == QgsSQLStatement::boGT )
2563  opText = QStringLiteral( "PropertyIsGreaterThan" );
2564  else if ( op == QgsSQLStatement::boLike )
2565  opText = QStringLiteral( "PropertyIsLike" );
2566  else if ( op == QgsSQLStatement::boILike )
2567  opText = QStringLiteral( "PropertyIsLike" );
2568 
2569  if ( opText.isEmpty() )
2570  {
2571  // not implemented binary operators
2572  mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( QgsSQLStatement::BINARY_OPERATOR_TEXT[op] );
2573  return QDomElement();
2574  }
2575 
2576  QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2577 
2579  {
2580  if ( op == QgsSQLStatement::boILike )
2581  boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2582 
2583  // setup wildCards to <ogc:PropertyIsLike>
2584  boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2585  boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2586  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2587  boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2588  else
2589  boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2590  }
2591 
2592  boElem.appendChild( leftElem );
2593  boElem.appendChild( rightElem );
2594  return boElem;
2595 }
2596 
2597 
2599 {
2600  QString value;
2601  switch ( node->value().type() )
2602  {
2603  case QVariant::Int:
2604  value = QString::number( node->value().toInt() );
2605  break;
2606  case QVariant::LongLong:
2607  value = QString::number( node->value().toLongLong() );
2608  break;
2609  case QVariant::Double:
2610  value = qgsDoubleToString( node->value().toDouble() );
2611  break;
2612  case QVariant::String:
2613  value = node->value().toString();
2614  break;
2615 
2616  default:
2617  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( node->value().type() );
2618  return QDomElement();
2619  }
2620 
2621  QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2622  litElem.appendChild( mDoc.createTextNode( value ) );
2623  return litElem;
2624 }
2625 
2626 
2628 {
2629  QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2630  if ( node->tableName().isEmpty() || mLayerProperties.size() == 1 )
2631  {
2632  if ( mLayerProperties.size() == 1 && !mLayerProperties[0].mNamespacePrefix.isEmpty() )
2633  propElem.appendChild( mDoc.createTextNode(
2634  mLayerProperties[0].mNamespacePrefix + QStringLiteral( ":" ) + node->name() ) );
2635  else
2636  propElem.appendChild( mDoc.createTextNode( node->name() ) );
2637  }
2638  else
2639  {
2640  QString tableName( mMapTableAliasToNames[node->tableName()] );
2641  if ( mMapUnprefixedTypenameToPrefixedTypename.contains( tableName ) )
2642  tableName = mMapUnprefixedTypenameToPrefixedTypename[tableName];
2643  propElem.appendChild( mDoc.createTextNode( tableName + "/" + node->name() ) );
2644  }
2645  return propElem;
2646 }
2647 
2649 {
2650  if ( node->list()->list().size() == 1 )
2651  return toOgcFilter( node->list()->list()[0] );
2652 
2653  QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2654  const QDomElement leftNode = toOgcFilter( node->node() );
2655 
2656  const auto constList = node->list()->list();
2657  for ( QgsSQLStatement::Node *n : constList )
2658  {
2659  const QDomElement listNode = toOgcFilter( n );
2660  if ( !mErrorMessage.isEmpty() )
2661  return QDomElement();
2662 
2663  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2664  eqElem.appendChild( leftNode.cloneNode() );
2665  eqElem.appendChild( listNode );
2666 
2667  orElem.appendChild( eqElem );
2668  }
2669 
2670  if ( node->isNotIn() )
2671  {
2672  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2673  notElem.appendChild( orElem );
2674  return notElem;
2675  }
2676 
2677  return orElem;
2678 }
2679 
2681 {
2682  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsBetween" );
2683  elem.appendChild( toOgcFilter( node->node() ) );
2684  QDomElement lowerBoundary = mDoc.createElement( mFilterPrefix + ":LowerBoundary" );
2685  lowerBoundary.appendChild( toOgcFilter( node->minVal() ) );
2686  elem.appendChild( lowerBoundary );
2687  QDomElement upperBoundary = mDoc.createElement( mFilterPrefix + ":UpperBoundary" );
2688  upperBoundary.appendChild( toOgcFilter( node->maxVal() ) );
2689  elem.appendChild( upperBoundary );
2690 
2691  if ( node->isNotBetween() )
2692  {
2693  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2694  notElem.appendChild( elem );
2695  return notElem;
2696  }
2697 
2698  return elem;
2699 }
2700 
2701 static QString mapBinarySpatialToOgc( const QString &name )
2702 {
2703  QString nameCompare( name );
2704 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 2)
2705  if ( name.size() > 3 && name.midRef( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2706  nameCompare = name.mid( 3 );
2707 #else
2708  if ( name.size() > 3 && QStringView {name}.mid( 0, 3 ).toString().compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2709  nameCompare = name.mid( 3 );
2710 #endif
2711  QStringList spatialOps;
2712  spatialOps << QStringLiteral( "BBOX" ) << QStringLiteral( "Intersects" ) << QStringLiteral( "Contains" ) << QStringLiteral( "Crosses" ) << QStringLiteral( "Equals" )
2713  << QStringLiteral( "Disjoint" ) << QStringLiteral( "Overlaps" ) << QStringLiteral( "Touches" ) << QStringLiteral( "Within" );
2714  const auto constSpatialOps = spatialOps;
2715  for ( QString op : constSpatialOps )
2716  {
2717  if ( nameCompare.compare( op, Qt::CaseInsensitive ) == 0 )
2718  return op;
2719  }
2720  return QString();
2721 }
2722 
2723 static QString mapTernarySpatialToOgc( const QString &name )
2724 {
2725  QString nameCompare( name );
2726 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 2)
2727  if ( name.size() > 3 && name.midRef( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2728  nameCompare = name.mid( 3 );
2729 #else
2730  if ( name.size() > 3 && QStringView {name}.mid( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2731  nameCompare = name.mid( 3 );
2732 #endif
2733  if ( nameCompare.compare( QLatin1String( "DWithin" ), Qt::CaseInsensitive ) == 0 )
2734  return QStringLiteral( "DWithin" );
2735  if ( nameCompare.compare( QLatin1String( "Beyond" ), Qt::CaseInsensitive ) == 0 )
2736  return QStringLiteral( "Beyond" );
2737  return QString();
2738 }
2739 
2740 QString QgsOgcUtilsSQLStatementToFilter::getGeometryColumnSRSName( const QgsSQLStatement::Node *node )
2741 {
2742  if ( node->nodeType() != QgsSQLStatement::ntColumnRef )
2743  return QString();
2744 
2745  const QgsSQLStatement::NodeColumnRef *col = static_cast<const QgsSQLStatement::NodeColumnRef *>( node );
2746  if ( !col->tableName().isEmpty() )
2747  {
2748  const auto constMLayerProperties = mLayerProperties;
2749  for ( const QgsOgcUtils::LayerProperties &prop : constMLayerProperties )
2750  {
2751  if ( prop.mName.compare( mMapTableAliasToNames[col->tableName()], Qt::CaseInsensitive ) == 0 &&
2752  prop.mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
2753  {
2754  return prop.mSRSName;
2755  }
2756  }
2757  }
2758  if ( !mLayerProperties.empty() &&
2759  mLayerProperties.at( 0 ).mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
2760  {
2761  return mLayerProperties.at( 0 ).mSRSName;
2762  }
2763  return QString();
2764 }
2765 
2766 bool QgsOgcUtilsSQLStatementToFilter::processSRSName( const QgsSQLStatement::NodeFunction *mainNode,
2767  QList<QgsSQLStatement::Node *> args,
2768  bool lastArgIsSRSName,
2769  QString &srsName,
2770  bool &axisInversion )
2771 {
2772  srsName = mCurrentSRSName;
2773  axisInversion = mInvertAxisOrientation;
2774 
2775  if ( lastArgIsSRSName )
2776  {
2777  QgsSQLStatement::Node *lastArg = args[ args.size() - 1 ];
2778  if ( lastArg->nodeType() != QgsSQLStatement::ntLiteral )
2779  {
2780  mErrorMessage = QObject::tr( "%1: Last argument must be string or integer literal" ).arg( mainNode->name() );
2781  return false;
2782  }
2783  const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( lastArg );
2784  if ( lit->value().type() == QVariant::Int )
2785  {
2786  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2787  {
2788  srsName = "EPSG:" + QString::number( lit->value().toInt() );
2789  }
2790  else
2791  {
2792  srsName = "urn:ogc:def:crs:EPSG::" + QString::number( lit->value().toInt() );
2793  }
2794  }
2795  else
2796  {
2797  srsName = lit->value().toString();
2798  if ( srsName.startsWith( QLatin1String( "EPSG:" ), Qt::CaseInsensitive ) )
2799  return true;
2800  }
2801  }
2802 
2804  if ( !srsName.isEmpty() )
2806  if ( crs.isValid() )
2807  {
2808  if ( mHonourAxisOrientation && crs.hasAxisInverted() )
2809  {
2810  axisInversion = !axisInversion;
2811  }
2812  }
2813 
2814  return true;
2815 }
2816 
2818 {
2819  // ST_GeometryFromText
2820  if ( node->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 )
2821  {
2822  QList<QgsSQLStatement::Node *> args = node->args()->list();
2823  if ( args.size() != 1 && args.size() != 2 )
2824  {
2825  mErrorMessage = QObject::tr( "Function %1 should have 1 or 2 arguments" ).arg( node->name() );
2826  return QDomElement();
2827  }
2828 
2829  QgsSQLStatement::Node *firstFnArg = args[0];
2830  if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
2831  {
2832  mErrorMessage = QObject::tr( "%1: First argument must be string literal" ).arg( node->name() );
2833  return QDomElement();
2834  }
2835 
2836  QString srsName;
2837  bool axisInversion;
2838  if ( ! processSRSName( node, args, args.size() == 2, srsName, axisInversion ) )
2839  {
2840  return QDomElement();
2841  }
2842 
2843  const QString wkt = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
2844  const QgsGeometry geom = QgsGeometry::fromWkt( wkt );
2845  const QDomElement geomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, srsName, axisInversion,
2846  QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2847  mGeomId ++;
2848  if ( geomElem.isNull() )
2849  {
2850  mErrorMessage = QObject::tr( "%1: invalid WKT" ).arg( node->name() );
2851  return QDomElement();
2852  }
2853  mGMLUsed = true;
2854  return geomElem;
2855  }
2856 
2857  // ST_MakeEnvelope
2858  if ( node->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 )
2859  {
2860  QList<QgsSQLStatement::Node *> args = node->args()->list();
2861  if ( args.size() != 4 && args.size() != 5 )
2862  {
2863  mErrorMessage = QObject::tr( "Function %1 should have 4 or 5 arguments" ).arg( node->name() );
2864  return QDomElement();
2865  }
2866 
2867  QgsRectangle rect;
2868 
2869  for ( int i = 0; i < 4; i++ )
2870  {
2871  QgsSQLStatement::Node *arg = args[i];
2872  if ( arg->nodeType() != QgsSQLStatement::ntLiteral )
2873  {
2874  mErrorMessage = QObject::tr( "%1: Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
2875  return QDomElement();
2876  }
2877  const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( arg );
2878  double val = 0.0;
2879  if ( lit->value().type() == QVariant::Int )
2880  val = lit->value().toInt();
2881  else if ( lit->value().type() == QVariant::LongLong )
2882  val = lit->value().toLongLong();
2883  else if ( lit->value().type() == QVariant::Double )
2884  val = lit->value().toDouble();
2885  else
2886  {
2887  mErrorMessage = QObject::tr( "%1 Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
2888  return QDomElement();
2889  }
2890  if ( i == 0 )
2891  rect.setXMinimum( val );
2892  else if ( i == 1 )
2893  rect.setYMinimum( val );
2894  else if ( i == 2 )
2895  rect.setXMaximum( val );
2896  else
2897  rect.setYMaximum( val );
2898  }
2899 
2900  QString srsName;
2901  bool axisInversion;
2902  if ( ! processSRSName( node, args, args.size() == 5, srsName, axisInversion ) )
2903  {
2904  return QDomElement();
2905  }
2906 
2907  mGMLUsed = true;
2908 
2909  return ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
2910  QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, srsName, axisInversion, 15 ) :
2911  QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, srsName, axisInversion, 15 );
2912  }
2913 
2914  // ST_GeomFromGML
2915  if ( node->name().compare( QLatin1String( "ST_GeomFromGML" ), Qt::CaseInsensitive ) == 0 )
2916  {
2917  QList<QgsSQLStatement::Node *> args = node->args()->list();
2918  if ( args.size() != 1 )
2919  {
2920  mErrorMessage = QObject::tr( "Function %1 should have 1 argument" ).arg( node->name() );
2921  return QDomElement();
2922  }
2923 
2924  QgsSQLStatement::Node *firstFnArg = args[0];
2925  if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
2926  {
2927  mErrorMessage = QObject::tr( "%1: Argument must be string literal" ).arg( node->name() );
2928  return QDomElement();
2929  }
2930 
2931  QDomDocument geomDoc;
2932  const QString gml = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
2933  if ( !geomDoc.setContent( gml, true ) )
2934  {
2935  mErrorMessage = QObject::tr( "ST_GeomFromGML: unable to parse XML" );
2936  return QDomElement();
2937  }
2938 
2939  const QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
2940  mGMLUsed = true;
2941  return geomNode.toElement();
2942  }
2943 
2944  // Binary geometry operators
2945  QString ogcName( mapBinarySpatialToOgc( node->name() ) );
2946  if ( !ogcName.isEmpty() )
2947  {
2948  QList<QgsSQLStatement::Node *> args = node->args()->list();
2949  if ( args.size() != 2 )
2950  {
2951  mErrorMessage = QObject::tr( "Function %1 should have 2 arguments" ).arg( node->name() );
2952  return QDomElement();
2953  }
2954 
2955  for ( int i = 0; i < 2; i ++ )
2956  {
2957  if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
2958  ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
2959  static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
2960  {
2961  mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
2962  break;
2963  }
2964  }
2965 
2966  //if( ogcName == "Intersects" && mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2967  // ogcName = "Intersect";
2968  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + ogcName );
2969  const auto constArgs = args;
2970  for ( QgsSQLStatement::Node *n : constArgs )
2971  {
2972  const QDomElement childElem = toOgcFilter( n );
2973  if ( !mErrorMessage.isEmpty() )
2974  {
2975  mCurrentSRSName.clear();
2976  return QDomElement();
2977  }
2978 
2979  funcElem.appendChild( childElem );
2980  }
2981 
2982  mCurrentSRSName.clear();
2983  return funcElem;
2984  }
2985 
2986  ogcName = mapTernarySpatialToOgc( node->name() );
2987  if ( !ogcName.isEmpty() )
2988  {
2989  QList<QgsSQLStatement::Node *> args = node->args()->list();
2990  if ( args.size() != 3 )
2991  {
2992  mErrorMessage = QObject::tr( "Function %1 should have 3 arguments" ).arg( node->name() );
2993  return QDomElement();
2994  }
2995 
2996  for ( int i = 0; i < 2; i ++ )
2997  {
2998  if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
2999  ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
3000  static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
3001  {
3002  mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
3003  break;
3004  }
3005  }
3006 
3007  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + node->name().mid( 3 ) );
3008  for ( int i = 0; i < 2; i++ )
3009  {
3010  const QDomElement childElem = toOgcFilter( args[i] );
3011  if ( !mErrorMessage.isEmpty() )
3012  {
3013  mCurrentSRSName.clear();
3014  return QDomElement();
3015  }
3016 
3017  funcElem.appendChild( childElem );
3018  }
3019  mCurrentSRSName.clear();
3020 
3021  QgsSQLStatement::Node *distanceNode = args[2];
3022  if ( distanceNode->nodeType() != QgsSQLStatement::ntLiteral )
3023  {
3024  mErrorMessage = QObject::tr( "Function %1 3rd argument should be a numeric value or a string made of a numeric value followed by a string" ).arg( node->name() );
3025  return QDomElement();
3026  }
3027  const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( distanceNode );
3028  if ( lit->value().isNull() )
3029  {
3030  mErrorMessage = QObject::tr( "Function %1 3rd argument should be a numeric value or a string made of a numeric value followed by a string" ).arg( node->name() );
3031  return QDomElement();
3032  }
3033  QString distance;
3034  QString unit( QStringLiteral( "m" ) );
3035  switch ( lit->value().type() )
3036  {
3037  case QVariant::Int:
3038  distance = QString::number( lit->value().toInt() );
3039  break;
3040  case QVariant::LongLong:
3041  distance = QString::number( lit->value().toLongLong() );
3042  break;
3043  case QVariant::Double:
3044  distance = qgsDoubleToString( lit->value().toDouble() );
3045  break;
3046  case QVariant::String:
3047  {
3048  distance = lit->value().toString();
3049  for ( int i = 0; i < distance.size(); i++ )
3050  {
3051  if ( !( ( distance[i] >= '0' && distance[i] <= '9' ) || distance[i] == '-' || distance[i] == '.' || distance[i] == 'e' || distance[i] == 'E' ) )
3052  {
3053  unit = distance.mid( i ).trimmed();
3054  distance = distance.mid( 0, i );
3055  break;
3056  }
3057  }
3058  break;
3059  }
3060 
3061  default:
3062  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( lit->value().type() );
3063  return QDomElement();
3064  }
3065 
3066  QDomElement distanceElem = mDoc.createElement( mFilterPrefix + ":Distance" );
3067  if ( mFilterVersion == QgsOgcUtils::FILTER_FES_2_0 )
3068  distanceElem.setAttribute( QStringLiteral( "uom" ), unit );
3069  else
3070  distanceElem.setAttribute( QStringLiteral( "unit" ), unit );
3071  distanceElem.appendChild( mDoc.createTextNode( distance ) );
3072  funcElem.appendChild( distanceElem );
3073  return funcElem;
3074  }
3075 
3076  // Other function
3077  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
3078  funcElem.setAttribute( QStringLiteral( "name" ), node->name() );
3079  const auto constList = node->args()->list();
3080  for ( QgsSQLStatement::Node *n : constList )
3081  {
3082  const QDomElement childElem = toOgcFilter( n );
3083  if ( !mErrorMessage.isEmpty() )
3084  return QDomElement();
3085 
3086  funcElem.appendChild( childElem );
3087  }
3088  return funcElem;
3089 }
3090 
3092  const QString &leftTable )
3093 {
3094  QgsSQLStatement::Node *onExpr = node->onExpr();
3095  if ( onExpr )
3096  {
3097  return toOgcFilter( onExpr );
3098  }
3099 
3100  QList<QDomElement> listElem;
3101  const auto constUsingColumns = node->usingColumns();
3102  for ( const QString &columnName : constUsingColumns )
3103  {
3104  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
3105  QDomElement propElem1 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3106  propElem1.appendChild( mDoc.createTextNode( leftTable + "/" + columnName ) );
3107  eqElem.appendChild( propElem1 );
3108  QDomElement propElem2 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3109  propElem2.appendChild( mDoc.createTextNode( node->tableDef()->name() + "/" + columnName ) );
3110  eqElem.appendChild( propElem2 );
3111  listElem.append( eqElem );
3112  }
3113 
3114  if ( listElem.size() == 1 )
3115  {
3116  return listElem[0];
3117  }
3118  else if ( listElem.size() > 1 )
3119  {
3120  QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3121  const auto constListElem = listElem;
3122  for ( const QDomElement &elem : constListElem )
3123  {
3124  andElem.appendChild( elem );
3125  }
3126  return andElem;
3127  }
3128 
3129  return QDomElement();
3130 }
3131 
3132 void QgsOgcUtilsSQLStatementToFilter::visit( const QgsSQLStatement::NodeTableDef *node )
3133 {
3134  if ( node->alias().isEmpty() )
3135  {
3136  mMapTableAliasToNames[ node->name()] = node->name();
3137  }
3138  else
3139  {
3140  mMapTableAliasToNames[ node->alias()] = node->name();
3141  }
3142 }
3143 
3145 {
3146  QList<QDomElement> listElem;
3147 
3148  if ( mFilterVersion != QgsOgcUtils::FILTER_FES_2_0 &&
3149  ( node->tables().size() != 1 || !node->joins().empty() ) )
3150  {
3151  mErrorMessage = QObject::tr( "Joins are only supported with WFS 2.0" );
3152  return QDomElement();
3153  }
3154 
3155  // Register all table name aliases
3156  const auto constTables = node->tables();
3157  for ( QgsSQLStatement::NodeTableDef *table : constTables )
3158  {
3159  visit( table );
3160  }
3161  const auto constJoins = node->joins();
3162  for ( QgsSQLStatement::NodeJoin *join : constJoins )
3163  {
3164  visit( join->tableDef() );
3165  }
3166 
3167  // Process JOIN conditions
3168  const QList< QgsSQLStatement::NodeTableDef *> nodeTables = node->tables();
3169  QString leftTable = nodeTables.at( nodeTables.length() - 1 )->name();
3170  for ( QgsSQLStatement::NodeJoin *join : constJoins )
3171  {
3172  const QDomElement joinElem = toOgcFilter( join, leftTable );
3173  if ( !mErrorMessage.isEmpty() )
3174  return QDomElement();
3175  listElem.append( joinElem );
3176  leftTable = join->tableDef()->name();
3177  }
3178 
3179  // Process WHERE conditions
3180  if ( node->where() )
3181  {
3182  const QDomElement whereElem = toOgcFilter( node->where() );
3183  if ( !mErrorMessage.isEmpty() )
3184  return QDomElement();
3185  listElem.append( whereElem );
3186  }
3187 
3188  // Concatenate all conditions
3189  if ( listElem.size() == 1 )
3190  {
3191  return listElem[0];
3192  }
3193  else if ( listElem.size() > 1 )
3194  {
3195  QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3196  const auto constListElem = listElem;
3197  for ( const QDomElement &elem : constListElem )
3198  {
3199  andElem.appendChild( elem );
3200  }
3201  return andElem;
3202  }
3203 
3204  return QDomElement();
3205 }
3206 
3208  : mLayer( layer )
3209 {
3210  mPropertyName = QStringLiteral( "PropertyName" );
3211  mPrefix = QStringLiteral( "ogc" );
3212 
3213  if ( version == QgsOgcUtils::FILTER_FES_2_0 )
3214  {
3215  mPropertyName = QStringLiteral( "ValueReference" );
3216  mPrefix = QStringLiteral( "fes" );
3217  }
3218 }
3219 
3221 {
3222  if ( element.isNull() )
3223  return nullptr;
3224 
3225  // check for binary operators
3226  if ( isBinaryOperator( element.tagName() ) )
3227  {
3228  return nodeBinaryOperatorFromOgcFilter( element );
3229  }
3230 
3231  // check for spatial operators
3232  if ( isSpatialOperator( element.tagName() ) )
3233  {
3234  return nodeSpatialOperatorFromOgcFilter( element );
3235  }
3236 
3237  // check for other OGC operators, convert them to expressions
3238  if ( element.tagName() == QLatin1String( "Not" ) )
3239  {
3240  return nodeNotFromOgcFilter( element );
3241  }
3242  else if ( element.tagName() == QLatin1String( "PropertyIsNull" ) )
3243  {
3244  return nodePropertyIsNullFromOgcFilter( element );
3245  }
3246  else if ( element.tagName() == QLatin1String( "Literal" ) )
3247  {
3248  return nodeLiteralFromOgcFilter( element );
3249  }
3250  else if ( element.tagName() == QLatin1String( "Function" ) )
3251  {
3252  return nodeFunctionFromOgcFilter( element );
3253  }
3254  else if ( element.tagName() == mPropertyName )
3255  {
3256  return nodeColumnRefFromOgcFilter( element );
3257  }
3258  else if ( element.tagName() == QLatin1String( "PropertyIsBetween" ) )
3259  {
3260  return nodeIsBetweenFromOgcFilter( element );
3261  }
3262 
3263  mErrorMessage += QObject::tr( "unable to convert '%1' element to a valid expression: it is not supported yet or it has invalid arguments" ).arg( element.tagName() );
3264  return nullptr;
3265 }
3266 
3268 {
3269  if ( element.isNull() )
3270  return nullptr;
3271 
3272  int op = binaryOperatorFromTagName( element.tagName() );
3273  if ( op < 0 )
3274  {
3275  mErrorMessage = QObject::tr( "'%1' binary operator not supported." ).arg( element.tagName() );
3276  return nullptr;
3277  }
3278 
3279  if ( op == QgsExpressionNodeBinaryOperator::boLike && element.hasAttribute( QStringLiteral( "matchCase" ) ) && element.attribute( QStringLiteral( "matchCase" ) ) == QLatin1String( "false" ) )
3280  {
3282  }
3283 
3284  QDomElement operandElem = element.firstChildElement();
3285  std::unique_ptr<QgsExpressionNode> expr( nodeFromOgcFilter( operandElem ) );
3286 
3287  if ( !expr )
3288  {
3289  mErrorMessage = QObject::tr( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
3290  return nullptr;
3291  }
3292 
3293  const std::unique_ptr<QgsExpressionNode> leftOp( expr->clone() );
3294  for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
3295  {
3296  std::unique_ptr<QgsExpressionNode> opRight( nodeFromOgcFilter( operandElem ) );
3297  if ( !opRight )
3298  {
3299  mErrorMessage = QObject::tr( "invalid right operand for '%1' binary operator" ).arg( element.tagName() );
3300  return nullptr;
3301  }
3302 
3304  {
3305  QString wildCard;
3306  if ( element.hasAttribute( QStringLiteral( "wildCard" ) ) )
3307  {
3308  wildCard = element.attribute( QStringLiteral( "wildCard" ) );
3309  }
3310  QString singleChar;
3311  if ( element.hasAttribute( QStringLiteral( "singleChar" ) ) )
3312  {
3313  singleChar = element.attribute( QStringLiteral( "singleChar" ) );
3314  }
3315  QString escape = QStringLiteral( "\\" );
3316  if ( element.hasAttribute( QStringLiteral( "escape" ) ) )
3317  {
3318  escape = element.attribute( QStringLiteral( "escape" ) );
3319  }
3320  if ( element.hasAttribute( QStringLiteral( "escapeChar" ) ) )
3321  {
3322  escape = element.attribute( QStringLiteral( "escapeChar" ) );
3323  }
3324  // replace
3325  QString oprValue = static_cast<const QgsExpressionNodeLiteral *>( opRight.get() )->value().toString();
3326  if ( !wildCard.isEmpty() && wildCard != QLatin1String( "%" ) )
3327  {
3328  oprValue.replace( '%', QLatin1String( "\\%" ) );
3329  if ( oprValue.startsWith( wildCard ) )
3330  {
3331  oprValue.replace( 0, 1, QStringLiteral( "%" ) );
3332  }
3333  const QRegularExpression rx( "[^" + QgsStringUtils::qRegExpEscape( escape ) + "](" + QgsStringUtils::qRegExpEscape( wildCard ) + ")" );
3334  QRegularExpressionMatch match = rx.match( oprValue );
3335  int pos;
3336  while ( match.hasMatch() )
3337  {
3338  pos = match.capturedStart();
3339  oprValue.replace( pos + 1, 1, QStringLiteral( "%" ) );
3340  pos += 1;
3341  match = rx.match( oprValue, pos );
3342  }
3343  oprValue.replace( escape + wildCard, wildCard );
3344  }
3345  if ( !singleChar.isEmpty() && singleChar != QLatin1String( "_" ) )
3346  {
3347  oprValue.replace( '_', QLatin1String( "\\_" ) );
3348  if ( oprValue.startsWith( singleChar ) )
3349  {
3350  oprValue.replace( 0, 1, QStringLiteral( "_" ) );
3351  }
3352  const QRegularExpression rx( "[^" + QgsStringUtils::qRegExpEscape( escape ) + "](" + QgsStringUtils::qRegExpEscape( singleChar ) + ")" );
3353  QRegularExpressionMatch match = rx.match( oprValue );
3354  int pos;
3355  while ( match.hasMatch() )
3356  {
3357  pos = match.capturedStart();
3358  oprValue.replace( pos + 1, 1, QStringLiteral( "_" ) );
3359  pos += 1;
3360  match = rx.match( oprValue, pos );
3361  }
3362  oprValue.replace( escape + singleChar, singleChar );
3363  }
3364  if ( !escape.isEmpty() && escape != QLatin1String( "\\" ) )
3365  {
3366  oprValue.replace( escape + escape, escape );
3367  }
3368  opRight.reset( new QgsExpressionNodeLiteral( oprValue ) );
3369  }
3370 
3371  expr.reset( new QgsExpressionNodeBinaryOperator( static_cast< QgsExpressionNodeBinaryOperator::BinaryOperator >( op ), expr.release(), opRight.release() ) );
3372  }
3373 
3374  if ( expr == leftOp )
3375  {
3376  mErrorMessage = QObject::tr( "only one operand for '%1' binary operator" ).arg( element.tagName() );
3377  return nullptr;
3378  }
3379 
3380  return dynamic_cast< QgsExpressionNodeBinaryOperator * >( expr.release() );
3381 }
3382 
3383 
3385 {
3386  // we are exploiting the fact that our function names are the same as the XML tag names
3387  const int opIdx = QgsExpression::functionIndex( element.tagName().toLower() );
3388 
3389  std::unique_ptr<QgsExpressionNode::NodeList> gml2Args( new QgsExpressionNode::NodeList() );
3390  QDomElement childElem = element.firstChildElement();
3391  QString gml2Str;
3392  while ( !childElem.isNull() && gml2Str.isEmpty() )
3393  {
3394  if ( childElem.tagName() != mPropertyName )
3395  {
3396  QTextStream gml2Stream( &gml2Str );
3397  childElem.save( gml2Stream, 0 );
3398  }
3399  childElem = childElem.nextSiblingElement();
3400  }
3401  if ( !gml2Str.isEmpty() )
3402  {
3403  gml2Args->append( new QgsExpressionNodeLiteral( QVariant( gml2Str.remove( '\n' ) ) ) );
3404  }
3405  else
3406  {
3407  mErrorMessage = QObject::tr( "No OGC Geometry found" );
3408  return nullptr;
3409  }
3410 
3411  std::unique_ptr<QgsExpressionNode::NodeList> opArgs( new QgsExpressionNode::NodeList() );
3412  opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "$geometry" ) ), new QgsExpressionNode::NodeList() ) );
3413  opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "geomFromGML" ) ), gml2Args.release() ) );
3414 
3415  return new QgsExpressionNodeFunction( opIdx, opArgs.release() );
3416 }
3417 
3419 {
3420  if ( element.isNull() || element.tagName() != mPropertyName )
3421  {
3422  mErrorMessage = QObject::tr( "%1:PropertyName expected, got %2" ).arg( mPrefix, element.tagName() );
3423  return nullptr;
3424  }
3425 
3426  return new QgsExpressionNodeColumnRef( element.firstChild().nodeValue() );
3427 }
3428 
3430 {
3431  if ( element.isNull() || element.tagName() != QLatin1String( "Literal" ) )
3432  {
3433  mErrorMessage = QObject::tr( "%1:Literal expected, got %2" ).arg( mPrefix, element.tagName() );
3434  return nullptr;
3435  }
3436 
3437  std::unique_ptr<QgsExpressionNode> root;
3438 
3439  // the literal content can have more children (e.g. CDATA section, text, ...)
3440  QDomNode childNode = element.firstChild();
3441  while ( !childNode.isNull() )
3442  {
3443  std::unique_ptr<QgsExpressionNode> operand;
3444 
3445  if ( childNode.nodeType() == QDomNode::ElementNode )
3446  {
3447  // found a element node (e.g. PropertyName), convert it
3448  const QDomElement operandElem = childNode.toElement();
3449  operand.reset( nodeFromOgcFilter( operandElem ) );
3450  if ( !operand )
3451  {
3452  mErrorMessage = QObject::tr( "'%1' is an invalid or not supported content for %2:Literal" ).arg( operandElem.tagName(), mPrefix );
3453  return nullptr;
3454  }
3455  }
3456  else
3457  {
3458  // probably a text/CDATA node
3459  QVariant value = childNode.nodeValue();
3460 
3461  bool converted = false;
3462 
3463  // try to convert the node content to corresponding field type if possible
3464  if ( mLayer )
3465  {
3466  QDomElement propertyNameElement = element.previousSiblingElement( mPropertyName );
3467  if ( propertyNameElement.isNull() || propertyNameElement.tagName() != mPropertyName )
3468  {
3469  propertyNameElement = element.nextSiblingElement( mPropertyName );
3470  }
3471  if ( !propertyNameElement.isNull() || propertyNameElement.tagName() == mPropertyName )
3472  {
3473  const int fieldIndex = mLayer->fields().indexOf( propertyNameElement.firstChild().nodeValue() );
3474  if ( fieldIndex != -1 )
3475  {
3476  const QgsField field = mLayer->fields().field( propertyNameElement.firstChild().nodeValue() );
3477  field.convertCompatible( value );
3478  converted = true;
3479  }
3480  }
3481  }
3482  if ( !converted )
3483  {
3484  // try to convert the node content to number if possible,
3485  // otherwise let's use it as string
3486  bool ok;
3487  const double d = value.toDouble( &ok );
3488  if ( ok )
3489  value = d;
3490  }
3491 
3492  operand.reset( new QgsExpressionNodeLiteral( value ) );
3493  if ( !operand )
3494  continue;
3495  }
3496 
3497  // use the concat operator to merge the ogc:Literal children
3498  if ( !root )
3499  {
3500  root = std::move( operand );
3501  }
3502  else
3503  {
3504  root.reset( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boConcat, root.release(), operand.release() ) );
3505  }
3506 
3507  childNode = childNode.nextSibling();
3508  }
3509 
3510  if ( root )
3511  return root.release();
3512 
3513  return nullptr;
3514 }
3515 
3517 {
3518  if ( element.tagName() != QLatin1String( "Not" ) )
3519  return nullptr;
3520 
3521  const QDomElement operandElem = element.firstChildElement();
3522  std::unique_ptr<QgsExpressionNode> operand( nodeFromOgcFilter( operandElem ) );
3523  if ( !operand )
3524  {
3525  mErrorMessage = QObject::tr( "invalid operand for '%1' unary operator" ).arg( element.tagName() );
3526  return nullptr;
3527  }
3528 
3530 }
3531 
3533 {
3534  // convert ogc:PropertyIsNull to IS operator with NULL right operand
3535  if ( element.tagName() != QLatin1String( "PropertyIsNull" ) )
3536  {
3537  return nullptr;
3538  }
3539 
3540  const QDomElement operandElem = element.firstChildElement();
3541  std::unique_ptr<QgsExpressionNode> opLeft( nodeFromOgcFilter( operandElem ) );
3542  if ( !opLeft )
3543  return nullptr;
3544 
3545  std::unique_ptr<QgsExpressionNode> opRight( new QgsExpressionNodeLiteral( QVariant() ) );
3546  return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boIs, opLeft.release(), opRight.release() );
3547 }
3548 
3550 {
3551  if ( element.isNull() || element.tagName() != QLatin1String( "Function" ) )
3552  {
3553  mErrorMessage = QObject::tr( "%1:Function expected, got %2" ).arg( mPrefix, element.tagName() );
3554  return nullptr;
3555  }
3556 
3557  for ( int i = 0; i < QgsExpression::Functions().size(); i++ )
3558  {
3559  const QgsExpressionFunction *funcDef = QgsExpression::Functions()[i];
3560 
3561  if ( element.attribute( QStringLiteral( "name" ) ) != funcDef->name() )
3562  continue;
3563 
3564  std::unique_ptr<QgsExpressionNode::NodeList> args( new QgsExpressionNode::NodeList() );
3565 
3566  QDomElement operandElem = element.firstChildElement();
3567  while ( !operandElem.isNull() )
3568  {
3569  std::unique_ptr<QgsExpressionNode> op( nodeFromOgcFilter( operandElem ) );
3570  if ( !op )
3571  {
3572  return nullptr;
3573  }
3574  args->append( op.release() );
3575 
3576  operandElem = operandElem.nextSiblingElement();
3577  }
3578 
3579  return new QgsExpressionNodeFunction( i, args.release() );
3580  }
3581 
3582  return nullptr;
3583 }
3584 
3586 {
3587  // <ogc:PropertyIsBetween> encode a Range check
3588  std::unique_ptr<QgsExpressionNode> operand;
3589  std::unique_ptr<QgsExpressionNode> lowerBound;
3590  std::unique_ptr<QgsExpressionNode> upperBound;
3591 
3592  QDomElement operandElem = element.firstChildElement();
3593  while ( !operandElem.isNull() )
3594  {
3595  if ( operandElem.tagName() == QLatin1String( "LowerBoundary" ) )
3596  {
3597  const QDomElement lowerBoundElem = operandElem.firstChildElement();
3598  lowerBound.reset( nodeFromOgcFilter( lowerBoundElem ) );
3599  }
3600  else if ( operandElem.tagName() == QLatin1String( "UpperBoundary" ) )
3601  {
3602  const QDomElement upperBoundElem = operandElem.firstChildElement();
3603  upperBound.reset( nodeFromOgcFilter( upperBoundElem ) );
3604  }
3605  else
3606  {
3607  // <ogc:expression>
3608  operand.reset( nodeFromOgcFilter( operandElem ) );
3609  }
3610 
3611  if ( operand && lowerBound && upperBound )
3612  break;
3613 
3614  operandElem = operandElem.nextSiblingElement();
3615  }
3616 
3617  if ( !operand || !lowerBound || !upperBound )
3618  {
3619  mErrorMessage = QObject::tr( "missing some required sub-elements in %1:PropertyIsBetween" ).arg( mPrefix );
3620  return nullptr;
3621  }
3622 
3623  std::unique_ptr<QgsExpressionNode> leOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boLE, operand->clone(), upperBound.release() ) );
3624  std::unique_ptr<QgsExpressionNode> geOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boGE, operand.release(), lowerBound.release() ) );
3625  return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boAnd, geOperator.release(), leOperator.release() );
3626 }
3627 
3629 {
3630  return mErrorMessage;
3631 }
GML_NAMESPACE
#define GML_NAMESPACE
Definition: qgsogcutils.cpp:43
QgsSQLStatement::Node
Abstract node class.
Definition: qgssqlstatement.h:234
QgsOgcUtils::GML_3_2_1
@ GML_3_2_1
Definition: qgsogcutils.h:81
QgsSQLStatement::NodeColumnRef::tableName
QString tableName() const
The name of the table. May be empty.
Definition: qgssqlstatement.h:547
QgsSQLStatement::NodeJoin
Join definition.
Definition: qgssqlstatement.h:683
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
QgsOgcUtilsExprToFilter::expressionNodeToOgcFilter
QDomElement expressionNodeToOgcFilter(const QgsExpressionNode *node, QgsExpression *expression, const QgsExpressionContext *context)
Convert an expression to a OGC filter.
Definition: qgsogcutils.cpp:2011
QgsMapLayer::crs
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
qgsexpressioncontextutils.h
qgsexpression_p.h
QgsExpressionNodeBinaryOperator::op
QgsExpressionNodeBinaryOperator::BinaryOperator op() const
Returns the binary operator.
Definition: qgsexpressionnodeimpl.h:162
QgsGeometry::asWkb
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Export the geometry to WKB.
Definition: qgsgeometry.cpp:2748
QgsOgcUtils::expressionFromOgcFilter
static QgsExpression * expressionFromOgcFilter(const QDomElement &element, QgsVectorLayer *layer=nullptr)
Parse XML with OGC filter into QGIS expression.
Definition: qgsogcutils.cpp:1670
qgsEnumValueToKey
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition: qgis.h:2440
OGC_NAMESPACE
#define OGC_NAMESPACE
Definition: qgsogcutils.cpp:45
QgsExpressionNodeBinaryOperator::boILike
@ boILike
Definition: qgsexpressionnodeimpl.h:123
QgsExpressionNodeBinaryOperator::boPlus
@ boPlus
Definition: qgsexpressionnodeimpl.h:129
QgsOgcUtilsExpressionFromFilter::nodeBinaryOperatorFromOgcFilter
QgsExpressionNodeBinaryOperator * nodeBinaryOperatorFromOgcFilter(const QDomElement &element)
Returns an expression node from a WFS filter embedded in a document with binary operators.
Definition: qgsogcutils.cpp:3267
QgsSQLStatement::NodeUnaryOperator::operand
QgsSQLStatement::Node * operand() const
Operand.
Definition: qgssqlstatement.h:354
QgsExpressionNodeLiteral
An expression node for literal values.
Definition: qgsexpressionnodeimpl.h:461
QgsWkbTypes::Point
@ Point
Definition: qgswkbtypes.h:72
QgsSQLStatement::NodeTableDef::name
QString name() const
Table name.
Definition: qgssqlstatement.h:653
QgsExpressionNodeBinaryOperator
A binary expression operator, which operates on two values.
Definition: qgsexpressionnodeimpl.h:99
QgsOgcUtilsExpressionFromFilter::nodeColumnRefFromOgcFilter
QgsExpressionNodeColumnRef * nodeColumnRefFromOgcFilter(const QDomElement &element)
Returns an expression node from a WFS filter embedded in a document with column references.
Definition: qgsogcutils.cpp:3418
QgsOgcUtilsExpressionFromFilter::nodeFunctionFromOgcFilter
QgsExpressionNodeFunction * nodeFunctionFromOgcFilter(const QDomElement &element)
Returns an expression node from a WFS filter embedded in a document with functions.
Definition: qgsogcutils.cpp:3549
QgsExpressionContextUtils::globalScope
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Definition: qgsexpressioncontextutils.cpp:40
qgsrectangle.h
QgsPolygonXY
QVector< QgsPolylineXY > QgsPolygonXY
Polygon: first item of the list is outer ring, inner rings (if any) start from second item.
Definition: qgsgeometry.h:76
QgsWkbTypes::MultiPolygon
@ MultiPolygon
Definition: qgswkbtypes.h:78
QgsWkbTypes::Point25D
@ Point25D
Definition: qgswkbtypes.h:125
QgsExpressionNodeInOperator::node
QgsExpressionNode * node() const
Returns the expression node.
Definition: qgsexpressionnodeimpl.h:360
QgsWkbTypes::LineString25D
@ LineString25D
Definition: qgswkbtypes.h:126
qgswkbptr.h
QgsExpressionNodeBinaryOperator::text
QString text() const
Returns a the name of this operator without the operands.
Definition: qgsexpressionnodeimpl.cpp:1973
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsGeometry::transform
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
Definition: qgsgeometry.cpp:3128
QgsExpressionNodeBinaryOperator::boLE
@ boLE
<=
Definition: qgsexpressionnodeimpl.h:116
QgsSQLStatement::NodeBinaryOperator::opLeft
QgsSQLStatement::Node * opLeft() const
Left operand.
Definition: qgssqlstatement.h:386
qgsexpression.h
QgsExpression::dump
QString dump() const
Returns an expression string, constructed from the internal abstract syntax tree.
Definition: qgsexpression.cpp:393
QgsExpressionNode::ntColumnRef
@ ntColumnRef
Definition: qgsexpressionnode.h:84
qgsstringutils.h
QgsExpressionNodeBinaryOperator::boGE
@ boGE
>=
Definition: qgsexpressionnodeimpl.h:117
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:105
QgsCoordinateReferenceSystem::fromOgcWmsCrs
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
Definition: qgscoordinatereferencesystem.cpp:195
QgsOgcUtilsExpressionFromFilter::errorMessage
QString errorMessage() const
Returns the underlying error message, or an empty string in case of no error.
Definition: qgsogcutils.cpp:3628
QgsExpression::rootNode
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
Definition: qgsexpression.cpp:1355
QgsExpressionNodeColumnRef
An expression node which takes it value from a feature's field.
Definition: qgsexpressionnodeimpl.h:512
QgsPolylineXY
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition: qgsgeometry.h:52
QgsExpressionNodeLiteral::value
QVariant value() const
The value of the literal.
Definition: qgsexpressionnodeimpl.h:481
QgsExpressionNodeFunction::fnIndex
int fnIndex() const
Returns the index of the node's function.
Definition: qgsexpressionnodeimpl.h:428
QgsWkbTypes::LineString
@ LineString
Definition: qgswkbtypes.h:73
QgsRectangle::yMinimum
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
QgsGeometry::fromWkb
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
Definition: qgsgeometry.cpp:344
QgsGeometry::fromWkt
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
QgsSQLStatement::boLE
@ boLE
Definition: qgssqlstatement.h:159
QgsSQLStatement::NodeTableDef
Table definition.
Definition: qgssqlstatement.h:638
QgsExpression::setExpression
void setExpression(const QString &expression)
Set the expression string, will reset the whole internal structure.
Definition: qgsexpression.cpp:51
QgsSQLStatement::boLT
@ boLT
Definition: qgssqlstatement.h:161
QgsOgcUtilsSQLStatementToFilter::toOgcFilter
QDomElement toOgcFilter(const QgsSQLStatement::Node *node)
Convert a SQL statement to a OGC filter.
Definition: qgsogcutils.cpp:2439
QgsExpressionNodeUnaryOperator
A unary node is either negative as in boolean (not) or as in numbers (minus).
Definition: qgsexpressionnodeimpl.h:27
QgsSQLStatement::NodeFunction
Function with a name and arguments node.
Definition: qgssqlstatement.h:483
QgsSQLStatement::NodeSelect::where
QgsSQLStatement::Node * where() const
Returns the where clause.
Definition: qgssqlstatement.h:778
QgsMultiPolygonXY
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
Definition: qgsgeometry.h:93
QgsSQLStatement::boGE
@ boGE
Definition: qgssqlstatement.h:160
QgsCoordinateReferenceSystem::hasAxisInverted
bool hasAxisInverted() const
Returns whether axis is inverted (e.g., for WMS 1.3) for the CRS.
Definition: qgscoordinatereferencesystem.cpp:793
QgsSQLStatement::NodeSelect
SELECT node.
Definition: qgssqlstatement.h:753
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:69
QgsStringUtils::qRegExpEscape
static QString qRegExpEscape(const QString &string)
Returns an escaped string matching the behavior of QRegExp::escape.
Definition: qgsstringutils.cpp:710
QgsOgcUtils
The QgsOgcUtils class provides various utility functions for conversion between OGC (Open Geospatial ...
Definition: qgsogcutils.h:51
QgsSQLStatement::NodeUnaryOperator
Unary logicial/arithmetical operator ( NOT, - )
Definition: qgssqlstatement.h:343
FALLTHROUGH
#define FALLTHROUGH
Definition: qgis.h:2847
field
const QgsField & field
Definition: qgsfield.h:463
QgsCoordinateReferenceSystem::createFromUserInput
bool createFromUserInput(const QString &definition)
Set up this CRS from various text formats.
Definition: qgscoordinatereferencesystem.cpp:347
QgsOgcUtilsExprToFilter::QgsOgcUtilsExprToFilter
QgsOgcUtilsExprToFilter(QDomDocument &doc, QgsOgcUtils::GMLVersion gmlVersion, QgsOgcUtils::FilterVersion filterVersion, const QString &geometryName, const QString &srsName, bool honourAxisOrientation, bool invertAxisOrientation)
Constructor.
Definition: qgsogcutils.cpp:48
QgsOgcUtilsSQLStatementToFilter::QgsOgcUtilsSQLStatementToFilter
QgsOgcUtilsSQLStatementToFilter(QDomDocument &doc, QgsOgcUtils::GMLVersion gmlVersion, QgsOgcUtils::FilterVersion filterVersion, const QList< QgsOgcUtils::LayerProperties > &layerProperties, bool honourAxisOrientation, bool invertAxisOrientation, const QMap< QString, QString > &mapUnprefixedTypenameToPrefixedTypename)
Constructor.
Definition: qgsogcutils.cpp:2418
QgsSQLStatement::ntFunction
@ ntFunction
Definition: qgssqlstatement.h:219
QgsWkbTypes::MultiPoint25D
@ MultiPoint25D
Definition: qgswkbtypes.h:128
QgsExpressionNodeUnaryOperator::op
QgsExpressionNodeUnaryOperator::UnaryOperator op() const
Returns the unary operator.
Definition: qgsexpressionnodeimpl.h:74
QgsOgcUtilsExpressionFromFilter::nodeFromOgcFilter
QgsExpressionNode * nodeFromOgcFilter(const QDomElement &element)
Returns an expression node from a WFS filter embedded in a document element.
Definition: qgsogcutils.cpp:3220
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
geometryName
const QString & geometryName
Definition: qgswfsgetfeature.cpp:113
QgsOgcUtilsExprToFilter::GMLNamespaceUsed
bool GMLNamespaceUsed() const
Returns whether the gml: namespace is used.
Definition: qgsogcutils.h:382
qgsogcutils.h
QgsWkbTypes::Polygon25D
@ Polygon25D
Definition: qgswkbtypes.h:127
qgsDoubleToString
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:2204
QgsSQLStatement::ntInOperator
@ ntInOperator
Definition: qgssqlstatement.h:217
QgsExpressionNodeInOperator
An expression node for value IN or NOT IN clauses.
Definition: qgsexpressionnodeimpl.h:343
QgsOgcUtils::Context::transformContext
QgsCoordinateTransformContext transformContext
Definition: qgsogcutils.h:71
QgsExpressionNodeUnaryOperator::text
QString text() const
Returns a the name of this operator without the operands.
Definition: qgsexpressionnodeimpl.cpp:194
QgsSQLStatement::NodeJoin::tableDef
QgsSQLStatement::NodeTableDef * tableDef() const
Table definition.
Definition: qgssqlstatement.h:693
QgsOgcUtilsSQLStatementToFilter::GMLNamespaceUsed
bool GMLNamespaceUsed() const
Returns whether the gml: namespace is used.
Definition: qgsogcutils.h:516
QgsExpressionNodeBinaryOperator::boMinus
@ boMinus
Definition: qgsexpressionnodeimpl.h:130
QgsOgcUtilsExprToFilter
Internal use by QgsOgcUtils.
Definition: qgsogcutils.h:366
QgsRectangle::normalize
void normalize()
Normalize the rectangle so it has non-negative width/height.
Definition: qgsrectangle.h:203
QgsSQLStatement::boOr
@ boOr
Definition: qgssqlstatement.h:153
QgsOgcUtilsExprToFilter::errorMessage
QString errorMessage() const
Returns the error message.
Definition: qgsogcutils.h:385
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3436
Q_GLOBAL_STATIC_WITH_ARGS
Q_GLOBAL_STATIC_WITH_ARGS(IntMap, BINARY_OPERATORS_TAG_NAMES_MAP,({ { QLatin1String("Or"), QgsExpressionNodeBinaryOperator::boOr }, { QLatin1String("And"), QgsExpressionNodeBinaryOperator::boAnd }, { QLatin1String("PropertyIsEqualTo"), QgsExpressionNodeBinaryOperator::boEQ }, { QLatin1String("PropertyIsNotEqualTo"), QgsExpressionNodeBinaryOperator::boNE }, { QLatin1String("PropertyIsLessThanOrEqualTo"), QgsExpressionNodeBinaryOperator::boLE }, { QLatin1String("PropertyIsGreaterThanOrEqualTo"), QgsExpressionNodeBinaryOperator::boGE }, { QLatin1String("PropertyIsLessThan"), QgsExpressionNodeBinaryOperator::boLT }, { QLatin1String("PropertyIsGreaterThan"), QgsExpressionNodeBinaryOperator::boGT }, { QLatin1String("PropertyIsLike"), QgsExpressionNodeBinaryOperator::boLike }, { QLatin1String("Add"), QgsExpressionNodeBinaryOperator::boPlus }, { QLatin1String("Sub"), QgsExpressionNodeBinaryOperator::boMinus }, { QLatin1String("Mul"), QgsExpressionNodeBinaryOperator::boMul }, { QLatin1String("Div"), QgsExpressionNodeBinaryOperator::boDiv }, })) static int binaryOperatorFromTagName(const QString &tagName)
Definition: qgsogcutils.cpp:1726
QgsOgcUtils::rectangleFromGMLBox
static QgsRectangle rectangleFromGMLBox(const QDomNode &boxNode)
Read rectangle from GML2 Box.
Definition: qgsogcutils.cpp:947
QgsRectangle::xMaximum
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
precision
int precision
Definition: qgswfsgetfeature.cpp:103
QgsWkbTypes::MultiLineString
@ MultiLineString
Definition: qgswkbtypes.h:77
QgsSQLStatement::ntBinaryOperator
@ ntBinaryOperator
Definition: qgssqlstatement.h:216
QgsOgcUtilsExpressionFromFilter::nodeSpatialOperatorFromOgcFilter
QgsExpressionNodeFunction * nodeSpatialOperatorFromOgcFilter(const QDomElement &element)
Returns an expression node from a WFS filter embedded in a document with spatial operators.
Definition: qgsogcutils.cpp:3384
QgsOgcUtils::rectangleToGMLBox
static QDomElement rectangleToGMLBox(QgsRectangle *box, QDomDocument &doc, int precision=17)
Exports the rectangle to GML2 Box.
Definition: qgsogcutils.cpp:1111
QgsCsException
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QgsSQLStatement::rootNode
const QgsSQLStatement::Node * rootNode() const
Returns the root node of the statement.
Definition: qgssqlstatement.cpp:175
QgsExpressionNode::ntFunction
@ ntFunction
Definition: qgsexpressionnode.h:82
QgsExpressionFunction::params
int params() const
The number of parameters this function takes.
Definition: qgsexpressionfunction.h:193
QgsSQLStatement::boIsNot
@ boIsNot
Definition: qgssqlstatement.h:168
QgsOgcUtilsExpressionFromFilter::nodeLiteralFromOgcFilter
QgsExpressionNode * nodeLiteralFromOgcFilter(const QDomElement &element)
Returns an expression node from a WFS filter embedded in a document with literal tag.
Definition: qgsogcutils.cpp:3429
QgsSQLStatement::uoNot
@ uoNot
Definition: qgssqlstatement.h:142
QgsSQLStatement::boNE
@ boNE
Definition: qgssqlstatement.h:158
QgsSQLStatement::NodeInOperator::node
QgsSQLStatement::Node * node() const
Variable at the left of IN.
Definition: qgssqlstatement.h:422
QgsExpressionNodeFunction::args
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
Definition: qgsexpressionnodeimpl.h:433
QgsSQLStatement::boLike
@ boLike
Definition: qgssqlstatement.h:163
QgsSQLStatement::uoMinus
@ uoMinus
Definition: qgssqlstatement.h:143
QgsSQLStatement::NodeUnaryOperator::op
QgsSQLStatement::UnaryOperator op() const
Operator.
Definition: qgssqlstatement.h:351
QgsSQLStatement::NodeJoin::usingColumns
QList< QString > usingColumns() const
Columns referenced by USING.
Definition: qgssqlstatement.h:699
QgsExpressionNodeBinaryOperator::boConcat
@ boConcat
Definition: qgsexpressionnodeimpl.h:138
QgsConstWkbPtr
A const WKB pointer.
Definition: qgswkbptr.h:137
QgsOgcUtilsExpressionFromFilter::nodePropertyIsNullFromOgcFilter
QgsExpressionNodeBinaryOperator * nodePropertyIsNullFromOgcFilter(const QDomElement &element)
Returns an expression node from a WFS filter embedded in a document with IsNull operator.
Definition: qgsogcutils.cpp:3532
QgsSQLStatement::NodeTableDef::alias
QString alias() const
Table alias.
Definition: qgssqlstatement.h:663
QgsOgcUtils::SQLStatementToOgcFilter
static QDomElement SQLStatementToOgcFilter(const QgsSQLStatement &statement, QDomDocument &doc, QgsOgcUtils::GMLVersion gmlVersion, FilterVersion filterVersion, const QList< LayerProperties > &layerProperties, bool honourAxisOrientation, bool invertAxisOrientation, const QMap< QString, QString > &mapUnprefixedTypenameToPrefixedTypename, QString *errorMessage=nullptr)
Creates OGC filter XML element from the WHERE and JOIN clauses of a SQL statement.
Definition: qgsogcutils.cpp:1955
QgsSQLStatement::ntLiteral
@ ntLiteral
Definition: qgssqlstatement.h:220
QgsAbstractGeometry::swapXy
virtual void swapXy()=0
Swaps the x and y coordinates from the geometry.
QgsSQLStatement::NodeLiteral::value
QVariant value() const
The value of the literal.
Definition: qgssqlstatement.h:519
QgsWkbException
Custom exception class for Wkb related exceptions.
Definition: qgswkbptr.h:30
QgsOgcUtils::rectangleFromGMLEnvelope
static QgsRectangle rectangleFromGMLEnvelope(const QDomNode &envelopeNode)
Read rectangle from GML3 Envelope.
Definition: qgsogcutils.cpp:1032
QgsSQLStatement::NodeBinaryOperator::op
QgsSQLStatement::BinaryOperator op() const
Operator.
Definition: qgssqlstatement.h:383
QgsWkbTypes::MultiLineString25D
@ MultiLineString25D
Definition: qgswkbtypes.h:129
Qgis::GeometryOperationResult
GeometryOperationResult
Success or failure of a geometry operation.
Definition: qgis.h:901
QgsSQLStatement::BINARY_OPERATOR_TEXT
static const char * BINARY_OPERATOR_TEXT[]
Definition: qgssqlstatement.h:200
QgsExpressionNodeBinaryOperator::boLT
@ boLT
<
Definition: qgsexpressionnodeimpl.h:118
qgsexpressionfunction.h
QgsSQLStatement
Class for parsing SQL statements.
Definition: qgssqlstatement.h:35
QgsRectangle::setXMinimum
void setXMinimum(double x) SIP_HOLDGIL
Set the minimum x value.
Definition: qgsrectangle.h:151
QgsCoordinateReferenceSystem::isValid
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Definition: qgscoordinatereferencesystem.cpp:977
QgsSQLStatement::ntSelect
@ ntSelect
Definition: qgssqlstatement.h:223
QgsExpressionNodeUnaryOperator::operand
QgsExpressionNode * operand() const
Returns the node the operator will operate upon.
Definition: qgsexpressionnodeimpl.h:79
QgsMultiPolylineXY
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
Definition: qgsgeometry.h:86
QgsSQLStatement::NodeInOperator::isNotIn
bool isNotIn() const
Whether this is a NOT IN operator.
Definition: qgssqlstatement.h:425
QgsSQLStatement::NodeSelect::tables
QList< QgsSQLStatement::NodeTableDef * > tables() const
Returns the list of tables.
Definition: qgssqlstatement.h:770
QgsGeometry::isNull
bool isNull
Definition: qgsgeometry.h:127
QgsExpressionNode::ntBinaryOperator
@ ntBinaryOperator
Definition: qgsexpressionnode.h:80
QgsRectangle::xMinimum
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
QgsExpressionNodeFunction
An expression node for expression functions.
Definition: qgsexpressionnodeimpl.h:395
QgsSQLStatement::NodeBinaryOperator::opRight
QgsSQLStatement::Node * opRight() const
Right operand.
Definition: qgssqlstatement.h:389
QgsExpressionNodeBinaryOperator::boNE
@ boNE
<>
Definition: qgsexpressionnodeimpl.h:115
QgsExpressionNode::ntLiteral
@ ntLiteral
Definition: qgsexpressionnode.h:83
QgsSQLStatement::NodeFunction::args
QgsSQLStatement::NodeList * args() const
Returns arguments.
Definition: qgssqlstatement.h:494
QgsSQLStatement::boIs
@ boIs
Definition: qgssqlstatement.h:167
QgsSQLStatement::boILike
@ boILike
Definition: qgssqlstatement.h:165
QgsSQLStatement::NodeBetweenOperator::node
QgsSQLStatement::Node * node() const
Variable at the left of BETWEEN.
Definition: qgssqlstatement.h:455
QgsExpression::Functions
static const QList< QgsExpressionFunction * > & Functions()
Definition: qgsexpressionfunction.cpp:7316
QgsExpressionNodeBinaryOperator::boIsNot
@ boIsNot
Definition: qgsexpressionnodeimpl.h:126
QgsCoordinateReferenceSystem
This class represents a coordinate reference system (CRS).
Definition: qgscoordinatereferencesystem.h:211
QgsOgcUtilsExpressionFromFilter
Internal use by QgsOgcUtils.
Definition: qgsogcutils.h:414
QgsExpressionNodeBinaryOperator::boLike
@ boLike
Definition: qgsexpressionnodeimpl.h:121
QgsRectangle::setXMaximum
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
Definition: qgsrectangle.h:156
QgsOgcUtilsSQLStatementToFilter::errorMessage
QString errorMessage() const
Returns the error message.
Definition: qgsogcutils.h:519
QgsOgcUtils::LayerProperties
Layer properties. Used by SQLStatementToOgcFilter().
Definition: qgsogcutils.h:242
QgsFields::field
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:168
QgsOgcUtilsSQLStatementToFilter
Internal use by QgsOgcUtils.
Definition: qgsogcutils.h:500
QgsExpressionNodeUnaryOperator::uoNot
@ uoNot
Definition: qgsexpressionnodeimpl.h:63
QgsSQLStatement::NodeInOperator
'x IN (y, z)' operator
Definition: qgssqlstatement.h:414
QgsSQLStatement::NodeBinaryOperator
Binary logical/arithmetical operator (AND, OR, =, +, ...)
Definition: qgssqlstatement.h:371
QgsExpressionNode::NodeList::list
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
Definition: qgsexpressionnode.h:148
QgsOgcUtils::FilterVersion
FilterVersion
OGC filter version.
Definition: qgsogcutils.h:178
qgsvectorlayer.h
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:58
QgsOgcUtils::geometryFromGML
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
Definition: qgsogcutils.cpp:188
QgsExpressionNode
Abstract base class for all nodes that can appear in an expression.
Definition: qgsexpressionnode.h:34
QgsExpression::functionIndex
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
Definition: qgsexpression.cpp:127
QgsSQLStatement::boGT
@ boGT
Definition: qgssqlstatement.h:162
QgsRectangle::setYMaximum
void setYMaximum(double y) SIP_HOLDGIL
Set the maximum y value.
Definition: qgsrectangle.h:166
QgsOgcUtils::Context::layer
const QgsMapLayer * layer
Definition: qgsogcutils.h:70
QgsSQLStatement::NodeBetweenOperator::maxVal
QgsSQLStatement::Node * maxVal() const
Maximum bound.
Definition: qgssqlstatement.h:464
QgsSQLStatement::NodeColumnRef
Reference to a column.
Definition: qgssqlstatement.h:535
QgsGeometry::get
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:141
QgsExpressionNodeBinaryOperator::boEQ
@ boEQ
=
Definition: qgsexpressionnodeimpl.h:114
QgsSQLStatement::UNARY_OPERATOR_TEXT
static const char * UNARY_OPERATOR_TEXT[]
Definition: qgssqlstatement.h:203
QgsStringMap
QMap< QString, QString > QgsStringMap
Definition: qgis.h:2781
QgsExpressionNodeBinaryOperator::boIs
@ boIs
Definition: qgsexpressionnodeimpl.h:125
FES_NAMESPACE
#define FES_NAMESPACE
Definition: qgsogcutils.cpp:46
QgsOgcUtils::expressionToOgcFilter
static QDomElement expressionToOgcFilter(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=nullptr)
Creates OGC filter XML element.
Definition: qgsogcutils.cpp:1855
QgsRectangle::yMaximum
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
QgsSQLStatement::NodeFunction::name
QString name() const
Returns function name.
Definition: qgssqlstatement.h:491
QgsExpressionNodeBinaryOperator::boGT
@ boGT
Definition: qgsexpressionnodeimpl.h:119
qgsgeometry.h
QgsSQLStatement::NodeSelect::joins
QList< QgsSQLStatement::NodeJoin * > joins() const
Returns the list of joins.
Definition: qgssqlstatement.h:776
QgsExpressionNodeInOperator::isNotIn
bool isNotIn() const
Returns true if this node is a "NOT IN" operator, or false if the node is a normal "IN" operator.
Definition: qgsexpressionnodeimpl.h:365
qgsexpressionnodeimpl.h
QgsExpressionNodeColumnRef::name
QString name() const
The name of the column.
Definition: qgsexpressionnodeimpl.h:534
QgsExpressionNodeUnaryOperator::uoMinus
@ uoMinus
Definition: qgsexpressionnodeimpl.h:64
QgsExpressionFunction::run
virtual QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
Evaluates the function, first evaluating all required arguments before passing them to the function's...
Definition: qgsexpressionfunction.cpp:89
QgsRectangle::setYMinimum
void setYMinimum(double y) SIP_HOLDGIL
Set the minimum y value.
Definition: qgsrectangle.h:161
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsExpressionFunction
A abstract base class for defining QgsExpression functions.
Definition: qgsexpressionfunction.h:40
QgsSQLStatement::NodeBetweenOperator::isNotBetween
bool isNotBetween() const
Whether this is a NOT BETWEEN operator.
Definition: qgssqlstatement.h:458
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsExpressionNode::ntUnaryOperator
@ ntUnaryOperator
Definition: qgsexpressionnode.h:79
QgsExpressionNodeBinaryOperator::opLeft
QgsExpressionNode * opLeft() const
Returns the node to the left of the operator.
Definition: qgsexpressionnodeimpl.h:168
QgsOgcUtils::geometryToGML
static QDomElement geometryToGML(const QgsGeometry &geometry, QDomDocument &doc, QgsOgcUtils::GMLVersion gmlVersion, const QString &srsName, bool invertAxisOrientation, const QString &gmlIdBase, int precision=17)
Exports the geometry to GML.
Definition: qgsogcutils.cpp:1197
srsName
const QString & srsName
Definition: qgswfsgetfeature.cpp:119
QgsWkbTypes::Polygon
@ Polygon
Definition: qgswkbtypes.h:74
Qgis::GeometryOperationResult::Success
@ Success
Operation succeeded.
QgsWkbTypes::MultiPoint
@ MultiPoint
Definition: qgswkbtypes.h:76
QgsExpressionFunction::name
QString name() const
The name of the function.
Definition: qgsexpressionfunction.h:190
QgsGeometry::fromRect
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
Definition: qgsgeometry.cpp:241
QgsOgcUtils::colorFromOgcFill
static QColor colorFromOgcFill(const QDomElement &fillElement)
Parse XML with OGC fill into QColor.
Definition: qgsogcutils.cpp:1631
QgsOgcUtils::GMLVersion
GMLVersion
GML version.
Definition: qgsogcutils.h:77
QgsSQLStatement::boAnd
@ boAnd
Definition: qgssqlstatement.h:154
QgsSQLStatement::ntBetweenOperator
@ ntBetweenOperator
Definition: qgssqlstatement.h:218
QgsExpressionNodeBinaryOperator::boDiv
@ boDiv
Definition: qgsexpressionnodeimpl.h:132
QgsGeometry::boundingBox
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Definition: qgsgeometry.cpp:1080
QgsSQLStatement::ntColumnRef
@ ntColumnRef
Definition: qgssqlstatement.h:221
QgsSQLStatement::Node::nodeType
virtual QgsSQLStatement::NodeType nodeType() const =0
Abstract virtual that returns the type of this node.
QgsExpressionNodeInOperator::list
QgsExpressionNode::NodeList * list() const
Returns the list of nodes to search for matching values within.
Definition: qgsexpressionnodeimpl.h:370
QgsSQLStatement::boEQ
@ boEQ
Definition: qgssqlstatement.h:157
QgsExpressionNodeBinaryOperator::opRight
QgsExpressionNode * opRight() const
Returns the node to the right of the operator.
Definition: qgsexpressionnodeimpl.h:174
QgsSQLStatement::BinaryOperator
BinaryOperator
list of binary operators
Definition: qgssqlstatement.h:150
QgsExpressionFunction::isStatic
virtual bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
Will be called during prepare to determine if the function is static.
Definition: qgsexpressionfunction.cpp:133
QgsExpressionNodeBinaryOperator::boAnd
@ boAnd
Definition: qgsexpressionnodeimpl.h:111
IntMap
QMap< QString, int > IntMap
Definition: qgsogcutils.cpp:1725
QgsSQLStatement::NodeList::list
QList< QgsSQLStatement::Node * > list()
Returns list.
Definition: qgssqlstatement.h:319
QgsSQLStatement::ntUnaryOperator
@ ntUnaryOperator
Definition: qgssqlstatement.h:215
QgsExpressionNodeBinaryOperator::boMul
@ boMul
Definition: qgsexpressionnodeimpl.h:131
QgsConstWkbPtr::readHeader
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
qgslogger.h
GML32_NAMESPACE
#define GML32_NAMESPACE
Definition: qgsogcutils.cpp:44
QgsExpressionNodeBinaryOperator::boOr
@ boOr
Definition: qgsexpressionnodeimpl.h:110
QgsOgcUtilsExpressionFromFilter::QgsOgcUtilsExpressionFromFilter
QgsOgcUtilsExpressionFromFilter(QgsOgcUtils::FilterVersion version=QgsOgcUtils::FILTER_OGC_1_0, const QgsVectorLayer *layer=nullptr)
Constructor for QgsOgcUtilsExpressionFromFilter.
Definition: qgsogcutils.cpp:3207
QgsSQLStatement::NodeInOperator::list
QgsSQLStatement::NodeList * list() const
Values list.
Definition: qgssqlstatement.h:428
QgsSQLStatement::NodeColumnRef::name
QString name() const
The name of the column.
Definition: qgssqlstatement.h:550
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings")....
Definition: qgsexpression.h:102
QgsCoordinateTransform
Class for doing transforms between two map coordinate systems.
Definition: qgscoordinatetransform.h:57
QgsOgcUtilsExpressionFromFilter::nodeNotFromOgcFilter
QgsExpressionNodeUnaryOperator * nodeNotFromOgcFilter(const QDomElement &element)
Returns an expression node from a WFS filter embedded in a document with Not operator.
Definition: qgsogcutils.cpp:3516
QgsSQLStatement::NodeBetweenOperator::minVal
QgsSQLStatement::Node * minVal() const
Minimum bound.
Definition: qgssqlstatement.h:461
QgsSQLStatement::NodeBetweenOperator
'X BETWEEN y and z' operator
Definition: qgssqlstatement.h:446
QgsOgcUtils::expressionToOgcExpression
static QDomElement expressionToOgcExpression(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=nullptr)
Creates an OGC expression XML element.
Definition: qgsogcutils.cpp:1861
QgsWkbTypes::MultiPolygon25D
@ MultiPolygon25D
Definition: qgswkbtypes.h:130
QgsSQLStatement::NodeJoin::onExpr
QgsSQLStatement::Node * onExpr() const
On expression. Will be nullptr if usingColumns() is not empty.
Definition: qgssqlstatement.h:696
qgscoordinatereferencesystem.h
QgsOgcUtils::GML_2_1_2
@ GML_2_1_2
Definition: qgsogcutils.h:79
QgsOgcUtils::FILTER_FES_2_0
@ FILTER_FES_2_0
Definition: qgsogcutils.h:182
QgsField::convertCompatible
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
Definition: qgsfield.cpp:402
QgsOgcUtils::rectangleToGMLEnvelope
static QDomElement rectangleToGMLEnvelope(QgsRectangle *env, QDomDocument &doc, int precision=17)
Exports the rectangle to GML3 Envelope.
Definition: qgsogcutils.cpp:1151
QgsGeometry::wkbType
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
Definition: qgsgeometry.cpp:357
QgsOgcUtils::Context
The Context struct stores the current layer and coordinate transform context.
Definition: qgsogcutils.h:59
QgsOgcUtils::FILTER_OGC_1_0
@ FILTER_OGC_1_0
Definition: qgsogcutils.h:180
QgsExpressionNode::NodeList
A list of expression nodes.
Definition: qgsexpressionnode.h:121
QgsFields::indexOf
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:207
QgsExpressionNode::nodeType
virtual QgsExpressionNode::NodeType nodeType() const =0
Gets the type of this node.
QgsExpressionNodeBinaryOperator::BinaryOperator
BinaryOperator
list of binary operators
Definition: qgsexpressionnodeimpl.h:107
QgsExpressionNode::ntInOperator
@ ntInOperator
Definition: qgsexpressionnode.h:81
QgsOgcUtilsExpressionFromFilter::nodeIsBetweenFromOgcFilter
QgsExpressionNode * nodeIsBetweenFromOgcFilter(const QDomElement &element)
Returns an expression node from a WFS filter embedded in a document with boudnaries operator.
Definition: qgsogcutils.cpp:3585
QgsSQLStatement::NodeLiteral
Literal value (integer, integer64, double, string)
Definition: qgssqlstatement.h:512
QgsField
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:50