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