QGIS API Documentation  3.2.0-Bonn (bc43194)
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  QgsPolylineXY 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.empty() )
174  {
175  return QgsGeometry();
176  }
177 
178  QgsPolylineXY::const_iterator point_it = pointCoordinate.constBegin();
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  QgsPolylineXY 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  QgsPolylineXY::const_iterator iter;
247  for ( iter = lineCoordinates.constBegin(); iter != lineCoordinates.constEnd(); ++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  QgsMultiPolylineXY ringCoordinates;
266 
267  //read coordinates for outer boundary
268  QgsPolylineXY 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  QgsPolylineXY 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  QgsPolylineXY 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 ( QgsMultiPolylineXY::const_iterator it = ringCoordinates.constBegin(); it != ringCoordinates.constEnd(); ++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 ( QgsMultiPolylineXY::const_iterator it = ringCoordinates.constBegin(); it != ringCoordinates.constEnd(); ++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  QgsPolylineXY::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  QgsPolylineXY pointList;
392  QgsPolylineXY 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.empty() )
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.empty() )
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 ( QgsPolylineXY::const_iterator it = pointList.constBegin(); it != pointList.constEnd(); ++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< QgsPolylineXY > 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  QgsPolylineXY 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  QgsPolylineXY 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  QgsPolylineXY 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  QgsPolylineXY 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< QgsPolylineXY >::const_iterator it = lineCoordinates.constBegin(); it != lineCoordinates.constEnd(); ++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< QgsPolylineXY >::const_iterator it = lineCoordinates.constBegin(); it != lineCoordinates.constEnd(); ++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 ( QgsPolylineXY::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  QgsMultiPolygonXY 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  QgsPolygonXY 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  QgsPolylineXY 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  QgsPolylineXY 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  QgsPolylineXY 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  QgsPolylineXY 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 ( QgsMultiPolygonXY::const_iterator it = multiPolygonPoints.constBegin(); it != multiPolygonPoints.constEnd(); ++it )
781  {
782  size += 1 + 2 * sizeof( int );
783  for ( QgsPolygonXY::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 ( QgsMultiPolygonXY::const_iterator it = multiPolygonPoints.constBegin(); it != multiPolygonPoints.constEnd(); ++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 ( QgsPolygonXY::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 ( QgsPolylineXY::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( QgsPolylineXY &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( QgsPointXY( 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( QgsPolylineXY &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( QgsPointXY( 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.asWkb() );
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 QgsPolylineXY &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<QgsPointXY>::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 QgsPolylineXY &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<QgsPointXY>::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 
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, layer );
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 {
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, QgsVectorLayer *layer )
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, layer );
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, layer );
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, QgsVectorLayer *layer )
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  {
1771  }
1772 
1773  QDomElement operandElem = element.firstChildElement();
1774  QgsExpressionNode *expr = nodeFromOgcFilter( operandElem, errorMessage, layer ), *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, layer );
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 
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 
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 
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 
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  {
1935 
1936  if ( element.attribute( QStringLiteral( "name" ) ) != funcDef->name() )
1937  continue;
1938 
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, QgsVectorLayer *layer )
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, layer );
1984  if ( !operand )
1985  {
1986  delete root;
1987 
1988  errorMessage = QObject::tr( "'%1' is an invalid or not supported content for ogc:Literal" ).arg( operandElem.tagName() );
1989  return nullptr;
1990  }
1991  }
1992  else
1993  {
1994  // probably a text/CDATA node
1995  QVariant value = childNode.nodeValue();
1996 
1997  bool converted = false;
1998 
1999  // try to convert the node content to corresponding field type if possible
2000  if ( layer != nullptr )
2001  {
2002  QDomElement propertyNameElement = element.previousSiblingElement( QLatin1String( "PropertyName" ) );
2003  if ( propertyNameElement.isNull() || propertyNameElement.tagName() != QLatin1String( "PropertyName" ) )
2004  {
2005  propertyNameElement = element.nextSiblingElement( QLatin1String( "PropertyName" ) );
2006  }
2007  if ( !propertyNameElement.isNull() || propertyNameElement.tagName() == QLatin1String( "PropertyName" ) )
2008  {
2009  int fieldIndex = layer->fields().indexOf( propertyNameElement.firstChild().nodeValue() );
2010  if ( fieldIndex != -1 )
2011  {
2012  QgsField field = layer->fields().field( propertyNameElement.firstChild().nodeValue() );
2013  field.convertCompatible( value );
2014  converted = true;
2015  }
2016  }
2017  }
2018  if ( !converted )
2019  {
2020  // try to convert the node content to number if possible,
2021  // otherwise let's use it as string
2022  bool ok;
2023  double d = value.toDouble( &ok );
2024  if ( ok )
2025  value = d;
2026  }
2027 
2028  operand = new QgsExpressionNodeLiteral( value );
2029  if ( !operand )
2030  continue;
2031  }
2032 
2033  // use the concat operator to merge the ogc:Literal children
2034  if ( !root )
2035  {
2036  root = operand;
2037  }
2038  else
2039  {
2041  }
2042 
2043  childNode = childNode.nextSibling();
2044  }
2045 
2046  if ( root )
2047  return root;
2048 
2049  return nullptr;
2050 }
2051 
2052 
2053 QgsExpressionNodeColumnRef *QgsOgcUtils::nodeColumnRefFromOgcFilter( QDomElement &element, QString &errorMessage )
2054 {
2055  if ( element.isNull() || element.tagName() != QLatin1String( "PropertyName" ) )
2056  {
2057  errorMessage = QObject::tr( "ogc:PropertyName expected, got %1" ).arg( element.tagName() );
2058  return nullptr;
2059  }
2060 
2061  return new QgsExpressionNodeColumnRef( element.firstChild().nodeValue() );
2062 }
2063 
2064 
2065 QgsExpressionNode *QgsOgcUtils::nodeIsBetweenFromOgcFilter( QDomElement &element, QString &errorMessage )
2066 {
2067  // <ogc:PropertyIsBetween> encode a Range check
2068  QgsExpressionNode *operand = nullptr, *lowerBound = nullptr;
2069  QgsExpressionNode *operand2 = nullptr, *upperBound = nullptr;
2070 
2071  QDomElement operandElem = element.firstChildElement();
2072  while ( !operandElem.isNull() )
2073  {
2074  if ( operandElem.tagName() == QLatin1String( "LowerBoundary" ) )
2075  {
2076  QDomElement lowerBoundElem = operandElem.firstChildElement();
2077  lowerBound = nodeFromOgcFilter( lowerBoundElem, errorMessage );
2078  }
2079  else if ( operandElem.tagName() == QLatin1String( "UpperBoundary" ) )
2080  {
2081  QDomElement upperBoundElem = operandElem.firstChildElement();
2082  upperBound = nodeFromOgcFilter( upperBoundElem, errorMessage );
2083  }
2084  else
2085  {
2086  // <ogc:expression>
2087  // both operand and operand2 contain the same expression,
2088  // they are respectively compared to lower bound and upper bound
2089  operand = nodeFromOgcFilter( operandElem, errorMessage );
2090  operand2 = nodeFromOgcFilter( operandElem, errorMessage );
2091  }
2092 
2093  if ( operand && lowerBound && operand2 && upperBound )
2094  break;
2095 
2096  operandElem = operandElem.nextSiblingElement();
2097  }
2098 
2099  if ( !operand || !lowerBound || !operand2 || !upperBound )
2100  {
2101  delete operand;
2102  delete lowerBound;
2103  delete upperBound;
2104 
2105  errorMessage = QObject::tr( "missing some required sub-elements in ogc:PropertyIsBetween" );
2106  return nullptr;
2107  }
2108 
2111  return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boAnd, geOperator, leOperator );
2112 }
2113 
2114 
2115 QgsExpressionNodeBinaryOperator *QgsOgcUtils::nodePropertyIsNullFromOgcFilter( QDomElement &element, QString &errorMessage )
2116 {
2117  // convert ogc:PropertyIsNull to IS operator with NULL right operand
2118  if ( element.tagName() != QLatin1String( "PropertyIsNull" ) )
2119  {
2120  return nullptr;
2121  }
2122 
2123  QDomElement operandElem = element.firstChildElement();
2124  QgsExpressionNode *opLeft = nodeFromOgcFilter( operandElem, errorMessage );
2125  if ( !opLeft )
2126  return nullptr;
2127 
2128  QgsExpressionNode *opRight = new QgsExpressionNodeLiteral( QVariant() );
2130 }
2131 
2132 
2134 
2135 
2136 QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression &exp, QDomDocument &doc, QString *errorMessage )
2137 {
2138  return expressionToOgcFilter( exp, doc, GML_2_1_2, FILTER_OGC_1_0,
2139  QStringLiteral( "geometry" ), QString(), false, false, errorMessage );
2140 }
2141 
2142 QDomElement QgsOgcUtils::expressionToOgcExpression( const QgsExpression &exp, QDomDocument &doc, QString *errorMessage )
2143 {
2144  return expressionToOgcExpression( exp, doc, GML_2_1_2, FILTER_OGC_1_0,
2145  QStringLiteral( "geometry" ), QString(), false, false, errorMessage );
2146 }
2147 
2149  QDomDocument &doc,
2150  GMLVersion gmlVersion,
2151  FilterVersion filterVersion,
2152  const QString &geometryName,
2153  const QString &srsName,
2154  bool honourAxisOrientation,
2155  bool invertAxisOrientation,
2156  QString *errorMessage )
2157 {
2158  if ( !exp.rootNode() )
2159  return QDomElement();
2160 
2161  QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
2162  QDomElement exprRootElem = utils.expressionNodeToOgcFilter( exp.rootNode() );
2163  if ( errorMessage )
2164  *errorMessage = utils.errorMessage();
2165  if ( exprRootElem.isNull() )
2166  return QDomElement();
2167 
2168  QDomElement filterElem =
2169  ( filterVersion == FILTER_FES_2_0 ) ?
2170  doc.createElementNS( FES_NAMESPACE, QStringLiteral( "fes:Filter" ) ) :
2171  doc.createElementNS( OGC_NAMESPACE, QStringLiteral( "ogc:Filter" ) );
2172  if ( utils.GMLNamespaceUsed() )
2173  {
2174  QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:gml" ) );
2175  if ( gmlVersion == GML_3_2_1 )
2176  attr.setValue( GML32_NAMESPACE );
2177  else
2178  attr.setValue( GML_NAMESPACE );
2179  filterElem.setAttributeNode( attr );
2180  }
2181  filterElem.appendChild( exprRootElem );
2182  return filterElem;
2183 }
2184 
2186  QDomDocument &doc,
2187  GMLVersion gmlVersion,
2188  FilterVersion filterVersion,
2189  const QString &geometryName,
2190  const QString &srsName,
2191  bool honourAxisOrientation,
2192  bool invertAxisOrientation,
2193  QString *errorMessage )
2194 {
2195  const QgsExpressionNode *node = exp.rootNode();
2196  if ( !node )
2197  return QDomElement();
2198 
2199  switch ( node->nodeType() )
2200  {
2204  {
2205  QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
2206  QDomElement exprRootElem = utils.expressionNodeToOgcFilter( node );
2207 
2208  if ( errorMessage )
2209  *errorMessage = utils.errorMessage();
2210 
2211  if ( !exprRootElem.isNull() )
2212  {
2213  return exprRootElem;
2214  }
2215  break;
2216  }
2217  default:
2218  {
2219  if ( errorMessage )
2220  *errorMessage = QObject::tr( "Node type not supported in expression translation: %1" ).arg( node->nodeType() );
2221  }
2222  }
2223  // got an error
2224  return QDomElement();
2225 }
2226 
2228  QDomDocument &doc,
2229  GMLVersion gmlVersion,
2230  FilterVersion filterVersion,
2231  const QList<LayerProperties> &layerProperties,
2232  bool honourAxisOrientation,
2233  bool invertAxisOrientation,
2234  const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename,
2235  QString *errorMessage )
2236 {
2237  if ( !statement.rootNode() )
2238  return QDomElement();
2239 
2240  QgsOgcUtilsSQLStatementToFilter utils( doc, gmlVersion, filterVersion,
2241  layerProperties, honourAxisOrientation, invertAxisOrientation,
2242  mapUnprefixedTypenameToPrefixedTypename );
2243  QDomElement exprRootElem = utils.toOgcFilter( statement.rootNode() );
2244  if ( errorMessage )
2245  *errorMessage = utils.errorMessage();
2246  if ( exprRootElem.isNull() )
2247  return QDomElement();
2248 
2249  QDomElement filterElem =
2250  ( filterVersion == FILTER_FES_2_0 ) ?
2251  doc.createElementNS( FES_NAMESPACE, QStringLiteral( "fes:Filter" ) ) :
2252  doc.createElementNS( OGC_NAMESPACE, QStringLiteral( "ogc:Filter" ) );
2253  if ( utils.GMLNamespaceUsed() )
2254  {
2255  QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:gml" ) );
2256  if ( gmlVersion == GML_3_2_1 )
2257  attr.setValue( GML32_NAMESPACE );
2258  else
2259  attr.setValue( GML_NAMESPACE );
2260  filterElem.setAttributeNode( attr );
2261  }
2262  filterElem.appendChild( exprRootElem );
2263  return filterElem;
2264 }
2265 
2266 //
2267 
2268 
2270 {
2271  switch ( node->nodeType() )
2272  {
2274  return expressionUnaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeUnaryOperator *>( node ) );
2276  return expressionBinaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeBinaryOperator *>( node ) );
2278  return expressionInOperatorToOgcFilter( static_cast<const QgsExpressionNodeInOperator *>( node ) );
2280  return expressionFunctionToOgcFilter( static_cast<const QgsExpressionNodeFunction *>( node ) );
2282  return expressionLiteralToOgcFilter( static_cast<const QgsExpressionNodeLiteral *>( node ) );
2284  return expressionColumnRefToOgcFilter( static_cast<const QgsExpressionNodeColumnRef *>( node ) );
2285 
2286  default:
2287  mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
2288  return QDomElement();
2289  }
2290 }
2291 
2292 
2293 QDomElement QgsOgcUtilsExprToFilter::expressionUnaryOperatorToOgcFilter( const QgsExpressionNodeUnaryOperator *node )
2294 {
2295 
2296  QDomElement operandElem = expressionNodeToOgcFilter( node->operand() );
2297  if ( !mErrorMessage.isEmpty() )
2298  return QDomElement();
2299 
2300  QDomElement uoElem;
2301  switch ( node->op() )
2302  {
2304  uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2305  if ( node->operand()->nodeType() == QgsExpressionNode::ntLiteral )
2306  {
2307  // operand expression already created a Literal node:
2308  // take the literal value, prepend - and remove old literal node
2309  uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2310  mDoc.removeChild( operandElem );
2311  }
2312  else
2313  {
2314  mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2315  return QDomElement();
2316  }
2317  break;
2319  uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2320  uoElem.appendChild( operandElem );
2321  break;
2322 
2323  default:
2324  mErrorMessage = QObject::tr( "Unary operator '%1' not implemented yet" ).arg( node->text() );
2325  return QDomElement();
2326  }
2327 
2328  return uoElem;
2329 }
2330 
2331 
2332 QDomElement QgsOgcUtilsExprToFilter::expressionBinaryOperatorToOgcFilter( const QgsExpressionNodeBinaryOperator *node )
2333 {
2334  QDomElement leftElem = expressionNodeToOgcFilter( node->opLeft() );
2335  if ( !mErrorMessage.isEmpty() )
2336  return QDomElement();
2337 
2339 
2340  // before right operator is parsed: to allow NULL handling
2342  {
2343  if ( node->opRight()->nodeType() == QgsExpressionNode::ntLiteral )
2344  {
2345  const QgsExpressionNodeLiteral *rightLit = static_cast<const QgsExpressionNodeLiteral *>( node->opRight() );
2346  if ( rightLit->value().isNull() )
2347  {
2348 
2349  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2350  elem.appendChild( leftElem );
2351 
2353  {
2354  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2355  notElem.appendChild( elem );
2356  return notElem;
2357  }
2358 
2359  return elem;
2360  }
2361 
2362  // continue with equal / not equal operator once the null case is handled
2364  }
2365 
2366  }
2367 
2368  QDomElement rightElem = expressionNodeToOgcFilter( node->opRight() );
2369  if ( !mErrorMessage.isEmpty() )
2370  return QDomElement();
2371 
2372 
2373  QString opText = binaryOperatorToTagName( op );
2374  if ( opText.isEmpty() )
2375  {
2376  // not implemented binary operators
2377  // TODO: regex, % (mod), ^ (pow) are not supported yet
2378  mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( node->text() );
2379  return QDomElement();
2380  }
2381 
2382  QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2383 
2385  {
2387  boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2388 
2389  // setup wildCards to <ogc:PropertyIsLike>
2390  boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2391  boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2392  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2393  boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2394  else
2395  boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2396  }
2397 
2398  boElem.appendChild( leftElem );
2399  boElem.appendChild( rightElem );
2400  return boElem;
2401 }
2402 
2403 
2404 QDomElement QgsOgcUtilsExprToFilter::expressionLiteralToOgcFilter( const QgsExpressionNodeLiteral *node )
2405 {
2406  QString value;
2407  switch ( node->value().type() )
2408  {
2409  case QVariant::Int:
2410  value = QString::number( node->value().toInt() );
2411  break;
2412  case QVariant::Double:
2413  value = qgsDoubleToString( node->value().toDouble() );
2414  break;
2415  case QVariant::String:
2416  value = node->value().toString();
2417  break;
2418 
2419  default:
2420  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( node->value().type() );
2421  return QDomElement();
2422  }
2423 
2424  QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2425  litElem.appendChild( mDoc.createTextNode( value ) );
2426  return litElem;
2427 }
2428 
2429 
2430 QDomElement QgsOgcUtilsExprToFilter::expressionColumnRefToOgcFilter( const QgsExpressionNodeColumnRef *node )
2431 {
2432  QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2433  propElem.appendChild( mDoc.createTextNode( node->name() ) );
2434  return propElem;
2435 }
2436 
2437 
2438 
2439 QDomElement QgsOgcUtilsExprToFilter::expressionInOperatorToOgcFilter( const QgsExpressionNodeInOperator *node )
2440 {
2441  if ( node->list()->list().size() == 1 )
2442  return expressionNodeToOgcFilter( node->list()->list()[0] );
2443 
2444  QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2445  QDomElement leftNode = expressionNodeToOgcFilter( node->node() );
2446 
2447  Q_FOREACH ( QgsExpressionNode *n, node->list()->list() )
2448  {
2449  QDomElement listNode = expressionNodeToOgcFilter( n );
2450  if ( !mErrorMessage.isEmpty() )
2451  return QDomElement();
2452 
2453  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2454  eqElem.appendChild( leftNode.cloneNode() );
2455  eqElem.appendChild( listNode );
2456 
2457  orElem.appendChild( eqElem );
2458  }
2459 
2460  if ( node->isNotIn() )
2461  {
2462  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2463  notElem.appendChild( orElem );
2464  return notElem;
2465  }
2466 
2467  return orElem;
2468 }
2469 
2470 static const QMap<QString, QString> BINARY_SPATIAL_OPS_MAP
2471 {
2472  { QStringLiteral( "disjoint" ), QStringLiteral( "Disjoint" ) },
2473  { QStringLiteral( "intersects" ), QStringLiteral( "Intersects" )},
2474  { QStringLiteral( "touches" ), QStringLiteral( "Touches" ) },
2475  { QStringLiteral( "crosses" ), QStringLiteral( "Crosses" ) },
2476  { QStringLiteral( "contains" ), QStringLiteral( "Contains" ) },
2477  { QStringLiteral( "overlaps" ), QStringLiteral( "Overlaps" ) },
2478  { QStringLiteral( "within" ), QStringLiteral( "Within" ) }
2479 };
2480 
2481 static bool isBinarySpatialOperator( const QString &fnName )
2482 {
2483  return BINARY_SPATIAL_OPS_MAP.contains( fnName );
2484 }
2485 
2486 static QString tagNameForSpatialOperator( const QString &fnName )
2487 {
2488  return BINARY_SPATIAL_OPS_MAP.value( fnName );
2489 }
2490 
2491 static bool isGeometryColumn( const QgsExpressionNode *node )
2492 {
2493  if ( node->nodeType() != QgsExpressionNode::ntFunction )
2494  return false;
2495 
2496  const QgsExpressionNodeFunction *fn = static_cast<const QgsExpressionNodeFunction *>( node );
2498  return fd->name() == QLatin1String( "$geometry" );
2499 }
2500 
2501 static QgsGeometry geometryFromConstExpr( const QgsExpressionNode *node )
2502 {
2503  // Right now we support only geomFromWKT(' ..... ')
2504  // Ideally we should support any constant sub-expression (not dependent on feature's geometry or attributes)
2505 
2506  if ( node->nodeType() == QgsExpressionNode::ntFunction )
2507  {
2508  const QgsExpressionNodeFunction *fnNode = static_cast<const QgsExpressionNodeFunction *>( node );
2510  if ( fnDef->name() == QLatin1String( "geom_from_wkt" ) )
2511  {
2512  const QList<QgsExpressionNode *> &args = fnNode->args()->list();
2513  if ( args[0]->nodeType() == QgsExpressionNode::ntLiteral )
2514  {
2515  QString wkt = static_cast<const QgsExpressionNodeLiteral *>( args[0] )->value().toString();
2516  return QgsGeometry::fromWkt( wkt );
2517  }
2518  }
2519  }
2520  return QgsGeometry();
2521 }
2522 
2523 
2524 QDomElement QgsOgcUtilsExprToFilter::expressionFunctionToOgcFilter( const QgsExpressionNodeFunction *node )
2525 {
2527 
2528  if ( fd->name() == QLatin1String( "intersects_bbox" ) )
2529  {
2530  QList<QgsExpressionNode *> argNodes = node->args()->list();
2531  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2532 
2533  QgsGeometry geom = geometryFromConstExpr( argNodes[1] );
2534  if ( !geom.isNull() && isGeometryColumn( argNodes[0] ) )
2535  {
2536  QgsRectangle rect = geom.boundingBox();
2537 
2538  mGMLUsed = true;
2539 
2540  QDomElement elemBox = ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
2541  QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, mSrsName, mInvertAxisOrientation ) :
2542  QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, mSrsName, mInvertAxisOrientation );
2543 
2544  QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2545  geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );
2546 
2547  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":BBOX" );
2548  funcElem.appendChild( geomProperty );
2549  funcElem.appendChild( elemBox );
2550  return funcElem;
2551  }
2552  else
2553  {
2554  mErrorMessage = QObject::tr( "<BBOX> is currently supported only in form: bbox($geometry, geomFromWKT('…'))" );
2555  return QDomElement();
2556  }
2557  }
2558 
2559  if ( isBinarySpatialOperator( fd->name() ) )
2560  {
2561  QList<QgsExpressionNode *> argNodes = node->args()->list();
2562  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2563 
2564  QgsExpressionNode *otherNode = nullptr;
2565  if ( isGeometryColumn( argNodes[0] ) )
2566  otherNode = argNodes[1];
2567  else if ( isGeometryColumn( argNodes[1] ) )
2568  otherNode = argNodes[0];
2569  else
2570  {
2571  mErrorMessage = QObject::tr( "Unable to translate spatial operator: at least one must refer to geometry." );
2572  return QDomElement();
2573  }
2574 
2575  QDomElement otherGeomElem;
2576 
2577  // the other node must be a geometry constructor
2578  if ( otherNode->nodeType() != QgsExpressionNode::ntFunction )
2579  {
2580  mErrorMessage = QObject::tr( "spatial operator: the other operator must be a geometry constructor function" );
2581  return QDomElement();
2582  }
2583 
2584  const QgsExpressionNodeFunction *otherFn = static_cast<const QgsExpressionNodeFunction *>( otherNode );
2585  QgsExpressionFunction *otherFnDef = QgsExpression::Functions()[otherFn->fnIndex()];
2586  if ( otherFnDef->name() == QLatin1String( "geom_from_wkt" ) )
2587  {
2588  QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2589  if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2590  {
2591  mErrorMessage = QObject::tr( "geom_from_wkt: argument must be string literal" );
2592  return QDomElement();
2593  }
2594  QString wkt = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2595  QgsGeometry geom = QgsGeometry::fromWkt( wkt );
2596  otherGeomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation,
2597  QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2598  mGeomId ++;
2599  }
2600  else if ( otherFnDef->name() == QLatin1String( "geom_from_gml" ) )
2601  {
2602  QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2603  if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2604  {
2605  mErrorMessage = QObject::tr( "geom_from_gml: argument must be string literal" );
2606  return QDomElement();
2607  }
2608 
2609  QDomDocument geomDoc;
2610  QString gml = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2611  if ( !geomDoc.setContent( gml, true ) )
2612  {
2613  mErrorMessage = QObject::tr( "geom_from_gml: unable to parse XML" );
2614  return QDomElement();
2615  }
2616 
2617  QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
2618  otherGeomElem = geomNode.toElement();
2619  }
2620  else
2621  {
2622  mErrorMessage = QObject::tr( "spatial operator: unknown geometry constructor function" );
2623  return QDomElement();
2624  }
2625 
2626  mGMLUsed = true;
2627 
2628  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + tagNameForSpatialOperator( fd->name() ) );
2629  QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2630  geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );
2631  funcElem.appendChild( geomProperty );
2632  funcElem.appendChild( otherGeomElem );
2633  return funcElem;
2634  }
2635 
2636  if ( fd->params() == 0 )
2637  {
2638  mErrorMessage = QObject::tr( "Special columns/constants are not supported." );
2639  return QDomElement();
2640  }
2641 
2642  // this is somehow wrong - we are just hoping that the other side supports the same functions as we do...
2643  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
2644  funcElem.setAttribute( QStringLiteral( "name" ), fd->name() );
2645  Q_FOREACH ( QgsExpressionNode *n, node->args()->list() )
2646  {
2647  QDomElement childElem = expressionNodeToOgcFilter( n );
2648  if ( !mErrorMessage.isEmpty() )
2649  return QDomElement();
2650 
2651  funcElem.appendChild( childElem );
2652  }
2653 
2654  return funcElem;
2655 }
2656 
2657 //
2658 
2660  QgsOgcUtils::GMLVersion gmlVersion,
2661  QgsOgcUtils::FilterVersion filterVersion,
2662  const QList<QgsOgcUtils::LayerProperties> &layerProperties,
2663  bool honourAxisOrientation,
2664  bool invertAxisOrientation,
2665  const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename )
2666  : mDoc( doc )
2667  , mGMLUsed( false )
2668  , mGMLVersion( gmlVersion )
2669  , mFilterVersion( filterVersion )
2670  , mLayerProperties( layerProperties )
2671  , mHonourAxisOrientation( honourAxisOrientation )
2672  , mInvertAxisOrientation( invertAxisOrientation )
2673  , mFilterPrefix( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "fes" : "ogc" )
2674  , mPropertyName( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "ValueReference" : "PropertyName" )
2675  , mGeomId( 1 )
2676  , mMapUnprefixedTypenameToPrefixedTypename( mapUnprefixedTypenameToPrefixedTypename )
2677 {
2678 }
2679 
2681 {
2682  switch ( node->nodeType() )
2683  {
2685  return toOgcFilter( static_cast<const QgsSQLStatement::NodeUnaryOperator *>( node ) );
2687  return toOgcFilter( static_cast<const QgsSQLStatement::NodeBinaryOperator *>( node ) );
2689  return toOgcFilter( static_cast<const QgsSQLStatement::NodeInOperator *>( node ) );
2691  return toOgcFilter( static_cast<const QgsSQLStatement::NodeBetweenOperator *>( node ) );
2693  return toOgcFilter( static_cast<const QgsSQLStatement::NodeFunction *>( node ) );
2695  return toOgcFilter( static_cast<const QgsSQLStatement::NodeLiteral *>( node ) );
2697  return toOgcFilter( static_cast<const QgsSQLStatement::NodeColumnRef *>( node ) );
2699  return toOgcFilter( static_cast<const QgsSQLStatement::NodeSelect *>( node ) );
2700 
2701  default:
2702  mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
2703  return QDomElement();
2704  }
2705 }
2706 
2707 
2709 {
2710 
2711  QDomElement operandElem = toOgcFilter( node->operand() );
2712  if ( !mErrorMessage.isEmpty() )
2713  return QDomElement();
2714 
2715  QDomElement uoElem;
2716  switch ( node->op() )
2717  {
2719  uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2720  if ( node->operand()->nodeType() == QgsSQLStatement::ntLiteral )
2721  {
2722  // operand expression already created a Literal node:
2723  // take the literal value, prepend - and remove old literal node
2724  uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2725  mDoc.removeChild( operandElem );
2726  }
2727  else
2728  {
2729  mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2730  return QDomElement();
2731  }
2732  break;
2734  uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2735  uoElem.appendChild( operandElem );
2736  break;
2737 
2738  default:
2739  mErrorMessage = QObject::tr( "Unary operator %1 not implemented yet" ).arg( QgsSQLStatement::UNARY_OPERATOR_TEXT[node->op()] );
2740  return QDomElement();
2741  }
2742 
2743  return uoElem;
2744 }
2745 
2746 
2748 {
2749  QDomElement leftElem = toOgcFilter( node->opLeft() );
2750  if ( !mErrorMessage.isEmpty() )
2751  return QDomElement();
2752 
2753  QgsSQLStatement::BinaryOperator op = node->op();
2754 
2755  // before right operator is parsed: to allow NULL handling
2756  if ( op == QgsSQLStatement::boIs || op == QgsSQLStatement::boIsNot )
2757  {
2758  if ( node->opRight()->nodeType() == QgsSQLStatement::ntLiteral )
2759  {
2760  const QgsSQLStatement::NodeLiteral *rightLit = static_cast<const QgsSQLStatement::NodeLiteral *>( node->opRight() );
2761  if ( rightLit->value().isNull() )
2762  {
2763 
2764  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2765  elem.appendChild( leftElem );
2766 
2767  if ( op == QgsSQLStatement::boIsNot )
2768  {
2769  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2770  notElem.appendChild( elem );
2771  return notElem;
2772  }
2773 
2774  return elem;
2775  }
2776 
2777  // continue with equal / not equal operator once the null case is handled
2779  }
2780 
2781  }
2782 
2783  QDomElement rightElem = toOgcFilter( node->opRight() );
2784  if ( !mErrorMessage.isEmpty() )
2785  return QDomElement();
2786 
2787 
2788  QString opText;
2789  if ( op == QgsSQLStatement::boOr )
2790  opText = QStringLiteral( "Or" );
2791  else if ( op == QgsSQLStatement::boAnd )
2792  opText = QStringLiteral( "And" );
2793  else if ( op == QgsSQLStatement::boEQ )
2794  opText = QStringLiteral( "PropertyIsEqualTo" );
2795  else if ( op == QgsSQLStatement::boNE )
2796  opText = QStringLiteral( "PropertyIsNotEqualTo" );
2797  else if ( op == QgsSQLStatement::boLE )
2798  opText = QStringLiteral( "PropertyIsLessThanOrEqualTo" );
2799  else if ( op == QgsSQLStatement::boGE )
2800  opText = QStringLiteral( "PropertyIsGreaterThanOrEqualTo" );
2801  else if ( op == QgsSQLStatement::boLT )
2802  opText = QStringLiteral( "PropertyIsLessThan" );
2803  else if ( op == QgsSQLStatement::boGT )
2804  opText = QStringLiteral( "PropertyIsGreaterThan" );
2805  else if ( op == QgsSQLStatement::boLike )
2806  opText = QStringLiteral( "PropertyIsLike" );
2807  else if ( op == QgsSQLStatement::boILike )
2808  opText = QStringLiteral( "PropertyIsLike" );
2809 
2810  if ( opText.isEmpty() )
2811  {
2812  // not implemented binary operators
2813  mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( QgsSQLStatement::BINARY_OPERATOR_TEXT[op] );
2814  return QDomElement();
2815  }
2816 
2817  QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2818 
2820  {
2821  if ( op == QgsSQLStatement::boILike )
2822  boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2823 
2824  // setup wildCards to <ogc:PropertyIsLike>
2825  boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2826  boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2827  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2828  boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2829  else
2830  boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2831  }
2832 
2833  boElem.appendChild( leftElem );
2834  boElem.appendChild( rightElem );
2835  return boElem;
2836 }
2837 
2838 
2840 {
2841  QString value;
2842  switch ( node->value().type() )
2843  {
2844  case QVariant::Int:
2845  value = QString::number( node->value().toInt() );
2846  break;
2847  case QVariant::LongLong:
2848  value = QString::number( node->value().toLongLong() );
2849  break;
2850  case QVariant::Double:
2851  value = qgsDoubleToString( node->value().toDouble() );
2852  break;
2853  case QVariant::String:
2854  value = node->value().toString();
2855  break;
2856 
2857  default:
2858  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( node->value().type() );
2859  return QDomElement();
2860  }
2861 
2862  QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2863  litElem.appendChild( mDoc.createTextNode( value ) );
2864  return litElem;
2865 }
2866 
2867 
2869 {
2870  QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2871  if ( node->tableName().isEmpty() || mLayerProperties.size() == 1 )
2872  propElem.appendChild( mDoc.createTextNode( node->name() ) );
2873  else
2874  {
2875  QString tableName( mMapTableAliasToNames[node->tableName()] );
2876  if ( mMapUnprefixedTypenameToPrefixedTypename.contains( tableName ) )
2877  tableName = mMapUnprefixedTypenameToPrefixedTypename[tableName];
2878  propElem.appendChild( mDoc.createTextNode( tableName + "/" + node->name() ) );
2879  }
2880  return propElem;
2881 }
2882 
2884 {
2885  if ( node->list()->list().size() == 1 )
2886  return toOgcFilter( node->list()->list()[0] );
2887 
2888  QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2889  QDomElement leftNode = toOgcFilter( node->node() );
2890 
2891  Q_FOREACH ( QgsSQLStatement::Node *n, node->list()->list() )
2892  {
2893  QDomElement listNode = toOgcFilter( n );
2894  if ( !mErrorMessage.isEmpty() )
2895  return QDomElement();
2896 
2897  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2898  eqElem.appendChild( leftNode.cloneNode() );
2899  eqElem.appendChild( listNode );
2900 
2901  orElem.appendChild( eqElem );
2902  }
2903 
2904  if ( node->isNotIn() )
2905  {
2906  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2907  notElem.appendChild( orElem );
2908  return notElem;
2909  }
2910 
2911  return orElem;
2912 }
2913 
2915 {
2916  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsBetween" );
2917  elem.appendChild( toOgcFilter( node->node() ) );
2918  QDomElement lowerBoundary = mDoc.createElement( mFilterPrefix + ":LowerBoundary" );
2919  lowerBoundary.appendChild( toOgcFilter( node->minVal() ) );
2920  elem.appendChild( lowerBoundary );
2921  QDomElement upperBoundary = mDoc.createElement( mFilterPrefix + ":UpperBoundary" );
2922  upperBoundary.appendChild( toOgcFilter( node->maxVal() ) );
2923  elem.appendChild( upperBoundary );
2924 
2925  if ( node->isNotBetween() )
2926  {
2927  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2928  notElem.appendChild( elem );
2929  return notElem;
2930  }
2931 
2932  return elem;
2933 }
2934 
2935 static QString mapBinarySpatialToOgc( const QString &name )
2936 {
2937  QString nameCompare( name );
2938  if ( name.size() > 3 && name.midRef( 0, 3 ).compare( QStringLiteral( "ST_" ), Qt::CaseInsensitive ) == 0 )
2939  nameCompare = name.mid( 3 );
2940  QStringList spatialOps;
2941  spatialOps << QStringLiteral( "BBOX" ) << QStringLiteral( "Intersects" ) << QStringLiteral( "Contains" ) << QStringLiteral( "Crosses" ) << QStringLiteral( "Equals" )
2942  << QStringLiteral( "Disjoint" ) << QStringLiteral( "Overlaps" ) << QStringLiteral( "Touches" ) << QStringLiteral( "Within" );
2943  Q_FOREACH ( QString op, spatialOps )
2944  {
2945  if ( nameCompare.compare( op, Qt::CaseInsensitive ) == 0 )
2946  return op;
2947  }
2948  return QString();
2949 }
2950 
2951 static QString mapTernarySpatialToOgc( const QString &name )
2952 {
2953  QString nameCompare( name );
2954  if ( name.size() > 3 && name.midRef( 0, 3 ).compare( QStringLiteral( "ST_" ), Qt::CaseInsensitive ) == 0 )
2955  nameCompare = name.mid( 3 );
2956  if ( nameCompare.compare( QLatin1String( "DWithin" ), Qt::CaseInsensitive ) == 0 )
2957  return QStringLiteral( "DWithin" );
2958  if ( nameCompare.compare( QLatin1String( "Beyond" ), Qt::CaseInsensitive ) == 0 )
2959  return QStringLiteral( "Beyond" );
2960  return QString();
2961 }
2962 
2963 QString QgsOgcUtilsSQLStatementToFilter::getGeometryColumnSRSName( const QgsSQLStatement::Node *node )
2964 {
2965  if ( node->nodeType() != QgsSQLStatement::ntColumnRef )
2966  return QString();
2967 
2968  const QgsSQLStatement::NodeColumnRef *col = static_cast<const QgsSQLStatement::NodeColumnRef *>( node );
2969  if ( !col->tableName().isEmpty() )
2970  {
2971  Q_FOREACH ( QgsOgcUtils::LayerProperties prop, mLayerProperties )
2972  {
2973  if ( prop.mName.compare( mMapTableAliasToNames[col->tableName()], Qt::CaseInsensitive ) == 0 &&
2974  prop.mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
2975  {
2976  return prop.mSRSName;
2977  }
2978  }
2979  }
2980  if ( !mLayerProperties.empty() &&
2981  mLayerProperties.at( 0 ).mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
2982  {
2983  return mLayerProperties.at( 0 ).mSRSName;
2984  }
2985  return QString();
2986 }
2987 
2988 bool QgsOgcUtilsSQLStatementToFilter::processSRSName( const QgsSQLStatement::NodeFunction *mainNode,
2989  QList<QgsSQLStatement::Node *> args,
2990  bool lastArgIsSRSName,
2991  QString &srsName,
2992  bool &axisInversion )
2993 {
2994  srsName = mCurrentSRSName;
2995  axisInversion = mInvertAxisOrientation;
2996 
2997  if ( lastArgIsSRSName )
2998  {
2999  QgsSQLStatement::Node *lastArg = args[ args.size() - 1 ];
3000  if ( lastArg->nodeType() != QgsSQLStatement::ntLiteral )
3001  {
3002  mErrorMessage = QObject::tr( "%1: Last argument must be string or integer literal" ).arg( mainNode->name() );
3003  return false;
3004  }
3005  const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( lastArg );
3006  if ( lit->value().type() == QVariant::Int )
3007  {
3008  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
3009  {
3010  srsName = "EPSG:" + QString::number( lit->value().toInt() );
3011  }
3012  else
3013  {
3014  srsName = "urn:ogc:def:crs:EPSG::" + QString::number( lit->value().toInt() );
3015  }
3016  }
3017  else
3018  {
3019  srsName = lit->value().toString();
3020  if ( srsName.startsWith( QLatin1String( "EPSG:" ), Qt::CaseInsensitive ) )
3021  return true;
3022  }
3023  }
3024 
3026  if ( !srsName.isEmpty() )
3028  if ( crs.isValid() )
3029  {
3030  if ( mHonourAxisOrientation && crs.hasAxisInverted() )
3031  {
3032  axisInversion = !axisInversion;
3033  }
3034  }
3035 
3036  return true;
3037 }
3038 
3040 {
3041  // ST_GeometryFromText
3042  if ( node->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 )
3043  {
3044  QList<QgsSQLStatement::Node *> args = node->args()->list();
3045  if ( args.size() != 1 && args.size() != 2 )
3046  {
3047  mErrorMessage = QObject::tr( "Function %1 should have 1 or 2 arguments" ).arg( node->name() );
3048  return QDomElement();
3049  }
3050 
3051  QgsSQLStatement::Node *firstFnArg = args[0];
3052  if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
3053  {
3054  mErrorMessage = QObject::tr( "%1: First argument must be string literal" ).arg( node->name() );
3055  return QDomElement();
3056  }
3057 
3058  QString srsName;
3059  bool axisInversion;
3060  if ( ! processSRSName( node, args, args.size() == 2, srsName, axisInversion ) )
3061  {
3062  return QDomElement();
3063  }
3064 
3065  QString wkt = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
3066  QgsGeometry geom = QgsGeometry::fromWkt( wkt );
3067  QDomElement geomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, srsName, axisInversion,
3068  QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
3069  mGeomId ++;
3070  if ( geomElem.isNull() )
3071  {
3072  mErrorMessage = QObject::tr( "%1: invalid WKT" ).arg( node->name() );
3073  return QDomElement();
3074  }
3075  mGMLUsed = true;
3076  return geomElem;
3077  }
3078 
3079  // ST_MakeEnvelope
3080  if ( node->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 )
3081  {
3082  QList<QgsSQLStatement::Node *> args = node->args()->list();
3083  if ( args.size() != 4 && args.size() != 5 )
3084  {
3085  mErrorMessage = QObject::tr( "Function %1 should have 4 or 5 arguments" ).arg( node->name() );
3086  return QDomElement();
3087  }
3088 
3089  QgsRectangle rect;
3090 
3091  for ( int i = 0; i < 4; i++ )
3092  {
3093  QgsSQLStatement::Node *arg = args[i];
3094  if ( arg->nodeType() != QgsSQLStatement::ntLiteral )
3095  {
3096  mErrorMessage = QObject::tr( "%1: Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
3097  return QDomElement();
3098  }
3099  const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( arg );
3100  double val = 0.0;
3101  if ( lit->value().type() == QVariant::Int )
3102  val = lit->value().toInt();
3103  else if ( lit->value().type() == QVariant::LongLong )
3104  val = lit->value().toLongLong();
3105  else if ( lit->value().type() == QVariant::Double )
3106  val = lit->value().toDouble();
3107  else
3108  {
3109  mErrorMessage = QObject::tr( "%1 Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
3110  return QDomElement();
3111  }
3112  if ( i == 0 )
3113  rect.setXMinimum( val );
3114  else if ( i == 1 )
3115  rect.setYMinimum( val );
3116  else if ( i == 2 )
3117  rect.setXMaximum( val );
3118  else
3119  rect.setYMaximum( val );
3120  }
3121 
3122  QString srsName;
3123  bool axisInversion;
3124  if ( ! processSRSName( node, args, args.size() == 5, srsName, axisInversion ) )
3125  {
3126  return QDomElement();
3127  }
3128 
3129  mGMLUsed = true;
3130 
3131  return ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
3132  QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, srsName, axisInversion, 15 ) :
3133  QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, srsName, axisInversion, 15 );
3134  }
3135 
3136  // ST_GeomFromGML
3137  if ( node->name().compare( QLatin1String( "ST_GeomFromGML" ), Qt::CaseInsensitive ) == 0 )
3138  {
3139  QList<QgsSQLStatement::Node *> args = node->args()->list();
3140  if ( args.size() != 1 )
3141  {
3142  mErrorMessage = QObject::tr( "Function %1 should have 1 argument" ).arg( node->name() );
3143  return QDomElement();
3144  }
3145 
3146  QgsSQLStatement::Node *firstFnArg = args[0];
3147  if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
3148  {
3149  mErrorMessage = QObject::tr( "%1: Argument must be string literal" ).arg( node->name() );
3150  return QDomElement();
3151  }
3152 
3153  QDomDocument geomDoc;
3154  QString gml = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
3155  if ( !geomDoc.setContent( gml, true ) )
3156  {
3157  mErrorMessage = QObject::tr( "ST_GeomFromGML: unable to parse XML" );
3158  return QDomElement();
3159  }
3160 
3161  QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
3162  mGMLUsed = true;
3163  return geomNode.toElement();
3164  }
3165 
3166  // Binary geometry operators
3167  QString ogcName( mapBinarySpatialToOgc( node->name() ) );
3168  if ( !ogcName.isEmpty() )
3169  {
3170  QList<QgsSQLStatement::Node *> args = node->args()->list();
3171  if ( args.size() != 2 )
3172  {
3173  mErrorMessage = QObject::tr( "Function %1 should have 2 arguments" ).arg( node->name() );
3174  return QDomElement();
3175  }
3176 
3177  for ( int i = 0; i < 2; i ++ )
3178  {
3179  if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
3180  ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
3181  static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
3182  {
3183  mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
3184  break;
3185  }
3186  }
3187 
3188  //if( ogcName == "Intersects" && mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
3189  // ogcName = "Intersect";
3190  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + ogcName );
3191  Q_FOREACH ( QgsSQLStatement::Node *n, args )
3192  {
3193  QDomElement childElem = toOgcFilter( n );
3194  if ( !mErrorMessage.isEmpty() )
3195  {
3196  mCurrentSRSName.clear();
3197  return QDomElement();
3198  }
3199 
3200  funcElem.appendChild( childElem );
3201  }
3202 
3203  mCurrentSRSName.clear();
3204  return funcElem;
3205  }
3206 
3207  ogcName = mapTernarySpatialToOgc( node->name() );
3208  if ( !ogcName.isEmpty() )
3209  {
3210  QList<QgsSQLStatement::Node *> args = node->args()->list();
3211  if ( args.size() != 3 )
3212  {
3213  mErrorMessage = QObject::tr( "Function %1 should have 3 arguments" ).arg( node->name() );
3214  return QDomElement();
3215  }
3216 
3217  for ( int i = 0; i < 2; i ++ )
3218  {
3219  if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
3220  ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
3221  static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
3222  {
3223  mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
3224  break;
3225  }
3226  }
3227 
3228  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + node->name().mid( 3 ) );
3229  for ( int i = 0; i < 2; i++ )
3230  {
3231  QDomElement childElem = toOgcFilter( args[i] );
3232  if ( !mErrorMessage.isEmpty() )
3233  {
3234  mCurrentSRSName.clear();
3235  return QDomElement();
3236  }
3237 
3238  funcElem.appendChild( childElem );
3239  }
3240  mCurrentSRSName.clear();
3241 
3242  QgsSQLStatement::Node *distanceNode = args[2];
3243  if ( distanceNode->nodeType() != QgsSQLStatement::ntLiteral )
3244  {
3245  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() );
3246  return QDomElement();
3247  }
3248  const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( distanceNode );
3249  if ( lit->value().isNull() )
3250  {
3251  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() );
3252  return QDomElement();
3253  }
3254  QString distance;
3255  QString unit( QStringLiteral( "m" ) );
3256  switch ( lit->value().type() )
3257  {
3258  case QVariant::Int:
3259  distance = QString::number( lit->value().toInt() );
3260  break;
3261  case QVariant::LongLong:
3262  distance = QString::number( lit->value().toLongLong() );
3263  break;
3264  case QVariant::Double:
3265  distance = qgsDoubleToString( lit->value().toDouble() );
3266  break;
3267  case QVariant::String:
3268  {
3269  distance = lit->value().toString();
3270  for ( int i = 0; i < distance.size(); i++ )
3271  {
3272  if ( !( ( distance[i] >= '0' && distance[i] <= '9' ) || distance[i] == '-' || distance[i] == '.' || distance[i] == 'e' || distance[i] == 'E' ) )
3273  {
3274  unit = distance.mid( i ).trimmed();
3275  distance = distance.mid( 0, i );
3276  break;
3277  }
3278  }
3279  break;
3280  }
3281 
3282  default:
3283  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( lit->value().type() );
3284  return QDomElement();
3285  }
3286 
3287  QDomElement distanceElem = mDoc.createElement( mFilterPrefix + ":Distance" );
3288  if ( mFilterVersion == QgsOgcUtils::FILTER_FES_2_0 )
3289  distanceElem.setAttribute( QStringLiteral( "uom" ), unit );
3290  else
3291  distanceElem.setAttribute( QStringLiteral( "unit" ), unit );
3292  distanceElem.appendChild( mDoc.createTextNode( distance ) );
3293  funcElem.appendChild( distanceElem );
3294  return funcElem;
3295  }
3296 
3297  // Other function
3298  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
3299  funcElem.setAttribute( QStringLiteral( "name" ), node->name() );
3300  Q_FOREACH ( QgsSQLStatement::Node *n, node->args()->list() )
3301  {
3302  QDomElement childElem = toOgcFilter( n );
3303  if ( !mErrorMessage.isEmpty() )
3304  return QDomElement();
3305 
3306  funcElem.appendChild( childElem );
3307  }
3308  return funcElem;
3309 }
3310 
3312  const QString &leftTable )
3313 {
3314  QgsSQLStatement::Node *onExpr = node->onExpr();
3315  if ( onExpr )
3316  {
3317  return toOgcFilter( onExpr );
3318  }
3319 
3320  QList<QDomElement> listElem;
3321  Q_FOREACH ( const QString &columnName, node->usingColumns() )
3322  {
3323  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
3324  QDomElement propElem1 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3325  propElem1.appendChild( mDoc.createTextNode( leftTable + "/" + columnName ) );
3326  eqElem.appendChild( propElem1 );
3327  QDomElement propElem2 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3328  propElem2.appendChild( mDoc.createTextNode( node->tableDef()->name() + "/" + columnName ) );
3329  eqElem.appendChild( propElem2 );
3330  listElem.append( eqElem );
3331  }
3332 
3333  if ( listElem.size() == 1 )
3334  {
3335  return listElem[0];
3336  }
3337  else if ( listElem.size() > 1 )
3338  {
3339  QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3340  Q_FOREACH ( const QDomElement &elem, listElem )
3341  {
3342  andElem.appendChild( elem );
3343  }
3344  return andElem;
3345  }
3346 
3347  return QDomElement();
3348 }
3349 
3350 void QgsOgcUtilsSQLStatementToFilter::visit( const QgsSQLStatement::NodeTableDef *node )
3351 {
3352  if ( node->alias().isEmpty() )
3353  {
3354  mMapTableAliasToNames[ node->name()] = node->name();
3355  }
3356  else
3357  {
3358  mMapTableAliasToNames[ node->alias()] = node->name();
3359  }
3360 }
3361 
3363 {
3364  QList<QDomElement> listElem;
3365 
3366  if ( mFilterVersion != QgsOgcUtils::FILTER_FES_2_0 &&
3367  ( node->tables().size() != 1 || !node->joins().empty() ) )
3368  {
3369  mErrorMessage = QObject::tr( "Joins are only supported with WFS 2.0" );
3370  return QDomElement();
3371  }
3372 
3373  // Register all table name aliases
3374  Q_FOREACH ( QgsSQLStatement::NodeTableDef *table, node->tables() )
3375  {
3376  visit( table );
3377  }
3378  Q_FOREACH ( QgsSQLStatement::NodeJoin *join, node->joins() )
3379  {
3380  visit( join->tableDef() );
3381  }
3382 
3383  // Process JOIN conditions
3384  QList< QgsSQLStatement::NodeTableDef *> nodeTables = node->tables();
3385  QString leftTable = nodeTables.at( nodeTables.length() - 1 )->name();
3386  Q_FOREACH ( QgsSQLStatement::NodeJoin *join, node->joins() )
3387  {
3388  QDomElement joinElem = toOgcFilter( join, leftTable );
3389  if ( !mErrorMessage.isEmpty() )
3390  return QDomElement();
3391  listElem.append( joinElem );
3392  leftTable = join->tableDef()->name();
3393  }
3394 
3395  // Process WHERE conditions
3396  if ( node->where() )
3397  {
3398  QDomElement whereElem = toOgcFilter( node->where() );
3399  if ( !mErrorMessage.isEmpty() )
3400  return QDomElement();
3401  listElem.append( whereElem );
3402  }
3403 
3404  // Concatenate all conditions
3405  if ( listElem.size() == 1 )
3406  {
3407  return listElem[0];
3408  }
3409  else if ( listElem.size() > 1 )
3410  {
3411  QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3412  Q_FOREACH ( const QDomElement &elem, listElem )
3413  {
3414  andElem.appendChild( elem );
3415  }
3416  return andElem;
3417  }
3418 
3419  return QDomElement();
3420 }
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsSQLStatement::NodeList * list() const
Values list.
A rectangle specified with double values.
Definition: qgsrectangle.h:40
Internal use by QgsOgcUtils.
Definition: qgsogcutils.h:328
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
QgsSQLStatement::Node * onExpr() const
On expression. Will be nullptr if usingColumns() is not empty.
int params() const
The number of parameters this function takes.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:134
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
void setExpression(const QString &expression)
Set the expression string, will reset the whole internal structure.
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer&#39;s length...
QgsExpressionNode * opRight() const
Returns the node to the right of the operator.
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
A class to represent a 2D point.
Definition: qgspointxy.h:43
QgsSQLStatement::NodeTableDef * tableDef() const
Table definition.
QString dump() const
Returns an expression string, constructed from the internal abstract syntax tree. ...
QgsSQLStatement::Node * operand() const
Operand.
QgsSQLStatement::Node * where() const
Returns the where clause.
QVector< QgsPolylineXY > QgsPolygonXY
Polygon: first item of the list is outer ring, inner rings (if any) start from second item...
Definition: qgsgeometry.h:66
static const char * BINARY_OPERATOR_TEXT[]
bool convertCompatible(QVariant &v) const
Converts the provided variant to a compatible format.
Definition: qgsfield.cpp:266
QString errorMessage() const
Returns the error message.
Definition: qgsogcutils.h:347
bool GMLNamespaceUsed() const
Returns whether the gml: namespace is used.
Definition: qgsogcutils.h:391
QDomElement expressionNodeToOgcFilter(const QgsExpressionNode *node)
Convert an expression to a OGC filter.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:104
QgsExpressionNode::NodeList * list() const
Returns the list of nodes to search for matching values within.
QgsExpressionNode * opLeft() const
Returns the node to the left of the operator.
static QDomElement rectangleToGMLBox(QgsRectangle *box, QDomDocument &doc, int precision=17)
Exports the rectangle to GML2 Box.
QDomElement toOgcFilter(const QgsSQLStatement::Node *node)
Convert a SQL statement to a OGC filter.
Binary logical/arithmetical operator (AND, OR, =, +, ...)
QString name() const
The name of the column.
Layer properties.
Definition: qgsogcutils.h:212
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
Definition: qgsgeometry.h:83
QString name() const
Returns function name.
QgsOgcUtilsSQLStatementToFilter(QDomDocument &doc, QgsOgcUtils::GMLVersion gmlVersion, QgsOgcUtils::FilterVersion filterVersion, const QList< QgsOgcUtils::LayerProperties > &layerProperties, bool honourAxisOrientation, bool invertAxisOrientation, const QMap< QString, QString > &mapUnprefixedTypenameToPrefixedTypename)
Constructor.
static QgsRectangle rectangleFromGMLBox(const QDomNode &boxNode)
Read rectangle from GML2 Box.
Abstract node class.
QgsSQLStatement::Node * node() const
Variable at the left of BETWEEN.
QList< QString > usingColumns() const
Columns referenced by USING.
const QString GML_NAMESPACE
static QgsRectangle rectangleFromGMLEnvelope(const QDomNode &envelopeNode)
Read rectangle from GML3 Envelope.
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
Definition: qgsgeometry.h:76
QgsSQLStatement::Node * minVal() const
Minimum bound.
bool GMLNamespaceUsed() const
Returns whether the gml: namespace is used.
Definition: qgsogcutils.h:344
QString mName
Layer name.
Definition: qgsogcutils.h:219
bool isNotIn() const
Whether this is a NOT IN operator.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
QString text() const
Returns a the name of this operator without the operands.
Class for parsing SQL statements.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:67
#define FALLTHROUGH
Definition: qgis.h:570
QString name() const
Table name.
QgsFields fields() const override
Returns the list of fields of this layer.
QString alias() const
Table alias.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
QgsSQLStatement::NodeList * args() const
Returns arguments.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:237
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:139
BinaryOperator
list of binary operators
An expression node for value IN or NOT IN clauses.
Literal value (integer, integer64, double, string)
QString errorMessage() const
Returns the error message.
Definition: qgsogcutils.h:394
QString name() const
The name of the column.
QList< QgsSQLStatement::NodeTableDef * > tables() const
Returns the list of tables.
Internal use by QgsOgcUtils.
Definition: qgsogcutils.h:375
An expression node which takes it value from a feature&#39;s field.
Unary logicial/arithmetical operator ( NOT, - )
QgsSQLStatement::BinaryOperator op() const
Operator.
const QgsSQLStatement::Node * rootNode() const
Returns root node of the statement. Root node is null is parsing has failed.
static QDomElement SQLStatementToOgcFilter(const QgsSQLStatement &statement, QDomDocument &doc, QgsOgcUtils::GMLVersion gmlVersion, FilterVersion filterVersion, const QList< LayerProperties > &layerProperties, bool honourAxisOrientation, bool invertAxisOrientation, const QMap< QString, QString > &mapUnprefixedTypenameToPrefixedTypename, QString *errorMessage=nullptr)
Creates OGC filter XML element from the WHERE and JOIN clauses of a SQL statement.
BinaryOperator
list of binary operators
QString mSRSName
SRS name.
Definition: qgsogcutils.h:223
Abstract base class for all nodes that can appear in an expression.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
An expression node for expression functions.
static const QList< QgsExpressionFunction * > & Functions()
QgsSQLStatement::Node * maxVal() const
Maximum bound.
bool isNotBetween() const
Whether this is a NOT BETWEEN operator.
FilterVersion
OGC filter version.
Definition: qgsogcutils.h:157
QByteArray asWkb() const
Export the geometry to WKB.
Reference to a column.
&#39;X BETWEEN y and z&#39; operator
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:176
QString name() const
The name of the function.
Custom exception class for Wkb related exceptions.
Definition: qgswkbptr.h:31
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:161
QVariant value() const
The value of the literal.
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
QList< QgsSQLStatement::NodeJoin * > joins() const
Returns the list of joins.
A abstract base class for defining QgsExpression functions.
A list of expression nodes.
static QgsGeometry geometryFromGML(const QString &xmlString)
Static method that creates geometry from GML.
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition: qgsgeometry.h:42
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
QgsExpressionNodeUnaryOperator::UnaryOperator op() const
Returns the unary operator.
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:189
An expression node for literal values.
const QgsExpressionNode * rootNode() const
Returns root node of the expression. Root node is null is parsing has failed.
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:144
static QDomElement expressionToOgcFilter(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=nullptr)
Creates OGC filter XML element.
QList< QgsSQLStatement::Node * > list()
Returns list.
A unary node is either negative as in boolean (not) or as in numbers (minus).
static QDomElement expressionToOgcExpression(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=nullptr)
Creates an OGC expression XML element.
A binary expression operator, which operates on two values.
bool hasAxisInverted() const
Returns whether axis is inverted (e.g., for WMS 1.3) for the CRS.
This class represents a coordinate reference system (CRS).
static QDomElement geometryToGML(const QgsGeometry &geometry, QDomDocument &doc, QgsOgcUtils::GMLVersion gmlVersion, const QString &srsName, bool invertAxisOrientation, const QString &gmlIdBase, int precision=17)
Exports the geometry to GML.
QString text() const
Returns a the name of this operator without the operands.
static QDomElement rectangleToGMLEnvelope(QgsRectangle *env, QDomDocument &doc, int precision=17)
Exports the rectangle to GML3 Envelope.
static const char * UNARY_OPERATOR_TEXT[]
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:166
QString mGeometryAttribute
Geometry attribute name.
Definition: qgsogcutils.h:221
static QColor colorFromOgcFill(const QDomElement &fillElement)
Parse XML with OGC fill into QColor.
GMLVersion
GML version.
Definition: qgsogcutils.h:57
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:171
void normalize()
Normalize the rectangle so it has non-negative width/height.
Definition: qgsrectangle.h:181
QgsSQLStatement::UnaryOperator op() const
Operator.
void append(QgsExpressionNode *node)
Takes ownership of the provided node.
static QgsExpression * expressionFromOgcFilter(const QDomElement &element, QgsVectorLayer *layer=nullptr)
Parse XML with OGC filter into QGIS expression.
QgsExpressionNode * node() const
Returns the expression node.
Represents a vector layer which manages a vector based data sets.
&#39;x IN (y, z)&#39; operator
bool isNotIn() const
Returns true if this node is a "NOT IN" operator, or false if the node is a normal "IN" operator...
virtual QgsExpressionNode::NodeType nodeType() const =0
Gets the type of this node.
QgsExpressionNode * operand() const
Returns the node the operator will operate upon.
QgsSQLStatement::Node * node() const
Variable at the left of IN.
The QgsOgcUtils class provides various utility functions for conversion between OGC (Open Geospatial ...
Definition: qgsogcutils.h:50
QgsExpressionNodeBinaryOperator::BinaryOperator op() const
Returns the binary operator.
QgsSQLStatement::Node * opLeft() const
Left operand.
QgsField field(int fieldIdx) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:150
QgsSQLStatement::Node * opRight() const
Right operand.
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:129
QVariant value() const
The value of the literal.
virtual QgsSQLStatement::NodeType nodeType() const =0
Abstract virtual that returns the type of this node.
int fnIndex() const
Returns the index of the node&#39;s function.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.