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