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