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