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