QGIS API Documentation  2.99.0-Master (6a61179)
qgsogcutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsogcutils.cpp
3  ---------------------
4  begin : March 2013
5  copyright : (C) 2013 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include "qgsogcutils.h"
16 
17 #include "qgsexpression.h"
18 #include "qgsexpressionprivate.h"
19 #include "qgsgeometry.h"
20 #include "qgswkbptr.h"
22 #include "qgsrectangle.h"
23 
24 #include <QColor>
25 #include <QStringList>
26 #include <QTextStream>
27 #include <QObject>
28 
29 #ifndef Q_OS_WIN
30 #include <netinet/in.h>
31 #else
32 #include <winsock.h>
33 #endif
34 
35 
36 static const QString GML_NAMESPACE = QStringLiteral( "http://www.opengis.net/gml" );
37 static const QString GML32_NAMESPACE = QStringLiteral( "http://www.opengis.net/gml/3.2" );
38 static const QString OGC_NAMESPACE = QStringLiteral( "http://www.opengis.net/ogc" );
39 static const QString FES_NAMESPACE = QStringLiteral( "http://www.opengis.net/fes/2.0" );
40 
42  QgsOgcUtils::GMLVersion gmlVersion,
43  QgsOgcUtils::FilterVersion filterVersion,
44  const QString& geometryName,
45  const QString& srsName,
46  bool honourAxisOrientation,
47  bool invertAxisOrientation )
48  : mDoc( doc )
49  , mGMLUsed( false )
50  , mGMLVersion( gmlVersion )
51  , mFilterVersion( filterVersion )
52  , mGeometryName( geometryName )
53  , mSrsName( srsName )
54  , mInvertAxisOrientation( invertAxisOrientation )
55  , mFilterPrefix(( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "fes" : "ogc" )
56  , mPropertyName(( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "ValueReference" : "PropertyName" )
57  , mGeomId( 1 )
58 {
60  if ( !mSrsName.isEmpty() )
62  if ( crs.isValid() )
63  {
64  if ( honourAxisOrientation && crs.hasAxisInverted() )
65  {
66  mInvertAxisOrientation = !mInvertAxisOrientation;
67  }
68  }
69 }
70 
71 QgsGeometry QgsOgcUtils::geometryFromGML( const QDomNode& geometryNode )
72 {
73  QDomElement geometryTypeElement = geometryNode.toElement();
74  QString geomType = geometryTypeElement.tagName();
75 
76  if ( !( geomType == QLatin1String( "Point" ) || geomType == QLatin1String( "LineString" ) || geomType == QLatin1String( "Polygon" ) ||
77  geomType == QLatin1String( "MultiPoint" ) || geomType == QLatin1String( "MultiLineString" ) || geomType == QLatin1String( "MultiPolygon" ) ||
78  geomType == QLatin1String( "Box" ) || geomType == QLatin1String( "Envelope" ) ) )
79  {
80  QDomNode geometryChild = geometryNode.firstChild();
81  if ( geometryChild.isNull() )
82  {
83  return QgsGeometry();
84  }
85  geometryTypeElement = geometryChild.toElement();
86  geomType = geometryTypeElement.tagName();
87  }
88 
89  if ( !( geomType == QLatin1String( "Point" ) || geomType == QLatin1String( "LineString" ) || geomType == QLatin1String( "Polygon" ) ||
90  geomType == QLatin1String( "MultiPoint" ) || geomType == QLatin1String( "MultiLineString" ) || geomType == QLatin1String( "MultiPolygon" ) ||
91  geomType == QLatin1String( "Box" ) || geomType == QLatin1String( "Envelope" ) ) )
92  return QgsGeometry();
93 
94  if ( geomType == QLatin1String( "Point" ) )
95  {
96  return geometryFromGMLPoint( geometryTypeElement );
97  }
98  else if ( geomType == QLatin1String( "LineString" ) )
99  {
100  return geometryFromGMLLineString( geometryTypeElement );
101  }
102  else if ( geomType == QLatin1String( "Polygon" ) )
103  {
104  return geometryFromGMLPolygon( geometryTypeElement );
105  }
106  else if ( geomType == QLatin1String( "MultiPoint" ) )
107  {
108  return geometryFromGMLMultiPoint( geometryTypeElement );
109  }
110  else if ( geomType == QLatin1String( "MultiLineString" ) )
111  {
112  return geometryFromGMLMultiLineString( geometryTypeElement );
113  }
114  else if ( geomType == QLatin1String( "MultiPolygon" ) )
115  {
116  return geometryFromGMLMultiPolygon( geometryTypeElement );
117  }
118  else if ( geomType == QLatin1String( "Box" ) )
119  {
120  return QgsGeometry::fromRect( rectangleFromGMLBox( geometryTypeElement ) );
121  }
122  else if ( geomType == QLatin1String( "Envelope" ) )
123  {
124  return QgsGeometry::fromRect( rectangleFromGMLEnvelope( geometryTypeElement ) );
125  }
126  else //unknown type
127  {
128  return QgsGeometry();
129  }
130 }
131 
132 QgsGeometry QgsOgcUtils::geometryFromGML( const QString& xmlString )
133 {
134  // wrap the string into a root tag to have "gml" namespace (and also as a default namespace)
135  QString xml = QStringLiteral( "<tmp xmlns=\"%1\" xmlns:gml=\"%1\">%2</tmp>" ).arg( GML_NAMESPACE, xmlString );
136  QDomDocument doc;
137  if ( !doc.setContent( xml, true ) )
138  return QgsGeometry();
139 
140  return geometryFromGML( doc.documentElement().firstChildElement() );
141 }
142 
143 
144 QgsGeometry QgsOgcUtils::geometryFromGMLPoint( const QDomElement& geometryElement )
145 {
146  QgsPolyline pointCoordinate;
147 
148  QDomNodeList coordList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
149  if ( !coordList.isEmpty() )
150  {
151  QDomElement coordElement = coordList.at( 0 ).toElement();
152  if ( readGMLCoordinates( pointCoordinate, coordElement ) != 0 )
153  {
154  return QgsGeometry();
155  }
156  }
157  else
158  {
159  QDomNodeList posList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "pos" ) );
160  if ( posList.size() < 1 )
161  {
162  return QgsGeometry();
163  }
164  QDomElement posElement = posList.at( 0 ).toElement();
165  if ( readGMLPositions( pointCoordinate, posElement ) != 0 )
166  {
167  return QgsGeometry();
168  }
169  }
170 
171  if ( pointCoordinate.size() < 1 )
172  {
173  return QgsGeometry();
174  }
175 
176  QgsPolyline::const_iterator point_it = pointCoordinate.begin();
177  char e = htonl( 1 ) != 1;
178  double x = point_it->x();
179  double y = point_it->y();
180  int size = 1 + sizeof( int ) + 2 * sizeof( double );
181 
183  unsigned char* wkb = new unsigned char[size];
184 
185  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
186  memcpy( &( wkb )[wkbPosition], &e, 1 );
187  wkbPosition += 1;
188  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
189  wkbPosition += sizeof( int );
190  memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
191  wkbPosition += sizeof( double );
192  memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
193 
194  QgsGeometry g;
195  g.fromWkb( wkb, size );
196  return g;
197 }
198 
199 QgsGeometry QgsOgcUtils::geometryFromGMLLineString( const QDomElement& geometryElement )
200 {
201  QgsPolyline lineCoordinates;
202 
203  QDomNodeList coordList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
204  if ( !coordList.isEmpty() )
205  {
206  QDomElement coordElement = coordList.at( 0 ).toElement();
207  if ( readGMLCoordinates( lineCoordinates, coordElement ) != 0 )
208  {
209  return QgsGeometry();
210  }
211  }
212  else
213  {
214  QDomNodeList posList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
215  if ( posList.size() < 1 )
216  {
217  return QgsGeometry();
218  }
219  QDomElement posElement = posList.at( 0 ).toElement();
220  if ( readGMLPositions( lineCoordinates, posElement ) != 0 )
221  {
222  return QgsGeometry();
223  }
224  }
225 
226  char e = htonl( 1 ) != 1;
227  int size = 1 + 2 * sizeof( int ) + lineCoordinates.size() * 2 * sizeof( double );
228 
230  unsigned char* wkb = new unsigned char[size];
231 
232  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
233  double x, y;
234  int nPoints = lineCoordinates.size();
235 
236  //fill the contents into *wkb
237  memcpy( &( wkb )[wkbPosition], &e, 1 );
238  wkbPosition += 1;
239  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
240  wkbPosition += sizeof( int );
241  memcpy( &( wkb )[wkbPosition], &nPoints, sizeof( int ) );
242  wkbPosition += sizeof( int );
243 
244  QgsPolyline::const_iterator iter;
245  for ( iter = lineCoordinates.begin(); iter != lineCoordinates.end(); ++iter )
246  {
247  x = iter->x();
248  y = iter->y();
249  memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
250  wkbPosition += sizeof( double );
251  memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
252  wkbPosition += sizeof( double );
253  }
254 
255  QgsGeometry g;
256  g.fromWkb( wkb, size );
257  return g;
258 }
259 
260 QgsGeometry QgsOgcUtils::geometryFromGMLPolygon( const QDomElement& geometryElement )
261 {
262  //read all the coordinates (as QgsPoint) into memory. Each linear ring has an entry in the vector
263  QgsMultiPolyline ringCoordinates;
264 
265  //read coordinates for outer boundary
266  QgsPolyline exteriorPointList;
267  QDomNodeList outerBoundaryList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "outerBoundaryIs" ) );
268  if ( !outerBoundaryList.isEmpty() ) //outer ring is necessary
269  {
270  QDomElement coordinatesElement = outerBoundaryList.at( 0 ).firstChild().firstChild().toElement();
271  if ( coordinatesElement.isNull() )
272  {
273  return QgsGeometry();
274  }
275  if ( readGMLCoordinates( exteriorPointList, coordinatesElement ) != 0 )
276  {
277  return QgsGeometry();
278  }
279  ringCoordinates.push_back( exteriorPointList );
280 
281  //read coordinates for inner boundary
282  QDomNodeList innerBoundaryList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "innerBoundaryIs" ) );
283  for ( int i = 0; i < innerBoundaryList.size(); ++i )
284  {
285  QgsPolyline interiorPointList;
286  coordinatesElement = innerBoundaryList.at( i ).firstChild().firstChild().toElement();
287  if ( coordinatesElement.isNull() )
288  {
289  return QgsGeometry();
290  }
291  if ( readGMLCoordinates( interiorPointList, coordinatesElement ) != 0 )
292  {
293  return QgsGeometry();
294  }
295  ringCoordinates.push_back( interiorPointList );
296  }
297  }
298  else
299  {
300  //read coordinates for exterior
301  QDomNodeList exteriorList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "exterior" ) );
302  if ( exteriorList.size() < 1 ) //outer ring is necessary
303  {
304  return QgsGeometry();
305  }
306  QDomElement posElement = exteriorList.at( 0 ).firstChild().firstChild().toElement();
307  if ( posElement.isNull() )
308  {
309  return QgsGeometry();
310  }
311  if ( readGMLPositions( exteriorPointList, posElement ) != 0 )
312  {
313  return QgsGeometry();
314  }
315  ringCoordinates.push_back( exteriorPointList );
316 
317  //read coordinates for inner boundary
318  QDomNodeList interiorList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "interior" ) );
319  for ( int i = 0; i < interiorList.size(); ++i )
320  {
321  QgsPolyline interiorPointList;
322  QDomElement posElement = interiorList.at( i ).firstChild().firstChild().toElement();
323  if ( posElement.isNull() )
324  {
325  return QgsGeometry();
326  }
327  if ( readGMLPositions( interiorPointList, posElement ) != 0 )
328  {
329  return QgsGeometry();
330  }
331  ringCoordinates.push_back( interiorPointList );
332  }
333  }
334 
335  //calculate number of bytes to allocate
336  int nrings = ringCoordinates.size();
337  if ( nrings < 1 )
338  return QgsGeometry();
339 
340  int npoints = 0;//total number of points
341  for ( QgsMultiPolyline::const_iterator it = ringCoordinates.begin(); it != ringCoordinates.end(); ++it )
342  {
343  npoints += it->size();
344  }
345  int size = 1 + 2 * sizeof( int ) + nrings * sizeof( int ) + 2 * npoints * sizeof( double );
346 
348  unsigned char* wkb = new unsigned char[size];
349 
350  //char e = QgsApplication::endian();
351  char e = htonl( 1 ) != 1;
352  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
353  int nPointsInRing = 0;
354  double x, y;
355 
356  //fill the contents into *wkb
357  memcpy( &( wkb )[wkbPosition], &e, 1 );
358  wkbPosition += 1;
359  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
360  wkbPosition += sizeof( int );
361  memcpy( &( wkb )[wkbPosition], &nrings, sizeof( int ) );
362  wkbPosition += sizeof( int );
363  for ( QgsMultiPolyline::const_iterator it = ringCoordinates.begin(); it != ringCoordinates.end(); ++it )
364  {
365  nPointsInRing = it->size();
366  memcpy( &( wkb )[wkbPosition], &nPointsInRing, sizeof( int ) );
367  wkbPosition += sizeof( int );
368  //iterate through the string list converting the strings to x-/y- doubles
369  QgsPolyline::const_iterator iter;
370  for ( iter = it->begin(); iter != it->end(); ++iter )
371  {
372  x = iter->x();
373  y = iter->y();
374  //qWarning("currentCoordinate: " + QString::number(x) + " // " + QString::number(y));
375  memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
376  wkbPosition += sizeof( double );
377  memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
378  wkbPosition += sizeof( double );
379  }
380  }
381 
382  QgsGeometry g;
383  g.fromWkb( wkb, size );
384  return g;
385 }
386 
387 QgsGeometry QgsOgcUtils::geometryFromGMLMultiPoint( const QDomElement& geometryElement )
388 {
389  QgsPolyline pointList;
390  QgsPolyline currentPoint;
391  QDomNodeList pointMemberList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "pointMember" ) );
392  if ( pointMemberList.size() < 1 )
393  {
394  return QgsGeometry();
395  }
396  QDomNodeList pointNodeList;
397  // coordinates or pos element
398  QDomNodeList coordinatesList;
399  QDomNodeList posList;
400  for ( int i = 0; i < pointMemberList.size(); ++i )
401  {
402  //<Point> element
403  pointNodeList = pointMemberList.at( i ).toElement().elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "Point" ) );
404  if ( pointNodeList.size() < 1 )
405  {
406  continue;
407  }
408  //<coordinates> element
409  coordinatesList = pointNodeList.at( 0 ).toElement().elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
410  if ( !coordinatesList.isEmpty() )
411  {
412  currentPoint.clear();
413  if ( readGMLCoordinates( currentPoint, coordinatesList.at( 0 ).toElement() ) != 0 )
414  {
415  continue;
416  }
417  if ( currentPoint.size() < 1 )
418  {
419  continue;
420  }
421  pointList.push_back(( *currentPoint.begin() ) );
422  continue;
423  }
424  else
425  {
426  //<pos> element
427  posList = pointNodeList.at( 0 ).toElement().elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "pos" ) );
428  if ( posList.size() < 1 )
429  {
430  continue;
431  }
432  currentPoint.clear();
433  if ( readGMLPositions( currentPoint, posList.at( 0 ).toElement() ) != 0 )
434  {
435  continue;
436  }
437  if ( currentPoint.size() < 1 )
438  {
439  continue;
440  }
441  pointList.push_back(( *currentPoint.begin() ) );
442  }
443  }
444 
445  int nPoints = pointList.size(); //number of points
446  if ( nPoints < 1 )
447  return QgsGeometry();
448 
449  //calculate the required wkb size
450  int size = 1 + 2 * sizeof( int ) + pointList.size() * ( 2 * sizeof( double ) + 1 + sizeof( int ) );
451 
453  unsigned char* wkb = new unsigned char[size];
454 
455  //fill the wkb content
456  char e = htonl( 1 ) != 1;
457  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
458  double x, y;
459  memcpy( &( wkb )[wkbPosition], &e, 1 );
460  wkbPosition += 1;
461  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
462  wkbPosition += sizeof( int );
463  memcpy( &( wkb )[wkbPosition], &nPoints, sizeof( int ) );
464  wkbPosition += sizeof( int );
465  type = QgsWkbTypes::Point;
466  for ( QgsPolyline::const_iterator it = pointList.begin(); it != pointList.end(); ++it )
467  {
468  memcpy( &( wkb )[wkbPosition], &e, 1 );
469  wkbPosition += 1;
470  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
471  wkbPosition += sizeof( int );
472  x = it->x();
473  memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
474  wkbPosition += sizeof( double );
475  y = it->y();
476  memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
477  wkbPosition += sizeof( double );
478  }
479 
480  QgsGeometry g;
481  g.fromWkb( wkb, size );
482  return g;
483 }
484 
485 QgsGeometry QgsOgcUtils::geometryFromGMLMultiLineString( const QDomElement& geometryElement )
486 {
487  //geoserver has
488  //<gml:MultiLineString>
489  //<gml:lineStringMember>
490  //<gml:LineString>
491 
492  //mapserver has directly
493  //<gml:MultiLineString
494  //<gml:LineString
495 
496  QList< QgsPolyline > lineCoordinates; //first list: lines, second list: points of one line
497  QDomElement currentLineStringElement;
498  QDomNodeList currentCoordList;
499  QDomNodeList currentPosList;
500 
501  QDomNodeList lineStringMemberList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "lineStringMember" ) );
502  if ( !lineStringMemberList.isEmpty() ) //geoserver
503  {
504  for ( int i = 0; i < lineStringMemberList.size(); ++i )
505  {
506  QDomNodeList lineStringNodeList = lineStringMemberList.at( i ).toElement().elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LineString" ) );
507  if ( lineStringNodeList.size() < 1 )
508  {
509  return QgsGeometry();
510  }
511  currentLineStringElement = lineStringNodeList.at( 0 ).toElement();
512  currentCoordList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
513  if ( !currentCoordList.isEmpty() )
514  {
515  QgsPolyline currentPointList;
516  if ( readGMLCoordinates( currentPointList, currentCoordList.at( 0 ).toElement() ) != 0 )
517  {
518  return QgsGeometry();
519  }
520  lineCoordinates.push_back( currentPointList );
521  }
522  else
523  {
524  currentPosList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
525  if ( currentPosList.size() < 1 )
526  {
527  return QgsGeometry();
528  }
529  QgsPolyline currentPointList;
530  if ( readGMLPositions( currentPointList, currentPosList.at( 0 ).toElement() ) != 0 )
531  {
532  return QgsGeometry();
533  }
534  lineCoordinates.push_back( currentPointList );
535  }
536  }
537  }
538  else
539  {
540  QDomNodeList lineStringList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LineString" ) );
541  if ( !lineStringList.isEmpty() ) //mapserver
542  {
543  for ( int i = 0; i < lineStringList.size(); ++i )
544  {
545  currentLineStringElement = lineStringList.at( i ).toElement();
546  currentCoordList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
547  if ( !currentCoordList.isEmpty() )
548  {
549  QgsPolyline currentPointList;
550  if ( readGMLCoordinates( currentPointList, currentCoordList.at( 0 ).toElement() ) != 0 )
551  {
552  return QgsGeometry();
553  }
554  lineCoordinates.push_back( currentPointList );
555  return QgsGeometry();
556  }
557  else
558  {
559  currentPosList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
560  if ( currentPosList.size() < 1 )
561  {
562  return QgsGeometry();
563  }
564  QgsPolyline currentPointList;
565  if ( readGMLPositions( currentPointList, currentPosList.at( 0 ).toElement() ) != 0 )
566  {
567  return QgsGeometry();
568  }
569  lineCoordinates.push_back( currentPointList );
570  }
571  }
572  }
573  else
574  {
575  return QgsGeometry();
576  }
577  }
578 
579  int nLines = lineCoordinates.size();
580  if ( nLines < 1 )
581  return QgsGeometry();
582 
583  //calculate the required wkb size
584  int size = ( lineCoordinates.size() + 1 ) * ( 1 + 2 * sizeof( int ) );
585  for ( QList< QgsPolyline >::const_iterator it = lineCoordinates.begin(); it != lineCoordinates.end(); ++it )
586  {
587  size += it->size() * 2 * sizeof( double );
588  }
589 
591  unsigned char* wkb = new unsigned char[size];
592 
593  //fill the wkb content
594  char e = htonl( 1 ) != 1;
595  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
596  int nPoints; //number of points in a line
597  double x, y;
598  memcpy( &( wkb )[wkbPosition], &e, 1 );
599  wkbPosition += 1;
600  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
601  wkbPosition += sizeof( int );
602  memcpy( &( wkb )[wkbPosition], &nLines, sizeof( int ) );
603  wkbPosition += sizeof( int );
605  for ( QList< QgsPolyline >::const_iterator it = lineCoordinates.begin(); it != lineCoordinates.end(); ++it )
606  {
607  memcpy( &( wkb )[wkbPosition], &e, 1 );
608  wkbPosition += 1;
609  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
610  wkbPosition += sizeof( int );
611  nPoints = it->size();
612  memcpy( &( wkb )[wkbPosition], &nPoints, sizeof( int ) );
613  wkbPosition += sizeof( int );
614  for ( QgsPolyline::const_iterator iter = it->begin(); iter != it->end(); ++iter )
615  {
616  x = iter->x();
617  y = iter->y();
618  // QgsDebugMsg( QString( "x, y is %1,%2" ).arg( x, 'f' ).arg( y, 'f' ) );
619  memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
620  wkbPosition += sizeof( double );
621  memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
622  wkbPosition += sizeof( double );
623  }
624  }
625 
626  QgsGeometry g;
627  g.fromWkb( wkb, size );
628  return g;
629 }
630 
631 QgsGeometry QgsOgcUtils::geometryFromGMLMultiPolygon( const QDomElement& geometryElement )
632 {
633  //first list: different polygons, second list: different rings, third list: different points
634  QgsMultiPolygon multiPolygonPoints;
635  QDomElement currentPolygonMemberElement;
636  QDomNodeList polygonList;
637  QDomElement currentPolygonElement;
638  // rings in GML2
639  QDomNodeList outerBoundaryList;
640  QDomElement currentOuterBoundaryElement;
641  QDomNodeList innerBoundaryList;
642  QDomElement currentInnerBoundaryElement;
643  // rings in GML3
644  QDomNodeList exteriorList;
645  QDomElement currentExteriorElement;
646  QDomElement currentInteriorElement;
647  QDomNodeList interiorList;
648  // lienar ring
649  QDomNodeList linearRingNodeList;
650  QDomElement currentLinearRingElement;
651  // Coordinates or position list
652  QDomNodeList currentCoordinateList;
653  QDomNodeList currentPosList;
654 
655  QDomNodeList polygonMemberList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "polygonMember" ) );
656  QgsPolygon currentPolygonList;
657  for ( int i = 0; i < polygonMemberList.size(); ++i )
658  {
659  currentPolygonList.resize( 0 ); // preserve capacity - don't use clear
660  currentPolygonMemberElement = polygonMemberList.at( i ).toElement();
661  polygonList = currentPolygonMemberElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "Polygon" ) );
662  if ( polygonList.size() < 1 )
663  {
664  continue;
665  }
666  currentPolygonElement = polygonList.at( 0 ).toElement();
667 
668  //find exterior ring
669  outerBoundaryList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "outerBoundaryIs" ) );
670  if ( !outerBoundaryList.isEmpty() )
671  {
672  currentOuterBoundaryElement = outerBoundaryList.at( 0 ).toElement();
673  QgsPolyline ringCoordinates;
674 
675  linearRingNodeList = currentOuterBoundaryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LinearRing" ) );
676  if ( linearRingNodeList.size() < 1 )
677  {
678  continue;
679  }
680  currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
681  currentCoordinateList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
682  if ( currentCoordinateList.size() < 1 )
683  {
684  continue;
685  }
686  if ( readGMLCoordinates( ringCoordinates, currentCoordinateList.at( 0 ).toElement() ) != 0 )
687  {
688  continue;
689  }
690  currentPolygonList.push_back( ringCoordinates );
691 
692  //find interior rings
693  QDomNodeList innerBoundaryList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "innerBoundaryIs" ) );
694  for ( int j = 0; j < innerBoundaryList.size(); ++j )
695  {
696  QgsPolyline ringCoordinates;
697  currentInnerBoundaryElement = innerBoundaryList.at( j ).toElement();
698  linearRingNodeList = currentInnerBoundaryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LinearRing" ) );
699  if ( linearRingNodeList.size() < 1 )
700  {
701  continue;
702  }
703  currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
704  currentCoordinateList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
705  if ( currentCoordinateList.size() < 1 )
706  {
707  continue;
708  }
709  if ( readGMLCoordinates( ringCoordinates, currentCoordinateList.at( 0 ).toElement() ) != 0 )
710  {
711  continue;
712  }
713  currentPolygonList.push_back( ringCoordinates );
714  }
715  }
716  else
717  {
718  //find exterior ring
719  exteriorList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "exterior" ) );
720  if ( exteriorList.size() < 1 )
721  {
722  continue;
723  }
724 
725  currentExteriorElement = exteriorList.at( 0 ).toElement();
726  QgsPolyline ringPositions;
727 
728  linearRingNodeList = currentExteriorElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LinearRing" ) );
729  if ( linearRingNodeList.size() < 1 )
730  {
731  continue;
732  }
733  currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
734  currentPosList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
735  if ( currentPosList.size() < 1 )
736  {
737  continue;
738  }
739  if ( readGMLPositions( ringPositions, currentPosList.at( 0 ).toElement() ) != 0 )
740  {
741  continue;
742  }
743  currentPolygonList.push_back( ringPositions );
744 
745  //find interior rings
746  QDomNodeList interiorList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "interior" ) );
747  for ( int j = 0; j < interiorList.size(); ++j )
748  {
749  QgsPolyline ringPositions;
750  currentInteriorElement = interiorList.at( j ).toElement();
751  linearRingNodeList = currentInteriorElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LinearRing" ) );
752  if ( linearRingNodeList.size() < 1 )
753  {
754  continue;
755  }
756  currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
757  currentPosList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
758  if ( currentPosList.size() < 1 )
759  {
760  continue;
761  }
762  if ( readGMLPositions( ringPositions, currentPosList.at( 0 ).toElement() ) != 0 )
763  {
764  continue;
765  }
766  currentPolygonList.push_back( ringPositions );
767  }
768  }
769  multiPolygonPoints.push_back( currentPolygonList );
770  }
771 
772  int nPolygons = multiPolygonPoints.size();
773  if ( nPolygons < 1 )
774  return QgsGeometry();
775 
776  int size = 1 + 2 * sizeof( int );
777  //calculate the wkb size
778  for ( QgsMultiPolygon::const_iterator it = multiPolygonPoints.begin(); it != multiPolygonPoints.end(); ++it )
779  {
780  size += 1 + 2 * sizeof( int );
781  for ( QgsPolygon::const_iterator iter = it->begin(); iter != it->end(); ++iter )
782  {
783  size += sizeof( int ) + 2 * iter->size() * sizeof( double );
784  }
785  }
786 
788  unsigned char* wkb = new unsigned char[size];
789 
790  char e = htonl( 1 ) != 1;
791  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
792  double x, y;
793  int nRings;
794  int nPointsInRing;
795 
796  //fill the contents into *wkb
797  memcpy( &( wkb )[wkbPosition], &e, 1 );
798  wkbPosition += 1;
799  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
800  wkbPosition += sizeof( int );
801  memcpy( &( wkb )[wkbPosition], &nPolygons, sizeof( int ) );
802  wkbPosition += sizeof( int );
803 
804  type = QgsWkbTypes::Polygon;
805 
806  for ( QgsMultiPolygon::const_iterator it = multiPolygonPoints.begin(); it != multiPolygonPoints.end(); ++it )
807  {
808  memcpy( &( wkb )[wkbPosition], &e, 1 );
809  wkbPosition += 1;
810  memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
811  wkbPosition += sizeof( int );
812  nRings = it->size();
813  memcpy( &( wkb )[wkbPosition], &nRings, sizeof( int ) );
814  wkbPosition += sizeof( int );
815  for ( QgsPolygon::const_iterator iter = it->begin(); iter != it->end(); ++iter )
816  {
817  nPointsInRing = iter->size();
818  memcpy( &( wkb )[wkbPosition], &nPointsInRing, sizeof( int ) );
819  wkbPosition += sizeof( int );
820  for ( QgsPolyline::const_iterator iterator = iter->begin(); iterator != iter->end(); ++iterator )
821  {
822  x = iterator->x();
823  y = iterator->y();
824  memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
825  wkbPosition += sizeof( double );
826  memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
827  wkbPosition += sizeof( double );
828  }
829  }
830  }
831 
832  QgsGeometry g;
833  g.fromWkb( wkb, size );
834  return g;
835 }
836 
837 bool QgsOgcUtils::readGMLCoordinates( QgsPolyline &coords, const QDomElement &elem )
838 {
839  QString coordSeparator = QStringLiteral( "," );
840  QString tupelSeparator = QStringLiteral( " " );
841  //"decimal" has to be "."
842 
843  coords.clear();
844 
845  if ( elem.hasAttribute( QStringLiteral( "cs" ) ) )
846  {
847  coordSeparator = elem.attribute( QStringLiteral( "cs" ) );
848  }
849  if ( elem.hasAttribute( QStringLiteral( "ts" ) ) )
850  {
851  tupelSeparator = elem.attribute( QStringLiteral( "ts" ) );
852  }
853 
854  QStringList tupels = elem.text().split( tupelSeparator, QString::SkipEmptyParts );
855  QStringList tupel_coords;
856  double x, y;
857  bool conversionSuccess;
858 
859  QStringList::const_iterator it;
860  for ( it = tupels.constBegin(); it != tupels.constEnd(); ++it )
861  {
862  tupel_coords = ( *it ).split( coordSeparator, QString::SkipEmptyParts );
863  if ( tupel_coords.size() < 2 )
864  {
865  continue;
866  }
867  x = tupel_coords.at( 0 ).toDouble( &conversionSuccess );
868  if ( !conversionSuccess )
869  {
870  return 1;
871  }
872  y = tupel_coords.at( 1 ).toDouble( &conversionSuccess );
873  if ( !conversionSuccess )
874  {
875  return 1;
876  }
877  coords.push_back( QgsPoint( x, y ) );
878  }
879  return 0;
880 }
881 
883 {
884  QgsRectangle rect;
885 
886  QDomElement boxElem = boxNode.toElement();
887  if ( boxElem.tagName() != QLatin1String( "Box" ) )
888  return rect;
889 
890  QDomElement bElem = boxElem.firstChild().toElement();
891  QString coordSeparator = QStringLiteral( "," );
892  QString tupelSeparator = QStringLiteral( " " );
893  if ( bElem.hasAttribute( QStringLiteral( "cs" ) ) )
894  {
895  coordSeparator = bElem.attribute( QStringLiteral( "cs" ) );
896  }
897  if ( bElem.hasAttribute( QStringLiteral( "ts" ) ) )
898  {
899  tupelSeparator = bElem.attribute( QStringLiteral( "ts" ) );
900  }
901 
902  QString bString = bElem.text();
903  bool ok1, ok2, ok3, ok4;
904  double xmin = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 0, 0 ).toDouble( &ok1 );
905  double ymin = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 1, 1 ).toDouble( &ok2 );
906  double xmax = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 0, 0 ).toDouble( &ok3 );
907  double ymax = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 1, 1 ).toDouble( &ok4 );
908 
909  if ( ok1 && ok2 && ok3 && ok4 )
910  {
911  rect = QgsRectangle( xmin, ymin, xmax, ymax );
912  rect.normalize();
913  }
914 
915  return rect;
916 }
917 
918 bool QgsOgcUtils::readGMLPositions( QgsPolyline &coords, const QDomElement &elem )
919 {
920  coords.clear();
921 
922  QStringList pos = elem.text().split( ' ', QString::SkipEmptyParts );
923  double x, y;
924  bool conversionSuccess;
925  int posSize = pos.size();
926 
927  int srsDimension = 2;
928  if ( elem.hasAttribute( QStringLiteral( "srsDimension" ) ) )
929  {
930  srsDimension = elem.attribute( QStringLiteral( "srsDimension" ) ).toInt( &conversionSuccess );
931  if ( !conversionSuccess )
932  {
933  srsDimension = 2;
934  }
935  }
936  else if ( elem.hasAttribute( QStringLiteral( "dimension" ) ) )
937  {
938  srsDimension = elem.attribute( QStringLiteral( "dimension" ) ).toInt( &conversionSuccess );
939  if ( !conversionSuccess )
940  {
941  srsDimension = 2;
942  }
943  }
944 
945  for ( int i = 0; i < posSize / srsDimension; i++ )
946  {
947  x = pos.at( i * srsDimension ).toDouble( &conversionSuccess );
948  if ( !conversionSuccess )
949  {
950  return 1;
951  }
952  y = pos.at( i * srsDimension + 1 ).toDouble( &conversionSuccess );
953  if ( !conversionSuccess )
954  {
955  return 1;
956  }
957  coords.push_back( QgsPoint( x, y ) );
958  }
959  return 0;
960 }
961 
962 
964 {
965  QgsRectangle rect;
966 
967  QDomElement envelopeElem = envelopeNode.toElement();
968  if ( envelopeElem.tagName() != QLatin1String( "Envelope" ) )
969  return rect;
970 
971  QDomNodeList lowerCornerList = envelopeElem.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "lowerCorner" ) );
972  if ( lowerCornerList.size() < 1 )
973  return rect;
974 
975  QDomNodeList upperCornerList = envelopeElem.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "upperCorner" ) );
976  if ( upperCornerList.size() < 1 )
977  return rect;
978 
979  bool conversionSuccess;
980  int srsDimension = 2;
981 
982  QDomElement elem = lowerCornerList.at( 0 ).toElement();
983  if ( elem.hasAttribute( QStringLiteral( "srsDimension" ) ) )
984  {
985  srsDimension = elem.attribute( QStringLiteral( "srsDimension" ) ).toInt( &conversionSuccess );
986  if ( !conversionSuccess )
987  {
988  srsDimension = 2;
989  }
990  }
991  else if ( elem.hasAttribute( QStringLiteral( "dimension" ) ) )
992  {
993  srsDimension = elem.attribute( QStringLiteral( "dimension" ) ).toInt( &conversionSuccess );
994  if ( !conversionSuccess )
995  {
996  srsDimension = 2;
997  }
998  }
999  QString bString = elem.text();
1000 
1001  double xmin = bString.section( ' ', 0, 0 ).toDouble( &conversionSuccess );
1002  if ( !conversionSuccess )
1003  return rect;
1004  double ymin = bString.section( ' ', 1, 1 ).toDouble( &conversionSuccess );
1005  if ( !conversionSuccess )
1006  return rect;
1007 
1008  elem = upperCornerList.at( 0 ).toElement();
1009  if ( elem.hasAttribute( QStringLiteral( "srsDimension" ) ) )
1010  {
1011  srsDimension = elem.attribute( QStringLiteral( "srsDimension" ) ).toInt( &conversionSuccess );
1012  if ( !conversionSuccess )
1013  {
1014  srsDimension = 2;
1015  }
1016  }
1017  else if ( elem.hasAttribute( QStringLiteral( "dimension" ) ) )
1018  {
1019  srsDimension = elem.attribute( QStringLiteral( "dimension" ) ).toInt( &conversionSuccess );
1020  if ( !conversionSuccess )
1021  {
1022  srsDimension = 2;
1023  }
1024  }
1025 
1026  Q_UNUSED( srsDimension );
1027 
1028  bString = elem.text();
1029  double xmax = bString.section( ' ', 0, 0 ).toDouble( &conversionSuccess );
1030  if ( !conversionSuccess )
1031  return rect;
1032  double ymax = bString.section( ' ', 1, 1 ).toDouble( &conversionSuccess );
1033  if ( !conversionSuccess )
1034  return rect;
1035 
1036  rect = QgsRectangle( xmin, ymin, xmax, ymax );
1037  rect.normalize();
1038 
1039  return rect;
1040 }
1041 
1042 QDomElement QgsOgcUtils::rectangleToGMLBox( QgsRectangle* box, QDomDocument& doc, int precision )
1043 {
1044  return rectangleToGMLBox( box, doc, QString(), false, precision );
1045 }
1046 
1047 QDomElement QgsOgcUtils::rectangleToGMLBox( QgsRectangle* box, QDomDocument& doc,
1048  const QString& srsName,
1049  bool invertAxisOrientation,
1050  int precision )
1051 {
1052  if ( !box )
1053  {
1054  return QDomElement();
1055  }
1056 
1057  QDomElement boxElem = doc.createElement( QStringLiteral( "gml:Box" ) );
1058  if ( !srsName.isEmpty() )
1059  {
1060  boxElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1061  }
1062  QDomElement coordElem = doc.createElement( QStringLiteral( "gml:coordinates" ) );
1063  coordElem.setAttribute( QStringLiteral( "cs" ), QStringLiteral( "," ) );
1064  coordElem.setAttribute( QStringLiteral( "ts" ), QStringLiteral( " " ) );
1065 
1066  QString coordString;
1067  coordString += qgsDoubleToString( invertAxisOrientation ? box->yMinimum() : box->xMinimum(), precision );
1068  coordString += ',';
1069  coordString += qgsDoubleToString( invertAxisOrientation ? box->xMinimum() : box->yMinimum(), precision );
1070  coordString += ' ';
1071  coordString += qgsDoubleToString( invertAxisOrientation ? box->yMaximum() : box->xMaximum(), precision );
1072  coordString += ',';
1073  coordString += qgsDoubleToString( invertAxisOrientation ? box->xMaximum() : box->yMaximum(), precision );
1074 
1075  QDomText coordText = doc.createTextNode( coordString );
1076  coordElem.appendChild( coordText );
1077  boxElem.appendChild( coordElem );
1078 
1079  return boxElem;
1080 }
1081 
1082 QDomElement QgsOgcUtils::rectangleToGMLEnvelope( QgsRectangle* env, QDomDocument& doc, int precision )
1083 {
1084  return rectangleToGMLEnvelope( env, doc, QString(), false, precision );
1085 }
1086 
1087 QDomElement QgsOgcUtils::rectangleToGMLEnvelope( QgsRectangle* env, QDomDocument& doc,
1088  const QString& srsName,
1089  bool invertAxisOrientation,
1090  int precision )
1091 {
1092  if ( !env )
1093  {
1094  return QDomElement();
1095  }
1096 
1097  QDomElement envElem = doc.createElement( QStringLiteral( "gml:Envelope" ) );
1098  if ( !srsName.isEmpty() )
1099  {
1100  envElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1101  }
1102  QString posList;
1103 
1104  QDomElement lowerCornerElem = doc.createElement( QStringLiteral( "gml:lowerCorner" ) );
1105  posList = qgsDoubleToString( invertAxisOrientation ? env->yMinimum() : env->xMinimum(), precision );
1106  posList += ' ';
1107  posList += qgsDoubleToString( invertAxisOrientation ? env->xMinimum() : env->yMinimum(), precision );
1108  QDomText lowerCornerText = doc.createTextNode( posList );
1109  lowerCornerElem.appendChild( lowerCornerText );
1110  envElem.appendChild( lowerCornerElem );
1111 
1112  QDomElement upperCornerElem = doc.createElement( QStringLiteral( "gml:upperCorner" ) );
1113  posList = qgsDoubleToString( invertAxisOrientation ? env->yMaximum() : env->xMaximum(), precision );
1114  posList += ' ';
1115  posList += qgsDoubleToString( invertAxisOrientation ? env->xMaximum() : env->yMaximum(), precision );
1116  QDomText upperCornerText = doc.createTextNode( posList );
1117  upperCornerElem.appendChild( upperCornerText );
1118  envElem.appendChild( upperCornerElem );
1119 
1120  return envElem;
1121 }
1122 
1123 QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry* geometry, QDomDocument& doc, const QString& format, int precision )
1124 {
1125  return geometryToGML( geometry, doc, ( format == QLatin1String( "GML2" ) ) ? GML_2_1_2 : GML_3_2_1, QString(), false, QString(), precision );
1126 }
1127 
1128 QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry* geometry, QDomDocument& doc,
1129  GMLVersion gmlVersion,
1130  const QString& srsName,
1131  bool invertAxisOrientation,
1132  const QString& gmlIdBase,
1133  int precision )
1134 {
1135  if ( !geometry )
1136  return QDomElement();
1137 
1138  // coordinate separator
1139  QString cs = QStringLiteral( "," );
1140  // tupel separator
1141  QString ts = QStringLiteral( " " );
1142  // coord element tagname
1143  QDomElement baseCoordElem;
1144 
1145  bool hasZValue = false;
1146 
1147  QByteArray wkb( geometry->exportToWkb() );
1148  QgsConstWkbPtr wkbPtr( wkb );
1149  try
1150  {
1151  wkbPtr.readHeader();
1152  }
1153  catch ( const QgsWkbException &e )
1154  {
1155  Q_UNUSED( e );
1156  // WKB exception while reading header
1157  return QDomElement();
1158  }
1159 
1160  if ( gmlVersion != GML_2_1_2 )
1161  {
1162  switch ( geometry->wkbType() )
1163  {
1164  case QgsWkbTypes::Point25D:
1165  case QgsWkbTypes::Point:
1168  baseCoordElem = doc.createElement( QStringLiteral( "gml:pos" ) );
1169  break;
1170  default:
1171  baseCoordElem = doc.createElement( QStringLiteral( "gml:posList" ) );
1172  break;
1173  }
1174  baseCoordElem.setAttribute( QStringLiteral( "srsDimension" ), QStringLiteral( "2" ) );
1175  cs = ' ';
1176  }
1177  else
1178  {
1179  baseCoordElem = doc.createElement( QStringLiteral( "gml:coordinates" ) );
1180  baseCoordElem.setAttribute( QStringLiteral( "cs" ), cs );
1181  baseCoordElem.setAttribute( QStringLiteral( "ts" ), ts );
1182  }
1183 
1184  try
1185  {
1186  switch ( geometry->wkbType() )
1187  {
1188  case QgsWkbTypes::Point25D:
1189  case QgsWkbTypes::Point:
1190  {
1191  QDomElement pointElem = doc.createElement( QStringLiteral( "gml:Point" ) );
1192  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1193  pointElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1194  if ( !srsName.isEmpty() )
1195  pointElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1196  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1197 
1198  double x, y;
1199 
1200  if ( invertAxisOrientation )
1201  wkbPtr >> y >> x;
1202  else
1203  wkbPtr >> x >> y;
1204  QDomText coordText = doc.createTextNode( qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision ) );
1205 
1206  coordElem.appendChild( coordText );
1207  pointElem.appendChild( coordElem );
1208  return pointElem;
1209  }
1211  hasZValue = true;
1212  //intentional fall-through
1213  FALLTHROUGH;
1215  {
1216  QDomElement multiPointElem = doc.createElement( QStringLiteral( "gml:MultiPoint" ) );
1217  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1218  multiPointElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1219  if ( !srsName.isEmpty() )
1220  multiPointElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1221 
1222  int nPoints;
1223  wkbPtr >> nPoints;
1224 
1225  for ( int idx = 0; idx < nPoints; ++idx )
1226  {
1227  QDomElement pointMemberElem = doc.createElement( QStringLiteral( "gml:pointMember" ) );
1228  QDomElement pointElem = doc.createElement( QStringLiteral( "gml:Point" ) );
1229  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1230  pointElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase + QStringLiteral( ".%1" ).arg( idx + 1 ) );
1231  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1232 
1233  wkbPtr.readHeader();
1234 
1235  double x, y;
1236  if ( invertAxisOrientation )
1237  wkbPtr >> y >> x;
1238  else
1239  wkbPtr >> x >> y;
1240  QDomText coordText = doc.createTextNode( qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision ) );
1241 
1242  coordElem.appendChild( coordText );
1243  pointElem.appendChild( coordElem );
1244 
1245  if ( hasZValue )
1246  {
1247  wkbPtr += sizeof( double );
1248  }
1249  pointMemberElem.appendChild( pointElem );
1250  multiPointElem.appendChild( pointMemberElem );
1251  }
1252  return multiPointElem;
1253  }
1255  hasZValue = true;
1256  //intentional fall-through
1257  FALLTHROUGH;
1259  {
1260  QDomElement lineStringElem = doc.createElement( QStringLiteral( "gml:LineString" ) );
1261  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1262  lineStringElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1263  if ( !srsName.isEmpty() )
1264  lineStringElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1265  // get number of points in the line
1266 
1267  int nPoints;
1268  wkbPtr >> nPoints;
1269 
1270  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1271  QString coordString;
1272  for ( int idx = 0; idx < nPoints; ++idx )
1273  {
1274  if ( idx != 0 )
1275  {
1276  coordString += ts;
1277  }
1278 
1279  double x, y;
1280  if ( invertAxisOrientation )
1281  wkbPtr >> y >> x;
1282  else
1283  wkbPtr >> x >> y;
1284  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1285 
1286  if ( hasZValue )
1287  {
1288  wkbPtr += sizeof( double );
1289  }
1290  }
1291  QDomText coordText = doc.createTextNode( coordString );
1292  coordElem.appendChild( coordText );
1293  lineStringElem.appendChild( coordElem );
1294  return lineStringElem;
1295  }
1297  hasZValue = true;
1298  //intentional fall-through
1299  FALLTHROUGH;
1301  {
1302  QDomElement multiLineStringElem = doc.createElement( QStringLiteral( "gml:MultiLineString" ) );
1303  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1304  multiLineStringElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1305  if ( !srsName.isEmpty() )
1306  multiLineStringElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1307 
1308  int nLines;
1309  wkbPtr >> nLines;
1310 
1311  for ( int jdx = 0; jdx < nLines; jdx++ )
1312  {
1313  QDomElement lineStringMemberElem = doc.createElement( QStringLiteral( "gml:lineStringMember" ) );
1314  QDomElement lineStringElem = doc.createElement( QStringLiteral( "gml:LineString" ) );
1315  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1316  lineStringElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase + QStringLiteral( ".%1" ).arg( jdx + 1 ) );
1317 
1318  wkbPtr.readHeader();
1319 
1320  int nPoints;
1321  wkbPtr >> nPoints;
1322 
1323  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1324  QString coordString;
1325  for ( int idx = 0; idx < nPoints; idx++ )
1326  {
1327  if ( idx != 0 )
1328  {
1329  coordString += ts;
1330  }
1331 
1332  double x, y;
1333  if ( invertAxisOrientation )
1334  wkbPtr >> y >> x;
1335  else
1336  wkbPtr >> x >> y;
1337 
1338  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1339 
1340  if ( hasZValue )
1341  {
1342  wkbPtr += sizeof( double );
1343  }
1344  }
1345  QDomText coordText = doc.createTextNode( coordString );
1346  coordElem.appendChild( coordText );
1347  lineStringElem.appendChild( coordElem );
1348  lineStringMemberElem.appendChild( lineStringElem );
1349  multiLineStringElem.appendChild( lineStringMemberElem );
1350  }
1351  return multiLineStringElem;
1352  }
1354  hasZValue = true;
1355  //intentional fall-through
1356  FALLTHROUGH;
1357  case QgsWkbTypes::Polygon:
1358  {
1359  QDomElement polygonElem = doc.createElement( QStringLiteral( "gml:Polygon" ) );
1360  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1361  polygonElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1362  if ( !srsName.isEmpty() )
1363  polygonElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1364 
1365  // get number of rings in the polygon
1366  int numRings;
1367  wkbPtr >> numRings;
1368 
1369  if ( numRings == 0 ) // sanity check for zero rings in polygon
1370  return QDomElement();
1371 
1372  int *ringNumPoints = new int[numRings]; // number of points in each ring
1373 
1374  for ( int idx = 0; idx < numRings; idx++ )
1375  {
1376  QString boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:outerBoundaryIs" : "gml:exterior";
1377  if ( idx != 0 )
1378  {
1379  boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:innerBoundaryIs" : "gml:interior";
1380  }
1381  QDomElement boundaryElem = doc.createElement( boundaryName );
1382  QDomElement ringElem = doc.createElement( QStringLiteral( "gml:LinearRing" ) );
1383  // get number of points in the ring
1384  int nPoints;
1385  wkbPtr >> nPoints;
1386  ringNumPoints[idx] = nPoints;
1387 
1388  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1389  QString coordString;
1390  for ( int jdx = 0; jdx < nPoints; jdx++ )
1391  {
1392  if ( jdx != 0 )
1393  {
1394  coordString += ts;
1395  }
1396 
1397  double x, y;
1398  if ( invertAxisOrientation )
1399  wkbPtr >> y >> x;
1400  else
1401  wkbPtr >> x >> y;
1402 
1403  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1404  if ( hasZValue )
1405  {
1406  wkbPtr += sizeof( double );
1407  }
1408  }
1409  QDomText coordText = doc.createTextNode( coordString );
1410  coordElem.appendChild( coordText );
1411  ringElem.appendChild( coordElem );
1412  boundaryElem.appendChild( ringElem );
1413  polygonElem.appendChild( boundaryElem );
1414  }
1415  delete [] ringNumPoints;
1416  return polygonElem;
1417  }
1419  hasZValue = true;
1420  //intentional fall-through
1421  FALLTHROUGH;
1423  {
1424  QDomElement multiPolygonElem = doc.createElement( QStringLiteral( "gml:MultiPolygon" ) );
1425  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1426  multiPolygonElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
1427  if ( !srsName.isEmpty() )
1428  multiPolygonElem.setAttribute( QStringLiteral( "srsName" ), srsName );
1429 
1430  int numPolygons;
1431  wkbPtr >> numPolygons;
1432 
1433  for ( int kdx = 0; kdx < numPolygons; kdx++ )
1434  {
1435  QDomElement polygonMemberElem = doc.createElement( QStringLiteral( "gml:polygonMember" ) );
1436  QDomElement polygonElem = doc.createElement( QStringLiteral( "gml:Polygon" ) );
1437  if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
1438  polygonElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase + QStringLiteral( ".%1" ).arg( kdx + 1 ) );
1439 
1440  wkbPtr.readHeader();
1441 
1442  int numRings;
1443  wkbPtr >> numRings;
1444 
1445  for ( int idx = 0; idx < numRings; idx++ )
1446  {
1447  QString boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:outerBoundaryIs" : "gml:exterior";
1448  if ( idx != 0 )
1449  {
1450  boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:innerBoundaryIs" : "gml:interior";
1451  }
1452  QDomElement boundaryElem = doc.createElement( boundaryName );
1453  QDomElement ringElem = doc.createElement( QStringLiteral( "gml:LinearRing" ) );
1454 
1455  int nPoints;
1456  wkbPtr >> nPoints;
1457 
1458  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1459  QString coordString;
1460  for ( int jdx = 0; jdx < nPoints; jdx++ )
1461  {
1462  if ( jdx != 0 )
1463  {
1464  coordString += ts;
1465  }
1466 
1467  double x, y;
1468  if ( invertAxisOrientation )
1469  wkbPtr >> y >> x;
1470  else
1471  wkbPtr >> x >> y;
1472 
1473  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1474 
1475  if ( hasZValue )
1476  {
1477  wkbPtr += sizeof( double );
1478  }
1479  }
1480  QDomText coordText = doc.createTextNode( coordString );
1481  coordElem.appendChild( coordText );
1482  ringElem.appendChild( coordElem );
1483  boundaryElem.appendChild( ringElem );
1484  polygonElem.appendChild( boundaryElem );
1485  polygonMemberElem.appendChild( polygonElem );
1486  multiPolygonElem.appendChild( polygonMemberElem );
1487  }
1488  }
1489  return multiPolygonElem;
1490  }
1491  default:
1492  return QDomElement();
1493  }
1494  }
1495  catch ( const QgsWkbException &e )
1496  {
1497  Q_UNUSED( e );
1498  return QDomElement();
1499  }
1500 }
1501 
1502 QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry *geometry, QDomDocument &doc, int precision )
1503 {
1504  return geometryToGML( geometry, doc, QStringLiteral( "GML2" ), precision );
1505 }
1506 
1507 QDomElement QgsOgcUtils::createGMLCoordinates( const QgsPolyline &points, QDomDocument &doc )
1508 {
1509  QDomElement coordElem = doc.createElement( QStringLiteral( "gml:coordinates" ) );
1510  coordElem.setAttribute( QStringLiteral( "cs" ), QStringLiteral( "," ) );
1511  coordElem.setAttribute( QStringLiteral( "ts" ), QStringLiteral( " " ) );
1512 
1513  QString coordString;
1514  QVector<QgsPoint>::const_iterator pointIt = points.constBegin();
1515  for ( ; pointIt != points.constEnd(); ++pointIt )
1516  {
1517  if ( pointIt != points.constBegin() )
1518  {
1519  coordString += ' ';
1520  }
1521  coordString += qgsDoubleToString( pointIt->x() );
1522  coordString += ',';
1523  coordString += qgsDoubleToString( pointIt->y() );
1524  }
1525 
1526  QDomText coordText = doc.createTextNode( coordString );
1527  coordElem.appendChild( coordText );
1528  return coordElem;
1529 }
1530 
1531 QDomElement QgsOgcUtils::createGMLPositions( const QgsPolyline &points, QDomDocument& doc )
1532 {
1533  QDomElement posElem = doc.createElement( QStringLiteral( "gml:pos" ) );
1534  if ( points.size() > 1 )
1535  posElem = doc.createElement( QStringLiteral( "gml:posList" ) );
1536  posElem.setAttribute( QStringLiteral( "srsDimension" ), QStringLiteral( "2" ) );
1537 
1538  QString coordString;
1539  QVector<QgsPoint>::const_iterator pointIt = points.constBegin();
1540  for ( ; pointIt != points.constEnd(); ++pointIt )
1541  {
1542  if ( pointIt != points.constBegin() )
1543  {
1544  coordString += ' ';
1545  }
1546  coordString += qgsDoubleToString( pointIt->x() );
1547  coordString += ' ';
1548  coordString += qgsDoubleToString( pointIt->y() );
1549  }
1550 
1551  QDomText coordText = doc.createTextNode( coordString );
1552  posElem.appendChild( coordText );
1553  return posElem;
1554 }
1555 
1556 
1557 
1558 // -----------------------------------------
1559 
1560 QColor QgsOgcUtils::colorFromOgcFill( const QDomElement& fillElement )
1561 {
1562  if ( fillElement.isNull() || !fillElement.hasChildNodes() )
1563  {
1564  return QColor();
1565  }
1566 
1567  QString cssName;
1568  QString elemText;
1569  QColor color;
1570  QDomElement cssElem = fillElement.firstChildElement( QStringLiteral( "CssParameter" ) );
1571  while ( !cssElem.isNull() )
1572  {
1573  cssName = cssElem.attribute( QStringLiteral( "name" ), QStringLiteral( "not_found" ) );
1574  if ( cssName != QLatin1String( "not_found" ) )
1575  {
1576  elemText = cssElem.text();
1577  if ( cssName == QLatin1String( "fill" ) )
1578  {
1579  color.setNamedColor( elemText );
1580  }
1581  else if ( cssName == QLatin1String( "fill-opacity" ) )
1582  {
1583  bool ok;
1584  double opacity = elemText.toDouble( &ok );
1585  if ( ok )
1586  {
1587  color.setAlphaF( opacity );
1588  }
1589  }
1590  }
1591 
1592  cssElem = cssElem.nextSiblingElement( QStringLiteral( "CssParameter" ) );
1593  }
1594 
1595  return color;
1596 }
1597 
1598 
1600 {
1601  if ( element.isNull() || !element.hasChildNodes() )
1602  return nullptr;
1603 
1604  QgsExpression *expr = new QgsExpression();
1605 
1606  QDomElement childElem = element.firstChildElement();
1607  while ( !childElem.isNull() )
1608  {
1609  QString errorMsg;
1610  QgsExpression::Node *node = nodeFromOgcFilter( childElem, errorMsg );
1611  if ( !node )
1612  {
1613  // invalid expression, parser error
1614  expr->d->mParserErrorString = errorMsg;
1615  return expr;
1616  }
1617 
1618  // use the concat binary operator to append to the root node
1619  if ( !expr->d->mRootNode )
1620  {
1621  expr->d->mRootNode = node;
1622  }
1623  else
1624  {
1625  expr->d->mRootNode = new QgsExpression::NodeBinaryOperator( QgsExpression::boConcat, expr->d->mRootNode, node );
1626  }
1627 
1628  childElem = childElem.nextSiblingElement();
1629  }
1630 
1631  // update expression string
1632  expr->d->mExp = expr->dump();
1633 
1634  return expr;
1635 }
1636 
1637 
1638 static const QMap<QString, int>& binaryOperatorsTagNamesMap()
1639 {
1640  static QMap<QString, int> binOps;
1641  if ( binOps.isEmpty() )
1642  {
1643  // logical
1644  binOps.insert( QStringLiteral( "Or" ), QgsExpression::boOr );
1645  binOps.insert( QStringLiteral( "And" ), QgsExpression::boAnd );
1646  // comparison
1647  binOps.insert( QStringLiteral( "PropertyIsEqualTo" ), QgsExpression::boEQ );
1648  binOps.insert( QStringLiteral( "PropertyIsNotEqualTo" ), QgsExpression::boNE );
1649  binOps.insert( QStringLiteral( "PropertyIsLessThanOrEqualTo" ), QgsExpression::boLE );
1650  binOps.insert( QStringLiteral( "PropertyIsGreaterThanOrEqualTo" ), QgsExpression::boGE );
1651  binOps.insert( QStringLiteral( "PropertyIsLessThan" ), QgsExpression::boLT );
1652  binOps.insert( QStringLiteral( "PropertyIsGreaterThan" ), QgsExpression::boGT );
1653  binOps.insert( QStringLiteral( "PropertyIsLike" ), QgsExpression::boLike );
1654  // arithmetics
1655  binOps.insert( QStringLiteral( "Add" ), QgsExpression::boPlus );
1656  binOps.insert( QStringLiteral( "Sub" ), QgsExpression::boMinus );
1657  binOps.insert( QStringLiteral( "Mul" ), QgsExpression::boMul );
1658  binOps.insert( QStringLiteral( "Div" ), QgsExpression::boDiv );
1659  }
1660  return binOps;
1661 }
1662 
1663 static int binaryOperatorFromTagName( const QString& tagName )
1664 {
1665 
1666  return binaryOperatorsTagNamesMap().value( tagName, -1 );
1667 }
1668 
1670 {
1671  if ( op == QgsExpression::boILike )
1672  {
1673  return QStringLiteral( "PropertyIsLike" );
1674  }
1675  return binaryOperatorsTagNamesMap().key( op, QString() );
1676 }
1677 
1678 static bool isBinaryOperator( const QString& tagName )
1679 {
1680  return binaryOperatorFromTagName( tagName ) >= 0;
1681 }
1682 
1683 
1684 static bool isSpatialOperator( const QString& tagName )
1685 {
1686  static QStringList spatialOps;
1687  if ( spatialOps.isEmpty() )
1688  {
1689  spatialOps << QStringLiteral( "BBOX" ) << QStringLiteral( "Intersects" ) << QStringLiteral( "Contains" ) << QStringLiteral( "Crosses" ) << QStringLiteral( "Equals" )
1690  << QStringLiteral( "Disjoint" ) << QStringLiteral( "Overlaps" ) << QStringLiteral( "Touches" ) << QStringLiteral( "Within" );
1691  }
1692 
1693  return spatialOps.contains( tagName );
1694 }
1695 
1696 
1697 
1698 QgsExpression::Node* QgsOgcUtils::nodeFromOgcFilter( QDomElement &element, QString &errorMessage )
1699 {
1700  if ( element.isNull() )
1701  return nullptr;
1702 
1703  // check for binary operators
1704  if ( isBinaryOperator( element.tagName() ) )
1705  {
1706  return nodeBinaryOperatorFromOgcFilter( element, errorMessage );
1707  }
1708 
1709  // check for spatial operators
1710  if ( isSpatialOperator( element.tagName() ) )
1711  {
1712  return nodeSpatialOperatorFromOgcFilter( element, errorMessage );
1713  }
1714 
1715  // check for other OGC operators, convert them to expressions
1716 
1717  if ( element.tagName() == QLatin1String( "Not" ) )
1718  {
1719  return nodeNotFromOgcFilter( element, errorMessage );
1720  }
1721  else if ( element.tagName() == QLatin1String( "PropertyIsNull" ) )
1722  {
1723  return nodePropertyIsNullFromOgcFilter( element, errorMessage );
1724  }
1725  else if ( element.tagName() == QLatin1String( "Literal" ) )
1726  {
1727  return nodeLiteralFromOgcFilter( element, errorMessage );
1728  }
1729  else if ( element.tagName() == QLatin1String( "Function" ) )
1730  {
1731  return nodeFunctionFromOgcFilter( element, errorMessage );
1732  }
1733  else if ( element.tagName() == QLatin1String( "PropertyName" ) )
1734  {
1735  return nodeColumnRefFromOgcFilter( element, errorMessage );
1736  }
1737  else if ( element.tagName() == QLatin1String( "PropertyIsBetween" ) )
1738  {
1739  return nodeIsBetweenFromOgcFilter( element, errorMessage );
1740  }
1741 
1742  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() );
1743  return nullptr;
1744 }
1745 
1746 
1747 
1748 QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodeBinaryOperatorFromOgcFilter( QDomElement &element, QString &errorMessage )
1749 {
1750  if ( element.isNull() )
1751  return nullptr;
1752 
1753  int op = binaryOperatorFromTagName( element.tagName() );
1754  if ( op < 0 )
1755  {
1756  if ( errorMessage.isEmpty() )
1757  errorMessage = QObject::tr( "'%1' binary operator not supported." ).arg( element.tagName() );
1758  return nullptr;
1759  }
1760 
1761  if ( op == QgsExpression::boLike && element.hasAttribute( QStringLiteral( "matchCase" ) ) && element.attribute( QStringLiteral( "matchCase" ) ) == QLatin1String( "false" ) )
1762  {
1764  }
1765 
1766  QDomElement operandElem = element.firstChildElement();
1767  QgsExpression::Node *expr = nodeFromOgcFilter( operandElem, errorMessage ), *leftOp = expr;
1768  if ( !expr )
1769  {
1770  if ( errorMessage.isEmpty() )
1771  errorMessage = QObject::tr( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
1772  return nullptr;
1773  }
1774 
1775  for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
1776  {
1777  QgsExpression::Node* opRight = nodeFromOgcFilter( operandElem, errorMessage );
1778  if ( !opRight )
1779  {
1780  if ( errorMessage.isEmpty() )
1781  errorMessage = QObject::tr( "invalid right operand for '%1' binary operator" ).arg( element.tagName() );
1782  delete expr;
1783  return nullptr;
1784  }
1785 
1786  if ( op == QgsExpression::boLike || op == QgsExpression::boILike )
1787  {
1788  QString wildCard;
1789  if ( element.hasAttribute( QStringLiteral( "wildCard" ) ) )
1790  {
1791  wildCard = element.attribute( QStringLiteral( "wildCard" ) );
1792  }
1793  QString singleChar;
1794  if ( element.hasAttribute( QStringLiteral( "singleChar" ) ) )
1795  {
1796  singleChar = element.attribute( QStringLiteral( "singleChar" ) );
1797  }
1798  QString escape = QStringLiteral( "\\" );
1799  if ( element.hasAttribute( QStringLiteral( "escape" ) ) )
1800  {
1801  escape = element.attribute( QStringLiteral( "escape" ) );
1802  }
1803  // replace
1804  QString oprValue = static_cast<const QgsExpression::NodeLiteral*>( opRight )->value().toString();
1805  if ( !wildCard.isEmpty() && wildCard != QLatin1String( "%" ) )
1806  {
1807  oprValue.replace( '%', QLatin1String( "\\%" ) );
1808  if ( oprValue.startsWith( wildCard ) )
1809  {
1810  oprValue.replace( 0, 1, QStringLiteral( "%" ) );
1811  }
1812  QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( wildCard ) + ")" );
1813  int pos = 0;
1814  while (( pos = rx.indexIn( oprValue, pos ) ) != -1 )
1815  {
1816  oprValue.replace( pos + 1, 1, QStringLiteral( "%" ) );
1817  pos += 1;
1818  }
1819  oprValue.replace( escape + wildCard, wildCard );
1820  }
1821  if ( !singleChar.isEmpty() && singleChar != QLatin1String( "_" ) )
1822  {
1823  oprValue.replace( '_', QLatin1String( "\\_" ) );
1824  if ( oprValue.startsWith( singleChar ) )
1825  {
1826  oprValue.replace( 0, 1, QStringLiteral( "_" ) );
1827  }
1828  QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( singleChar ) + ")" );
1829  int pos = 0;
1830  while (( pos = rx.indexIn( oprValue, pos ) ) != -1 )
1831  {
1832  oprValue.replace( pos + 1, 1, QStringLiteral( "_" ) );
1833  pos += 1;
1834  }
1835  oprValue.replace( escape + singleChar, singleChar );
1836  }
1837  if ( !escape.isEmpty() && escape != QLatin1String( "\\" ) )
1838  {
1839  oprValue.replace( escape + escape, escape );
1840  }
1841  opRight = new QgsExpression::NodeLiteral( oprValue );
1842  }
1843 
1844  expr = new QgsExpression::NodeBinaryOperator( static_cast< QgsExpression::BinaryOperator >( op ), expr, opRight );
1845  }
1846 
1847  if ( expr == leftOp )
1848  {
1849  if ( errorMessage.isEmpty() )
1850  errorMessage = QObject::tr( "only one operand for '%1' binary operator" ).arg( element.tagName() );
1851  delete expr;
1852  return nullptr;
1853  }
1854 
1856  if ( !ret )
1857  delete expr;
1858 
1859  return ret;
1860 }
1861 
1862 
1863 QgsExpression::NodeFunction* QgsOgcUtils::nodeSpatialOperatorFromOgcFilter( QDomElement& element, QString &errorMessage )
1864 {
1865  // we are exploiting the fact that our function names are the same as the XML tag names
1866  int opIdx = QgsExpression::functionIndex( element.tagName().toLower() );
1867 
1869  QDomElement childElem = element.firstChildElement();
1870  QString gml2Str;
1871  while ( !childElem.isNull() && gml2Str.isEmpty() )
1872  {
1873  if ( childElem.tagName() != QLatin1String( "PropertyName" ) )
1874  {
1875  QTextStream gml2Stream( &gml2Str );
1876  childElem.save( gml2Stream, 0 );
1877  }
1878  childElem = childElem.nextSiblingElement();
1879  }
1880  if ( !gml2Str.isEmpty() )
1881  {
1882  gml2Args->append( new QgsExpression::NodeLiteral( QVariant( gml2Str.remove( '\n' ) ) ) );
1883  }
1884  else
1885  {
1886  errorMessage = QObject::tr( "No OGC Geometry found" );
1887  delete gml2Args;
1888  return nullptr;
1889  }
1890 
1892  opArgs->append( new QgsExpression::NodeFunction( QgsExpression::functionIndex( QStringLiteral( "$geometry" ) ), new QgsExpression::NodeList() ) );
1893  opArgs->append( new QgsExpression::NodeFunction( QgsExpression::functionIndex( QStringLiteral( "geomFromGML" ) ), gml2Args ) );
1894 
1895  return new QgsExpression::NodeFunction( opIdx, opArgs );
1896 }
1897 
1898 
1899 QgsExpression::NodeUnaryOperator* QgsOgcUtils::nodeNotFromOgcFilter( QDomElement &element, QString &errorMessage )
1900 {
1901  if ( element.tagName() != QLatin1String( "Not" ) )
1902  return nullptr;
1903 
1904  QDomElement operandElem = element.firstChildElement();
1905  QgsExpression::Node* operand = nodeFromOgcFilter( operandElem, errorMessage );
1906  if ( !operand )
1907  {
1908  if ( errorMessage.isEmpty() )
1909  errorMessage = QObject::tr( "invalid operand for '%1' unary operator" ).arg( element.tagName() );
1910  return nullptr;
1911  }
1912 
1914 }
1915 
1916 
1917 QgsExpression::NodeFunction* QgsOgcUtils::nodeFunctionFromOgcFilter( QDomElement &element, QString &errorMessage )
1918 {
1919  if ( element.isNull() || element.tagName() != QLatin1String( "Function" ) )
1920  {
1921  errorMessage = QObject::tr( "ogc:Function expected, got %1" ).arg( element.tagName() );
1922  return nullptr;
1923  }
1924 
1925  for ( int i = 0; i < QgsExpression::Functions().size(); i++ )
1926  {
1928 
1929  if ( element.attribute( QStringLiteral( "name" ) ) != funcDef->name() )
1930  continue;
1931 
1933 
1934  QDomElement operandElem = element.firstChildElement();
1935  while ( !operandElem.isNull() )
1936  {
1937  QgsExpression::Node* op = nodeFromOgcFilter( operandElem, errorMessage );
1938  if ( !op )
1939  {
1940  delete args;
1941  return nullptr;
1942  }
1943  args->append( op );
1944 
1945  operandElem = operandElem.nextSiblingElement();
1946  }
1947 
1948  return new QgsExpression::NodeFunction( i, args );
1949  }
1950 
1951  return nullptr;
1952 }
1953 
1954 
1955 
1956 QgsExpression::Node* QgsOgcUtils::nodeLiteralFromOgcFilter( QDomElement &element, QString &errorMessage )
1957 {
1958  if ( element.isNull() || element.tagName() != QLatin1String( "Literal" ) )
1959  {
1960  errorMessage = QObject::tr( "ogc:Literal expected, got %1" ).arg( element.tagName() );
1961  return nullptr;
1962  }
1963 
1964  QgsExpression::Node *root = nullptr;
1965 
1966  // the literal content can have more children (e.g. CDATA section, text, ...)
1967  QDomNode childNode = element.firstChild();
1968  while ( !childNode.isNull() )
1969  {
1970  QgsExpression::Node* operand = nullptr;
1971 
1972  if ( childNode.nodeType() == QDomNode::ElementNode )
1973  {
1974  // found a element node (e.g. PropertyName), convert it
1975  QDomElement operandElem = childNode.toElement();
1976  operand = nodeFromOgcFilter( operandElem, errorMessage );
1977  if ( !operand )
1978  {
1979  if ( root )
1980  delete root;
1981 
1982  errorMessage = QObject::tr( "'%1' is an invalid or not supported content for ogc:Literal" ).arg( operandElem.tagName() );
1983  return nullptr;
1984  }
1985  }
1986  else
1987  {
1988  // probably a text/CDATA node
1989  QVariant value = childNode.nodeValue();
1990 
1991  // try to convert the node content to number if possible,
1992  // otherwise let's use it as string
1993  bool ok;
1994  double d = value.toDouble( &ok );
1995  if ( ok )
1996  value = d;
1997 
1998  operand = new QgsExpression::NodeLiteral( value );
1999  if ( !operand )
2000  continue;
2001  }
2002 
2003  // use the concat operator to merge the ogc:Literal children
2004  if ( !root )
2005  {
2006  root = operand;
2007  }
2008  else
2009  {
2010  root = new QgsExpression::NodeBinaryOperator( QgsExpression::boConcat, root, operand );
2011  }
2012 
2013  childNode = childNode.nextSibling();
2014  }
2015 
2016  if ( root )
2017  return root;
2018 
2019  return nullptr;
2020 }
2021 
2022 
2023 QgsExpression::NodeColumnRef* QgsOgcUtils::nodeColumnRefFromOgcFilter( QDomElement &element, QString &errorMessage )
2024 {
2025  if ( element.isNull() || element.tagName() != QLatin1String( "PropertyName" ) )
2026  {
2027  errorMessage = QObject::tr( "ogc:PropertyName expected, got %1" ).arg( element.tagName() );
2028  return nullptr;
2029  }
2030 
2031  return new QgsExpression::NodeColumnRef( element.firstChild().nodeValue() );
2032 }
2033 
2034 
2035 QgsExpression::Node* QgsOgcUtils::nodeIsBetweenFromOgcFilter( QDomElement& element, QString& errorMessage )
2036 {
2037  // <ogc:PropertyIsBetween> encode a Range check
2038  QgsExpression::Node *operand = nullptr, *lowerBound = nullptr;
2039  QgsExpression::Node *operand2 = nullptr, *upperBound = nullptr;
2040 
2041  QDomElement operandElem = element.firstChildElement();
2042  while ( !operandElem.isNull() )
2043  {
2044  if ( operandElem.tagName() == QLatin1String( "LowerBoundary" ) )
2045  {
2046  QDomElement lowerBoundElem = operandElem.firstChildElement();
2047  lowerBound = nodeFromOgcFilter( lowerBoundElem, errorMessage );
2048  }
2049  else if ( operandElem.tagName() == QLatin1String( "UpperBoundary" ) )
2050  {
2051  QDomElement upperBoundElem = operandElem.firstChildElement();
2052  upperBound = nodeFromOgcFilter( upperBoundElem, errorMessage );
2053  }
2054  else
2055  {
2056  // <ogc:expression>
2057  // both operand and operand2 contain the same expression,
2058  // they are respectively compared to lower bound and upper bound
2059  operand = nodeFromOgcFilter( operandElem, errorMessage );
2060  operand2 = nodeFromOgcFilter( operandElem, errorMessage );
2061  }
2062 
2063  if ( operand && lowerBound && operand2 && upperBound )
2064  break;
2065 
2066  operandElem = operandElem.nextSiblingElement();
2067  }
2068 
2069  if ( !operand || !lowerBound || !operand2 || !upperBound )
2070  {
2071  if ( operand )
2072  delete operand;
2073 
2074  if ( lowerBound )
2075  delete lowerBound;
2076 
2077  if ( upperBound )
2078  delete upperBound;
2079 
2080  errorMessage = QObject::tr( "missing some required sub-elements in ogc:PropertyIsBetween" );
2081  return nullptr;
2082  }
2083 
2084  QgsExpression::Node *geOperator = new QgsExpression::NodeBinaryOperator( QgsExpression::boGE, operand, lowerBound );
2085  QgsExpression::Node *leOperator = new QgsExpression::NodeBinaryOperator( QgsExpression::boLE, operand2, upperBound );
2086  return new QgsExpression::NodeBinaryOperator( QgsExpression::boAnd, geOperator, leOperator );
2087 }
2088 
2089 
2090 QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodePropertyIsNullFromOgcFilter( QDomElement& element, QString& errorMessage )
2091 {
2092  // convert ogc:PropertyIsNull to IS operator with NULL right operand
2093  if ( element.tagName() != QLatin1String( "PropertyIsNull" ) )
2094  {
2095  return nullptr;
2096  }
2097 
2098  QDomElement operandElem = element.firstChildElement();
2099  QgsExpression::Node* opLeft = nodeFromOgcFilter( operandElem, errorMessage );
2100  if ( !opLeft )
2101  return nullptr;
2102 
2103  QgsExpression::Node* opRight = new QgsExpression::NodeLiteral( QVariant() );
2104  return new QgsExpression::NodeBinaryOperator( QgsExpression::boIs, opLeft, opRight );
2105 }
2106 
2107 
2109 
2110 
2111 QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression& exp, QDomDocument& doc, QString* errorMessage )
2112 {
2113  return expressionToOgcFilter( exp, doc, GML_2_1_2, FILTER_OGC_1_0,
2114  QStringLiteral( "geometry" ), QString(), false, false, errorMessage );
2115 }
2116 
2117 QDomElement QgsOgcUtils::expressionToOgcExpression( const QgsExpression& exp, QDomDocument& doc, QString* errorMessage )
2118 {
2119  return expressionToOgcExpression( exp, doc, GML_2_1_2, FILTER_OGC_1_0,
2120  QStringLiteral( "geometry" ), QString(), false, false, errorMessage );
2121 }
2122 
2124  QDomDocument& doc,
2125  GMLVersion gmlVersion,
2126  FilterVersion filterVersion,
2127  const QString& geometryName,
2128  const QString& srsName,
2129  bool honourAxisOrientation,
2130  bool invertAxisOrientation,
2131  QString* errorMessage )
2132 {
2133  if ( !exp.rootNode() )
2134  return QDomElement();
2135 
2136  QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
2137  QDomElement exprRootElem = utils.expressionNodeToOgcFilter( exp.rootNode() );
2138  if ( errorMessage )
2139  *errorMessage = utils.errorMessage();
2140  if ( exprRootElem.isNull() )
2141  return QDomElement();
2142 
2143  QDomElement filterElem =
2144  ( filterVersion == FILTER_FES_2_0 ) ?
2145  doc.createElementNS( FES_NAMESPACE, QStringLiteral( "fes:Filter" ) ) :
2146  doc.createElementNS( OGC_NAMESPACE, QStringLiteral( "ogc:Filter" ) );
2147  if ( utils.GMLNamespaceUsed() )
2148  {
2149  QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:gml" ) );
2150  if ( gmlVersion == GML_3_2_1 )
2151  attr.setValue( GML32_NAMESPACE );
2152  else
2153  attr.setValue( GML_NAMESPACE );
2154  filterElem.setAttributeNode( attr );
2155  }
2156  filterElem.appendChild( exprRootElem );
2157  return filterElem;
2158 }
2159 
2161  QDomDocument& doc,
2162  GMLVersion gmlVersion,
2163  FilterVersion filterVersion,
2164  const QString& geometryName,
2165  const QString& srsName,
2166  bool honourAxisOrientation,
2167  bool invertAxisOrientation,
2168  QString* errorMessage )
2169 {
2170  const QgsExpression::Node* node = exp.rootNode();
2171  if ( !node )
2172  return QDomElement();
2173 
2174  switch ( node->nodeType() )
2175  {
2179  {
2180  QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
2181  QDomElement exprRootElem = utils.expressionNodeToOgcFilter( node );
2182 
2183  if ( errorMessage )
2184  *errorMessage = utils.errorMessage();
2185 
2186  if ( !exprRootElem.isNull() )
2187  {
2188  return exprRootElem;
2189  }
2190  break;
2191  }
2192  default:
2193  *errorMessage = QObject::tr( "Node type not supported in expression translation: %1" ).arg( node->nodeType() );
2194  }
2195  // got an error
2196  return QDomElement();
2197 }
2198 
2200  QDomDocument& doc,
2201  GMLVersion gmlVersion,
2202  FilterVersion filterVersion,
2203  const QList<LayerProperties>& layerProperties,
2204  bool honourAxisOrientation,
2205  bool invertAxisOrientation,
2206  const QMap< QString, QString>& mapUnprefixedTypenameToPrefixedTypename,
2207  QString* errorMessage )
2208 {
2209  if ( !statement.rootNode() )
2210  return QDomElement();
2211 
2212  QgsOgcUtilsSQLStatementToFilter utils( doc, gmlVersion, filterVersion,
2213  layerProperties, honourAxisOrientation, invertAxisOrientation,
2214  mapUnprefixedTypenameToPrefixedTypename );
2215  QDomElement exprRootElem = utils.toOgcFilter( statement.rootNode() );
2216  if ( errorMessage )
2217  *errorMessage = utils.errorMessage();
2218  if ( exprRootElem.isNull() )
2219  return QDomElement();
2220 
2221  QDomElement filterElem =
2222  ( filterVersion == FILTER_FES_2_0 ) ?
2223  doc.createElementNS( FES_NAMESPACE, QStringLiteral( "fes:Filter" ) ) :
2224  doc.createElementNS( OGC_NAMESPACE, QStringLiteral( "ogc:Filter" ) );
2225  if ( utils.GMLNamespaceUsed() )
2226  {
2227  QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:gml" ) );
2228  if ( gmlVersion == GML_3_2_1 )
2229  attr.setValue( GML32_NAMESPACE );
2230  else
2231  attr.setValue( GML_NAMESPACE );
2232  filterElem.setAttributeNode( attr );
2233  }
2234  filterElem.appendChild( exprRootElem );
2235  return filterElem;
2236 }
2237 
2238 //
2239 
2240 
2242 {
2243  switch ( node->nodeType() )
2244  {
2246  return expressionUnaryOperatorToOgcFilter( static_cast<const QgsExpression::NodeUnaryOperator*>( node ) );
2248  return expressionBinaryOperatorToOgcFilter( static_cast<const QgsExpression::NodeBinaryOperator*>( node ) );
2250  return expressionInOperatorToOgcFilter( static_cast<const QgsExpression::NodeInOperator*>( node ) );
2252  return expressionFunctionToOgcFilter( static_cast<const QgsExpression::NodeFunction*>( node ) );
2254  return expressionLiteralToOgcFilter( static_cast<const QgsExpression::NodeLiteral*>( node ) );
2256  return expressionColumnRefToOgcFilter( static_cast<const QgsExpression::NodeColumnRef*>( node ) );
2257 
2258  default:
2259  mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
2260  return QDomElement();
2261  }
2262 }
2263 
2264 
2265 QDomElement QgsOgcUtilsExprToFilter::expressionUnaryOperatorToOgcFilter( const QgsExpression::NodeUnaryOperator* node )
2266 {
2267 
2268  QDomElement operandElem = expressionNodeToOgcFilter( node->operand() );
2269  if ( !mErrorMessage.isEmpty() )
2270  return QDomElement();
2271 
2272  QDomElement uoElem;
2273  switch ( node->op() )
2274  {
2276  uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2277  if ( node->operand()->nodeType() == QgsExpression::ntLiteral )
2278  {
2279  // operand expression already created a Literal node:
2280  // take the literal value, prepend - and remove old literal node
2281  uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2282  mDoc.removeChild( operandElem );
2283  }
2284  else
2285  {
2286  mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2287  return QDomElement();
2288  }
2289  break;
2290  case QgsExpression::uoNot:
2291  uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2292  uoElem.appendChild( operandElem );
2293  break;
2294 
2295  default:
2296  mErrorMessage = QObject::tr( "Unary operator %1 not implemented yet" ).arg( QgsExpression::UnaryOperatorText[node->op()] );
2297  return QDomElement();
2298  }
2299 
2300  return uoElem;
2301 }
2302 
2303 
2304 QDomElement QgsOgcUtilsExprToFilter::expressionBinaryOperatorToOgcFilter( const QgsExpression::NodeBinaryOperator* node )
2305 {
2306  QDomElement leftElem = expressionNodeToOgcFilter( node->opLeft() );
2307  if ( !mErrorMessage.isEmpty() )
2308  return QDomElement();
2309 
2310  QgsExpression::BinaryOperator op = node->op();
2311 
2312  // before right operator is parsed: to allow NULL handling
2313  if ( op == QgsExpression::boIs || op == QgsExpression::boIsNot )
2314  {
2315  if ( node->opRight()->nodeType() == QgsExpression::ntLiteral )
2316  {
2317  const QgsExpression::NodeLiteral* rightLit = static_cast<const QgsExpression::NodeLiteral*>( node->opRight() );
2318  if ( rightLit->value().isNull() )
2319  {
2320 
2321  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2322  elem.appendChild( leftElem );
2323 
2324  if ( op == QgsExpression::boIsNot )
2325  {
2326  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2327  notElem.appendChild( elem );
2328  return notElem;
2329  }
2330 
2331  return elem;
2332  }
2333 
2334  // continue with equal / not equal operator once the null case is handled
2336  }
2337 
2338  }
2339 
2340  QDomElement rightElem = expressionNodeToOgcFilter( node->opRight() );
2341  if ( !mErrorMessage.isEmpty() )
2342  return QDomElement();
2343 
2344 
2345  QString opText = binaryOperatorToTagName( op );
2346  if ( opText.isEmpty() )
2347  {
2348  // not implemented binary operators
2349  // TODO: regex, % (mod), ^ (pow) are not supported yet
2350  mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( QgsExpression::BinaryOperatorText[op] );
2351  return QDomElement();
2352  }
2353 
2354  QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2355 
2356  if ( op == QgsExpression::boLike || op == QgsExpression::boILike )
2357  {
2358  if ( op == QgsExpression::boILike )
2359  boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2360 
2361  // setup wildCards to <ogc:PropertyIsLike>
2362  boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2363  boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2364  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2365  boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2366  else
2367  boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2368  }
2369 
2370  boElem.appendChild( leftElem );
2371  boElem.appendChild( rightElem );
2372  return boElem;
2373 }
2374 
2375 
2376 QDomElement QgsOgcUtilsExprToFilter::expressionLiteralToOgcFilter( const QgsExpression::NodeLiteral* node )
2377 {
2378  QString value;
2379  switch ( node->value().type() )
2380  {
2381  case QVariant::Int:
2382  value = QString::number( node->value().toInt() );
2383  break;
2384  case QVariant::Double:
2385  value = qgsDoubleToString( node->value().toDouble() );
2386  break;
2387  case QVariant::String:
2388  value = node->value().toString();
2389  break;
2390 
2391  default:
2392  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( node->value().type() );
2393  return QDomElement();
2394  }
2395 
2396  QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2397  litElem.appendChild( mDoc.createTextNode( value ) );
2398  return litElem;
2399 }
2400 
2401 
2402 QDomElement QgsOgcUtilsExprToFilter::expressionColumnRefToOgcFilter( const QgsExpression::NodeColumnRef* node )
2403 {
2404  QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2405  propElem.appendChild( mDoc.createTextNode( node->name() ) );
2406  return propElem;
2407 }
2408 
2409 
2410 
2411 QDomElement QgsOgcUtilsExprToFilter::expressionInOperatorToOgcFilter( const QgsExpression::NodeInOperator* node )
2412 {
2413  if ( node->list()->list().size() == 1 )
2414  return expressionNodeToOgcFilter( node->list()->list()[0] );
2415 
2416  QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2417  QDomElement leftNode = expressionNodeToOgcFilter( node->node() );
2418 
2419  Q_FOREACH ( QgsExpression::Node* n, node->list()->list() )
2420  {
2421  QDomElement listNode = expressionNodeToOgcFilter( n );
2422  if ( !mErrorMessage.isEmpty() )
2423  return QDomElement();
2424 
2425  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2426  eqElem.appendChild( leftNode.cloneNode() );
2427  eqElem.appendChild( listNode );
2428 
2429  orElem.appendChild( eqElem );
2430  }
2431 
2432  if ( node->isNotIn() )
2433  {
2434  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2435  notElem.appendChild( orElem );
2436  return notElem;
2437  }
2438 
2439  return orElem;
2440 }
2441 
2442 static QMap<QString, QString> binarySpatialOpsMap()
2443 {
2444  static QMap<QString, QString> binSpatialOps;
2445  if ( binSpatialOps.isEmpty() )
2446  {
2447  binSpatialOps.insert( QStringLiteral( "disjoint" ), QStringLiteral( "Disjoint" ) );
2448  binSpatialOps.insert( QStringLiteral( "intersects" ), QStringLiteral( "Intersects" ) );
2449  binSpatialOps.insert( QStringLiteral( "touches" ), QStringLiteral( "Touches" ) );
2450  binSpatialOps.insert( QStringLiteral( "crosses" ), QStringLiteral( "Crosses" ) );
2451  binSpatialOps.insert( QStringLiteral( "contains" ), QStringLiteral( "Contains" ) );
2452  binSpatialOps.insert( QStringLiteral( "overlaps" ), QStringLiteral( "Overlaps" ) );
2453  binSpatialOps.insert( QStringLiteral( "within" ), QStringLiteral( "Within" ) );
2454  }
2455  return binSpatialOps;
2456 }
2457 
2458 static bool isBinarySpatialOperator( const QString& fnName )
2459 {
2460  return binarySpatialOpsMap().contains( fnName );
2461 }
2462 
2463 static QString tagNameForSpatialOperator( const QString& fnName )
2464 {
2465  return binarySpatialOpsMap().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.isEmpty() && 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::UnaryOperatorText[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::BinaryOperatorText[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:35
Internal use by QgsOgcUtils.
Definition: qgsogcutils.h:296
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:176
Function with a name and arguments node.
QgsOgcUtilsExprToFilter(QDomDocument &doc, QgsOgcUtils::GMLVersion gmlVersion, QgsOgcUtils::FilterVersion filterVersion, const QString &geometryName, const QString &srsName, bool honourAxisOrientation, bool invertAxisOrientation)
Constructor.
Definition: qgsogcutils.cpp:41
static const QString FES_NAMESPACE
Definition: qgsogcutils.cpp:39
Node * onExpr() const
On expression. Will be nullptr if usingColumns() is not empty.
static QString mapBinarySpatialToOgc(const QString &name)
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer&#39;s length...
A abstract base class for defining QgsExpression functions.
static const char * BinaryOperatorText[]
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:46
QString dump() const
Return an expression string, constructed from the internal abstract syntax tree.
const QString & errorMessage() const
Return the error message.
Definition: qgsogcutils.h:361
Node * operand() const
Operand.
static bool isGeometryColumn(const QgsExpression::Node *node)
bool GMLNamespaceUsed() const
Return whether the gml: namespace is used.
Definition: qgsogcutils.h:358
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:78
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:189
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 QMap< QString, QString > binarySpatialOpsMap()
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:312
static const QList< Function * > & Functions()
static int binaryOperatorFromTagName(const QString &tagName)
QString mName
Layer name.
Definition: qgsogcutils.h:196
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.
static const char * UnaryOperatorText[]
Class for parsing SQL statements.
static const QString GML_NAMESPACE
Definition: qgsogcutils.cpp:36
Node * opRight() const
Right operand.
QgsExpressionPrivate * d
#define FALLTHROUGH
Definition: qgis.h:375
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:184
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:181
Literal value (integer, integer64, double, string)
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:61
Internal use by QgsOgcUtils.
Definition: qgsogcutils.h:342
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:200
QVector< QgsPolyline > QgsPolygon
Polygon: first item of the list is outer ring, inner rings (if any) start from second item...
Definition: qgsgeometry.h:52
static QString tagNameForSpatialOperator(const QString &fnName)
A class to represent a point.
Definition: qgspoint.h:111
bool isNotBetween() const
Whether this is a NOT BETWEEN operator.
static const QMap< QString, int > & binaryOperatorsTagNamesMap()
FilterVersion
OGC filter version.
Definition: qgsogcutils.h:140
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:206
QVector< QgsPolyline > QgsMultiPolyline
A collection of QgsPolylines that share a common collection of attributes.
Definition: qgsgeometry.h:58
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:191
static const QString OGC_NAMESPACE
Definition: qgsogcutils.cpp:38
const QString & errorMessage() const
Return the error message.
Definition: qgsogcutils.h:315
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.
static const char * UnaryOperatorText[]
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:186
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 (eg.
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.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:196
static QString binaryOperatorToTagName(QgsExpression::BinaryOperator op)
QString mGeometryAttribute
Geometry attribute name.
Definition: qgsogcutils.h:198
static QColor colorFromOgcFill(const QDomElement &fillElement)
Parse XML with OGC fill into QColor.
GMLVersion
GML version.
Definition: qgsogcutils.h:50
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:201
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:43
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:171
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.
static const char * BinaryOperatorText[]