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