QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
qgsogcutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsogcutils.cpp
3  ---------------------
4  begin : March 2013
5  copyright : (C) 2013 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include "qgsogcutils.h"
16 
17 #include "qgsexpression.h"
18 #include "qgsexpressionnodeimpl.h"
19 #include "qgsexpressionfunction.h"
20 #include "qgsexpression_p.h"
21 #include "qgsgeometry.h"
22 #include "qgswkbptr.h"
24 #include "qgsrectangle.h"
25 #include "qgsvectorlayer.h"
27 
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 #define GML_NAMESPACE QStringLiteral( "http://www.opengis.net/gml" )
41 #define GML32_NAMESPACE QStringLiteral( "http://www.opengis.net/gml/3.2" )
42 #define OGC_NAMESPACE QStringLiteral( "http://www.opengis.net/ogc" )
43 #define 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 tuple_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  tuple_coords = ( *it ).split( coordSeparator, QString::SkipEmptyParts );
867  if ( tuple_coords.size() < 2 )
868  {
869  continue;
870  }
871  x = tuple_coords.at( 0 ).toDouble( &conversionSuccess );
872  if ( !conversionSuccess )
873  {
874  return true;
875  }
876  y = tuple_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  // tuple 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 typedef QMap<QString, int> IntMap;
1659 Q_GLOBAL_STATIC_WITH_ARGS( IntMap, BINARY_OPERATORS_TAG_NAMES_MAP, (
1660 {
1661  // logical
1662  { QLatin1String( "Or" ), QgsExpressionNodeBinaryOperator::boOr },
1663  { QLatin1String( "And" ), QgsExpressionNodeBinaryOperator::boAnd },
1664  // comparison
1665  { QLatin1String( "PropertyIsEqualTo" ), QgsExpressionNodeBinaryOperator::boEQ },
1666  { QLatin1String( "PropertyIsNotEqualTo" ), QgsExpressionNodeBinaryOperator::boNE },
1667  { QLatin1String( "PropertyIsLessThanOrEqualTo" ), QgsExpressionNodeBinaryOperator::boLE },
1668  { QLatin1String( "PropertyIsGreaterThanOrEqualTo" ), QgsExpressionNodeBinaryOperator::boGE },
1669  { QLatin1String( "PropertyIsLessThan" ), QgsExpressionNodeBinaryOperator::boLT },
1670  { QLatin1String( "PropertyIsGreaterThan" ), QgsExpressionNodeBinaryOperator::boGT },
1671  { QLatin1String( "PropertyIsLike" ), QgsExpressionNodeBinaryOperator::boLike },
1672  // arithmetic
1673  { QLatin1String( "Add" ), QgsExpressionNodeBinaryOperator::boPlus },
1674  { QLatin1String( "Sub" ), QgsExpressionNodeBinaryOperator::boMinus },
1675  { QLatin1String( "Mul" ), QgsExpressionNodeBinaryOperator::boMul },
1676  { QLatin1String( "Div" ), QgsExpressionNodeBinaryOperator::boDiv },
1677 } ) )
1678 
1679 static int binaryOperatorFromTagName( const QString &tagName )
1680 {
1681 
1682  return BINARY_OPERATORS_TAG_NAMES_MAP()->value( tagName, -1 );
1683 }
1684 
1685 static QString binaryOperatorToTagName( QgsExpressionNodeBinaryOperator::BinaryOperator op )
1686 {
1688  {
1689  return QStringLiteral( "PropertyIsLike" );
1690  }
1691  return BINARY_OPERATORS_TAG_NAMES_MAP()->key( op, QString() );
1692 }
1693 
1694 static bool isBinaryOperator( const QString &tagName )
1695 {
1696  return binaryOperatorFromTagName( tagName ) >= 0;
1697 }
1698 
1699 
1700 static bool isSpatialOperator( const QString &tagName )
1701 {
1702  static QStringList spatialOps;
1703  if ( spatialOps.isEmpty() )
1704  {
1705  spatialOps << QStringLiteral( "BBOX" ) << QStringLiteral( "Intersects" ) << QStringLiteral( "Contains" ) << QStringLiteral( "Crosses" ) << QStringLiteral( "Equals" )
1706  << QStringLiteral( "Disjoint" ) << QStringLiteral( "Overlaps" ) << QStringLiteral( "Touches" ) << QStringLiteral( "Within" );
1707  }
1708 
1709  return spatialOps.contains( tagName );
1710 }
1711 
1712 QgsExpressionNode *QgsOgcUtils::nodeFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer )
1713 {
1715  QgsExpressionNode *node = utils.nodeFromOgcFilter( element );
1716  errorMessage = utils.errorMessage();
1717  return node;
1718 }
1719 
1720 QgsExpressionNodeBinaryOperator *QgsOgcUtils::nodeBinaryOperatorFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer )
1721 {
1724  errorMessage = utils.errorMessage();
1725  return node;
1726 }
1727 
1728 QgsExpressionNodeFunction *QgsOgcUtils::nodeSpatialOperatorFromOgcFilter( QDomElement &element, QString &errorMessage )
1729 {
1732  errorMessage = utils.errorMessage();
1733  return node;
1734 }
1735 
1736 QgsExpressionNodeUnaryOperator *QgsOgcUtils::nodeNotFromOgcFilter( QDomElement &element, QString &errorMessage )
1737 {
1739  QgsExpressionNodeUnaryOperator *node = utils.nodeNotFromOgcFilter( element );
1740  errorMessage = utils.errorMessage();
1741  return node;
1742 }
1743 
1744 QgsExpressionNodeFunction *QgsOgcUtils::nodeFunctionFromOgcFilter( QDomElement &element, QString &errorMessage )
1745 {
1747  QgsExpressionNodeFunction *node = utils.nodeFunctionFromOgcFilter( element );
1748  errorMessage = utils.errorMessage();
1749  return node;
1750 }
1751 
1752 QgsExpressionNode *QgsOgcUtils::nodeLiteralFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer )
1753 {
1755  QgsExpressionNode *node = utils.nodeLiteralFromOgcFilter( element );
1756  errorMessage = utils.errorMessage();
1757  return node;
1758 }
1759 
1760 QgsExpressionNodeColumnRef *QgsOgcUtils::nodeColumnRefFromOgcFilter( QDomElement &element, QString &errorMessage )
1761 {
1763  QgsExpressionNodeColumnRef *node = utils.nodeColumnRefFromOgcFilter( element );
1764  errorMessage = utils.errorMessage();
1765  return node;
1766 }
1767 
1768 QgsExpressionNode *QgsOgcUtils::nodeIsBetweenFromOgcFilter( QDomElement &element, QString &errorMessage )
1769 {
1771  QgsExpressionNode *node = utils.nodeIsBetweenFromOgcFilter( element );
1772  errorMessage = utils.errorMessage();
1773  return node;
1774 }
1775 
1776 QgsExpressionNodeBinaryOperator *QgsOgcUtils::nodePropertyIsNullFromOgcFilter( QDomElement &element, QString &errorMessage )
1777 {
1780  errorMessage = utils.errorMessage();
1781  return node;
1782 }
1783 
1784 
1786 
1787 
1788 QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression &exp, QDomDocument &doc, QString *errorMessage )
1789 {
1790  return expressionToOgcFilter( exp, doc, GML_2_1_2, FILTER_OGC_1_0,
1791  QStringLiteral( "geometry" ), QString(), false, false, errorMessage );
1792 }
1793 
1794 QDomElement QgsOgcUtils::expressionToOgcExpression( const QgsExpression &exp, QDomDocument &doc, QString *errorMessage )
1795 {
1796  return expressionToOgcExpression( exp, doc, GML_2_1_2, FILTER_OGC_1_0,
1797  QStringLiteral( "geometry" ), QString(), false, false, errorMessage );
1798 }
1799 
1800 QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression &expression,
1801  QDomDocument &doc,
1802  GMLVersion gmlVersion,
1803  FilterVersion filterVersion,
1804  const QString &geometryName,
1805  const QString &srsName,
1806  bool honourAxisOrientation,
1807  bool invertAxisOrientation,
1808  QString *errorMessage )
1809 {
1810  if ( !expression.rootNode() )
1811  return QDomElement();
1812 
1813  QgsExpression exp = expression;
1814 
1815  QgsExpressionContext context;
1817  QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
1818  QDomElement exprRootElem = utils.expressionNodeToOgcFilter( exp.rootNode(), &exp, &context );
1819  if ( errorMessage )
1820  *errorMessage = utils.errorMessage();
1821  if ( exprRootElem.isNull() )
1822  return QDomElement();
1823 
1824  QDomElement filterElem =
1825  ( filterVersion == FILTER_FES_2_0 ) ?
1826  doc.createElementNS( FES_NAMESPACE, QStringLiteral( "fes:Filter" ) ) :
1827  doc.createElementNS( OGC_NAMESPACE, QStringLiteral( "ogc:Filter" ) );
1828  if ( utils.GMLNamespaceUsed() )
1829  {
1830  QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:gml" ) );
1831  if ( gmlVersion == GML_3_2_1 )
1832  attr.setValue( GML32_NAMESPACE );
1833  else
1834  attr.setValue( GML_NAMESPACE );
1835  filterElem.setAttributeNode( attr );
1836  }
1837  filterElem.appendChild( exprRootElem );
1838  return filterElem;
1839 }
1840 
1842  QDomDocument &doc,
1843  GMLVersion gmlVersion,
1844  FilterVersion filterVersion,
1845  const QString &geometryName,
1846  const QString &srsName,
1847  bool honourAxisOrientation,
1848  bool invertAxisOrientation,
1849  QString *errorMessage )
1850 {
1851  QgsExpressionContext context;
1853 
1854  QgsExpression exp = expression;
1855 
1856  const QgsExpressionNode *node = exp.rootNode();
1857  if ( !node )
1858  return QDomElement();
1859 
1860  switch ( node->nodeType() )
1861  {
1865  {
1866  QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
1867  QDomElement exprRootElem = utils.expressionNodeToOgcFilter( node, &exp, &context );
1868 
1869  if ( errorMessage )
1870  *errorMessage = utils.errorMessage();
1871 
1872  if ( !exprRootElem.isNull() )
1873  {
1874  return exprRootElem;
1875  }
1876  break;
1877  }
1878  default:
1879  {
1880  if ( errorMessage )
1881  *errorMessage = QObject::tr( "Node type not supported in expression translation: %1" ).arg( node->nodeType() );
1882  }
1883  }
1884  // got an error
1885  return QDomElement();
1886 }
1887 
1889  QDomDocument &doc,
1890  GMLVersion gmlVersion,
1891  FilterVersion filterVersion,
1892  const QList<LayerProperties> &layerProperties,
1893  bool honourAxisOrientation,
1894  bool invertAxisOrientation,
1895  const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename,
1896  QString *errorMessage )
1897 {
1898  if ( !statement.rootNode() )
1899  return QDomElement();
1900 
1901  QgsOgcUtilsSQLStatementToFilter utils( doc, gmlVersion, filterVersion,
1902  layerProperties, honourAxisOrientation, invertAxisOrientation,
1903  mapUnprefixedTypenameToPrefixedTypename );
1904  QDomElement exprRootElem = utils.toOgcFilter( statement.rootNode() );
1905  if ( errorMessage )
1906  *errorMessage = utils.errorMessage();
1907  if ( exprRootElem.isNull() )
1908  return QDomElement();
1909 
1910  QDomElement filterElem =
1911  ( filterVersion == FILTER_FES_2_0 ) ?
1912  doc.createElementNS( FES_NAMESPACE, QStringLiteral( "fes:Filter" ) ) :
1913  doc.createElementNS( OGC_NAMESPACE, QStringLiteral( "ogc:Filter" ) );
1914  if ( utils.GMLNamespaceUsed() )
1915  {
1916  QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:gml" ) );
1917  if ( gmlVersion == GML_3_2_1 )
1918  attr.setValue( GML32_NAMESPACE );
1919  else
1920  attr.setValue( GML_NAMESPACE );
1921  filterElem.setAttributeNode( attr );
1922  }
1923  filterElem.appendChild( exprRootElem );
1924  return filterElem;
1925 }
1926 
1927 //
1928 
1929 
1931 {
1932  switch ( node->nodeType() )
1933  {
1935  return expressionUnaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeUnaryOperator *>( node ), expression, context );
1937  return expressionBinaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeBinaryOperator *>( node ), expression, context );
1939  return expressionInOperatorToOgcFilter( static_cast<const QgsExpressionNodeInOperator *>( node ), expression, context );
1941  return expressionFunctionToOgcFilter( static_cast<const QgsExpressionNodeFunction *>( node ), expression, context );
1943  return expressionLiteralToOgcFilter( static_cast<const QgsExpressionNodeLiteral *>( node ), expression, context );
1945  return expressionColumnRefToOgcFilter( static_cast<const QgsExpressionNodeColumnRef *>( node ), expression, context );
1946 
1947  default:
1948  mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
1949  return QDomElement();
1950  }
1951 }
1952 
1953 QDomElement QgsOgcUtilsExprToFilter::expressionUnaryOperatorToOgcFilter( const QgsExpressionNodeUnaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
1954 {
1955  QDomElement operandElem = expressionNodeToOgcFilter( node->operand(), expression, context );
1956  if ( !mErrorMessage.isEmpty() )
1957  return QDomElement();
1958 
1959  QDomElement uoElem;
1960  switch ( node->op() )
1961  {
1963  uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
1964  if ( node->operand()->nodeType() == QgsExpressionNode::ntLiteral )
1965  {
1966  // operand expression already created a Literal node:
1967  // take the literal value, prepend - and remove old literal node
1968  uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
1969  mDoc.removeChild( operandElem );
1970  }
1971  else
1972  {
1973  mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
1974  return QDomElement();
1975  }
1976  break;
1978  uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
1979  uoElem.appendChild( operandElem );
1980  break;
1981 
1982  default:
1983  mErrorMessage = QObject::tr( "Unary operator '%1' not implemented yet" ).arg( node->text() );
1984  return QDomElement();
1985  }
1986 
1987  return uoElem;
1988 }
1989 
1990 
1991 QDomElement QgsOgcUtilsExprToFilter::expressionBinaryOperatorToOgcFilter( const QgsExpressionNodeBinaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
1992 {
1993  QDomElement leftElem = expressionNodeToOgcFilter( node->opLeft(), expression, context );
1994  if ( !mErrorMessage.isEmpty() )
1995  return QDomElement();
1996 
1998 
1999  // before right operator is parsed: to allow NULL handling
2001  {
2002  if ( node->opRight()->nodeType() == QgsExpressionNode::ntLiteral )
2003  {
2004  const QgsExpressionNodeLiteral *rightLit = static_cast<const QgsExpressionNodeLiteral *>( node->opRight() );
2005  if ( rightLit->value().isNull() )
2006  {
2007 
2008  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2009  elem.appendChild( leftElem );
2010 
2012  {
2013  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2014  notElem.appendChild( elem );
2015  return notElem;
2016  }
2017 
2018  return elem;
2019  }
2020 
2021  // continue with equal / not equal operator once the null case is handled
2023  }
2024 
2025  }
2026 
2027  QDomElement rightElem = expressionNodeToOgcFilter( node->opRight(), expression, context );
2028  if ( !mErrorMessage.isEmpty() )
2029  return QDomElement();
2030 
2031 
2032  QString opText = binaryOperatorToTagName( op );
2033  if ( opText.isEmpty() )
2034  {
2035  // not implemented binary operators
2036  // TODO: regex, % (mod), ^ (pow) are not supported yet
2037  mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( node->text() );
2038  return QDomElement();
2039  }
2040 
2041  QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2042 
2044  {
2046  boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2047 
2048  // setup wildCards to <ogc:PropertyIsLike>
2049  boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2050  boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2051  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2052  boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2053  else
2054  boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2055  }
2056 
2057  boElem.appendChild( leftElem );
2058  boElem.appendChild( rightElem );
2059  return boElem;
2060 }
2061 
2062 
2063 QDomElement QgsOgcUtilsExprToFilter::expressionLiteralToOgcFilter( const QgsExpressionNodeLiteral *node, QgsExpression *expression, const QgsExpressionContext *context )
2064 {
2065  Q_UNUSED( expression )
2066  Q_UNUSED( context )
2067  QString value;
2068  switch ( node->value().type() )
2069  {
2070  case QVariant::Int:
2071  value = QString::number( node->value().toInt() );
2072  break;
2073  case QVariant::Double:
2074  value = qgsDoubleToString( node->value().toDouble() );
2075  break;
2076  case QVariant::String:
2077  value = node->value().toString();
2078  break;
2079  case QVariant::Date:
2080  value = node->value().toDate().toString( Qt::ISODate );
2081  break;
2082  case QVariant::DateTime:
2083  value = node->value().toDateTime().toString( Qt::ISODate );
2084  break;
2085 
2086  default:
2087  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( node->value().type() );
2088  return QDomElement();
2089  }
2090 
2091  QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2092  litElem.appendChild( mDoc.createTextNode( value ) );
2093  return litElem;
2094 }
2095 
2096 
2097 QDomElement QgsOgcUtilsExprToFilter::expressionColumnRefToOgcFilter( const QgsExpressionNodeColumnRef *node, QgsExpression *expression, const QgsExpressionContext *context )
2098 {
2099  Q_UNUSED( expression )
2100  Q_UNUSED( context )
2101  QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2102  propElem.appendChild( mDoc.createTextNode( node->name() ) );
2103  return propElem;
2104 }
2105 
2106 
2107 
2108 QDomElement QgsOgcUtilsExprToFilter::expressionInOperatorToOgcFilter( const QgsExpressionNodeInOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
2109 {
2110  if ( node->list()->list().size() == 1 )
2111  return expressionNodeToOgcFilter( node->list()->list()[0], expression, context );
2112 
2113  QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2114  QDomElement leftNode = expressionNodeToOgcFilter( node->node(), expression, context );
2115 
2116  const auto constList = node->list()->list();
2117  for ( QgsExpressionNode *n : constList )
2118  {
2119  QDomElement listNode = expressionNodeToOgcFilter( n, expression, context );
2120  if ( !mErrorMessage.isEmpty() )
2121  return QDomElement();
2122 
2123  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2124  eqElem.appendChild( leftNode.cloneNode() );
2125  eqElem.appendChild( listNode );
2126 
2127  orElem.appendChild( eqElem );
2128  }
2129 
2130  if ( node->isNotIn() )
2131  {
2132  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2133  notElem.appendChild( orElem );
2134  return notElem;
2135  }
2136 
2137  return orElem;
2138 }
2139 
2140 Q_GLOBAL_STATIC_WITH_ARGS( QgsStringMap, BINARY_SPATIAL_OPS_MAP, (
2141 {
2142  { QLatin1String( "disjoint" ), QLatin1String( "Disjoint" ) },
2143  { QLatin1String( "intersects" ), QLatin1String( "Intersects" )},
2144  { QLatin1String( "touches" ), QLatin1String( "Touches" ) },
2145  { QLatin1String( "crosses" ), QLatin1String( "Crosses" ) },
2146  { QLatin1String( "contains" ), QLatin1String( "Contains" ) },
2147  { QLatin1String( "overlaps" ), QLatin1String( "Overlaps" ) },
2148  { QLatin1String( "within" ), QLatin1String( "Within" ) }
2149 } ) )
2150 
2151 static bool isBinarySpatialOperator( const QString &fnName )
2152 {
2153  return BINARY_SPATIAL_OPS_MAP()->contains( fnName );
2154 }
2155 
2156 static QString tagNameForSpatialOperator( const QString &fnName )
2157 {
2158  return BINARY_SPATIAL_OPS_MAP()->value( fnName );
2159 }
2160 
2161 static bool isGeometryColumn( const QgsExpressionNode *node )
2162 {
2163  if ( node->nodeType() != QgsExpressionNode::ntFunction )
2164  return false;
2165 
2166  const QgsExpressionNodeFunction *fn = static_cast<const QgsExpressionNodeFunction *>( node );
2168  return fd->name() == QLatin1String( "$geometry" );
2169 }
2170 
2171 static QgsGeometry geometryFromConstExpr( const QgsExpressionNode *node )
2172 {
2173  // Right now we support only geomFromWKT(' ..... ')
2174  // Ideally we should support any constant sub-expression (not dependent on feature's geometry or attributes)
2175 
2176  if ( node->nodeType() == QgsExpressionNode::ntFunction )
2177  {
2178  const QgsExpressionNodeFunction *fnNode = static_cast<const QgsExpressionNodeFunction *>( node );
2180  if ( fnDef->name() == QLatin1String( "geom_from_wkt" ) )
2181  {
2182  const QList<QgsExpressionNode *> &args = fnNode->args()->list();
2183  if ( args[0]->nodeType() == QgsExpressionNode::ntLiteral )
2184  {
2185  QString wkt = static_cast<const QgsExpressionNodeLiteral *>( args[0] )->value().toString();
2186  return QgsGeometry::fromWkt( wkt );
2187  }
2188  }
2189  }
2190  return QgsGeometry();
2191 }
2192 
2193 
2194 QDomElement QgsOgcUtilsExprToFilter::expressionFunctionToOgcFilter( const QgsExpressionNodeFunction *node, QgsExpression *expression, const QgsExpressionContext *context )
2195 {
2197 
2198  if ( fd->name() == QLatin1String( "intersects_bbox" ) )
2199  {
2200  QList<QgsExpressionNode *> argNodes = node->args()->list();
2201  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2202 
2203  QgsGeometry geom = geometryFromConstExpr( argNodes[1] );
2204  if ( !geom.isNull() && isGeometryColumn( argNodes[0] ) )
2205  {
2206  QgsRectangle rect = geom.boundingBox();
2207 
2208  mGMLUsed = true;
2209 
2210  QDomElement elemBox = ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
2211  QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, mSrsName, mInvertAxisOrientation ) :
2212  QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, mSrsName, mInvertAxisOrientation );
2213 
2214  QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2215  geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );
2216 
2217  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":BBOX" );
2218  funcElem.appendChild( geomProperty );
2219  funcElem.appendChild( elemBox );
2220  return funcElem;
2221  }
2222  else
2223  {
2224  mErrorMessage = QObject::tr( "<BBOX> is currently supported only in form: bbox($geometry, geomFromWKT('…'))" );
2225  return QDomElement();
2226  }
2227  }
2228 
2229  if ( isBinarySpatialOperator( fd->name() ) )
2230  {
2231  QList<QgsExpressionNode *> argNodes = node->args()->list();
2232  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2233 
2234  QgsExpressionNode *otherNode = nullptr;
2235  if ( isGeometryColumn( argNodes[0] ) )
2236  otherNode = argNodes[1];
2237  else if ( isGeometryColumn( argNodes[1] ) )
2238  otherNode = argNodes[0];
2239  else
2240  {
2241  mErrorMessage = QObject::tr( "Unable to translate spatial operator: at least one must refer to geometry." );
2242  return QDomElement();
2243  }
2244 
2245  QDomElement otherGeomElem;
2246 
2247  // the other node must be a geometry constructor
2248  if ( otherNode->nodeType() != QgsExpressionNode::ntFunction )
2249  {
2250  mErrorMessage = QObject::tr( "spatial operator: the other operator must be a geometry constructor function" );
2251  return QDomElement();
2252  }
2253 
2254  const QgsExpressionNodeFunction *otherFn = static_cast<const QgsExpressionNodeFunction *>( otherNode );
2255  QgsExpressionFunction *otherFnDef = QgsExpression::Functions()[otherFn->fnIndex()];
2256  if ( otherFnDef->name() == QLatin1String( "geom_from_wkt" ) )
2257  {
2258  QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2259  if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2260  {
2261  mErrorMessage = QObject::tr( "geom_from_wkt: argument must be string literal" );
2262  return QDomElement();
2263  }
2264  QString wkt = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2265  QgsGeometry geom = QgsGeometry::fromWkt( wkt );
2266  otherGeomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation,
2267  QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2268  mGeomId ++;
2269  }
2270  else if ( otherFnDef->name() == QLatin1String( "geom_from_gml" ) )
2271  {
2272  QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2273  if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2274  {
2275  mErrorMessage = QObject::tr( "geom_from_gml: argument must be string literal" );
2276  return QDomElement();
2277  }
2278 
2279  QDomDocument geomDoc;
2280  QString gml = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2281  if ( !geomDoc.setContent( gml, true ) )
2282  {
2283  mErrorMessage = QObject::tr( "geom_from_gml: unable to parse XML" );
2284  return QDomElement();
2285  }
2286 
2287  QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
2288  otherGeomElem = geomNode.toElement();
2289  }
2290  else
2291  {
2292  mErrorMessage = QObject::tr( "spatial operator: unknown geometry constructor function" );
2293  return QDomElement();
2294  }
2295 
2296  mGMLUsed = true;
2297 
2298  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + tagNameForSpatialOperator( fd->name() ) );
2299  QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2300  geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );
2301  funcElem.appendChild( geomProperty );
2302  funcElem.appendChild( otherGeomElem );
2303  return funcElem;
2304  }
2305 
2306  if ( fd->isStatic( node, expression, context ) )
2307  {
2308  QVariant result = fd->run( node->args(), context, expression, node );
2309  QgsExpressionNodeLiteral literal( result );
2310  return expressionLiteralToOgcFilter( &literal, expression, context );
2311  }
2312 
2313  if ( fd->params() == 0 )
2314  {
2315  mErrorMessage = QObject::tr( "Special columns/constants are not supported." );
2316  return QDomElement();
2317  }
2318 
2319  // this is somehow wrong - we are just hoping that the other side supports the same functions as we do...
2320  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
2321  funcElem.setAttribute( QStringLiteral( "name" ), fd->name() );
2322  const auto constList = node->args()->list();
2323  for ( QgsExpressionNode *n : constList )
2324  {
2325  QDomElement childElem = expressionNodeToOgcFilter( n, expression, context );
2326  if ( !mErrorMessage.isEmpty() )
2327  return QDomElement();
2328 
2329  funcElem.appendChild( childElem );
2330  }
2331 
2332  return funcElem;
2333 }
2334 
2335 //
2336 
2338  QgsOgcUtils::GMLVersion gmlVersion,
2339  QgsOgcUtils::FilterVersion filterVersion,
2340  const QList<QgsOgcUtils::LayerProperties> &layerProperties,
2341  bool honourAxisOrientation,
2342  bool invertAxisOrientation,
2343  const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename )
2344  : mDoc( doc )
2345  , mGMLUsed( false )
2346  , mGMLVersion( gmlVersion )
2347  , mFilterVersion( filterVersion )
2348  , mLayerProperties( layerProperties )
2349  , mHonourAxisOrientation( honourAxisOrientation )
2350  , mInvertAxisOrientation( invertAxisOrientation )
2351  , mFilterPrefix( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "fes" : "ogc" )
2352  , mPropertyName( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "ValueReference" : "PropertyName" )
2353  , mGeomId( 1 )
2354  , mMapUnprefixedTypenameToPrefixedTypename( mapUnprefixedTypenameToPrefixedTypename )
2355 {
2356 }
2357 
2359 {
2360  switch ( node->nodeType() )
2361  {
2363  return toOgcFilter( static_cast<const QgsSQLStatement::NodeUnaryOperator *>( node ) );
2365  return toOgcFilter( static_cast<const QgsSQLStatement::NodeBinaryOperator *>( node ) );
2367  return toOgcFilter( static_cast<const QgsSQLStatement::NodeInOperator *>( node ) );
2369  return toOgcFilter( static_cast<const QgsSQLStatement::NodeBetweenOperator *>( node ) );
2371  return toOgcFilter( static_cast<const QgsSQLStatement::NodeFunction *>( node ) );
2373  return toOgcFilter( static_cast<const QgsSQLStatement::NodeLiteral *>( node ) );
2375  return toOgcFilter( static_cast<const QgsSQLStatement::NodeColumnRef *>( node ) );
2377  return toOgcFilter( static_cast<const QgsSQLStatement::NodeSelect *>( node ) );
2378 
2379  default:
2380  mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
2381  return QDomElement();
2382  }
2383 }
2384 
2385 
2387 {
2388 
2389  QDomElement operandElem = toOgcFilter( node->operand() );
2390  if ( !mErrorMessage.isEmpty() )
2391  return QDomElement();
2392 
2393  QDomElement uoElem;
2394  switch ( node->op() )
2395  {
2397  uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2398  if ( node->operand()->nodeType() == QgsSQLStatement::ntLiteral )
2399  {
2400  // operand expression already created a Literal node:
2401  // take the literal value, prepend - and remove old literal node
2402  uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2403  mDoc.removeChild( operandElem );
2404  }
2405  else
2406  {
2407  mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2408  return QDomElement();
2409  }
2410  break;
2412  uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2413  uoElem.appendChild( operandElem );
2414  break;
2415 
2416  default:
2417  mErrorMessage = QObject::tr( "Unary operator %1 not implemented yet" ).arg( QgsSQLStatement::UNARY_OPERATOR_TEXT[node->op()] );
2418  return QDomElement();
2419  }
2420 
2421  return uoElem;
2422 }
2423 
2424 
2426 {
2427  QDomElement leftElem = toOgcFilter( node->opLeft() );
2428  if ( !mErrorMessage.isEmpty() )
2429  return QDomElement();
2430 
2431  QgsSQLStatement::BinaryOperator op = node->op();
2432 
2433  // before right operator is parsed: to allow NULL handling
2434  if ( op == QgsSQLStatement::boIs || op == QgsSQLStatement::boIsNot )
2435  {
2436  if ( node->opRight()->nodeType() == QgsSQLStatement::ntLiteral )
2437  {
2438  const QgsSQLStatement::NodeLiteral *rightLit = static_cast<const QgsSQLStatement::NodeLiteral *>( node->opRight() );
2439  if ( rightLit->value().isNull() )
2440  {
2441 
2442  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2443  elem.appendChild( leftElem );
2444 
2445  if ( op == QgsSQLStatement::boIsNot )
2446  {
2447  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2448  notElem.appendChild( elem );
2449  return notElem;
2450  }
2451 
2452  return elem;
2453  }
2454 
2455  // continue with equal / not equal operator once the null case is handled
2457  }
2458 
2459  }
2460 
2461  QDomElement rightElem = toOgcFilter( node->opRight() );
2462  if ( !mErrorMessage.isEmpty() )
2463  return QDomElement();
2464 
2465 
2466  QString opText;
2467  if ( op == QgsSQLStatement::boOr )
2468  opText = QStringLiteral( "Or" );
2469  else if ( op == QgsSQLStatement::boAnd )
2470  opText = QStringLiteral( "And" );
2471  else if ( op == QgsSQLStatement::boEQ )
2472  opText = QStringLiteral( "PropertyIsEqualTo" );
2473  else if ( op == QgsSQLStatement::boNE )
2474  opText = QStringLiteral( "PropertyIsNotEqualTo" );
2475  else if ( op == QgsSQLStatement::boLE )
2476  opText = QStringLiteral( "PropertyIsLessThanOrEqualTo" );
2477  else if ( op == QgsSQLStatement::boGE )
2478  opText = QStringLiteral( "PropertyIsGreaterThanOrEqualTo" );
2479  else if ( op == QgsSQLStatement::boLT )
2480  opText = QStringLiteral( "PropertyIsLessThan" );
2481  else if ( op == QgsSQLStatement::boGT )
2482  opText = QStringLiteral( "PropertyIsGreaterThan" );
2483  else if ( op == QgsSQLStatement::boLike )
2484  opText = QStringLiteral( "PropertyIsLike" );
2485  else if ( op == QgsSQLStatement::boILike )
2486  opText = QStringLiteral( "PropertyIsLike" );
2487 
2488  if ( opText.isEmpty() )
2489  {
2490  // not implemented binary operators
2491  mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( QgsSQLStatement::BINARY_OPERATOR_TEXT[op] );
2492  return QDomElement();
2493  }
2494 
2495  QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2496 
2498  {
2499  if ( op == QgsSQLStatement::boILike )
2500  boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2501 
2502  // setup wildCards to <ogc:PropertyIsLike>
2503  boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2504  boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2505  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2506  boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2507  else
2508  boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2509  }
2510 
2511  boElem.appendChild( leftElem );
2512  boElem.appendChild( rightElem );
2513  return boElem;
2514 }
2515 
2516 
2518 {
2519  QString value;
2520  switch ( node->value().type() )
2521  {
2522  case QVariant::Int:
2523  value = QString::number( node->value().toInt() );
2524  break;
2525  case QVariant::LongLong:
2526  value = QString::number( node->value().toLongLong() );
2527  break;
2528  case QVariant::Double:
2529  value = qgsDoubleToString( node->value().toDouble() );
2530  break;
2531  case QVariant::String:
2532  value = node->value().toString();
2533  break;
2534 
2535  default:
2536  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( node->value().type() );
2537  return QDomElement();
2538  }
2539 
2540  QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2541  litElem.appendChild( mDoc.createTextNode( value ) );
2542  return litElem;
2543 }
2544 
2545 
2547 {
2548  QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2549  if ( node->tableName().isEmpty() || mLayerProperties.size() == 1 )
2550  propElem.appendChild( mDoc.createTextNode( node->name() ) );
2551  else
2552  {
2553  QString tableName( mMapTableAliasToNames[node->tableName()] );
2554  if ( mMapUnprefixedTypenameToPrefixedTypename.contains( tableName ) )
2555  tableName = mMapUnprefixedTypenameToPrefixedTypename[tableName];
2556  propElem.appendChild( mDoc.createTextNode( tableName + "/" + node->name() ) );
2557  }
2558  return propElem;
2559 }
2560 
2562 {
2563  if ( node->list()->list().size() == 1 )
2564  return toOgcFilter( node->list()->list()[0] );
2565 
2566  QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2567  QDomElement leftNode = toOgcFilter( node->node() );
2568 
2569  const auto constList = node->list()->list();
2570  for ( QgsSQLStatement::Node *n : constList )
2571  {
2572  QDomElement listNode = toOgcFilter( n );
2573  if ( !mErrorMessage.isEmpty() )
2574  return QDomElement();
2575 
2576  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2577  eqElem.appendChild( leftNode.cloneNode() );
2578  eqElem.appendChild( listNode );
2579 
2580  orElem.appendChild( eqElem );
2581  }
2582 
2583  if ( node->isNotIn() )
2584  {
2585  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2586  notElem.appendChild( orElem );
2587  return notElem;
2588  }
2589 
2590  return orElem;
2591 }
2592 
2594 {
2595  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsBetween" );
2596  elem.appendChild( toOgcFilter( node->node() ) );
2597  QDomElement lowerBoundary = mDoc.createElement( mFilterPrefix + ":LowerBoundary" );
2598  lowerBoundary.appendChild( toOgcFilter( node->minVal() ) );
2599  elem.appendChild( lowerBoundary );
2600  QDomElement upperBoundary = mDoc.createElement( mFilterPrefix + ":UpperBoundary" );
2601  upperBoundary.appendChild( toOgcFilter( node->maxVal() ) );
2602  elem.appendChild( upperBoundary );
2603 
2604  if ( node->isNotBetween() )
2605  {
2606  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2607  notElem.appendChild( elem );
2608  return notElem;
2609  }
2610 
2611  return elem;
2612 }
2613 
2614 static QString mapBinarySpatialToOgc( const QString &name )
2615 {
2616  QString nameCompare( name );
2617  if ( name.size() > 3 && name.midRef( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2618  nameCompare = name.mid( 3 );
2619  QStringList spatialOps;
2620  spatialOps << QStringLiteral( "BBOX" ) << QStringLiteral( "Intersects" ) << QStringLiteral( "Contains" ) << QStringLiteral( "Crosses" ) << QStringLiteral( "Equals" )
2621  << QStringLiteral( "Disjoint" ) << QStringLiteral( "Overlaps" ) << QStringLiteral( "Touches" ) << QStringLiteral( "Within" );
2622  const auto constSpatialOps = spatialOps;
2623  for ( QString op : constSpatialOps )
2624  {
2625  if ( nameCompare.compare( op, Qt::CaseInsensitive ) == 0 )
2626  return op;
2627  }
2628  return QString();
2629 }
2630 
2631 static QString mapTernarySpatialToOgc( const QString &name )
2632 {
2633  QString nameCompare( name );
2634  if ( name.size() > 3 && name.midRef( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2635  nameCompare = name.mid( 3 );
2636  if ( nameCompare.compare( QLatin1String( "DWithin" ), Qt::CaseInsensitive ) == 0 )
2637  return QStringLiteral( "DWithin" );
2638  if ( nameCompare.compare( QLatin1String( "Beyond" ), Qt::CaseInsensitive ) == 0 )
2639  return QStringLiteral( "Beyond" );
2640  return QString();
2641 }
2642 
2643 QString QgsOgcUtilsSQLStatementToFilter::getGeometryColumnSRSName( const QgsSQLStatement::Node *node )
2644 {
2645  if ( node->nodeType() != QgsSQLStatement::ntColumnRef )
2646  return QString();
2647 
2648  const QgsSQLStatement::NodeColumnRef *col = static_cast<const QgsSQLStatement::NodeColumnRef *>( node );
2649  if ( !col->tableName().isEmpty() )
2650  {
2651  const auto constMLayerProperties = mLayerProperties;
2652  for ( QgsOgcUtils::LayerProperties prop : constMLayerProperties )
2653  {
2654  if ( prop.mName.compare( mMapTableAliasToNames[col->tableName()], Qt::CaseInsensitive ) == 0 &&
2655  prop.mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
2656  {
2657  return prop.mSRSName;
2658  }
2659  }
2660  }
2661  if ( !mLayerProperties.empty() &&
2662  mLayerProperties.at( 0 ).mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
2663  {
2664  return mLayerProperties.at( 0 ).mSRSName;
2665  }
2666  return QString();
2667 }
2668 
2669 bool QgsOgcUtilsSQLStatementToFilter::processSRSName( const QgsSQLStatement::NodeFunction *mainNode,
2670  QList<QgsSQLStatement::Node *> args,
2671  bool lastArgIsSRSName,
2672  QString &srsName,
2673  bool &axisInversion )
2674 {
2675  srsName = mCurrentSRSName;
2676  axisInversion = mInvertAxisOrientation;
2677 
2678  if ( lastArgIsSRSName )
2679  {
2680  QgsSQLStatement::Node *lastArg = args[ args.size() - 1 ];
2681  if ( lastArg->nodeType() != QgsSQLStatement::ntLiteral )
2682  {
2683  mErrorMessage = QObject::tr( "%1: Last argument must be string or integer literal" ).arg( mainNode->name() );
2684  return false;
2685  }
2686  const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( lastArg );
2687  if ( lit->value().type() == QVariant::Int )
2688  {
2689  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2690  {
2691  srsName = "EPSG:" + QString::number( lit->value().toInt() );
2692  }
2693  else
2694  {
2695  srsName = "urn:ogc:def:crs:EPSG::" + QString::number( lit->value().toInt() );
2696  }
2697  }
2698  else
2699  {
2700  srsName = lit->value().toString();
2701  if ( srsName.startsWith( QLatin1String( "EPSG:" ), Qt::CaseInsensitive ) )
2702  return true;
2703  }
2704  }
2705 
2707  if ( !srsName.isEmpty() )
2709  if ( crs.isValid() )
2710  {
2711  if ( mHonourAxisOrientation && crs.hasAxisInverted() )
2712  {
2713  axisInversion = !axisInversion;
2714  }
2715  }
2716 
2717  return true;
2718 }
2719 
2721 {
2722  // ST_GeometryFromText
2723  if ( node->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 )
2724  {
2725  QList<QgsSQLStatement::Node *> args = node->args()->list();
2726  if ( args.size() != 1 && args.size() != 2 )
2727  {
2728  mErrorMessage = QObject::tr( "Function %1 should have 1 or 2 arguments" ).arg( node->name() );
2729  return QDomElement();
2730  }
2731 
2732  QgsSQLStatement::Node *firstFnArg = args[0];
2733  if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
2734  {
2735  mErrorMessage = QObject::tr( "%1: First argument must be string literal" ).arg( node->name() );
2736  return QDomElement();
2737  }
2738 
2739  QString srsName;
2740  bool axisInversion;
2741  if ( ! processSRSName( node, args, args.size() == 2, srsName, axisInversion ) )
2742  {
2743  return QDomElement();
2744  }
2745 
2746  QString wkt = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
2747  QgsGeometry geom = QgsGeometry::fromWkt( wkt );
2748  QDomElement geomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, srsName, axisInversion,
2749  QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2750  mGeomId ++;
2751  if ( geomElem.isNull() )
2752  {
2753  mErrorMessage = QObject::tr( "%1: invalid WKT" ).arg( node->name() );
2754  return QDomElement();
2755  }
2756  mGMLUsed = true;
2757  return geomElem;
2758  }
2759 
2760  // ST_MakeEnvelope
2761  if ( node->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 )
2762  {
2763  QList<QgsSQLStatement::Node *> args = node->args()->list();
2764  if ( args.size() != 4 && args.size() != 5 )
2765  {
2766  mErrorMessage = QObject::tr( "Function %1 should have 4 or 5 arguments" ).arg( node->name() );
2767  return QDomElement();
2768  }
2769 
2770  QgsRectangle rect;
2771 
2772  for ( int i = 0; i < 4; i++ )
2773  {
2774  QgsSQLStatement::Node *arg = args[i];
2775  if ( arg->nodeType() != QgsSQLStatement::ntLiteral )
2776  {
2777  mErrorMessage = QObject::tr( "%1: Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
2778  return QDomElement();
2779  }
2780  const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( arg );
2781  double val = 0.0;
2782  if ( lit->value().type() == QVariant::Int )
2783  val = lit->value().toInt();
2784  else if ( lit->value().type() == QVariant::LongLong )
2785  val = lit->value().toLongLong();
2786  else if ( lit->value().type() == QVariant::Double )
2787  val = lit->value().toDouble();
2788  else
2789  {
2790  mErrorMessage = QObject::tr( "%1 Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
2791  return QDomElement();
2792  }
2793  if ( i == 0 )
2794  rect.setXMinimum( val );
2795  else if ( i == 1 )
2796  rect.setYMinimum( val );
2797  else if ( i == 2 )
2798  rect.setXMaximum( val );
2799  else
2800  rect.setYMaximum( val );
2801  }
2802 
2803  QString srsName;
2804  bool axisInversion;
2805  if ( ! processSRSName( node, args, args.size() == 5, srsName, axisInversion ) )
2806  {
2807  return QDomElement();
2808  }
2809 
2810  mGMLUsed = true;
2811 
2812  return ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
2813  QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, srsName, axisInversion, 15 ) :
2814  QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, srsName, axisInversion, 15 );
2815  }
2816 
2817  // ST_GeomFromGML
2818  if ( node->name().compare( QLatin1String( "ST_GeomFromGML" ), Qt::CaseInsensitive ) == 0 )
2819  {
2820  QList<QgsSQLStatement::Node *> args = node->args()->list();
2821  if ( args.size() != 1 )
2822  {
2823  mErrorMessage = QObject::tr( "Function %1 should have 1 argument" ).arg( node->name() );
2824  return QDomElement();
2825  }
2826 
2827  QgsSQLStatement::Node *firstFnArg = args[0];
2828  if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
2829  {
2830  mErrorMessage = QObject::tr( "%1: Argument must be string literal" ).arg( node->name() );
2831  return QDomElement();
2832  }
2833 
2834  QDomDocument geomDoc;
2835  QString gml = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
2836  if ( !geomDoc.setContent( gml, true ) )
2837  {
2838  mErrorMessage = QObject::tr( "ST_GeomFromGML: unable to parse XML" );
2839  return QDomElement();
2840  }
2841 
2842  QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
2843  mGMLUsed = true;
2844  return geomNode.toElement();
2845  }
2846 
2847  // Binary geometry operators
2848  QString ogcName( mapBinarySpatialToOgc( node->name() ) );
2849  if ( !ogcName.isEmpty() )
2850  {
2851  QList<QgsSQLStatement::Node *> args = node->args()->list();
2852  if ( args.size() != 2 )
2853  {
2854  mErrorMessage = QObject::tr( "Function %1 should have 2 arguments" ).arg( node->name() );
2855  return QDomElement();
2856  }
2857 
2858  for ( int i = 0; i < 2; i ++ )
2859  {
2860  if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
2861  ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
2862  static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
2863  {
2864  mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
2865  break;
2866  }
2867  }
2868 
2869  //if( ogcName == "Intersects" && mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2870  // ogcName = "Intersect";
2871  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + ogcName );
2872  const auto constArgs = args;
2873  for ( QgsSQLStatement::Node *n : constArgs )
2874  {
2875  QDomElement childElem = toOgcFilter( n );
2876  if ( !mErrorMessage.isEmpty() )
2877  {
2878  mCurrentSRSName.clear();
2879  return QDomElement();
2880  }
2881 
2882  funcElem.appendChild( childElem );
2883  }
2884 
2885  mCurrentSRSName.clear();
2886  return funcElem;
2887  }
2888 
2889  ogcName = mapTernarySpatialToOgc( node->name() );
2890  if ( !ogcName.isEmpty() )
2891  {
2892  QList<QgsSQLStatement::Node *> args = node->args()->list();
2893  if ( args.size() != 3 )
2894  {
2895  mErrorMessage = QObject::tr( "Function %1 should have 3 arguments" ).arg( node->name() );
2896  return QDomElement();
2897  }
2898 
2899  for ( int i = 0; i < 2; i ++ )
2900  {
2901  if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
2902  ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
2903  static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
2904  {
2905  mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
2906  break;
2907  }
2908  }
2909 
2910  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + node->name().mid( 3 ) );
2911  for ( int i = 0; i < 2; i++ )
2912  {
2913  QDomElement childElem = toOgcFilter( args[i] );
2914  if ( !mErrorMessage.isEmpty() )
2915  {
2916  mCurrentSRSName.clear();
2917  return QDomElement();
2918  }
2919 
2920  funcElem.appendChild( childElem );
2921  }
2922  mCurrentSRSName.clear();
2923 
2924  QgsSQLStatement::Node *distanceNode = args[2];
2925  if ( distanceNode->nodeType() != QgsSQLStatement::ntLiteral )
2926  {
2927  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() );
2928  return QDomElement();
2929  }
2930  const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( distanceNode );
2931  if ( lit->value().isNull() )
2932  {
2933  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() );
2934  return QDomElement();
2935  }
2936  QString distance;
2937  QString unit( QStringLiteral( "m" ) );
2938  switch ( lit->value().type() )
2939  {
2940  case QVariant::Int:
2941  distance = QString::number( lit->value().toInt() );
2942  break;
2943  case QVariant::LongLong:
2944  distance = QString::number( lit->value().toLongLong() );
2945  break;
2946  case QVariant::Double:
2947  distance = qgsDoubleToString( lit->value().toDouble() );
2948  break;
2949  case QVariant::String:
2950  {
2951  distance = lit->value().toString();
2952  for ( int i = 0; i < distance.size(); i++ )
2953  {
2954  if ( !( ( distance[i] >= '0' && distance[i] <= '9' ) || distance[i] == '-' || distance[i] == '.' || distance[i] == 'e' || distance[i] == 'E' ) )
2955  {
2956  unit = distance.mid( i ).trimmed();
2957  distance = distance.mid( 0, i );
2958  break;
2959  }
2960  }
2961  break;
2962  }
2963 
2964  default:
2965  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( lit->value().type() );
2966  return QDomElement();
2967  }
2968 
2969  QDomElement distanceElem = mDoc.createElement( mFilterPrefix + ":Distance" );
2970  if ( mFilterVersion == QgsOgcUtils::FILTER_FES_2_0 )
2971  distanceElem.setAttribute( QStringLiteral( "uom" ), unit );
2972  else
2973  distanceElem.setAttribute( QStringLiteral( "unit" ), unit );
2974  distanceElem.appendChild( mDoc.createTextNode( distance ) );
2975  funcElem.appendChild( distanceElem );
2976  return funcElem;
2977  }
2978 
2979  // Other function
2980  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
2981  funcElem.setAttribute( QStringLiteral( "name" ), node->name() );
2982  const auto constList = node->args()->list();
2983  for ( QgsSQLStatement::Node *n : constList )
2984  {
2985  QDomElement childElem = toOgcFilter( n );
2986  if ( !mErrorMessage.isEmpty() )
2987  return QDomElement();
2988 
2989  funcElem.appendChild( childElem );
2990  }
2991  return funcElem;
2992 }
2993 
2995  const QString &leftTable )
2996 {
2997  QgsSQLStatement::Node *onExpr = node->onExpr();
2998  if ( onExpr )
2999  {
3000  return toOgcFilter( onExpr );
3001  }
3002 
3003  QList<QDomElement> listElem;
3004  const auto constUsingColumns = node->usingColumns();
3005  for ( const QString &columnName : constUsingColumns )
3006  {
3007  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
3008  QDomElement propElem1 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3009  propElem1.appendChild( mDoc.createTextNode( leftTable + "/" + columnName ) );
3010  eqElem.appendChild( propElem1 );
3011  QDomElement propElem2 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3012  propElem2.appendChild( mDoc.createTextNode( node->tableDef()->name() + "/" + columnName ) );
3013  eqElem.appendChild( propElem2 );
3014  listElem.append( eqElem );
3015  }
3016 
3017  if ( listElem.size() == 1 )
3018  {
3019  return listElem[0];
3020  }
3021  else if ( listElem.size() > 1 )
3022  {
3023  QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3024  const auto constListElem = listElem;
3025  for ( const QDomElement &elem : constListElem )
3026  {
3027  andElem.appendChild( elem );
3028  }
3029  return andElem;
3030  }
3031 
3032  return QDomElement();
3033 }
3034 
3035 void QgsOgcUtilsSQLStatementToFilter::visit( const QgsSQLStatement::NodeTableDef *node )
3036 {
3037  if ( node->alias().isEmpty() )
3038  {
3039  mMapTableAliasToNames[ node->name()] = node->name();
3040  }
3041  else
3042  {
3043  mMapTableAliasToNames[ node->alias()] = node->name();
3044  }
3045 }
3046 
3048 {
3049  QList<QDomElement> listElem;
3050 
3051  if ( mFilterVersion != QgsOgcUtils::FILTER_FES_2_0 &&
3052  ( node->tables().size() != 1 || !node->joins().empty() ) )
3053  {
3054  mErrorMessage = QObject::tr( "Joins are only supported with WFS 2.0" );
3055  return QDomElement();
3056  }
3057 
3058  // Register all table name aliases
3059  const auto constTables = node->tables();
3060  for ( QgsSQLStatement::NodeTableDef *table : constTables )
3061  {
3062  visit( table );
3063  }
3064  const auto constJoins = node->joins();
3065  for ( QgsSQLStatement::NodeJoin *join : constJoins )
3066  {
3067  visit( join->tableDef() );
3068  }
3069 
3070  // Process JOIN conditions
3071  QList< QgsSQLStatement::NodeTableDef *> nodeTables = node->tables();
3072  QString leftTable = nodeTables.at( nodeTables.length() - 1 )->name();
3073  for ( QgsSQLStatement::NodeJoin *join : constJoins )
3074  {
3075  QDomElement joinElem = toOgcFilter( join, leftTable );
3076  if ( !mErrorMessage.isEmpty() )
3077  return QDomElement();
3078  listElem.append( joinElem );
3079  leftTable = join->tableDef()->name();
3080  }
3081 
3082  // Process WHERE conditions
3083  if ( node->where() )
3084  {
3085  QDomElement whereElem = toOgcFilter( node->where() );
3086  if ( !mErrorMessage.isEmpty() )
3087  return QDomElement();
3088  listElem.append( whereElem );
3089  }
3090 
3091  // Concatenate all conditions
3092  if ( listElem.size() == 1 )
3093  {
3094  return listElem[0];
3095  }
3096  else if ( listElem.size() > 1 )
3097  {
3098  QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3099  const auto constListElem = listElem;
3100  for ( const QDomElement &elem : constListElem )
3101  {
3102  andElem.appendChild( elem );
3103  }
3104  return andElem;
3105  }
3106 
3107  return QDomElement();
3108 }
3109 
3111  : mLayer( layer )
3112 {
3113  mPropertyName = QStringLiteral( "PropertyName" );
3114  mPrefix = QStringLiteral( "ogc" );
3115 
3116  if ( version == QgsOgcUtils::FILTER_FES_2_0 )
3117  {
3118  mPropertyName = QStringLiteral( "ValueReference" );
3119  mPrefix = QStringLiteral( "fes" );
3120  }
3121 }
3122 
3124 {
3125  if ( element.isNull() )
3126  return nullptr;
3127 
3128  // check for binary operators
3129  if ( isBinaryOperator( element.tagName() ) )
3130  {
3131  return nodeBinaryOperatorFromOgcFilter( element );
3132  }
3133 
3134  // check for spatial operators
3135  if ( isSpatialOperator( element.tagName() ) )
3136  {
3137  return nodeSpatialOperatorFromOgcFilter( element );
3138  }
3139 
3140  // check for other OGC operators, convert them to expressions
3141  if ( element.tagName() == QLatin1String( "Not" ) )
3142  {
3143  return nodeNotFromOgcFilter( element );
3144  }
3145  else if ( element.tagName() == QLatin1String( "PropertyIsNull" ) )
3146  {
3147  return nodePropertyIsNullFromOgcFilter( element );
3148  }
3149  else if ( element.tagName() == QLatin1String( "Literal" ) )
3150  {
3151  return nodeLiteralFromOgcFilter( element );
3152  }
3153  else if ( element.tagName() == QLatin1String( "Function" ) )
3154  {
3155  return nodeFunctionFromOgcFilter( element );
3156  }
3157  else if ( element.tagName() == mPropertyName )
3158  {
3159  return nodeColumnRefFromOgcFilter( element );
3160  }
3161  else if ( element.tagName() == QLatin1String( "PropertyIsBetween" ) )
3162  {
3163  return nodeIsBetweenFromOgcFilter( element );
3164  }
3165 
3166  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() );
3167  return nullptr;
3168 }
3169 
3171 {
3172  if ( element.isNull() )
3173  return nullptr;
3174 
3175  int op = binaryOperatorFromTagName( element.tagName() );
3176  if ( op < 0 )
3177  {
3178  mErrorMessage = QObject::tr( "'%1' binary operator not supported." ).arg( element.tagName() );
3179  return nullptr;
3180  }
3181 
3182  if ( op == QgsExpressionNodeBinaryOperator::boLike && element.hasAttribute( QStringLiteral( "matchCase" ) ) && element.attribute( QStringLiteral( "matchCase" ) ) == QLatin1String( "false" ) )
3183  {
3185  }
3186 
3187  QDomElement operandElem = element.firstChildElement();
3188  std::unique_ptr<QgsExpressionNode> expr( nodeFromOgcFilter( operandElem ) );
3189 
3190  if ( !expr )
3191  {
3192  mErrorMessage = QObject::tr( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
3193  return nullptr;
3194  }
3195 
3196  std::unique_ptr<QgsExpressionNode> leftOp( expr->clone() );
3197  for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
3198  {
3199  std::unique_ptr<QgsExpressionNode> opRight( nodeFromOgcFilter( operandElem ) );
3200  if ( !opRight )
3201  {
3202  mErrorMessage = QObject::tr( "invalid right operand for '%1' binary operator" ).arg( element.tagName() );
3203  return nullptr;
3204  }
3205 
3207  {
3208  QString wildCard;
3209  if ( element.hasAttribute( QStringLiteral( "wildCard" ) ) )
3210  {
3211  wildCard = element.attribute( QStringLiteral( "wildCard" ) );
3212  }
3213  QString singleChar;
3214  if ( element.hasAttribute( QStringLiteral( "singleChar" ) ) )
3215  {
3216  singleChar = element.attribute( QStringLiteral( "singleChar" ) );
3217  }
3218  QString escape = QStringLiteral( "\\" );
3219  if ( element.hasAttribute( QStringLiteral( "escape" ) ) )
3220  {
3221  escape = element.attribute( QStringLiteral( "escape" ) );
3222  }
3223  if ( element.hasAttribute( QStringLiteral( "escapeChar" ) ) )
3224  {
3225  escape = element.attribute( QStringLiteral( "escapeChar" ) );
3226  }
3227  // replace
3228  QString oprValue = static_cast<const QgsExpressionNodeLiteral *>( opRight.get() )->value().toString();
3229  if ( !wildCard.isEmpty() && wildCard != QLatin1String( "%" ) )
3230  {
3231  oprValue.replace( '%', QLatin1String( "\\%" ) );
3232  if ( oprValue.startsWith( wildCard ) )
3233  {
3234  oprValue.replace( 0, 1, QStringLiteral( "%" ) );
3235  }
3236  QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( wildCard ) + ")" );
3237  int pos = 0;
3238  while ( ( pos = rx.indexIn( oprValue, pos ) ) != -1 )
3239  {
3240  oprValue.replace( pos + 1, 1, QStringLiteral( "%" ) );
3241  pos += 1;
3242  }
3243  oprValue.replace( escape + wildCard, wildCard );
3244  }
3245  if ( !singleChar.isEmpty() && singleChar != QLatin1String( "_" ) )
3246  {
3247  oprValue.replace( '_', QLatin1String( "\\_" ) );
3248  if ( oprValue.startsWith( singleChar ) )
3249  {
3250  oprValue.replace( 0, 1, QStringLiteral( "_" ) );
3251  }
3252  QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( singleChar ) + ")" );
3253  int pos = 0;
3254  while ( ( pos = rx.indexIn( oprValue, pos ) ) != -1 )
3255  {
3256  oprValue.replace( pos + 1, 1, QStringLiteral( "_" ) );
3257  pos += 1;
3258  }
3259  oprValue.replace( escape + singleChar, singleChar );
3260  }
3261  if ( !escape.isEmpty() && escape != QLatin1String( "\\" ) )
3262  {
3263  oprValue.replace( escape + escape, escape );
3264  }
3265  opRight.reset( new QgsExpressionNodeLiteral( oprValue ) );
3266  }
3267 
3268  expr.reset( new QgsExpressionNodeBinaryOperator( static_cast< QgsExpressionNodeBinaryOperator::BinaryOperator >( op ), expr.release(), opRight.release() ) );
3269  }
3270 
3271  if ( expr == leftOp )
3272  {
3273  mErrorMessage = QObject::tr( "only one operand for '%1' binary operator" ).arg( element.tagName() );
3274  return nullptr;
3275  }
3276 
3277  return dynamic_cast< QgsExpressionNodeBinaryOperator * >( expr.release() );
3278 }
3279 
3280 
3282 {
3283  // we are exploiting the fact that our function names are the same as the XML tag names
3284  const int opIdx = QgsExpression::functionIndex( element.tagName().toLower() );
3285 
3286  std::unique_ptr<QgsExpressionNode::NodeList> gml2Args( new QgsExpressionNode::NodeList() );
3287  QDomElement childElem = element.firstChildElement();
3288  QString gml2Str;
3289  while ( !childElem.isNull() && gml2Str.isEmpty() )
3290  {
3291  if ( childElem.tagName() != mPropertyName )
3292  {
3293  QTextStream gml2Stream( &gml2Str );
3294  childElem.save( gml2Stream, 0 );
3295  }
3296  childElem = childElem.nextSiblingElement();
3297  }
3298  if ( !gml2Str.isEmpty() )
3299  {
3300  gml2Args->append( new QgsExpressionNodeLiteral( QVariant( gml2Str.remove( '\n' ) ) ) );
3301  }
3302  else
3303  {
3304  mErrorMessage = QObject::tr( "No OGC Geometry found" );
3305  return nullptr;
3306  }
3307 
3308  std::unique_ptr<QgsExpressionNode::NodeList> opArgs( new QgsExpressionNode::NodeList() );
3309  opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "$geometry" ) ), new QgsExpressionNode::NodeList() ) );
3310  opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "geomFromGML" ) ), gml2Args.release() ) );
3311 
3312  return new QgsExpressionNodeFunction( opIdx, opArgs.release() );
3313 }
3314 
3316 {
3317  if ( element.isNull() || element.tagName() != mPropertyName )
3318  {
3319  mErrorMessage = QObject::tr( "%1:PropertyName expected, got %2" ).arg( mPrefix, element.tagName() );
3320  return nullptr;
3321  }
3322 
3323  return new QgsExpressionNodeColumnRef( element.firstChild().nodeValue() );
3324 }
3325 
3327 {
3328  if ( element.isNull() || element.tagName() != QLatin1String( "Literal" ) )
3329  {
3330  mErrorMessage = QObject::tr( "%1:Literal expected, got %2" ).arg( mPrefix, element.tagName() );
3331  return nullptr;
3332  }
3333 
3334  std::unique_ptr<QgsExpressionNode> root;
3335 
3336  // the literal content can have more children (e.g. CDATA section, text, ...)
3337  QDomNode childNode = element.firstChild();
3338  while ( !childNode.isNull() )
3339  {
3340  std::unique_ptr<QgsExpressionNode> operand;
3341 
3342  if ( childNode.nodeType() == QDomNode::ElementNode )
3343  {
3344  // found a element node (e.g. PropertyName), convert it
3345  const QDomElement operandElem = childNode.toElement();
3346  operand.reset( nodeFromOgcFilter( operandElem ) );
3347  if ( !operand )
3348  {
3349  mErrorMessage = QObject::tr( "'%1' is an invalid or not supported content for %2:Literal" ).arg( operandElem.tagName(), mPrefix );
3350  return nullptr;
3351  }
3352  }
3353  else
3354  {
3355  // probably a text/CDATA node
3356  QVariant value = childNode.nodeValue();
3357 
3358  bool converted = false;
3359 
3360  // try to convert the node content to corresponding field type if possible
3361  if ( mLayer )
3362  {
3363  QDomElement propertyNameElement = element.previousSiblingElement( mPropertyName );
3364  if ( propertyNameElement.isNull() || propertyNameElement.tagName() != mPropertyName )
3365  {
3366  propertyNameElement = element.nextSiblingElement( mPropertyName );
3367  }
3368  if ( !propertyNameElement.isNull() || propertyNameElement.tagName() == mPropertyName )
3369  {
3370  const int fieldIndex = mLayer->fields().indexOf( propertyNameElement.firstChild().nodeValue() );
3371  if ( fieldIndex != -1 )
3372  {
3373  QgsField field = mLayer->fields().field( propertyNameElement.firstChild().nodeValue() );
3374  field.convertCompatible( value );
3375  converted = true;
3376  }
3377  }
3378  }
3379  if ( !converted )
3380  {
3381  // try to convert the node content to number if possible,
3382  // otherwise let's use it as string
3383  bool ok;
3384  const double d = value.toDouble( &ok );
3385  if ( ok )
3386  value = d;
3387  }
3388 
3389  operand.reset( new QgsExpressionNodeLiteral( value ) );
3390  if ( !operand )
3391  continue;
3392  }
3393 
3394  // use the concat operator to merge the ogc:Literal children
3395  if ( !root )
3396  {
3397  root = std::move( operand );
3398  }
3399  else
3400  {
3401  root.reset( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boConcat, root.release(), operand.release() ) );
3402  }
3403 
3404  childNode = childNode.nextSibling();
3405  }
3406 
3407  if ( root )
3408  return root.release();
3409 
3410  return nullptr;
3411 }
3412 
3414 {
3415  if ( element.tagName() != QLatin1String( "Not" ) )
3416  return nullptr;
3417 
3418  const QDomElement operandElem = element.firstChildElement();
3419  std::unique_ptr<QgsExpressionNode> operand( nodeFromOgcFilter( operandElem ) );
3420  if ( !operand )
3421  {
3422  mErrorMessage = QObject::tr( "invalid operand for '%1' unary operator" ).arg( element.tagName() );
3423  return nullptr;
3424  }
3425 
3427 }
3428 
3430 {
3431  // convert ogc:PropertyIsNull to IS operator with NULL right operand
3432  if ( element.tagName() != QLatin1String( "PropertyIsNull" ) )
3433  {
3434  return nullptr;
3435  }
3436 
3437  const QDomElement operandElem = element.firstChildElement();
3438  std::unique_ptr<QgsExpressionNode> opLeft( nodeFromOgcFilter( operandElem ) );
3439  if ( !opLeft )
3440  return nullptr;
3441 
3442  std::unique_ptr<QgsExpressionNode> opRight( new QgsExpressionNodeLiteral( QVariant() ) );
3443  return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boIs, opLeft.release(), opRight.release() );
3444 }
3445 
3447 {
3448  if ( element.isNull() || element.tagName() != QLatin1String( "Function" ) )
3449  {
3450  mErrorMessage = QObject::tr( "%1:Function expected, got %2" ).arg( mPrefix, element.tagName() );
3451  return nullptr;
3452  }
3453 
3454  for ( int i = 0; i < QgsExpression::Functions().size(); i++ )
3455  {
3456  const QgsExpressionFunction *funcDef = QgsExpression::Functions()[i];
3457 
3458  if ( element.attribute( QStringLiteral( "name" ) ) != funcDef->name() )
3459  continue;
3460 
3461  std::unique_ptr<QgsExpressionNode::NodeList> args( new QgsExpressionNode::NodeList() );
3462 
3463  QDomElement operandElem = element.firstChildElement();
3464  while ( !operandElem.isNull() )
3465  {
3466  std::unique_ptr<QgsExpressionNode> op( nodeFromOgcFilter( operandElem ) );
3467  if ( !op )
3468  {
3469  return nullptr;
3470  }
3471  args->append( op.release() );
3472 
3473  operandElem = operandElem.nextSiblingElement();
3474  }
3475 
3476  return new QgsExpressionNodeFunction( i, args.release() );
3477  }
3478 
3479  return nullptr;
3480 }
3481 
3483 {
3484  // <ogc:PropertyIsBetween> encode a Range check
3485  std::unique_ptr<QgsExpressionNode> operand;
3486  std::unique_ptr<QgsExpressionNode> lowerBound;
3487  std::unique_ptr<QgsExpressionNode> upperBound;
3488 
3489  QDomElement operandElem = element.firstChildElement();
3490  while ( !operandElem.isNull() )
3491  {
3492  if ( operandElem.tagName() == QLatin1String( "LowerBoundary" ) )
3493  {
3494  QDomElement lowerBoundElem = operandElem.firstChildElement();
3495  lowerBound.reset( nodeFromOgcFilter( lowerBoundElem ) );
3496  }
3497  else if ( operandElem.tagName() == QLatin1String( "UpperBoundary" ) )
3498  {
3499  QDomElement upperBoundElem = operandElem.firstChildElement();
3500  upperBound.reset( nodeFromOgcFilter( upperBoundElem ) );
3501  }
3502  else
3503  {
3504  // <ogc:expression>
3505  operand.reset( nodeFromOgcFilter( operandElem ) );
3506  }
3507 
3508  if ( operand && lowerBound && upperBound )
3509  break;
3510 
3511  operandElem = operandElem.nextSiblingElement();
3512  }
3513 
3514  if ( !operand || !lowerBound || !upperBound )
3515  {
3516  mErrorMessage = QObject::tr( "missing some required sub-elements in %1:PropertyIsBetween" ).arg( mPrefix );
3517  return nullptr;
3518  }
3519 
3520  std::unique_ptr<QgsExpressionNode> leOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boLE, operand->clone(), upperBound.release() ) );
3521  std::unique_ptr<QgsExpressionNode> geOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boGE, operand.release(), lowerBound.release() ) );
3522  return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boAnd, geOperator.release(), leOperator.release() );
3523 }
3524 
3526 {
3527  return mErrorMessage;
3528 }
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.
#define OGC_NAMESPACE
Definition: qgsogcutils.cpp:42
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:316
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, =, +, ...)
QMap< QString, QString > QgsStringMap
Definition: qgis.h:694
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.
Q_GLOBAL_STATIC_WITH_ARGS(IntMap, BINARY_OPERATORS_TAG_NAMES_MAP,({ { QLatin1String("Or"), QgsExpressionNodeBinaryOperator::boOr }, { QLatin1String("And"), QgsExpressionNodeBinaryOperator::boAnd }, { QLatin1String("PropertyIsEqualTo"), QgsExpressionNodeBinaryOperator::boEQ }, { QLatin1String("PropertyIsNotEqualTo"), QgsExpressionNodeBinaryOperator::boNE }, { QLatin1String("PropertyIsLessThanOrEqualTo"), QgsExpressionNodeBinaryOperator::boLE }, { QLatin1String("PropertyIsGreaterThanOrEqualTo"), QgsExpressionNodeBinaryOperator::boGE }, { QLatin1String("PropertyIsLessThan"), QgsExpressionNodeBinaryOperator::boLT }, { QLatin1String("PropertyIsGreaterThan"), QgsExpressionNodeBinaryOperator::boGT }, { QLatin1String("PropertyIsLike"), QgsExpressionNodeBinaryOperator::boLike }, { QLatin1String("Add"), QgsExpressionNodeBinaryOperator::boPlus }, { QLatin1String("Sub"), QgsExpressionNodeBinaryOperator::boMinus }, { QLatin1String("Mul"), QgsExpressionNodeBinaryOperator::boMul }, { QLatin1String("Div"), QgsExpressionNodeBinaryOperator::boDiv }, })) static int binaryOperatorFromTagName(const QString &tagName)
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:763
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:275
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:49
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.
#define FES_NAMESPACE
Definition: qgsogcutils.cpp:43
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
#define GML32_NAMESPACE
Definition: qgsogcutils.cpp:41
QMap< QString, int > IntMap
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.
#define GML_NAMESPACE
Definition: qgsogcutils.cpp:40
int fnIndex() const
Returns the index of the node&#39;s function.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.