QGIS API Documentation  2.14.11-Essen
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 
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 {
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
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'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
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
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
NodeList * list() const
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
WkbType
Used for symbology operations.
Definition: qgis.h:57
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:264
static const QString GML_NAMESPACE
Definition: qgsogcutils.cpp:34
QgsExpressionPrivate * d
NodeList * args() const
#define FALLTHROUGH
Definition: qgis.h:439
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)
Definition: qgis.h:274
bool isEmpty() const
bool isEmpty() const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
QGis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
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)
iterator end()
QString toLower() const
BinaryOperator op() const
QDomNode removeChild(const QDomNode &oldChild)
bool isNull() const
static const QString OGC_NAMESPACE
Definition: qgsogcutils.cpp:36
const Key key(const T &value) const
static bool isSpatialOperator(const QString &tagName)
QString & replace(int position, int n, QChar after)
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
QString escape(const QString &plain)
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.
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
UnaryOperator op() 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:261
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[]