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