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