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