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