QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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  QStringList tupels = elem.text().split( tupelSeparator, QString::SkipEmptyParts );
891  QStringList tuple_coords;
892  double x, y;
893  bool conversionSuccess;
894 
895  QStringList::const_iterator it;
896  for ( it = tupels.constBegin(); it != tupels.constEnd(); ++it )
897  {
898  tuple_coords = ( *it ).split( coordSeparator, QString::SkipEmptyParts );
899  if ( tuple_coords.size() < 2 )
900  {
901  continue;
902  }
903  x = tuple_coords.at( 0 ).toDouble( &conversionSuccess );
904  if ( !conversionSuccess )
905  {
906  return true;
907  }
908  y = tuple_coords.at( 1 ).toDouble( &conversionSuccess );
909  if ( !conversionSuccess )
910  {
911  return true;
912  }
913  coords.push_back( QgsPointXY( x, y ) );
914  }
915  return false;
916 }
917 
919 {
920  QgsRectangle rect;
921 
922  QDomElement boxElem = boxNode.toElement();
923  if ( boxElem.tagName() != QLatin1String( "Box" ) )
924  return rect;
925 
926  QDomElement bElem = boxElem.firstChild().toElement();
927  QString coordSeparator = QStringLiteral( "," );
928  QString tupelSeparator = QStringLiteral( " " );
929  if ( bElem.hasAttribute( QStringLiteral( "cs" ) ) )
930  {
931  coordSeparator = bElem.attribute( QStringLiteral( "cs" ) );
932  }
933  if ( bElem.hasAttribute( QStringLiteral( "ts" ) ) )
934  {
935  tupelSeparator = bElem.attribute( QStringLiteral( "ts" ) );
936  }
937 
938  QString bString = bElem.text();
939  bool ok1, ok2, ok3, ok4;
940  double xmin = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 0, 0 ).toDouble( &ok1 );
941  double ymin = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 1, 1 ).toDouble( &ok2 );
942  double xmax = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 0, 0 ).toDouble( &ok3 );
943  double ymax = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 1, 1 ).toDouble( &ok4 );
944 
945  if ( ok1 && ok2 && ok3 && ok4 )
946  {
947  rect = QgsRectangle( xmin, ymin, xmax, ymax );
948  rect.normalize();
949  }
950 
951  return rect;
952 }
953 
954 bool QgsOgcUtils::readGMLPositions( QgsPolylineXY &coords, const QDomElement &elem )
955 {
956  coords.clear();
957 
958  QStringList pos = elem.text().split( ' ', QString::SkipEmptyParts );
959  double x, y;
960  bool conversionSuccess;
961  int posSize = pos.size();
962 
963  int srsDimension = 2;
964  if ( elem.hasAttribute( QStringLiteral( "srsDimension" ) ) )
965  {
966  srsDimension = elem.attribute( QStringLiteral( "srsDimension" ) ).toInt( &conversionSuccess );
967  if ( !conversionSuccess )
968  {
969  srsDimension = 2;
970  }
971  }
972  else if ( elem.hasAttribute( QStringLiteral( "dimension" ) ) )
973  {
974  srsDimension = elem.attribute( QStringLiteral( "dimension" ) ).toInt( &conversionSuccess );
975  if ( !conversionSuccess )
976  {
977  srsDimension = 2;
978  }
979  }
980 
981  for ( int i = 0; i < posSize / srsDimension; i++ )
982  {
983  x = pos.at( i * srsDimension ).toDouble( &conversionSuccess );
984  if ( !conversionSuccess )
985  {
986  return true;
987  }
988  y = pos.at( i * srsDimension + 1 ).toDouble( &conversionSuccess );
989  if ( !conversionSuccess )
990  {
991  return true;
992  }
993  coords.push_back( QgsPointXY( x, y ) );
994  }
995  return false;
996 }
997 
998 
1000 {
1001  QgsRectangle rect;
1002 
1003  QDomElement envelopeElem = envelopeNode.toElement();
1004  if ( envelopeElem.tagName() != QLatin1String( "Envelope" ) )
1005  return rect;
1006 
1007  QDomNodeList lowerCornerList = envelopeElem.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "lowerCorner" ) );
1008  if ( lowerCornerList.size() < 1 )
1009  return rect;
1010 
1011  QDomNodeList upperCornerList = envelopeElem.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "upperCorner" ) );
1012  if ( upperCornerList.size() < 1 )
1013  return rect;
1014 
1015  bool conversionSuccess;
1016  int srsDimension = 2;
1017 
1018  QDomElement elem = lowerCornerList.at( 0 ).toElement();
1019  if ( elem.hasAttribute( QStringLiteral( "srsDimension" ) ) )
1020  {
1021  srsDimension = elem.attribute( QStringLiteral( "srsDimension" ) ).toInt( &conversionSuccess );
1022  if ( !conversionSuccess )
1023  {
1024  srsDimension = 2;
1025  }
1026  }
1027  else if ( elem.hasAttribute( QStringLiteral( "dimension" ) ) )
1028  {
1029  srsDimension = elem.attribute( QStringLiteral( "dimension" ) ).toInt( &conversionSuccess );
1030  if ( !conversionSuccess )
1031  {
1032  srsDimension = 2;
1033  }
1034  }
1035  QString bString = elem.text();
1036 
1037  double xmin = bString.section( ' ', 0, 0 ).toDouble( &conversionSuccess );
1038  if ( !conversionSuccess )
1039  return rect;
1040  double ymin = bString.section( ' ', 1, 1 ).toDouble( &conversionSuccess );
1041  if ( !conversionSuccess )
1042  return rect;
1043 
1044  elem = upperCornerList.at( 0 ).toElement();
1045  if ( elem.hasAttribute( QStringLiteral( "srsDimension" ) ) )
1046  {
1047  srsDimension = elem.attribute( QStringLiteral( "srsDimension" ) ).toInt( &conversionSuccess );
1048  if ( !conversionSuccess )
1049  {
1050  srsDimension = 2;
1051  }
1052  }
1053  else if ( elem.hasAttribute( QStringLiteral( "dimension" ) ) )
1054  {
1055  srsDimension = elem.attribute( QStringLiteral( "dimension" ) ).toInt( &conversionSuccess );
1056  if ( !conversionSuccess )
1057  {
1058  srsDimension = 2;
1059  }
1060  }
1061 
1062  Q_UNUSED( srsDimension )
1063 
1064  bString = elem.text();
1065  double xmax = bString.section( ' ', 0, 0 ).toDouble( &conversionSuccess );
1066  if ( !conversionSuccess )
1067  return rect;
1068  double ymax = bString.section( ' ', 1, 1 ).toDouble( &conversionSuccess );
1069  if ( !conversionSuccess )
1070  return rect;
1071 
1072  rect = QgsRectangle( xmin, ymin, xmax, ymax );
1073  rect.normalize();
1074 
1075  return rect;
1076 }
1077 
1078 QDomElement QgsOgcUtils::rectangleToGMLBox( QgsRectangle *box, QDomDocument &doc, int precision )
1079 {
1080  return rectangleToGMLBox( box, doc, QString(), false, precision );
1081 }
1082 
1083 QDomElement QgsOgcUtils::rectangleToGMLBox( QgsRectangle *box, QDomDocument &doc,
1084  const QString &srsName,
1085  bool invertAxisOrientation,
1086  int precision )
1087 {
1088  if ( !box )
1089  {
1090  return QDomElement();
1091  }
1092 
1093  QDomElement boxElem = doc.createElement( QStringLiteral( "gml:Box" ) );
1094  if ( !srsName.isEmpty() )
1095  {
1096  boxElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1097  }
1098  QDomElement coordElem = doc.createElement( QStringLiteral( "gml:coordinates" ) );
1099  coordElem.setAttribute( QStringLiteral( "cs" ), QStringLiteral( "," ) );
1100  coordElem.setAttribute( QStringLiteral( "ts" ), QStringLiteral( " " ) );
1101 
1102  QString coordString;
1103  coordString += qgsDoubleToString( invertAxisOrientation ? box->yMinimum() : box->xMinimum(), precision );
1104  coordString += ',';
1105  coordString += qgsDoubleToString( invertAxisOrientation ? box->xMinimum() : box->yMinimum(), precision );
1106  coordString += ' ';
1107  coordString += qgsDoubleToString( invertAxisOrientation ? box->yMaximum() : box->xMaximum(), precision );
1108  coordString += ',';
1109  coordString += qgsDoubleToString( invertAxisOrientation ? box->xMaximum() : box->yMaximum(), precision );
1110 
1111  QDomText coordText = doc.createTextNode( coordString );
1112  coordElem.appendChild( coordText );
1113  boxElem.appendChild( coordElem );
1114 
1115  return boxElem;
1116 }
1117 
1118 QDomElement QgsOgcUtils::rectangleToGMLEnvelope( QgsRectangle *env, QDomDocument &doc, int precision )
1119 {
1120  return rectangleToGMLEnvelope( env, doc, QString(), false, precision );
1121 }
1122 
1123 QDomElement QgsOgcUtils::rectangleToGMLEnvelope( QgsRectangle *env, QDomDocument &doc,
1124  const QString &srsName,
1125  bool invertAxisOrientation,
1126  int precision )
1127 {
1128  if ( !env )
1129  {
1130  return QDomElement();
1131  }
1132 
1133  QDomElement envElem = doc.createElement( QStringLiteral( "gml:Envelope" ) );
1134  if ( !srsName.isEmpty() )
1135  {
1136  envElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1137  }
1138  QString posList;
1139 
1140  QDomElement lowerCornerElem = doc.createElement( QStringLiteral( "gml:lowerCorner" ) );
1141  posList = qgsDoubleToString( invertAxisOrientation ? env->yMinimum() : env->xMinimum(), precision );
1142  posList += ' ';
1143  posList += qgsDoubleToString( invertAxisOrientation ? env->xMinimum() : env->yMinimum(), precision );
1144  QDomText lowerCornerText = doc.createTextNode( posList );
1145  lowerCornerElem.appendChild( lowerCornerText );
1146  envElem.appendChild( lowerCornerElem );
1147 
1148  QDomElement upperCornerElem = doc.createElement( QStringLiteral( "gml:upperCorner" ) );
1149  posList = qgsDoubleToString( invertAxisOrientation ? env->yMaximum() : env->xMaximum(), precision );
1150  posList += ' ';
1151  posList += qgsDoubleToString( invertAxisOrientation ? env->xMaximum() : env->yMaximum(), precision );
1152  QDomText upperCornerText = doc.createTextNode( posList );
1153  upperCornerElem.appendChild( upperCornerText );
1154  envElem.appendChild( upperCornerElem );
1155 
1156  return envElem;
1157 }
1158 
1159 QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry &geometry, QDomDocument &doc, const QString &format, int precision )
1160 {
1161  return geometryToGML( geometry, doc, ( format == QLatin1String( "GML2" ) ) ? GML_2_1_2 : GML_3_2_1, QString(), false, QString(), precision );
1162 }
1163 
1164 QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry &geometry, QDomDocument &doc,
1165  GMLVersion gmlVersion,
1166  const QString &srsName,
1167  bool invertAxisOrientation,
1168  const QString &gmlIdBase,
1169  int precision )
1170 {
1171  if ( geometry.isNull() )
1172  return QDomElement();
1173 
1174  // coordinate separator
1175  QString cs = QStringLiteral( "," );
1176  // tuple separator
1177  QString ts = QStringLiteral( " " );
1178  // coord element tagname
1179  QDomElement baseCoordElem;
1180 
1181  bool hasZValue = false;
1182 
1183  QByteArray wkb( geometry.asWkb() );
1184  QgsConstWkbPtr wkbPtr( wkb );
1185  try
1186  {
1187  wkbPtr.readHeader();
1188  }
1189  catch ( const QgsWkbException &e )
1190  {
1191  Q_UNUSED( e )
1192  // WKB exception while reading header
1193  return QDomElement();
1194  }
1195 
1196  if ( gmlVersion != GML_2_1_2 )
1197  {
1198  switch ( geometry.wkbType() )
1199  {
1200  case QgsWkbTypes::Point25D:
1201  case QgsWkbTypes::Point:
1204  baseCoordElem = doc.createElement( QStringLiteral( "gml:pos" ) );
1205  break;
1206  default:
1207  baseCoordElem = doc.createElement( QStringLiteral( "gml:posList" ) );
1208  break;
1209  }
1210  baseCoordElem.setAttribute( QStringLiteral( "srsDimension" ), QStringLiteral( "2" ) );
1211  cs = ' ';
1212  }
1213  else
1214  {
1215  baseCoordElem = doc.createElement( QStringLiteral( "gml:coordinates" ) );
1216  baseCoordElem.setAttribute( QStringLiteral( "cs" ), cs );
1217  baseCoordElem.setAttribute( QStringLiteral( "ts" ), ts );
1218  }
1219 
1220  try
1221  {
1222  switch ( geometry.wkbType() )
1223  {
1224  case QgsWkbTypes::Point25D:
1225  case QgsWkbTypes::Point:
1226  {
1227  QDomElement pointElem = doc.createElement( QStringLiteral( "gml:Point" ) );
1228  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1229  pointElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1230  if ( !srsName.isEmpty() )
1231  pointElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1232  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1233 
1234  double x, y;
1235 
1236  if ( invertAxisOrientation )
1237  wkbPtr >> y >> x;
1238  else
1239  wkbPtr >> x >> y;
1240  QDomText coordText = doc.createTextNode( qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision ) );
1241 
1242  coordElem.appendChild( coordText );
1243  pointElem.appendChild( coordElem );
1244  return pointElem;
1245  }
1247  hasZValue = true;
1248  //intentional fall-through
1249  FALLTHROUGH
1251  {
1252  QDomElement multiPointElem = doc.createElement( QStringLiteral( "gml:MultiPoint" ) );
1253  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1254  multiPointElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1255  if ( !srsName.isEmpty() )
1256  multiPointElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1257 
1258  int nPoints;
1259  wkbPtr >> nPoints;
1260 
1261  for ( int idx = 0; idx < nPoints; ++idx )
1262  {
1263  QDomElement pointMemberElem = doc.createElement( QStringLiteral( "gml:pointMember" ) );
1264  QDomElement pointElem = doc.createElement( QStringLiteral( "gml:Point" ) );
1265  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1266  pointElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase + QStringLiteral( ".%1" ).arg( idx + 1 ) );
1267  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1268 
1269  wkbPtr.readHeader();
1270 
1271  double x, y;
1272  if ( invertAxisOrientation )
1273  wkbPtr >> y >> x;
1274  else
1275  wkbPtr >> x >> y;
1276  QDomText coordText = doc.createTextNode( qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision ) );
1277 
1278  coordElem.appendChild( coordText );
1279  pointElem.appendChild( coordElem );
1280 
1281  if ( hasZValue )
1282  {
1283  wkbPtr += sizeof( double );
1284  }
1285  pointMemberElem.appendChild( pointElem );
1286  multiPointElem.appendChild( pointMemberElem );
1287  }
1288  return multiPointElem;
1289  }
1291  hasZValue = true;
1292  //intentional fall-through
1293  FALLTHROUGH
1295  {
1296  QDomElement lineStringElem = doc.createElement( QStringLiteral( "gml:LineString" ) );
1297  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1298  lineStringElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1299  if ( !srsName.isEmpty() )
1300  lineStringElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1301  // get number of points in the line
1302 
1303  int nPoints;
1304  wkbPtr >> nPoints;
1305 
1306  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1307  QString coordString;
1308  for ( int idx = 0; idx < nPoints; ++idx )
1309  {
1310  if ( idx != 0 )
1311  {
1312  coordString += ts;
1313  }
1314 
1315  double x, y;
1316  if ( invertAxisOrientation )
1317  wkbPtr >> y >> x;
1318  else
1319  wkbPtr >> x >> y;
1320  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1321 
1322  if ( hasZValue )
1323  {
1324  wkbPtr += sizeof( double );
1325  }
1326  }
1327  QDomText coordText = doc.createTextNode( coordString );
1328  coordElem.appendChild( coordText );
1329  lineStringElem.appendChild( coordElem );
1330  return lineStringElem;
1331  }
1333  hasZValue = true;
1334  //intentional fall-through
1335  FALLTHROUGH
1337  {
1338  QDomElement multiLineStringElem = doc.createElement( QStringLiteral( "gml:MultiLineString" ) );
1339  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1340  multiLineStringElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1341  if ( !srsName.isEmpty() )
1342  multiLineStringElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1343 
1344  int nLines;
1345  wkbPtr >> nLines;
1346 
1347  for ( int jdx = 0; jdx < nLines; jdx++ )
1348  {
1349  QDomElement lineStringMemberElem = doc.createElement( QStringLiteral( "gml:lineStringMember" ) );
1350  QDomElement lineStringElem = doc.createElement( QStringLiteral( "gml:LineString" ) );
1351  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1352  lineStringElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase + QStringLiteral( ".%1" ).arg( jdx + 1 ) );
1353 
1354  wkbPtr.readHeader();
1355 
1356  int nPoints;
1357  wkbPtr >> nPoints;
1358 
1359  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1360  QString coordString;
1361  for ( int idx = 0; idx < nPoints; idx++ )
1362  {
1363  if ( idx != 0 )
1364  {
1365  coordString += ts;
1366  }
1367 
1368  double x, y;
1369  if ( invertAxisOrientation )
1370  wkbPtr >> y >> x;
1371  else
1372  wkbPtr >> x >> y;
1373 
1374  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1375 
1376  if ( hasZValue )
1377  {
1378  wkbPtr += sizeof( double );
1379  }
1380  }
1381  QDomText coordText = doc.createTextNode( coordString );
1382  coordElem.appendChild( coordText );
1383  lineStringElem.appendChild( coordElem );
1384  lineStringMemberElem.appendChild( lineStringElem );
1385  multiLineStringElem.appendChild( lineStringMemberElem );
1386  }
1387  return multiLineStringElem;
1388  }
1390  hasZValue = true;
1391  //intentional fall-through
1392  FALLTHROUGH
1393  case QgsWkbTypes::Polygon:
1394  {
1395  QDomElement polygonElem = doc.createElement( QStringLiteral( "gml:Polygon" ) );
1396  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1397  polygonElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1398  if ( !srsName.isEmpty() )
1399  polygonElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1400 
1401  // get number of rings in the polygon
1402  int numRings;
1403  wkbPtr >> numRings;
1404 
1405  if ( numRings == 0 ) // sanity check for zero rings in polygon
1406  return QDomElement();
1407 
1408  int *ringNumPoints = new int[numRings]; // number of points in each ring
1409 
1410  for ( int idx = 0; idx < numRings; idx++ )
1411  {
1412  QString boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:outerBoundaryIs" : "gml:exterior";
1413  if ( idx != 0 )
1414  {
1415  boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:innerBoundaryIs" : "gml:interior";
1416  }
1417  QDomElement boundaryElem = doc.createElement( boundaryName );
1418  QDomElement ringElem = doc.createElement( QStringLiteral( "gml:LinearRing" ) );
1419  // get number of points in the ring
1420  int nPoints;
1421  wkbPtr >> nPoints;
1422  ringNumPoints[idx] = nPoints;
1423 
1424  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1425  QString coordString;
1426  for ( int jdx = 0; jdx < nPoints; jdx++ )
1427  {
1428  if ( jdx != 0 )
1429  {
1430  coordString += ts;
1431  }
1432 
1433  double x, y;
1434  if ( invertAxisOrientation )
1435  wkbPtr >> y >> x;
1436  else
1437  wkbPtr >> x >> y;
1438 
1439  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1440  if ( hasZValue )
1441  {
1442  wkbPtr += sizeof( double );
1443  }
1444  }
1445  QDomText coordText = doc.createTextNode( coordString );
1446  coordElem.appendChild( coordText );
1447  ringElem.appendChild( coordElem );
1448  boundaryElem.appendChild( ringElem );
1449  polygonElem.appendChild( boundaryElem );
1450  }
1451  delete [] ringNumPoints;
1452  return polygonElem;
1453  }
1455  hasZValue = true;
1456  //intentional fall-through
1457  FALLTHROUGH
1459  {
1460  QDomElement multiPolygonElem = doc.createElement( QStringLiteral( "gml:MultiPolygon" ) );
1461  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1462  multiPolygonElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1463  if ( !srsName.isEmpty() )
1464  multiPolygonElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1465 
1466  int numPolygons;
1467  wkbPtr >> numPolygons;
1468 
1469  for ( int kdx = 0; kdx < numPolygons; kdx++ )
1470  {
1471  QDomElement polygonMemberElem = doc.createElement( QStringLiteral( "gml:polygonMember" ) );
1472  QDomElement polygonElem = doc.createElement( QStringLiteral( "gml:Polygon" ) );
1473  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1474  polygonElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase + QStringLiteral( ".%1" ).arg( kdx + 1 ) );
1475 
1476  wkbPtr.readHeader();
1477 
1478  int numRings;
1479  wkbPtr >> numRings;
1480 
1481  for ( int idx = 0; idx < numRings; idx++ )
1482  {
1483  QString boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:outerBoundaryIs" : "gml:exterior";
1484  if ( idx != 0 )
1485  {
1486  boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:innerBoundaryIs" : "gml:interior";
1487  }
1488  QDomElement boundaryElem = doc.createElement( boundaryName );
1489  QDomElement ringElem = doc.createElement( QStringLiteral( "gml:LinearRing" ) );
1490 
1491  int nPoints;
1492  wkbPtr >> nPoints;
1493 
1494  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1495  QString coordString;
1496  for ( int jdx = 0; jdx < nPoints; jdx++ )
1497  {
1498  if ( jdx != 0 )
1499  {
1500  coordString += ts;
1501  }
1502 
1503  double x, y;
1504  if ( invertAxisOrientation )
1505  wkbPtr >> y >> x;
1506  else
1507  wkbPtr >> x >> y;
1508 
1509  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1510 
1511  if ( hasZValue )
1512  {
1513  wkbPtr += sizeof( double );
1514  }
1515  }
1516  QDomText coordText = doc.createTextNode( coordString );
1517  coordElem.appendChild( coordText );
1518  ringElem.appendChild( coordElem );
1519  boundaryElem.appendChild( ringElem );
1520  polygonElem.appendChild( boundaryElem );
1521  polygonMemberElem.appendChild( polygonElem );
1522  multiPolygonElem.appendChild( polygonMemberElem );
1523  }
1524  }
1525  return multiPolygonElem;
1526  }
1527  default:
1528  return QDomElement();
1529  }
1530  }
1531  catch ( const QgsWkbException &e )
1532  {
1533  Q_UNUSED( e )
1534  return QDomElement();
1535  }
1536 }
1537 
1538 QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry &geometry, QDomDocument &doc, int precision )
1539 {
1540  return geometryToGML( geometry, doc, QStringLiteral( "GML2" ), precision );
1541 }
1542 
1543 QDomElement QgsOgcUtils::createGMLCoordinates( const QgsPolylineXY &points, QDomDocument &doc )
1544 {
1545  QDomElement coordElem = doc.createElement( QStringLiteral( "gml:coordinates" ) );
1546  coordElem.setAttribute( QStringLiteral( "cs" ), QStringLiteral( "," ) );
1547  coordElem.setAttribute( QStringLiteral( "ts" ), QStringLiteral( " " ) );
1548 
1549  QString coordString;
1550  QVector<QgsPointXY>::const_iterator pointIt = points.constBegin();
1551  for ( ; pointIt != points.constEnd(); ++pointIt )
1552  {
1553  if ( pointIt != points.constBegin() )
1554  {
1555  coordString += ' ';
1556  }
1557  coordString += qgsDoubleToString( pointIt->x() );
1558  coordString += ',';
1559  coordString += qgsDoubleToString( pointIt->y() );
1560  }
1561 
1562  QDomText coordText = doc.createTextNode( coordString );
1563  coordElem.appendChild( coordText );
1564  return coordElem;
1565 }
1566 
1567 QDomElement QgsOgcUtils::createGMLPositions( const QgsPolylineXY &points, QDomDocument &doc )
1568 {
1569  QDomElement posElem = doc.createElement( QStringLiteral( "gml:pos" ) );
1570  if ( points.size() > 1 )
1571  posElem = doc.createElement( QStringLiteral( "gml:posList" ) );
1572  posElem.setAttribute( QStringLiteral( "srsDimension" ), QStringLiteral( "2" ) );
1573 
1574  QString coordString;
1575  QVector<QgsPointXY>::const_iterator pointIt = points.constBegin();
1576  for ( ; pointIt != points.constEnd(); ++pointIt )
1577  {
1578  if ( pointIt != points.constBegin() )
1579  {
1580  coordString += ' ';
1581  }
1582  coordString += qgsDoubleToString( pointIt->x() );
1583  coordString += ' ';
1584  coordString += qgsDoubleToString( pointIt->y() );
1585  }
1586 
1587  QDomText coordText = doc.createTextNode( coordString );
1588  posElem.appendChild( coordText );
1589  return posElem;
1590 }
1591 
1592 
1593 
1594 // -----------------------------------------
1595 
1596 QColor QgsOgcUtils::colorFromOgcFill( const QDomElement &fillElement )
1597 {
1598  if ( fillElement.isNull() || !fillElement.hasChildNodes() )
1599  {
1600  return QColor();
1601  }
1602 
1603  QString cssName;
1604  QString elemText;
1605  QColor color;
1606  QDomElement cssElem = fillElement.firstChildElement( QStringLiteral( "CssParameter" ) );
1607  while ( !cssElem.isNull() )
1608  {
1609  cssName = cssElem.attribute( QStringLiteral( "name" ), QStringLiteral( "not_found" ) );
1610  if ( cssName != QLatin1String( "not_found" ) )
1611  {
1612  elemText = cssElem.text();
1613  if ( cssName == QLatin1String( "fill" ) )
1614  {
1615  color.setNamedColor( elemText );
1616  }
1617  else if ( cssName == QLatin1String( "fill-opacity" ) )
1618  {
1619  bool ok;
1620  double opacity = elemText.toDouble( &ok );
1621  if ( ok )
1622  {
1623  color.setAlphaF( opacity );
1624  }
1625  }
1626  }
1627 
1628  cssElem = cssElem.nextSiblingElement( QStringLiteral( "CssParameter" ) );
1629  }
1630 
1631  return color;
1632 }
1633 
1634 
1636 {
1637  return expressionFromOgcFilter( element, QgsOgcUtils::FILTER_OGC_1_0, layer );
1638 }
1639 
1640 QgsExpression *QgsOgcUtils::expressionFromOgcFilter( const QDomElement &element, const FilterVersion version, QgsVectorLayer *layer )
1641 {
1642  if ( element.isNull() || !element.hasChildNodes() )
1643  return nullptr;
1644 
1645  QgsExpression *expr = new QgsExpression();
1646 
1647  // check if it is a single string value not having DOM elements
1648  // that express OGC operators
1649  if ( element.firstChild().nodeType() == QDomNode::TextNode )
1650  {
1651  expr->setExpression( element.firstChild().nodeValue() );
1652  return expr;
1653  }
1654 
1655  QgsOgcUtilsExpressionFromFilter utils( version, layer );
1656 
1657  // then check OGC DOM elements that contain OGC tags specifying
1658  // OGC operators.
1659  QDomElement childElem = element.firstChildElement();
1660  while ( !childElem.isNull() )
1661  {
1662  QgsExpressionNode *node = utils.nodeFromOgcFilter( childElem );
1663 
1664  if ( !node )
1665  {
1666  // invalid expression, parser error
1667  expr->d->mParserErrorString = utils.errorMessage();
1668  return expr;
1669  }
1670 
1671  // use the concat binary operator to append to the root node
1672  if ( !expr->d->mRootNode )
1673  {
1674  expr->d->mRootNode = node;
1675  }
1676  else
1677  {
1678  expr->d->mRootNode = new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boConcat, expr->d->mRootNode, node );
1679  }
1680 
1681  childElem = childElem.nextSiblingElement();
1682  }
1683 
1684  // update expression string
1685  expr->d->mExp = expr->dump();
1686 
1687  return expr;
1688 }
1689 
1690 typedef QMap<QString, int> IntMap;
1691 Q_GLOBAL_STATIC_WITH_ARGS( IntMap, BINARY_OPERATORS_TAG_NAMES_MAP, (
1692 {
1693  // logical
1694  { QLatin1String( "Or" ), QgsExpressionNodeBinaryOperator::boOr },
1695  { QLatin1String( "And" ), QgsExpressionNodeBinaryOperator::boAnd },
1696  // comparison
1697  { QLatin1String( "PropertyIsEqualTo" ), QgsExpressionNodeBinaryOperator::boEQ },
1698  { QLatin1String( "PropertyIsNotEqualTo" ), QgsExpressionNodeBinaryOperator::boNE },
1699  { QLatin1String( "PropertyIsLessThanOrEqualTo" ), QgsExpressionNodeBinaryOperator::boLE },
1700  { QLatin1String( "PropertyIsGreaterThanOrEqualTo" ), QgsExpressionNodeBinaryOperator::boGE },
1701  { QLatin1String( "PropertyIsLessThan" ), QgsExpressionNodeBinaryOperator::boLT },
1702  { QLatin1String( "PropertyIsGreaterThan" ), QgsExpressionNodeBinaryOperator::boGT },
1703  { QLatin1String( "PropertyIsLike" ), QgsExpressionNodeBinaryOperator::boLike },
1704  // arithmetic
1705  { QLatin1String( "Add" ), QgsExpressionNodeBinaryOperator::boPlus },
1706  { QLatin1String( "Sub" ), QgsExpressionNodeBinaryOperator::boMinus },
1707  { QLatin1String( "Mul" ), QgsExpressionNodeBinaryOperator::boMul },
1708  { QLatin1String( "Div" ), QgsExpressionNodeBinaryOperator::boDiv },
1709 } ) )
1710 
1711 static int binaryOperatorFromTagName( const QString &tagName )
1712 {
1713 
1714  return BINARY_OPERATORS_TAG_NAMES_MAP()->value( tagName, -1 );
1715 }
1716 
1717 static QString binaryOperatorToTagName( QgsExpressionNodeBinaryOperator::BinaryOperator op )
1718 {
1720  {
1721  return QStringLiteral( "PropertyIsLike" );
1722  }
1723  return BINARY_OPERATORS_TAG_NAMES_MAP()->key( op, QString() );
1724 }
1725 
1726 static bool isBinaryOperator( const QString &tagName )
1727 {
1728  return binaryOperatorFromTagName( tagName ) >= 0;
1729 }
1730 
1731 
1732 static bool isSpatialOperator( const QString &tagName )
1733 {
1734  static QStringList spatialOps;
1735  if ( spatialOps.isEmpty() )
1736  {
1737  spatialOps << QStringLiteral( "BBOX" ) << QStringLiteral( "Intersects" ) << QStringLiteral( "Contains" ) << QStringLiteral( "Crosses" ) << QStringLiteral( "Equals" )
1738  << QStringLiteral( "Disjoint" ) << QStringLiteral( "Overlaps" ) << QStringLiteral( "Touches" ) << QStringLiteral( "Within" );
1739  }
1740 
1741  return spatialOps.contains( tagName );
1742 }
1743 
1744 QgsExpressionNode *QgsOgcUtils::nodeFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer )
1745 {
1747  QgsExpressionNode *node = utils.nodeFromOgcFilter( element );
1748  errorMessage = utils.errorMessage();
1749  return node;
1750 }
1751 
1752 QgsExpressionNodeBinaryOperator *QgsOgcUtils::nodeBinaryOperatorFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer )
1753 {
1755  QgsExpressionNodeBinaryOperator *node = utils.nodeBinaryOperatorFromOgcFilter( element );
1756  errorMessage = utils.errorMessage();
1757  return node;
1758 }
1759 
1760 QgsExpressionNodeFunction *QgsOgcUtils::nodeSpatialOperatorFromOgcFilter( QDomElement &element, QString &errorMessage )
1761 {
1763  QgsExpressionNodeFunction *node = utils.nodeSpatialOperatorFromOgcFilter( element );
1764  errorMessage = utils.errorMessage();
1765  return node;
1766 }
1767 
1768 QgsExpressionNodeUnaryOperator *QgsOgcUtils::nodeNotFromOgcFilter( QDomElement &element, QString &errorMessage )
1769 {
1771  QgsExpressionNodeUnaryOperator *node = utils.nodeNotFromOgcFilter( element );
1772  errorMessage = utils.errorMessage();
1773  return node;
1774 }
1775 
1776 QgsExpressionNodeFunction *QgsOgcUtils::nodeFunctionFromOgcFilter( QDomElement &element, QString &errorMessage )
1777 {
1779  QgsExpressionNodeFunction *node = utils.nodeFunctionFromOgcFilter( element );
1780  errorMessage = utils.errorMessage();
1781  return node;
1782 }
1783 
1784 QgsExpressionNode *QgsOgcUtils::nodeLiteralFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer )
1785 {
1787  QgsExpressionNode *node = utils.nodeLiteralFromOgcFilter( element );
1788  errorMessage = utils.errorMessage();
1789  return node;
1790 }
1791 
1792 QgsExpressionNodeColumnRef *QgsOgcUtils::nodeColumnRefFromOgcFilter( QDomElement &element, QString &errorMessage )
1793 {
1795  QgsExpressionNodeColumnRef *node = utils.nodeColumnRefFromOgcFilter( element );
1796  errorMessage = utils.errorMessage();
1797  return node;
1798 }
1799 
1800 QgsExpressionNode *QgsOgcUtils::nodeIsBetweenFromOgcFilter( QDomElement &element, QString &errorMessage )
1801 {
1803  QgsExpressionNode *node = utils.nodeIsBetweenFromOgcFilter( element );
1804  errorMessage = utils.errorMessage();
1805  return node;
1806 }
1807 
1808 QgsExpressionNodeBinaryOperator *QgsOgcUtils::nodePropertyIsNullFromOgcFilter( QDomElement &element, QString &errorMessage )
1809 {
1811  QgsExpressionNodeBinaryOperator *node = utils.nodePropertyIsNullFromOgcFilter( element );
1812  errorMessage = utils.errorMessage();
1813  return node;
1814 }
1815 
1816 
1818 
1819 
1820 QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression &exp, QDomDocument &doc, QString *errorMessage )
1821 {
1822  return expressionToOgcFilter( exp, doc, GML_2_1_2, FILTER_OGC_1_0,
1823  QStringLiteral( "geometry" ), QString(), false, false, errorMessage );
1824 }
1825 
1826 QDomElement QgsOgcUtils::expressionToOgcExpression( const QgsExpression &exp, QDomDocument &doc, QString *errorMessage )
1827 {
1829  QStringLiteral( "geometry" ), QString(), false, false, errorMessage );
1830 }
1831 
1832 QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression &expression,
1833  QDomDocument &doc,
1834  GMLVersion gmlVersion,
1835  FilterVersion filterVersion,
1836  const QString &geometryName,
1837  const QString &srsName,
1838  bool honourAxisOrientation,
1839  bool invertAxisOrientation,
1840  QString *errorMessage )
1841 {
1842  if ( !expression.rootNode() )
1843  return QDomElement();
1844 
1845  QgsExpression exp = expression;
1846 
1847  QgsExpressionContext context;
1849  QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
1850  QDomElement exprRootElem = utils.expressionNodeToOgcFilter( exp.rootNode(), &exp, &context );
1851  if ( errorMessage )
1852  *errorMessage = utils.errorMessage();
1853  if ( exprRootElem.isNull() )
1854  return QDomElement();
1855 
1856  QDomElement filterElem =
1857  ( filterVersion == FILTER_FES_2_0 ) ?
1858  doc.createElementNS( FES_NAMESPACE, QStringLiteral( "fes:Filter" ) ) :
1859  doc.createElementNS( OGC_NAMESPACE, QStringLiteral( "ogc:Filter" ) );
1860  if ( utils.GMLNamespaceUsed() )
1861  {
1862  QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:gml" ) );
1863  if ( gmlVersion == GML_3_2_1 )
1864  attr.setValue( GML32_NAMESPACE );
1865  else
1866  attr.setValue( GML_NAMESPACE );
1867  filterElem.setAttributeNode( attr );
1868  }
1869  filterElem.appendChild( exprRootElem );
1870  return filterElem;
1871 }
1872 
1874  QDomDocument &doc,
1875  GMLVersion gmlVersion,
1876  FilterVersion filterVersion,
1877  const QString &geometryName,
1878  const QString &srsName,
1879  bool honourAxisOrientation,
1880  bool invertAxisOrientation,
1881  QString *errorMessage )
1882 {
1883  QgsExpressionContext context;
1885 
1886  QgsExpression exp = expression;
1887 
1888  const QgsExpressionNode *node = exp.rootNode();
1889  if ( !node )
1890  return QDomElement();
1891 
1892  switch ( node->nodeType() )
1893  {
1897  {
1898  QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
1899  QDomElement exprRootElem = utils.expressionNodeToOgcFilter( node, &exp, &context );
1900 
1901  if ( errorMessage )
1902  *errorMessage = utils.errorMessage();
1903 
1904  if ( !exprRootElem.isNull() )
1905  {
1906  return exprRootElem;
1907  }
1908  break;
1909  }
1910  default:
1911  {
1912  if ( errorMessage )
1913  *errorMessage = QObject::tr( "Node type not supported in expression translation: %1" ).arg( node->nodeType() );
1914  }
1915  }
1916  // got an error
1917  return QDomElement();
1918 }
1919 
1921  QDomDocument &doc,
1922  GMLVersion gmlVersion,
1923  FilterVersion filterVersion,
1924  const QList<LayerProperties> &layerProperties,
1925  bool honourAxisOrientation,
1926  bool invertAxisOrientation,
1927  const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename,
1928  QString *errorMessage )
1929 {
1930  if ( !statement.rootNode() )
1931  return QDomElement();
1932 
1933  QgsOgcUtilsSQLStatementToFilter utils( doc, gmlVersion, filterVersion,
1934  layerProperties, honourAxisOrientation, invertAxisOrientation,
1935  mapUnprefixedTypenameToPrefixedTypename );
1936  QDomElement exprRootElem = utils.toOgcFilter( statement.rootNode() );
1937  if ( errorMessage )
1938  *errorMessage = utils.errorMessage();
1939  if ( exprRootElem.isNull() )
1940  return QDomElement();
1941 
1942  QDomElement filterElem =
1943  ( filterVersion == FILTER_FES_2_0 ) ?
1944  doc.createElementNS( FES_NAMESPACE, QStringLiteral( "fes:Filter" ) ) :
1945  doc.createElementNS( OGC_NAMESPACE, QStringLiteral( "ogc:Filter" ) );
1946  if ( utils.GMLNamespaceUsed() )
1947  {
1948  QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:gml" ) );
1949  if ( gmlVersion == GML_3_2_1 )
1950  attr.setValue( GML32_NAMESPACE );
1951  else
1952  attr.setValue( GML_NAMESPACE );
1953  filterElem.setAttributeNode( attr );
1954  }
1955  filterElem.appendChild( exprRootElem );
1956  return filterElem;
1957 }
1958 
1959 //
1960 
1961 
1963 {
1964  switch ( node->nodeType() )
1965  {
1967  return expressionUnaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeUnaryOperator *>( node ), expression, context );
1969  return expressionBinaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeBinaryOperator *>( node ), expression, context );
1971  return expressionInOperatorToOgcFilter( static_cast<const QgsExpressionNodeInOperator *>( node ), expression, context );
1973  return expressionFunctionToOgcFilter( static_cast<const QgsExpressionNodeFunction *>( node ), expression, context );
1975  return expressionLiteralToOgcFilter( static_cast<const QgsExpressionNodeLiteral *>( node ), expression, context );
1977  return expressionColumnRefToOgcFilter( static_cast<const QgsExpressionNodeColumnRef *>( node ), expression, context );
1978 
1979  default:
1980  mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
1981  return QDomElement();
1982  }
1983 }
1984 
1985 QDomElement QgsOgcUtilsExprToFilter::expressionUnaryOperatorToOgcFilter( const QgsExpressionNodeUnaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
1986 {
1987  QDomElement operandElem = expressionNodeToOgcFilter( node->operand(), expression, context );
1988  if ( !mErrorMessage.isEmpty() )
1989  return QDomElement();
1990 
1991  QDomElement uoElem;
1992  switch ( node->op() )
1993  {
1995  uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
1996  if ( node->operand()->nodeType() == QgsExpressionNode::ntLiteral )
1997  {
1998  // operand expression already created a Literal node:
1999  // take the literal value, prepend - and remove old literal node
2000  uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2001  mDoc.removeChild( operandElem );
2002  }
2003  else
2004  {
2005  mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2006  return QDomElement();
2007  }
2008  break;
2010  uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2011  uoElem.appendChild( operandElem );
2012  break;
2013 
2014  default:
2015  mErrorMessage = QObject::tr( "Unary operator '%1' not implemented yet" ).arg( node->text() );
2016  return QDomElement();
2017  }
2018 
2019  return uoElem;
2020 }
2021 
2022 
2023 QDomElement QgsOgcUtilsExprToFilter::expressionBinaryOperatorToOgcFilter( const QgsExpressionNodeBinaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
2024 {
2025  QDomElement leftElem = expressionNodeToOgcFilter( node->opLeft(), expression, context );
2026  if ( !mErrorMessage.isEmpty() )
2027  return QDomElement();
2028 
2030 
2031  // before right operator is parsed: to allow NULL handling
2033  {
2034  if ( node->opRight()->nodeType() == QgsExpressionNode::ntLiteral )
2035  {
2036  const QgsExpressionNodeLiteral *rightLit = static_cast<const QgsExpressionNodeLiteral *>( node->opRight() );
2037  if ( rightLit->value().isNull() )
2038  {
2039 
2040  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2041  elem.appendChild( leftElem );
2042 
2044  {
2045  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2046  notElem.appendChild( elem );
2047  return notElem;
2048  }
2049 
2050  return elem;
2051  }
2052 
2053  // continue with equal / not equal operator once the null case is handled
2055  }
2056 
2057  }
2058 
2059  QDomElement rightElem = expressionNodeToOgcFilter( node->opRight(), expression, context );
2060  if ( !mErrorMessage.isEmpty() )
2061  return QDomElement();
2062 
2063 
2064  QString opText = binaryOperatorToTagName( op );
2065  if ( opText.isEmpty() )
2066  {
2067  // not implemented binary operators
2068  // TODO: regex, % (mod), ^ (pow) are not supported yet
2069  mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( node->text() );
2070  return QDomElement();
2071  }
2072 
2073  QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2074 
2076  {
2078  boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2079 
2080  // setup wildCards to <ogc:PropertyIsLike>
2081  boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2082  boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2083  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2084  boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2085  else
2086  boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2087  }
2088 
2089  boElem.appendChild( leftElem );
2090  boElem.appendChild( rightElem );
2091  return boElem;
2092 }
2093 
2094 
2095 QDomElement QgsOgcUtilsExprToFilter::expressionLiteralToOgcFilter( const QgsExpressionNodeLiteral *node, QgsExpression *expression, const QgsExpressionContext *context )
2096 {
2097  Q_UNUSED( expression )
2098  Q_UNUSED( context )
2099  QString value;
2100  switch ( node->value().type() )
2101  {
2102  case QVariant::Int:
2103  value = QString::number( node->value().toInt() );
2104  break;
2105  case QVariant::Double:
2106  value = qgsDoubleToString( node->value().toDouble() );
2107  break;
2108  case QVariant::String:
2109  value = node->value().toString();
2110  break;
2111  case QVariant::Date:
2112  value = node->value().toDate().toString( Qt::ISODate );
2113  break;
2114  case QVariant::DateTime:
2115  value = node->value().toDateTime().toString( Qt::ISODate );
2116  break;
2117 
2118  default:
2119  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( node->value().type() );
2120  return QDomElement();
2121  }
2122 
2123  QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2124  litElem.appendChild( mDoc.createTextNode( value ) );
2125  return litElem;
2126 }
2127 
2128 
2129 QDomElement QgsOgcUtilsExprToFilter::expressionColumnRefToOgcFilter( const QgsExpressionNodeColumnRef *node, QgsExpression *expression, const QgsExpressionContext *context )
2130 {
2131  Q_UNUSED( expression )
2132  Q_UNUSED( context )
2133  QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2134  propElem.appendChild( mDoc.createTextNode( node->name() ) );
2135  return propElem;
2136 }
2137 
2138 
2139 
2140 QDomElement QgsOgcUtilsExprToFilter::expressionInOperatorToOgcFilter( const QgsExpressionNodeInOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
2141 {
2142  if ( node->list()->list().size() == 1 )
2143  return expressionNodeToOgcFilter( node->list()->list()[0], expression, context );
2144 
2145  QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2146  QDomElement leftNode = expressionNodeToOgcFilter( node->node(), expression, context );
2147 
2148  const auto constList = node->list()->list();
2149  for ( QgsExpressionNode *n : constList )
2150  {
2151  QDomElement listNode = expressionNodeToOgcFilter( n, expression, context );
2152  if ( !mErrorMessage.isEmpty() )
2153  return QDomElement();
2154 
2155  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2156  eqElem.appendChild( leftNode.cloneNode() );
2157  eqElem.appendChild( listNode );
2158 
2159  orElem.appendChild( eqElem );
2160  }
2161 
2162  if ( node->isNotIn() )
2163  {
2164  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2165  notElem.appendChild( orElem );
2166  return notElem;
2167  }
2168 
2169  return orElem;
2170 }
2171 
2172 Q_GLOBAL_STATIC_WITH_ARGS( QgsStringMap, BINARY_SPATIAL_OPS_MAP, (
2173 {
2174  { QLatin1String( "disjoint" ), QLatin1String( "Disjoint" ) },
2175  { QLatin1String( "intersects" ), QLatin1String( "Intersects" )},
2176  { QLatin1String( "touches" ), QLatin1String( "Touches" ) },
2177  { QLatin1String( "crosses" ), QLatin1String( "Crosses" ) },
2178  { QLatin1String( "contains" ), QLatin1String( "Contains" ) },
2179  { QLatin1String( "overlaps" ), QLatin1String( "Overlaps" ) },
2180  { QLatin1String( "within" ), QLatin1String( "Within" ) }
2181 } ) )
2182 
2183 static bool isBinarySpatialOperator( const QString &fnName )
2184 {
2185  return BINARY_SPATIAL_OPS_MAP()->contains( fnName );
2186 }
2187 
2188 static QString tagNameForSpatialOperator( const QString &fnName )
2189 {
2190  return BINARY_SPATIAL_OPS_MAP()->value( fnName );
2191 }
2192 
2193 static bool isGeometryColumn( const QgsExpressionNode *node )
2194 {
2195  if ( node->nodeType() != QgsExpressionNode::ntFunction )
2196  return false;
2197 
2198  const QgsExpressionNodeFunction *fn = static_cast<const QgsExpressionNodeFunction *>( node );
2200  return fd->name() == QLatin1String( "$geometry" );
2201 }
2202 
2203 static QgsGeometry geometryFromConstExpr( const QgsExpressionNode *node )
2204 {
2205  // Right now we support only geomFromWKT(' ..... ')
2206  // Ideally we should support any constant sub-expression (not dependent on feature's geometry or attributes)
2207 
2208  if ( node->nodeType() == QgsExpressionNode::ntFunction )
2209  {
2210  const QgsExpressionNodeFunction *fnNode = static_cast<const QgsExpressionNodeFunction *>( node );
2212  if ( fnDef->name() == QLatin1String( "geom_from_wkt" ) )
2213  {
2214  const QList<QgsExpressionNode *> &args = fnNode->args()->list();
2215  if ( args[0]->nodeType() == QgsExpressionNode::ntLiteral )
2216  {
2217  QString wkt = static_cast<const QgsExpressionNodeLiteral *>( args[0] )->value().toString();
2218  return QgsGeometry::fromWkt( wkt );
2219  }
2220  }
2221  }
2222  return QgsGeometry();
2223 }
2224 
2225 
2226 QDomElement QgsOgcUtilsExprToFilter::expressionFunctionToOgcFilter( const QgsExpressionNodeFunction *node, QgsExpression *expression, const QgsExpressionContext *context )
2227 {
2229 
2230  if ( fd->name() == QLatin1String( "intersects_bbox" ) )
2231  {
2232  QList<QgsExpressionNode *> argNodes = node->args()->list();
2233  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2234 
2235  QgsGeometry geom = geometryFromConstExpr( argNodes[1] );
2236  if ( !geom.isNull() && isGeometryColumn( argNodes[0] ) )
2237  {
2238  QgsRectangle rect = geom.boundingBox();
2239 
2240  mGMLUsed = true;
2241 
2242  QDomElement elemBox = ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
2243  QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, mSrsName, mInvertAxisOrientation ) :
2244  QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, mSrsName, mInvertAxisOrientation );
2245 
2246  QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2247  geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );
2248 
2249  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":BBOX" );
2250  funcElem.appendChild( geomProperty );
2251  funcElem.appendChild( elemBox );
2252  return funcElem;
2253  }
2254  else
2255  {
2256  mErrorMessage = QObject::tr( "<BBOX> is currently supported only in form: bbox($geometry, geomFromWKT('…'))" );
2257  return QDomElement();
2258  }
2259  }
2260 
2261  if ( isBinarySpatialOperator( fd->name() ) )
2262  {
2263  QList<QgsExpressionNode *> argNodes = node->args()->list();
2264  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2265 
2266  QgsExpressionNode *otherNode = nullptr;
2267  if ( isGeometryColumn( argNodes[0] ) )
2268  otherNode = argNodes[1];
2269  else if ( isGeometryColumn( argNodes[1] ) )
2270  otherNode = argNodes[0];
2271  else
2272  {
2273  mErrorMessage = QObject::tr( "Unable to translate spatial operator: at least one must refer to geometry." );
2274  return QDomElement();
2275  }
2276 
2277  QDomElement otherGeomElem;
2278 
2279  // the other node must be a geometry constructor
2280  if ( otherNode->nodeType() != QgsExpressionNode::ntFunction )
2281  {
2282  mErrorMessage = QObject::tr( "spatial operator: the other operator must be a geometry constructor function" );
2283  return QDomElement();
2284  }
2285 
2286  const QgsExpressionNodeFunction *otherFn = static_cast<const QgsExpressionNodeFunction *>( otherNode );
2287  QgsExpressionFunction *otherFnDef = QgsExpression::Functions()[otherFn->fnIndex()];
2288  if ( otherFnDef->name() == QLatin1String( "geom_from_wkt" ) )
2289  {
2290  QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2291  if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2292  {
2293  mErrorMessage = QObject::tr( "geom_from_wkt: argument must be string literal" );
2294  return QDomElement();
2295  }
2296  QString wkt = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2297  QgsGeometry geom = QgsGeometry::fromWkt( wkt );
2298  otherGeomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation,
2299  QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2300  mGeomId ++;
2301  }
2302  else if ( otherFnDef->name() == QLatin1String( "geom_from_gml" ) )
2303  {
2304  QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2305  if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2306  {
2307  mErrorMessage = QObject::tr( "geom_from_gml: argument must be string literal" );
2308  return QDomElement();
2309  }
2310 
2311  QDomDocument geomDoc;
2312  QString gml = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2313  if ( !geomDoc.setContent( gml, true ) )
2314  {
2315  mErrorMessage = QObject::tr( "geom_from_gml: unable to parse XML" );
2316  return QDomElement();
2317  }
2318 
2319  QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
2320  otherGeomElem = geomNode.toElement();
2321  }
2322  else
2323  {
2324  mErrorMessage = QObject::tr( "spatial operator: unknown geometry constructor function" );
2325  return QDomElement();
2326  }
2327 
2328  mGMLUsed = true;
2329 
2330  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + tagNameForSpatialOperator( fd->name() ) );
2331  QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2332  geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );
2333  funcElem.appendChild( geomProperty );
2334  funcElem.appendChild( otherGeomElem );
2335  return funcElem;
2336  }
2337 
2338  if ( fd->isStatic( node, expression, context ) )
2339  {
2340  QVariant result = fd->run( node->args(), context, expression, node );
2341  QgsExpressionNodeLiteral literal( result );
2342  return expressionLiteralToOgcFilter( &literal, expression, context );
2343  }
2344 
2345  if ( fd->params() == 0 )
2346  {
2347  mErrorMessage = QObject::tr( "Special columns/constants are not supported." );
2348  return QDomElement();
2349  }
2350 
2351  // this is somehow wrong - we are just hoping that the other side supports the same functions as we do...
2352  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
2353  funcElem.setAttribute( QStringLiteral( "name" ), fd->name() );
2354  const auto constList = node->args()->list();
2355  for ( QgsExpressionNode *n : constList )
2356  {
2357  QDomElement childElem = expressionNodeToOgcFilter( n, expression, context );
2358  if ( !mErrorMessage.isEmpty() )
2359  return QDomElement();
2360 
2361  funcElem.appendChild( childElem );
2362  }
2363 
2364  return funcElem;
2365 }
2366 
2367 //
2368 
2370  QgsOgcUtils::GMLVersion gmlVersion,
2371  QgsOgcUtils::FilterVersion filterVersion,
2372  const QList<QgsOgcUtils::LayerProperties> &layerProperties,
2373  bool honourAxisOrientation,
2374  bool invertAxisOrientation,
2375  const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename )
2376  : mDoc( doc )
2377  , mGMLUsed( false )
2378  , mGMLVersion( gmlVersion )
2379  , mFilterVersion( filterVersion )
2380  , mLayerProperties( layerProperties )
2381  , mHonourAxisOrientation( honourAxisOrientation )
2382  , mInvertAxisOrientation( invertAxisOrientation )
2383  , mFilterPrefix( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "fes" : "ogc" )
2384  , mPropertyName( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "ValueReference" : "PropertyName" )
2385  , mGeomId( 1 )
2386  , mMapUnprefixedTypenameToPrefixedTypename( mapUnprefixedTypenameToPrefixedTypename )
2387 {
2388 }
2389 
2391 {
2392  switch ( node->nodeType() )
2393  {
2395  return toOgcFilter( static_cast<const QgsSQLStatement::NodeUnaryOperator *>( node ) );
2397  return toOgcFilter( static_cast<const QgsSQLStatement::NodeBinaryOperator *>( node ) );
2399  return toOgcFilter( static_cast<const QgsSQLStatement::NodeInOperator *>( node ) );
2401  return toOgcFilter( static_cast<const QgsSQLStatement::NodeBetweenOperator *>( node ) );
2403  return toOgcFilter( static_cast<const QgsSQLStatement::NodeFunction *>( node ) );
2405  return toOgcFilter( static_cast<const QgsSQLStatement::NodeLiteral *>( node ) );
2407  return toOgcFilter( static_cast<const QgsSQLStatement::NodeColumnRef *>( node ) );
2409  return toOgcFilter( static_cast<const QgsSQLStatement::NodeSelect *>( node ) );
2410 
2411  default:
2412  mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
2413  return QDomElement();
2414  }
2415 }
2416 
2417 
2419 {
2420 
2421  QDomElement operandElem = toOgcFilter( node->operand() );
2422  if ( !mErrorMessage.isEmpty() )
2423  return QDomElement();
2424 
2425  QDomElement uoElem;
2426  switch ( node->op() )
2427  {
2429  uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2430  if ( node->operand()->nodeType() == QgsSQLStatement::ntLiteral )
2431  {
2432  // operand expression already created a Literal node:
2433  // take the literal value, prepend - and remove old literal node
2434  uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2435  mDoc.removeChild( operandElem );
2436  }
2437  else
2438  {
2439  mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2440  return QDomElement();
2441  }
2442  break;
2444  uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2445  uoElem.appendChild( operandElem );
2446  break;
2447 
2448  default:
2449  mErrorMessage = QObject::tr( "Unary operator %1 not implemented yet" ).arg( QgsSQLStatement::UNARY_OPERATOR_TEXT[node->op()] );
2450  return QDomElement();
2451  }
2452 
2453  return uoElem;
2454 }
2455 
2456 
2458 {
2459  QDomElement leftElem = toOgcFilter( node->opLeft() );
2460  if ( !mErrorMessage.isEmpty() )
2461  return QDomElement();
2462 
2463  QgsSQLStatement::BinaryOperator op = node->op();
2464 
2465  // before right operator is parsed: to allow NULL handling
2466  if ( op == QgsSQLStatement::boIs || op == QgsSQLStatement::boIsNot )
2467  {
2468  if ( node->opRight()->nodeType() == QgsSQLStatement::ntLiteral )
2469  {
2470  const QgsSQLStatement::NodeLiteral *rightLit = static_cast<const QgsSQLStatement::NodeLiteral *>( node->opRight() );
2471  if ( rightLit->value().isNull() )
2472  {
2473 
2474  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2475  elem.appendChild( leftElem );
2476 
2477  if ( op == QgsSQLStatement::boIsNot )
2478  {
2479  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2480  notElem.appendChild( elem );
2481  return notElem;
2482  }
2483 
2484  return elem;
2485  }
2486 
2487  // continue with equal / not equal operator once the null case is handled
2489  }
2490 
2491  }
2492 
2493  QDomElement rightElem = toOgcFilter( node->opRight() );
2494  if ( !mErrorMessage.isEmpty() )
2495  return QDomElement();
2496 
2497 
2498  QString opText;
2499  if ( op == QgsSQLStatement::boOr )
2500  opText = QStringLiteral( "Or" );
2501  else if ( op == QgsSQLStatement::boAnd )
2502  opText = QStringLiteral( "And" );
2503  else if ( op == QgsSQLStatement::boEQ )
2504  opText = QStringLiteral( "PropertyIsEqualTo" );
2505  else if ( op == QgsSQLStatement::boNE )
2506  opText = QStringLiteral( "PropertyIsNotEqualTo" );
2507  else if ( op == QgsSQLStatement::boLE )
2508  opText = QStringLiteral( "PropertyIsLessThanOrEqualTo" );
2509  else if ( op == QgsSQLStatement::boGE )
2510  opText = QStringLiteral( "PropertyIsGreaterThanOrEqualTo" );
2511  else if ( op == QgsSQLStatement::boLT )
2512  opText = QStringLiteral( "PropertyIsLessThan" );
2513  else if ( op == QgsSQLStatement::boGT )
2514  opText = QStringLiteral( "PropertyIsGreaterThan" );
2515  else if ( op == QgsSQLStatement::boLike )
2516  opText = QStringLiteral( "PropertyIsLike" );
2517  else if ( op == QgsSQLStatement::boILike )
2518  opText = QStringLiteral( "PropertyIsLike" );
2519 
2520  if ( opText.isEmpty() )
2521  {
2522  // not implemented binary operators
2523  mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( QgsSQLStatement::BINARY_OPERATOR_TEXT[op] );
2524  return QDomElement();
2525  }
2526 
2527  QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2528 
2530  {
2531  if ( op == QgsSQLStatement::boILike )
2532  boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2533 
2534  // setup wildCards to <ogc:PropertyIsLike>
2535  boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2536  boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2537  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2538  boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2539  else
2540  boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2541  }
2542 
2543  boElem.appendChild( leftElem );
2544  boElem.appendChild( rightElem );
2545  return boElem;
2546 }
2547 
2548 
2550 {
2551  QString value;
2552  switch ( node->value().type() )
2553  {
2554  case QVariant::Int:
2555  value = QString::number( node->value().toInt() );
2556  break;
2557  case QVariant::LongLong:
2558  value = QString::number( node->value().toLongLong() );
2559  break;
2560  case QVariant::Double:
2561  value = qgsDoubleToString( node->value().toDouble() );
2562  break;
2563  case QVariant::String:
2564  value = node->value().toString();
2565  break;
2566 
2567  default:
2568  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( node->value().type() );
2569  return QDomElement();
2570  }
2571 
2572  QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2573  litElem.appendChild( mDoc.createTextNode( value ) );
2574  return litElem;
2575 }
2576 
2577 
2579 {
2580  QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2581  if ( node->tableName().isEmpty() || mLayerProperties.size() == 1 )
2582  propElem.appendChild( mDoc.createTextNode( node->name() ) );
2583  else
2584  {
2585  QString tableName( mMapTableAliasToNames[node->tableName()] );
2586  if ( mMapUnprefixedTypenameToPrefixedTypename.contains( tableName ) )
2587  tableName = mMapUnprefixedTypenameToPrefixedTypename[tableName];
2588  propElem.appendChild( mDoc.createTextNode( tableName + "/" + node->name() ) );
2589  }
2590  return propElem;
2591 }
2592 
2594 {
2595  if ( node->list()->list().size() == 1 )
2596  return toOgcFilter( node->list()->list()[0] );
2597 
2598  QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2599  QDomElement leftNode = toOgcFilter( node->node() );
2600 
2601  const auto constList = node->list()->list();
2602  for ( QgsSQLStatement::Node *n : constList )
2603  {
2604  QDomElement listNode = toOgcFilter( n );
2605  if ( !mErrorMessage.isEmpty() )
2606  return QDomElement();
2607 
2608  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2609  eqElem.appendChild( leftNode.cloneNode() );
2610  eqElem.appendChild( listNode );
2611 
2612  orElem.appendChild( eqElem );
2613  }
2614 
2615  if ( node->isNotIn() )
2616  {
2617  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2618  notElem.appendChild( orElem );
2619  return notElem;
2620  }
2621 
2622  return orElem;
2623 }
2624 
2626 {
2627  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsBetween" );
2628  elem.appendChild( toOgcFilter( node->node() ) );
2629  QDomElement lowerBoundary = mDoc.createElement( mFilterPrefix + ":LowerBoundary" );
2630  lowerBoundary.appendChild( toOgcFilter( node->minVal() ) );
2631  elem.appendChild( lowerBoundary );
2632  QDomElement upperBoundary = mDoc.createElement( mFilterPrefix + ":UpperBoundary" );
2633  upperBoundary.appendChild( toOgcFilter( node->maxVal() ) );
2634  elem.appendChild( upperBoundary );
2635 
2636  if ( node->isNotBetween() )
2637  {
2638  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2639  notElem.appendChild( elem );
2640  return notElem;
2641  }
2642 
2643  return elem;
2644 }
2645 
2646 static QString mapBinarySpatialToOgc( const QString &name )
2647 {
2648  QString nameCompare( name );
2649  if ( name.size() > 3 && name.midRef( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2650  nameCompare = name.mid( 3 );
2651  QStringList spatialOps;
2652  spatialOps << QStringLiteral( "BBOX" ) << QStringLiteral( "Intersects" ) << QStringLiteral( "Contains" ) << QStringLiteral( "Crosses" ) << QStringLiteral( "Equals" )
2653  << QStringLiteral( "Disjoint" ) << QStringLiteral( "Overlaps" ) << QStringLiteral( "Touches" ) << QStringLiteral( "Within" );
2654  const auto constSpatialOps = spatialOps;
2655  for ( QString op : constSpatialOps )
2656  {
2657  if ( nameCompare.compare( op, Qt::CaseInsensitive ) == 0 )
2658  return op;
2659  }
2660  return QString();
2661 }
2662 
2663 static QString mapTernarySpatialToOgc( const QString &name )
2664 {
2665  QString nameCompare( name );
2666  if ( name.size() > 3 && name.midRef( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2667  nameCompare = name.mid( 3 );
2668  if ( nameCompare.compare( QLatin1String( "DWithin" ), Qt::CaseInsensitive ) == 0 )
2669  return QStringLiteral( "DWithin" );
2670  if ( nameCompare.compare( QLatin1String( "Beyond" ), Qt::CaseInsensitive ) == 0 )
2671  return QStringLiteral( "Beyond" );
2672  return QString();
2673 }
2674 
2675 QString QgsOgcUtilsSQLStatementToFilter::getGeometryColumnSRSName( const QgsSQLStatement::Node *node )
2676 {
2677  if ( node->nodeType() != QgsSQLStatement::ntColumnRef )
2678  return QString();
2679 
2680  const QgsSQLStatement::NodeColumnRef *col = static_cast<const QgsSQLStatement::NodeColumnRef *>( node );
2681  if ( !col->tableName().isEmpty() )
2682  {
2683  const auto constMLayerProperties = mLayerProperties;
2684  for ( QgsOgcUtils::LayerProperties prop : constMLayerProperties )
2685  {
2686  if ( prop.mName.compare( mMapTableAliasToNames[col->tableName()], Qt::CaseInsensitive ) == 0 &&
2687  prop.mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
2688  {
2689  return prop.mSRSName;
2690  }
2691  }
2692  }
2693  if ( !mLayerProperties.empty() &&
2694  mLayerProperties.at( 0 ).mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
2695  {
2696  return mLayerProperties.at( 0 ).mSRSName;
2697  }
2698  return QString();
2699 }
2700 
2701 bool QgsOgcUtilsSQLStatementToFilter::processSRSName( const QgsSQLStatement::NodeFunction *mainNode,
2702  QList<QgsSQLStatement::Node *> args,
2703  bool lastArgIsSRSName,
2704  QString &srsName,
2705  bool &axisInversion )
2706 {
2707  srsName = mCurrentSRSName;
2708  axisInversion = mInvertAxisOrientation;
2709 
2710  if ( lastArgIsSRSName )
2711  {
2712  QgsSQLStatement::Node *lastArg = args[ args.size() - 1 ];
2713  if ( lastArg->nodeType() != QgsSQLStatement::ntLiteral )
2714  {
2715  mErrorMessage = QObject::tr( "%1: Last argument must be string or integer literal" ).arg( mainNode->name() );
2716  return false;
2717  }
2718  const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( lastArg );
2719  if ( lit->value().type() == QVariant::Int )
2720  {
2721  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2722  {
2723  srsName = "EPSG:" + QString::number( lit->value().toInt() );
2724  }
2725  else
2726  {
2727  srsName = "urn:ogc:def:crs:EPSG::" + QString::number( lit->value().toInt() );
2728  }
2729  }
2730  else
2731  {
2732  srsName = lit->value().toString();
2733  if ( srsName.startsWith( QLatin1String( "EPSG:" ), Qt::CaseInsensitive ) )
2734  return true;
2735  }
2736  }
2737 
2739  if ( !srsName.isEmpty() )
2741  if ( crs.isValid() )
2742  {
2743  if ( mHonourAxisOrientation && crs.hasAxisInverted() )
2744  {
2745  axisInversion = !axisInversion;
2746  }
2747  }
2748 
2749  return true;
2750 }
2751 
2753 {
2754  // ST_GeometryFromText
2755  if ( node->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 )
2756  {
2757  QList<QgsSQLStatement::Node *> args = node->args()->list();
2758  if ( args.size() != 1 && args.size() != 2 )
2759  {
2760  mErrorMessage = QObject::tr( "Function %1 should have 1 or 2 arguments" ).arg( node->name() );
2761  return QDomElement();
2762  }
2763 
2764  QgsSQLStatement::Node *firstFnArg = args[0];
2765  if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
2766  {
2767  mErrorMessage = QObject::tr( "%1: First argument must be string literal" ).arg( node->name() );
2768  return QDomElement();
2769  }
2770 
2771  QString srsName;
2772  bool axisInversion;
2773  if ( ! processSRSName( node, args, args.size() == 2, srsName, axisInversion ) )
2774  {
2775  return QDomElement();
2776  }
2777 
2778  QString wkt = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
2779  QgsGeometry geom = QgsGeometry::fromWkt( wkt );
2780  QDomElement geomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, srsName, axisInversion,
2781  QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2782  mGeomId ++;
2783  if ( geomElem.isNull() )
2784  {
2785  mErrorMessage = QObject::tr( "%1: invalid WKT" ).arg( node->name() );
2786  return QDomElement();
2787  }
2788  mGMLUsed = true;
2789  return geomElem;
2790  }
2791 
2792  // ST_MakeEnvelope
2793  if ( node->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 )
2794  {
2795  QList<QgsSQLStatement::Node *> args = node->args()->list();
2796  if ( args.size() != 4 && args.size() != 5 )
2797  {
2798  mErrorMessage = QObject::tr( "Function %1 should have 4 or 5 arguments" ).arg( node->name() );
2799  return QDomElement();
2800  }
2801 
2802  QgsRectangle rect;
2803 
2804  for ( int i = 0; i < 4; i++ )
2805  {
2806  QgsSQLStatement::Node *arg = args[i];
2807  if ( arg->nodeType() != QgsSQLStatement::ntLiteral )
2808  {
2809  mErrorMessage = QObject::tr( "%1: Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
2810  return QDomElement();
2811  }
2812  const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( arg );
2813  double val = 0.0;
2814  if ( lit->value().type() == QVariant::Int )
2815  val = lit->value().toInt();
2816  else if ( lit->value().type() == QVariant::LongLong )
2817  val = lit->value().toLongLong();
2818  else if ( lit->value().type() == QVariant::Double )
2819  val = lit->value().toDouble();
2820  else
2821  {
2822  mErrorMessage = QObject::tr( "%1 Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
2823  return QDomElement();
2824  }
2825  if ( i == 0 )
2826  rect.setXMinimum( val );
2827  else if ( i == 1 )
2828  rect.setYMinimum( val );
2829  else if ( i == 2 )
2830  rect.setXMaximum( val );
2831  else
2832  rect.setYMaximum( val );
2833  }
2834 
2835  QString srsName;
2836  bool axisInversion;
2837  if ( ! processSRSName( node, args, args.size() == 5, srsName, axisInversion ) )
2838  {
2839  return QDomElement();
2840  }
2841 
2842  mGMLUsed = true;
2843 
2844  return ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
2845  QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, srsName, axisInversion, 15 ) :
2846  QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, srsName, axisInversion, 15 );
2847  }
2848 
2849  // ST_GeomFromGML
2850  if ( node->name().compare( QLatin1String( "ST_GeomFromGML" ), Qt::CaseInsensitive ) == 0 )
2851  {
2852  QList<QgsSQLStatement::Node *> args = node->args()->list();
2853  if ( args.size() != 1 )
2854  {
2855  mErrorMessage = QObject::tr( "Function %1 should have 1 argument" ).arg( node->name() );
2856  return QDomElement();
2857  }
2858 
2859  QgsSQLStatement::Node *firstFnArg = args[0];
2860  if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
2861  {
2862  mErrorMessage = QObject::tr( "%1: Argument must be string literal" ).arg( node->name() );
2863  return QDomElement();
2864  }
2865 
2866  QDomDocument geomDoc;
2867  QString gml = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
2868  if ( !geomDoc.setContent( gml, true ) )
2869  {
2870  mErrorMessage = QObject::tr( "ST_GeomFromGML: unable to parse XML" );
2871  return QDomElement();
2872  }
2873 
2874  QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
2875  mGMLUsed = true;
2876  return geomNode.toElement();
2877  }
2878 
2879  // Binary geometry operators
2880  QString ogcName( mapBinarySpatialToOgc( node->name() ) );
2881  if ( !ogcName.isEmpty() )
2882  {
2883  QList<QgsSQLStatement::Node *> args = node->args()->list();
2884  if ( args.size() != 2 )
2885  {
2886  mErrorMessage = QObject::tr( "Function %1 should have 2 arguments" ).arg( node->name() );
2887  return QDomElement();
2888  }
2889 
2890  for ( int i = 0; i < 2; i ++ )
2891  {
2892  if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
2893  ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
2894  static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
2895  {
2896  mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
2897  break;
2898  }
2899  }
2900 
2901  //if( ogcName == "Intersects" && mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2902  // ogcName = "Intersect";
2903  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + ogcName );
2904  const auto constArgs = args;
2905  for ( QgsSQLStatement::Node *n : constArgs )
2906  {
2907  QDomElement childElem = toOgcFilter( n );
2908  if ( !mErrorMessage.isEmpty() )
2909  {
2910  mCurrentSRSName.clear();
2911  return QDomElement();
2912  }
2913 
2914  funcElem.appendChild( childElem );
2915  }
2916 
2917  mCurrentSRSName.clear();
2918  return funcElem;
2919  }
2920 
2921  ogcName = mapTernarySpatialToOgc( node->name() );
2922  if ( !ogcName.isEmpty() )
2923  {
2924  QList<QgsSQLStatement::Node *> args = node->args()->list();
2925  if ( args.size() != 3 )
2926  {
2927  mErrorMessage = QObject::tr( "Function %1 should have 3 arguments" ).arg( node->name() );
2928  return QDomElement();
2929  }
2930 
2931  for ( int i = 0; i < 2; i ++ )
2932  {
2933  if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
2934  ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
2935  static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
2936  {
2937  mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
2938  break;
2939  }
2940  }
2941 
2942  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + node->name().mid( 3 ) );
2943  for ( int i = 0; i < 2; i++ )
2944  {
2945  QDomElement childElem = toOgcFilter( args[i] );
2946  if ( !mErrorMessage.isEmpty() )
2947  {
2948  mCurrentSRSName.clear();
2949  return QDomElement();
2950  }
2951 
2952  funcElem.appendChild( childElem );
2953  }
2954  mCurrentSRSName.clear();
2955 
2956  QgsSQLStatement::Node *distanceNode = args[2];
2957  if ( distanceNode->nodeType() != QgsSQLStatement::ntLiteral )
2958  {
2959  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() );
2960  return QDomElement();
2961  }
2962  const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( distanceNode );
2963  if ( lit->value().isNull() )
2964  {
2965  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() );
2966  return QDomElement();
2967  }
2968  QString distance;
2969  QString unit( QStringLiteral( "m" ) );
2970  switch ( lit->value().type() )
2971  {
2972  case QVariant::Int:
2973  distance = QString::number( lit->value().toInt() );
2974  break;
2975  case QVariant::LongLong:
2976  distance = QString::number( lit->value().toLongLong() );
2977  break;
2978  case QVariant::Double:
2979  distance = qgsDoubleToString( lit->value().toDouble() );
2980  break;
2981  case QVariant::String:
2982  {
2983  distance = lit->value().toString();
2984  for ( int i = 0; i < distance.size(); i++ )
2985  {
2986  if ( !( ( distance[i] >= '0' && distance[i] <= '9' ) || distance[i] == '-' || distance[i] == '.' || distance[i] == 'e' || distance[i] == 'E' ) )
2987  {
2988  unit = distance.mid( i ).trimmed();
2989  distance = distance.mid( 0, i );
2990  break;
2991  }
2992  }
2993  break;
2994  }
2995 
2996  default:
2997  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( lit->value().type() );
2998  return QDomElement();
2999  }
3000 
3001  QDomElement distanceElem = mDoc.createElement( mFilterPrefix + ":Distance" );
3002  if ( mFilterVersion == QgsOgcUtils::FILTER_FES_2_0 )
3003  distanceElem.setAttribute( QStringLiteral( "uom" ), unit );
3004  else
3005  distanceElem.setAttribute( QStringLiteral( "unit" ), unit );
3006  distanceElem.appendChild( mDoc.createTextNode( distance ) );
3007  funcElem.appendChild( distanceElem );
3008  return funcElem;
3009  }
3010 
3011  // Other function
3012  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
3013  funcElem.setAttribute( QStringLiteral( "name" ), node->name() );
3014  const auto constList = node->args()->list();
3015  for ( QgsSQLStatement::Node *n : constList )
3016  {
3017  QDomElement childElem = toOgcFilter( n );
3018  if ( !mErrorMessage.isEmpty() )
3019  return QDomElement();
3020 
3021  funcElem.appendChild( childElem );
3022  }
3023  return funcElem;
3024 }
3025 
3027  const QString &leftTable )
3028 {
3029  QgsSQLStatement::Node *onExpr = node->onExpr();
3030  if ( onExpr )
3031  {
3032  return toOgcFilter( onExpr );
3033  }
3034 
3035  QList<QDomElement> listElem;
3036  const auto constUsingColumns = node->usingColumns();
3037  for ( const QString &columnName : constUsingColumns )
3038  {
3039  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
3040  QDomElement propElem1 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3041  propElem1.appendChild( mDoc.createTextNode( leftTable + "/" + columnName ) );
3042  eqElem.appendChild( propElem1 );
3043  QDomElement propElem2 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3044  propElem2.appendChild( mDoc.createTextNode( node->tableDef()->name() + "/" + columnName ) );
3045  eqElem.appendChild( propElem2 );
3046  listElem.append( eqElem );
3047  }
3048 
3049  if ( listElem.size() == 1 )
3050  {
3051  return listElem[0];
3052  }
3053  else if ( listElem.size() > 1 )
3054  {
3055  QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3056  const auto constListElem = listElem;
3057  for ( const QDomElement &elem : constListElem )
3058  {
3059  andElem.appendChild( elem );
3060  }
3061  return andElem;
3062  }
3063 
3064  return QDomElement();
3065 }
3066 
3067 void QgsOgcUtilsSQLStatementToFilter::visit( const QgsSQLStatement::NodeTableDef *node )
3068 {
3069  if ( node->alias().isEmpty() )
3070  {
3071  mMapTableAliasToNames[ node->name()] = node->name();
3072  }
3073  else
3074  {
3075  mMapTableAliasToNames[ node->alias()] = node->name();
3076  }
3077 }
3078 
3080 {
3081  QList<QDomElement> listElem;
3082 
3083  if ( mFilterVersion != QgsOgcUtils::FILTER_FES_2_0 &&
3084  ( node->tables().size() != 1 || !node->joins().empty() ) )
3085  {
3086  mErrorMessage = QObject::tr( "Joins are only supported with WFS 2.0" );
3087  return QDomElement();
3088  }
3089 
3090  // Register all table name aliases
3091  const auto constTables = node->tables();
3092  for ( QgsSQLStatement::NodeTableDef *table : constTables )
3093  {
3094  visit( table );
3095  }
3096  const auto constJoins = node->joins();
3097  for ( QgsSQLStatement::NodeJoin *join : constJoins )
3098  {
3099  visit( join->tableDef() );
3100  }
3101 
3102  // Process JOIN conditions
3103  QList< QgsSQLStatement::NodeTableDef *> nodeTables = node->tables();
3104  QString leftTable = nodeTables.at( nodeTables.length() - 1 )->name();
3105  for ( QgsSQLStatement::NodeJoin *join : constJoins )
3106  {
3107  QDomElement joinElem = toOgcFilter( join, leftTable );
3108  if ( !mErrorMessage.isEmpty() )
3109  return QDomElement();
3110  listElem.append( joinElem );
3111  leftTable = join->tableDef()->name();
3112  }
3113 
3114  // Process WHERE conditions
3115  if ( node->where() )
3116  {
3117  QDomElement whereElem = toOgcFilter( node->where() );
3118  if ( !mErrorMessage.isEmpty() )
3119  return QDomElement();
3120  listElem.append( whereElem );
3121  }
3122 
3123  // Concatenate all conditions
3124  if ( listElem.size() == 1 )
3125  {
3126  return listElem[0];
3127  }
3128  else if ( listElem.size() > 1 )
3129  {
3130  QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3131  const auto constListElem = listElem;
3132  for ( const QDomElement &elem : constListElem )
3133  {
3134  andElem.appendChild( elem );
3135  }
3136  return andElem;
3137  }
3138 
3139  return QDomElement();
3140 }
3141 
3143  : mLayer( layer )
3144 {
3145  mPropertyName = QStringLiteral( "PropertyName" );
3146  mPrefix = QStringLiteral( "ogc" );
3147 
3148  if ( version == QgsOgcUtils::FILTER_FES_2_0 )
3149  {
3150  mPropertyName = QStringLiteral( "ValueReference" );
3151  mPrefix = QStringLiteral( "fes" );
3152  }
3153 }
3154 
3156 {
3157  if ( element.isNull() )
3158  return nullptr;
3159 
3160  // check for binary operators
3161  if ( isBinaryOperator( element.tagName() ) )
3162  {
3163  return nodeBinaryOperatorFromOgcFilter( element );
3164  }
3165 
3166  // check for spatial operators
3167  if ( isSpatialOperator( element.tagName() ) )
3168  {
3169  return nodeSpatialOperatorFromOgcFilter( element );
3170  }
3171 
3172  // check for other OGC operators, convert them to expressions
3173  if ( element.tagName() == QLatin1String( "Not" ) )
3174  {
3175  return nodeNotFromOgcFilter( element );
3176  }
3177  else if ( element.tagName() == QLatin1String( "PropertyIsNull" ) )
3178  {
3179  return nodePropertyIsNullFromOgcFilter( element );
3180  }
3181  else if ( element.tagName() == QLatin1String( "Literal" ) )
3182  {
3183  return nodeLiteralFromOgcFilter( element );
3184  }
3185  else if ( element.tagName() == QLatin1String( "Function" ) )
3186  {
3187  return nodeFunctionFromOgcFilter( element );
3188  }
3189  else if ( element.tagName() == mPropertyName )
3190  {
3191  return nodeColumnRefFromOgcFilter( element );
3192  }
3193  else if ( element.tagName() == QLatin1String( "PropertyIsBetween" ) )
3194  {
3195  return nodeIsBetweenFromOgcFilter( element );
3196  }
3197 
3198  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() );
3199  return nullptr;
3200 }
3201 
3203 {
3204  if ( element.isNull() )
3205  return nullptr;
3206 
3207  int op = binaryOperatorFromTagName( element.tagName() );
3208  if ( op < 0 )
3209  {
3210  mErrorMessage = QObject::tr( "'%1' binary operator not supported." ).arg( element.tagName() );
3211  return nullptr;
3212  }
3213 
3214  if ( op == QgsExpressionNodeBinaryOperator::boLike && element.hasAttribute( QStringLiteral( "matchCase" ) ) && element.attribute( QStringLiteral( "matchCase" ) ) == QLatin1String( "false" ) )
3215  {
3217  }
3218 
3219  QDomElement operandElem = element.firstChildElement();
3220  std::unique_ptr<QgsExpressionNode> expr( nodeFromOgcFilter( operandElem ) );
3221 
3222  if ( !expr )
3223  {
3224  mErrorMessage = QObject::tr( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
3225  return nullptr;
3226  }
3227 
3228  std::unique_ptr<QgsExpressionNode> leftOp( expr->clone() );
3229  for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
3230  {
3231  std::unique_ptr<QgsExpressionNode> opRight( nodeFromOgcFilter( operandElem ) );
3232  if ( !opRight )
3233  {
3234  mErrorMessage = QObject::tr( "invalid right operand for '%1' binary operator" ).arg( element.tagName() );
3235  return nullptr;
3236  }
3237 
3239  {
3240  QString wildCard;
3241  if ( element.hasAttribute( QStringLiteral( "wildCard" ) ) )
3242  {
3243  wildCard = element.attribute( QStringLiteral( "wildCard" ) );
3244  }
3245  QString singleChar;
3246  if ( element.hasAttribute( QStringLiteral( "singleChar" ) ) )
3247  {
3248  singleChar = element.attribute( QStringLiteral( "singleChar" ) );
3249  }
3250  QString escape = QStringLiteral( "\\" );
3251  if ( element.hasAttribute( QStringLiteral( "escape" ) ) )
3252  {
3253  escape = element.attribute( QStringLiteral( "escape" ) );
3254  }
3255  if ( element.hasAttribute( QStringLiteral( "escapeChar" ) ) )
3256  {
3257  escape = element.attribute( QStringLiteral( "escapeChar" ) );
3258  }
3259  // replace
3260  QString oprValue = static_cast<const QgsExpressionNodeLiteral *>( opRight.get() )->value().toString();
3261  if ( !wildCard.isEmpty() && wildCard != QLatin1String( "%" ) )
3262  {
3263  oprValue.replace( '%', QLatin1String( "\\%" ) );
3264  if ( oprValue.startsWith( wildCard ) )
3265  {
3266  oprValue.replace( 0, 1, QStringLiteral( "%" ) );
3267  }
3268  QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( wildCard ) + ")" );
3269  int pos = 0;
3270  while ( ( pos = rx.indexIn( oprValue, pos ) ) != -1 )
3271  {
3272  oprValue.replace( pos + 1, 1, QStringLiteral( "%" ) );
3273  pos += 1;
3274  }
3275  oprValue.replace( escape + wildCard, wildCard );
3276  }
3277  if ( !singleChar.isEmpty() && singleChar != QLatin1String( "_" ) )
3278  {
3279  oprValue.replace( '_', QLatin1String( "\\_" ) );
3280  if ( oprValue.startsWith( singleChar ) )
3281  {
3282  oprValue.replace( 0, 1, QStringLiteral( "_" ) );
3283  }
3284  QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( singleChar ) + ")" );
3285  int pos = 0;
3286  while ( ( pos = rx.indexIn( oprValue, pos ) ) != -1 )
3287  {
3288  oprValue.replace( pos + 1, 1, QStringLiteral( "_" ) );
3289  pos += 1;
3290  }
3291  oprValue.replace( escape + singleChar, singleChar );
3292  }
3293  if ( !escape.isEmpty() && escape != QLatin1String( "\\" ) )
3294  {
3295  oprValue.replace( escape + escape, escape );
3296  }
3297  opRight.reset( new QgsExpressionNodeLiteral( oprValue ) );
3298  }
3299 
3300  expr.reset( new QgsExpressionNodeBinaryOperator( static_cast< QgsExpressionNodeBinaryOperator::BinaryOperator >( op ), expr.release(), opRight.release() ) );
3301  }
3302 
3303  if ( expr == leftOp )
3304  {
3305  mErrorMessage = QObject::tr( "only one operand for '%1' binary operator" ).arg( element.tagName() );
3306  return nullptr;
3307  }
3308 
3309  return dynamic_cast< QgsExpressionNodeBinaryOperator * >( expr.release() );
3310 }
3311 
3312 
3314 {
3315  // we are exploiting the fact that our function names are the same as the XML tag names
3316  const int opIdx = QgsExpression::functionIndex( element.tagName().toLower() );
3317 
3318  std::unique_ptr<QgsExpressionNode::NodeList> gml2Args( new QgsExpressionNode::NodeList() );
3319  QDomElement childElem = element.firstChildElement();
3320  QString gml2Str;
3321  while ( !childElem.isNull() && gml2Str.isEmpty() )
3322  {
3323  if ( childElem.tagName() != mPropertyName )
3324  {
3325  QTextStream gml2Stream( &gml2Str );
3326  childElem.save( gml2Stream, 0 );
3327  }
3328  childElem = childElem.nextSiblingElement();
3329  }
3330  if ( !gml2Str.isEmpty() )
3331  {
3332  gml2Args->append( new QgsExpressionNodeLiteral( QVariant( gml2Str.remove( '\n' ) ) ) );
3333  }
3334  else
3335  {
3336  mErrorMessage = QObject::tr( "No OGC Geometry found" );
3337  return nullptr;
3338  }
3339 
3340  std::unique_ptr<QgsExpressionNode::NodeList> opArgs( new QgsExpressionNode::NodeList() );
3341  opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "$geometry" ) ), new QgsExpressionNode::NodeList() ) );
3342  opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "geomFromGML" ) ), gml2Args.release() ) );
3343 
3344  return new QgsExpressionNodeFunction( opIdx, opArgs.release() );
3345 }
3346 
3348 {
3349  if ( element.isNull() || element.tagName() != mPropertyName )
3350  {
3351  mErrorMessage = QObject::tr( "%1:PropertyName expected, got %2" ).arg( mPrefix, element.tagName() );
3352  return nullptr;
3353  }
3354 
3355  return new QgsExpressionNodeColumnRef( element.firstChild().nodeValue() );
3356 }
3357 
3359 {
3360  if ( element.isNull() || element.tagName() != QLatin1String( "Literal" ) )
3361  {
3362  mErrorMessage = QObject::tr( "%1:Literal expected, got %2" ).arg( mPrefix, element.tagName() );
3363  return nullptr;
3364  }
3365 
3366  std::unique_ptr<QgsExpressionNode> root;
3367 
3368  // the literal content can have more children (e.g. CDATA section, text, ...)
3369  QDomNode childNode = element.firstChild();
3370  while ( !childNode.isNull() )
3371  {
3372  std::unique_ptr<QgsExpressionNode> operand;
3373 
3374  if ( childNode.nodeType() == QDomNode::ElementNode )
3375  {
3376  // found a element node (e.g. PropertyName), convert it
3377  const QDomElement operandElem = childNode.toElement();
3378  operand.reset( nodeFromOgcFilter( operandElem ) );
3379  if ( !operand )
3380  {
3381  mErrorMessage = QObject::tr( "'%1' is an invalid or not supported content for %2:Literal" ).arg( operandElem.tagName(), mPrefix );
3382  return nullptr;
3383  }
3384  }
3385  else
3386  {
3387  // probably a text/CDATA node
3388  QVariant value = childNode.nodeValue();
3389 
3390  bool converted = false;
3391 
3392  // try to convert the node content to corresponding field type if possible
3393  if ( mLayer )
3394  {
3395  QDomElement propertyNameElement = element.previousSiblingElement( mPropertyName );
3396  if ( propertyNameElement.isNull() || propertyNameElement.tagName() != mPropertyName )
3397  {
3398  propertyNameElement = element.nextSiblingElement( mPropertyName );
3399  }
3400  if ( !propertyNameElement.isNull() || propertyNameElement.tagName() == mPropertyName )
3401  {
3402  const int fieldIndex = mLayer->fields().indexOf( propertyNameElement.firstChild().nodeValue() );
3403  if ( fieldIndex != -1 )
3404  {
3405  QgsField field = mLayer->fields().field( propertyNameElement.firstChild().nodeValue() );
3406  field.convertCompatible( value );
3407  converted = true;
3408  }
3409  }
3410  }
3411  if ( !converted )
3412  {
3413  // try to convert the node content to number if possible,
3414  // otherwise let's use it as string
3415  bool ok;
3416  const double d = value.toDouble( &ok );
3417  if ( ok )
3418  value = d;
3419  }
3420 
3421  operand.reset( new QgsExpressionNodeLiteral( value ) );
3422  if ( !operand )
3423  continue;
3424  }
3425 
3426  // use the concat operator to merge the ogc:Literal children
3427  if ( !root )
3428  {
3429  root = std::move( operand );
3430  }
3431  else
3432  {
3433  root.reset( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boConcat, root.release(), operand.release() ) );
3434  }
3435 
3436  childNode = childNode.nextSibling();
3437  }
3438 
3439  if ( root )
3440  return root.release();
3441 
3442  return nullptr;
3443 }
3444 
3446 {
3447  if ( element.tagName() != QLatin1String( "Not" ) )
3448  return nullptr;
3449 
3450  const QDomElement operandElem = element.firstChildElement();
3451  std::unique_ptr<QgsExpressionNode> operand( nodeFromOgcFilter( operandElem ) );
3452  if ( !operand )
3453  {
3454  mErrorMessage = QObject::tr( "invalid operand for '%1' unary operator" ).arg( element.tagName() );
3455  return nullptr;
3456  }
3457 
3459 }
3460 
3462 {
3463  // convert ogc:PropertyIsNull to IS operator with NULL right operand
3464  if ( element.tagName() != QLatin1String( "PropertyIsNull" ) )
3465  {
3466  return nullptr;
3467  }
3468 
3469  const QDomElement operandElem = element.firstChildElement();
3470  std::unique_ptr<QgsExpressionNode> opLeft( nodeFromOgcFilter( operandElem ) );
3471  if ( !opLeft )
3472  return nullptr;
3473 
3474  std::unique_ptr<QgsExpressionNode> opRight( new QgsExpressionNodeLiteral( QVariant() ) );
3475  return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boIs, opLeft.release(), opRight.release() );
3476 }
3477 
3479 {
3480  if ( element.isNull() || element.tagName() != QLatin1String( "Function" ) )
3481  {
3482  mErrorMessage = QObject::tr( "%1:Function expected, got %2" ).arg( mPrefix, element.tagName() );
3483  return nullptr;
3484  }
3485 
3486  for ( int i = 0; i < QgsExpression::Functions().size(); i++ )
3487  {
3488  const QgsExpressionFunction *funcDef = QgsExpression::Functions()[i];
3489 
3490  if ( element.attribute( QStringLiteral( "name" ) ) != funcDef->name() )
3491  continue;
3492 
3493  std::unique_ptr<QgsExpressionNode::NodeList> args( new QgsExpressionNode::NodeList() );
3494 
3495  QDomElement operandElem = element.firstChildElement();
3496  while ( !operandElem.isNull() )
3497  {
3498  std::unique_ptr<QgsExpressionNode> op( nodeFromOgcFilter( operandElem ) );
3499  if ( !op )
3500  {
3501  return nullptr;
3502  }
3503  args->append( op.release() );
3504 
3505  operandElem = operandElem.nextSiblingElement();
3506  }
3507 
3508  return new QgsExpressionNodeFunction( i, args.release() );
3509  }
3510 
3511  return nullptr;
3512 }
3513 
3515 {
3516  // <ogc:PropertyIsBetween> encode a Range check
3517  std::unique_ptr<QgsExpressionNode> operand;
3518  std::unique_ptr<QgsExpressionNode> lowerBound;
3519  std::unique_ptr<QgsExpressionNode> upperBound;
3520 
3521  QDomElement operandElem = element.firstChildElement();
3522  while ( !operandElem.isNull() )
3523  {
3524  if ( operandElem.tagName() == QLatin1String( "LowerBoundary" ) )
3525  {
3526  QDomElement lowerBoundElem = operandElem.firstChildElement();
3527  lowerBound.reset( nodeFromOgcFilter( lowerBoundElem ) );
3528  }
3529  else if ( operandElem.tagName() == QLatin1String( "UpperBoundary" ) )
3530  {
3531  QDomElement upperBoundElem = operandElem.firstChildElement();
3532  upperBound.reset( nodeFromOgcFilter( upperBoundElem ) );
3533  }
3534  else
3535  {
3536  // <ogc:expression>
3537  operand.reset( nodeFromOgcFilter( operandElem ) );
3538  }
3539 
3540  if ( operand && lowerBound && upperBound )
3541  break;
3542 
3543  operandElem = operandElem.nextSiblingElement();
3544  }
3545 
3546  if ( !operand || !lowerBound || !upperBound )
3547  {
3548  mErrorMessage = QObject::tr( "missing some required sub-elements in %1:PropertyIsBetween" ).arg( mPrefix );
3549  return nullptr;
3550  }
3551 
3552  std::unique_ptr<QgsExpressionNode> leOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boLE, operand->clone(), upperBound.release() ) );
3553  std::unique_ptr<QgsExpressionNode> geOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boGE, operand.release(), lowerBound.release() ) );
3554  return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boAnd, geOperator.release(), leOperator.release() );
3555 }
3556 
3558 {
3559  return mErrorMessage;
3560 }
GML_NAMESPACE
#define GML_NAMESPACE
Definition: qgsogcutils.cpp:41
QgsSQLStatement::Node
Definition: qgssqlstatement.h:229
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:542
QgsSQLStatement::NodeJoin
Definition: qgssqlstatement.h:664
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:369
QgsOgcUtilsExprToFilter::expressionNodeToOgcFilter
QDomElement expressionNodeToOgcFilter(const QgsExpressionNode *node, QgsExpression *expression, const QgsExpressionContext *context)
Convert an expression to a OGC filter.
Definition: qgsogcutils.cpp:1962
QgsMapLayer::crs
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:88
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:2521
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:2836
QgsOgcUtils::expressionFromOgcFilter
static QgsExpression * expressionFromOgcFilter(const QDomElement &element, QgsVectorLayer *layer=nullptr)
Parse XML with OGC filter into QGIS expression.
Definition: qgsogcutils.cpp:1635
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:3202
QgsSQLStatement::NodeUnaryOperator::operand
QgsSQLStatement::Node * operand() const
Operand.
Definition: qgssqlstatement.h:349
QgsExpressionNodeLiteral
An expression node for literal values.
Definition: qgsexpressionnodeimpl.h:364
QgsWkbTypes::Point
@ Point
Definition: qgswkbtypes.h:71
QgsSQLStatement::NodeTableDef::name
QString name() const
Table name.
Definition: qgssqlstatement.h:642
QgsExpressionNodeBinaryOperator
A binary expression operator, which operates on two values.
Definition: qgsexpressionnodeimpl.h:91
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:3347
QgsOgcUtilsExpressionFromFilter::nodeFunctionFromOgcFilter
QgsExpressionNodeFunction * nodeFunctionFromOgcFilter(const QDomElement &element)
Returns an expression node from a WFS filter embedded in a document with functions.
Definition: qgsogcutils.cpp:3478
QgsExpressionContextUtils::globalScope
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Definition: qgsexpressioncontextutils.cpp:33
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:74
QgsWkbTypes::MultiPolygon
@ MultiPolygon
Definition: qgswkbtypes.h:77
QgsWkbTypes::Point25D
@ Point25D
Definition: qgswkbtypes.h:124
QgsExpressionNodeInOperator::node
QgsExpressionNode * node() const
Returns the expression node.
Definition: qgsexpressionnodeimpl.h:281
QgsWkbTypes::LineString25D
@ LineString25D
Definition: qgswkbtypes.h:125
QgsRectangle::setXMinimum
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
qgswkbptr.h
QgsExpressionNodeBinaryOperator::text
QString text() const
Returns a the name of this operator without the operands.
Definition: qgsexpressionnodeimpl.cpp:1612
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:381
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
QgsExpressionNodeBinaryOperator::boGE
@ boGE
>=
Definition: qgsexpressionnodeimpl.h:109
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:105
QgsCoordinateReferenceSystem::fromOgcWmsCrs
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
Definition: qgscoordinatereferencesystem.cpp:200
QgsOgcUtilsExpressionFromFilter::errorMessage
QString errorMessage() const
Returns the underlying error message, or an empty string in case of no error.
Definition: qgsogcutils.cpp:3557
QgsExpression::rootNode
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
Definition: qgsexpression.cpp:1074
QgsExpressionNodeColumnRef
An expression node which takes it value from a feature's field.
Definition: qgsexpressionnodeimpl.h:400
QgsPolylineXY
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition: qgsgeometry.h:50
QgsExpressionNodeLiteral::value
QVariant value() const
The value of the literal.
Definition: qgsexpressionnodeimpl.h:376
QgsRectangle::xMaximum
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
QgsExpressionNodeFunction::fnIndex
int fnIndex() const
Returns the index of the node's function.
Definition: qgsexpressionnodeimpl.h:331
QgsWkbTypes::LineString
@ LineString
Definition: qgswkbtypes.h:72
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:154
QgsSQLStatement::NodeTableDef
Definition: qgssqlstatement.h:633
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:156
QgsOgcUtilsSQLStatementToFilter::toOgcFilter
QDomElement toOgcFilter(const QgsSQLStatement::Node *node)
Convert a SQL statement to a OGC filter.
Definition: qgsogcutils.cpp:2390
QgsExpressionNodeUnaryOperator
Definition: qgsexpressionnodeimpl.h:27
QgsGeometry::wkbType
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
Definition: qgsgeometry.cpp:345
QgsSQLStatement::NodeFunction
Definition: qgssqlstatement.h:478
QgsSQLStatement::NodeSelect::where
QgsSQLStatement::Node * where() const
Returns the where clause.
Definition: qgssqlstatement.h:759
QgsMultiPolygonXY
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
Definition: qgsgeometry.h:91
QgsSQLStatement::boGE
@ boGE
Definition: qgssqlstatement.h:155
QgsCoordinateReferenceSystem::hasAxisInverted
bool hasAxisInverted() const
Returns whether axis is inverted (e.g., for WMS 1.3) for the CRS.
Definition: qgscoordinatereferencesystem.cpp:808
QgsSQLStatement::NodeSelect
Definition: qgssqlstatement.h:734
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
QgsOgcUtils
The QgsOgcUtils class provides various utility functions for conversion between OGC (Open Geospatial ...
Definition: qgsogcutils.h:50
QgsSQLStatement::NodeUnaryOperator
Definition: qgssqlstatement.h:338
QgsGeometry::OperationResult
OperationResult
Success or failure of a geometry operation.
Definition: qgsgeometry.h:134
FALLTHROUGH
#define FALLTHROUGH
Definition: qgis.h:783
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:2369
QgsSQLStatement::ntFunction
@ ntFunction
Definition: qgssqlstatement.h:214
QgsWkbTypes::MultiPoint25D
@ MultiPoint25D
Definition: qgswkbtypes.h:127
QgsExpressionNodeUnaryOperator::op
QgsExpressionNodeUnaryOperator::UnaryOperator op() const
Returns the unary operator.
Definition: qgsexpressionnodeimpl.h:66
QgsOgcUtilsExpressionFromFilter::nodeFromOgcFilter
QgsExpressionNode * nodeFromOgcFilter(const QDomElement &element)
Returns an expression node from a WFS filter embedded in a document element.
Definition: qgsogcutils.cpp:3155
QgsRectangle
Definition: qgsrectangle.h:41
geometryName
const QString & geometryName
Definition: qgswfsgetfeature.cpp:113
QgsOgcUtilsExprToFilter::GMLNamespaceUsed
bool GMLNamespaceUsed() const
Returns whether the gml: namespace is used.
Definition: qgsogcutils.h:375
qgsogcutils.h
QgsWkbTypes::Polygon25D
@ Polygon25D
Definition: qgswkbtypes.h:126
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:212
QgsGeometry::fromRect
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
Definition: qgsgeometry.cpp:229
QgsExpressionNodeInOperator
An expression node for value IN or NOT IN clauses.
Definition: qgsexpressionnodeimpl.h:264
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:173
QgsSQLStatement::NodeJoin::tableDef
QgsSQLStatement::NodeTableDef * tableDef() const
Table definition.
Definition: qgssqlstatement.h:674
QgsOgcUtilsSQLStatementToFilter::GMLNamespaceUsed
bool GMLNamespaceUsed() const
Returns whether the gml: namespace is used.
Definition: qgsogcutils.h:509
QgsExpressionNodeBinaryOperator::boMinus
@ boMinus
Definition: qgsexpressionnodeimpl.h:122
QgsOgcUtilsExprToFilter
Definition: qgsogcutils.h:359
QgsRectangle::normalize
void normalize()
Normalize the rectangle so it has non-negative width/height.
Definition: qgsrectangle.h:182
QgsSQLStatement::boOr
@ boOr
Definition: qgssqlstatement.h:148
QgsOgcUtilsExprToFilter::errorMessage
QString errorMessage() const
Returns the error message.
Definition: qgsogcutils.h:378
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3280
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:1691
QgsOgcUtils::rectangleFromGMLBox
static QgsRectangle rectangleFromGMLBox(const QDomNode &boxNode)
Read rectangle from GML2 Box.
Definition: qgsogcutils.cpp:918
precision
int precision
Definition: qgswfsgetfeature.cpp:103
QgsWkbTypes::MultiLineString
@ MultiLineString
Definition: qgswkbtypes.h:76
QgsSQLStatement::ntBinaryOperator
@ ntBinaryOperator
Definition: qgssqlstatement.h:211
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:3313
QgsOgcUtils::rectangleToGMLBox
static QDomElement rectangleToGMLBox(QgsRectangle *box, QDomDocument &doc, int precision=17)
Exports the rectangle to GML2 Box.
Definition: qgsogcutils.cpp:1078
QgsCsException
Definition: qgsexception.h:65
QgsSQLStatement::rootNode
const QgsSQLStatement::Node * rootNode() const
Returns the root node of the statement.
Definition: qgssqlstatement.cpp:157
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:163
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:3358
QgsSQLStatement::uoNot
@ uoNot
Definition: qgssqlstatement.h:137
QgsSQLStatement::boNE
@ boNE
Definition: qgssqlstatement.h:153
QgsSQLStatement::NodeInOperator::node
QgsSQLStatement::Node * node() const
Variable at the left of IN.
Definition: qgssqlstatement.h:417
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:158
QgsSQLStatement::uoMinus
@ uoMinus
Definition: qgssqlstatement.h:138
QgsSQLStatement::NodeUnaryOperator::op
QgsSQLStatement::UnaryOperator op() const
Operator.
Definition: qgssqlstatement.h:346
QgsSQLStatement::NodeJoin::usingColumns
QList< QString > usingColumns() const
Columns referenced by USING.
Definition: qgssqlstatement.h:680
QgsExpressionNodeBinaryOperator::boConcat
@ boConcat
Definition: qgsexpressionnodeimpl.h:130
QgsConstWkbPtr
Definition: qgswkbptr.h:127
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:3461
QgsSQLStatement::NodeTableDef::alias
QString alias() const
Table alias.
Definition: qgssqlstatement.h:645
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:1920
QgsSQLStatement::ntLiteral
@ ntLiteral
Definition: qgssqlstatement.h:215
QgsSQLStatement::NodeLiteral::value
QVariant value() const
The value of the literal.
Definition: qgssqlstatement.h:514
QgsWkbException
Definition: qgswkbptr.h:30
QgsOgcUtils::rectangleFromGMLEnvelope
static QgsRectangle rectangleFromGMLEnvelope(const QDomNode &envelopeNode)
Read rectangle from GML3 Envelope.
Definition: qgsogcutils.cpp:999
QgsField::convertCompatible
bool convertCompatible(QVariant &v) const
Converts the provided variant to a compatible format.
Definition: qgsfield.cpp:339
QgsSQLStatement::NodeBinaryOperator::op
QgsSQLStatement::BinaryOperator op() const
Operator.
Definition: qgssqlstatement.h:378
QgsWkbTypes::MultiLineString25D
@ MultiLineString25D
Definition: qgswkbtypes.h:128
QgsSQLStatement::BINARY_OPERATOR_TEXT
static const char * BINARY_OPERATOR_TEXT[]
Definition: qgssqlstatement.h:195
QgsExpressionNodeBinaryOperator::boLT
@ boLT
<
Definition: qgsexpressionnodeimpl.h:110
qgsexpressionfunction.h
QgsSQLStatement
Definition: qgssqlstatement.h:36
QgsCoordinateReferenceSystem::isValid
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Definition: qgscoordinatereferencesystem.cpp:902
QgsSQLStatement::ntSelect
@ ntSelect
Definition: qgssqlstatement.h:218
QgsExpressionNodeUnaryOperator::operand
QgsExpressionNode * operand() const
Returns the node the operator will operate upon.
Definition: qgsexpressionnodeimpl.h:71
QgsRectangle::setXMaximum
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
QgsMultiPolylineXY
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
Definition: qgsgeometry.h:84
QgsSQLStatement::NodeInOperator::isNotIn
bool isNotIn() const
Whether this is a NOT IN operator.
Definition: qgssqlstatement.h:420
QgsSQLStatement::NodeSelect::tables
QList< QgsSQLStatement::NodeTableDef * > tables() const
Returns the list of tables.
Definition: qgssqlstatement.h:751
QgsGeometry::isNull
bool isNull
Definition: qgsgeometry.h:125
QgsExpressionNode::ntBinaryOperator
@ ntBinaryOperator
Definition: qgsexpressionnode.h:77
QgsExpressionNodeFunction
An expression node for expression functions.
Definition: qgsexpressionnodeimpl.h:316
QgsSQLStatement::NodeBinaryOperator::opRight
QgsSQLStatement::Node * opRight() const
Right operand.
Definition: qgssqlstatement.h:384
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:489
QgsSQLStatement::boIs
@ boIs
Definition: qgssqlstatement.h:162
QgsSQLStatement::boILike
@ boILike
Definition: qgssqlstatement.h:160
QgsSQLStatement::NodeBetweenOperator::node
QgsSQLStatement::Node * node() const
Variable at the left of BETWEEN.
Definition: qgssqlstatement.h:450
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:5649
QgsExpressionNodeBinaryOperator::boIsNot
@ boIsNot
Definition: qgsexpressionnodeimpl.h:118
QgsCoordinateReferenceSystem
Definition: qgscoordinatereferencesystem.h:206
QgsOgcUtilsExpressionFromFilter
Internal use by QgsOgcUtils.
Definition: qgsogcutils.h:407
QgsExpressionNodeBinaryOperator::boLike
@ boLike
Definition: qgsexpressionnodeimpl.h:113
QgsOgcUtilsSQLStatementToFilter::errorMessage
QString errorMessage() const
Returns the error message.
Definition: qgsogcutils.h:512
QgsOgcUtils::LayerProperties
Definition: qgsogcutils.h:241
QgsFields::field
QgsField field(int fieldIdx) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:168
QgsOgcUtilsSQLStatementToFilter
Definition: qgsogcutils.h:493
QgsExpressionNodeUnaryOperator::uoNot
@ uoNot
Definition: qgsexpressionnodeimpl.h:63
QgsRectangle::yMaximum
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsSQLStatement::NodeInOperator
Definition: qgssqlstatement.h:409
QgsSQLStatement::NodeBinaryOperator
Definition: qgssqlstatement.h:366
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:177
QgsCoordinateReferenceSystem::createFromString
bool createFromString(const QString &definition)
Set up this CRS from a string definition.
Definition: qgscoordinatereferencesystem.cpp:272
qgsvectorlayer.h
QgsPointXY
Definition: qgspointxy.h:43
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
Definition: qgsexpressionnode.h:34
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:157
QgsOgcUtils::Context::layer
const QgsMapLayer * layer
Definition: qgsogcutils.h:69
QgsSQLStatement::NodeBetweenOperator::maxVal
QgsSQLStatement::Node * maxVal() const
Maximum bound.
Definition: qgssqlstatement.h:459
QgsSQLStatement::NodeColumnRef
Definition: qgssqlstatement.h:530
QgsExpressionNodeBinaryOperator::boEQ
@ boEQ
=
Definition: qgsexpressionnodeimpl.h:106
QgsSQLStatement::UNARY_OPERATOR_TEXT
static const char * UNARY_OPERATOR_TEXT[]
Definition: qgssqlstatement.h:198
QgsStringMap
QMap< QString, QString > QgsStringMap
Definition: qgis.h:714
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:1820
QgsSQLStatement::NodeFunction::name
QString name() const
Returns function name.
Definition: qgssqlstatement.h:486
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:757
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:64
QgsExpressionFunction::run
virtual QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
Evaluates the function, first evaluating all required arguments before passing them to the function's...
Definition: qgsexpressionfunction.cpp:73
QgsGeometry
Definition: qgsgeometry.h:122
QgsExpressionFunction
Definition: qgsexpressionfunction.h:40
QgsSQLStatement::NodeBetweenOperator::isNotBetween
bool isNotBetween() const
Whether this is a NOT BETWEEN operator.
Definition: qgssqlstatement.h:453
QgsVectorLayer
Definition: qgsvectorlayer.h:385
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:1164
QgsWkbTypes::Polygon
@ Polygon
Definition: qgswkbtypes.h:73
QgsWkbTypes::MultiPoint
@ MultiPoint
Definition: qgswkbtypes.h:75
QgsExpressionFunction::name
QString name() const
The name of the function.
Definition: qgsexpressionfunction.h:190
QgsOgcUtils::colorFromOgcFill
static QColor colorFromOgcFill(const QDomElement &fillElement)
Parse XML with OGC fill into QColor.
Definition: qgsogcutils.cpp:1596
QgsOgcUtils::GMLVersion
GMLVersion
GML version.
Definition: qgsogcutils.h:76
QgsSQLStatement::boAnd
@ boAnd
Definition: qgssqlstatement.h:149
QgsRectangle::yMinimum
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
QgsSQLStatement::ntBetweenOperator
@ ntBetweenOperator
Definition: qgssqlstatement.h:213
QgsExpressionNodeBinaryOperator::boDiv
@ boDiv
Definition: qgsexpressionnodeimpl.h:124
QgsGeometry::boundingBox
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Definition: qgsgeometry.cpp:962
QgsSQLStatement::ntColumnRef
@ ntColumnRef
Definition: qgssqlstatement.h:216
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:152
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:145
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:117
QgsExpressionNodeBinaryOperator::boAnd
@ boAnd
Definition: qgsexpressionnodeimpl.h:103
IntMap
QMap< QString, int > IntMap
Definition: qgsogcutils.cpp:1690
QgsSQLStatement::NodeList::list
QList< QgsSQLStatement::Node * > list()
Returns list.
Definition: qgssqlstatement.h:314
QgsSQLStatement::ntUnaryOperator
@ ntUnaryOperator
Definition: qgssqlstatement.h:210
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:3142
QgsSQLStatement::NodeInOperator::list
QgsSQLStatement::NodeList * list() const
Values list.
Definition: qgssqlstatement.h:423
QgsSQLStatement::NodeColumnRef::name
QString name() const
The name of the column.
Definition: qgssqlstatement.h:545
QgsExpression
Definition: qgsexpression.h:113
QgsCoordinateTransform
Definition: qgscoordinatetransform.h:52
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:3445
QgsSQLStatement::NodeBetweenOperator::minVal
QgsSQLStatement::Node * minVal() const
Minimum bound.
Definition: qgssqlstatement.h:456
QgsSQLStatement::NodeBetweenOperator
Definition: qgssqlstatement.h:441
QgsOgcUtils::expressionToOgcExpression
static QDomElement expressionToOgcExpression(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=nullptr)
Creates an OGC expression XML element.
Definition: qgsogcutils.cpp:1826
QgsWkbTypes::MultiPolygon25D
@ MultiPolygon25D
Definition: qgswkbtypes.h:129
QgsSQLStatement::NodeJoin::onExpr
QgsSQLStatement::Node * onExpr() const
On expression. Will be nullptr if usingColumns() is not empty.
Definition: qgssqlstatement.h:677
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
QgsRectangle::setYMinimum
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:140
QgsRectangle::setYMaximum
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:145
QgsOgcUtils::rectangleToGMLEnvelope
static QDomElement rectangleToGMLEnvelope(QgsRectangle *env, QDomDocument &doc, int precision=17)
Exports the rectangle to GML3 Envelope.
Definition: qgsogcutils.cpp:1118
QgsRectangle::xMinimum
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
QgsOgcUtils::Context
The Context struct stores the current layer and coordinate transform context.
Definition: qgsogcutils.h:58
QgsOgcUtils::FILTER_OGC_1_0
@ FILTER_OGC_1_0
Definition: qgsogcutils.h:179
QgsExpressionNode::NodeList
A list of expression nodes.
Definition: qgsexpressionnode.h:117
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:99
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:3514
QgsSQLStatement::NodeLiteral
Definition: qgssqlstatement.h:507
QgsField
Definition: qgsfield.h:49