QGIS API Documentation  2.14.18-Essen
qgsogcutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsogcutils.cpp
3  ---------------------
4  begin : March 2013
5  copyright : (C) 2013 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include "qgsogcutils.h"
16 
17 #include "qgsexpression.h"
18 #include "qgsexpressionprivate.h"
19 #include "qgsgeometry.h"
20 #include "qgswkbptr.h"
22 #include "qgslogger.h"
23 
24 #include <QColor>
25 #include <QStringList>
26 #include <QTextStream>
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  // check if it is a single string value => no DomElement
1589  if ( element.firstChild().nodeType() == QDomNode::TextNode )
1590  {
1591  QgsExpression *expr = new QgsExpression( element.firstChild().nodeValue() );
1592  expr->d->mParserErrorString = QString();
1593  return expr;
1594  }
1595 
1596  // now parse OGC operators. OGC operator does not have a only text value
1597  // but only sub element operators
1598  QgsExpression *expr = new QgsExpression();
1599 
1600  QDomElement childElem = element.firstChildElement();
1601  while ( !childElem.isNull() )
1602  {
1603  QString errorMsg;
1604  QgsExpression::Node *node = nodeFromOgcFilter( childElem, errorMsg );
1605  if ( !node )
1606  {
1607  // invalid expression, parser error
1608  expr->d->mParserErrorString = errorMsg;
1609  return expr;
1610  }
1611 
1612  // use the concat binary operator to append to the root node
1613  if ( !expr->d->mRootNode )
1614  {
1615  expr->d->mRootNode = node;
1616  }
1617  else
1618  {
1619  expr->d->mRootNode = new QgsExpression::NodeBinaryOperator( QgsExpression::boConcat, expr->d->mRootNode, node );
1620  }
1621 
1622  childElem = childElem.nextSiblingElement();
1623  }
1624 
1625  // update expression string
1626  expr->d->mExp = expr->dump();
1627 
1628  return expr;
1629 }
1630 
1631 
1633 {
1634  static QMap<QString, int> binOps;
1635  if ( binOps.isEmpty() )
1636  {
1637  // logical
1638  binOps.insert( "Or", QgsExpression::boOr );
1639  binOps.insert( "And", QgsExpression::boAnd );
1640  // comparison
1641  binOps.insert( "PropertyIsEqualTo", QgsExpression::boEQ );
1642  binOps.insert( "PropertyIsNotEqualTo", QgsExpression::boNE );
1643  binOps.insert( "PropertyIsLessThanOrEqualTo", QgsExpression::boLE );
1644  binOps.insert( "PropertyIsGreaterThanOrEqualTo", QgsExpression::boGE );
1645  binOps.insert( "PropertyIsLessThan", QgsExpression::boLT );
1646  binOps.insert( "PropertyIsGreaterThan", QgsExpression::boGT );
1647  binOps.insert( "PropertyIsLike", QgsExpression::boLike );
1648  // arithmetics
1649  binOps.insert( "Add", QgsExpression::boPlus );
1650  binOps.insert( "Sub", QgsExpression::boMinus );
1651  binOps.insert( "Mul", QgsExpression::boMul );
1652  binOps.insert( "Div", QgsExpression::boDiv );
1653  }
1654  return binOps;
1655 }
1656 
1657 static int binaryOperatorFromTagName( const QString& tagName )
1658 {
1659 
1660  return binaryOperatorsTagNamesMap().value( tagName, -1 );
1661 }
1662 
1664 {
1665  if ( op == QgsExpression::boILike )
1666  {
1667  return "PropertyIsLike";
1668  }
1669  return binaryOperatorsTagNamesMap().key( op, QString() );
1670 }
1671 
1672 static bool isBinaryOperator( const QString& tagName )
1673 {
1674  return binaryOperatorFromTagName( tagName ) >= 0;
1675 }
1676 
1677 
1678 static bool isSpatialOperator( const QString& tagName )
1679 {
1680  static QStringList spatialOps;
1681  if ( spatialOps.isEmpty() )
1682  {
1683  spatialOps << "BBOX" << "Intersects" << "Contains" << "Crosses" << "Equals"
1684  << "Disjoint" << "Overlaps" << "Touches" << "Within";
1685  }
1686 
1687  return spatialOps.contains( tagName );
1688 }
1689 
1690 
1691 
1692 QgsExpression::Node* QgsOgcUtils::nodeFromOgcFilter( QDomElement &element, QString &errorMessage )
1693 {
1694  if ( element.isNull() )
1695  return nullptr;
1696 
1697  // check for binary operators
1698  if ( isBinaryOperator( element.tagName() ) )
1699  {
1700  return nodeBinaryOperatorFromOgcFilter( element, errorMessage );
1701  }
1702 
1703  // check for spatial operators
1704  if ( isSpatialOperator( element.tagName() ) )
1705  {
1706  return nodeSpatialOperatorFromOgcFilter( element, errorMessage );
1707  }
1708 
1709  // check for other OGC operators, convert them to expressions
1710 
1711  if ( element.tagName() == "Not" )
1712  {
1713  return nodeNotFromOgcFilter( element, errorMessage );
1714  }
1715  else if ( element.tagName() == "PropertyIsNull" )
1716  {
1717  return nodePropertyIsNullFromOgcFilter( element, errorMessage );
1718  }
1719  else if ( element.tagName() == "Literal" )
1720  {
1721  return nodeLiteralFromOgcFilter( element, errorMessage );
1722  }
1723  else if ( element.tagName() == "Function" )
1724  {
1725  return nodeFunctionFromOgcFilter( element, errorMessage );
1726  }
1727  else if ( element.tagName() == "PropertyName" )
1728  {
1729  return nodeColumnRefFromOgcFilter( element, errorMessage );
1730  }
1731  else if ( element.tagName() == "PropertyIsBetween" )
1732  {
1733  return nodeIsBetweenFromOgcFilter( element, errorMessage );
1734  }
1735 
1736  errorMessage += QString( "unable to convert '%1' element to a valid expression: it is not supported yet or it has invalid arguments" ).arg( element.tagName() );
1737  return nullptr;
1738 }
1739 
1740 
1741 
1742 QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodeBinaryOperatorFromOgcFilter( QDomElement &element, QString &errorMessage )
1743 {
1744  if ( element.isNull() )
1745  return nullptr;
1746 
1747  int op = binaryOperatorFromTagName( element.tagName() );
1748  if ( op < 0 )
1749  {
1750  if ( errorMessage.isEmpty() )
1751  errorMessage = QString( "'%1' binary operator not supported." ).arg( element.tagName() );
1752  return nullptr;
1753  }
1754 
1755  if ( op == QgsExpression::boLike && element.hasAttribute( "matchCase" ) && element.attribute( "matchCase" ) == "false" )
1756  {
1758  }
1759 
1760  QDomElement operandElem = element.firstChildElement();
1761  QgsExpression::Node *expr = nodeFromOgcFilter( operandElem, errorMessage ), *leftOp = expr;
1762  if ( !expr )
1763  {
1764  if ( errorMessage.isEmpty() )
1765  errorMessage = QString( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
1766  return nullptr;
1767  }
1768 
1769  for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
1770  {
1771  QgsExpression::Node* opRight = nodeFromOgcFilter( operandElem, errorMessage );
1772  if ( !opRight )
1773  {
1774  if ( errorMessage.isEmpty() )
1775  errorMessage = QString( "invalid right operand for '%1' binary operator" ).arg( element.tagName() );
1776  delete expr;
1777  return nullptr;
1778  }
1779 
1780  if ( op == QgsExpression::boLike || op == QgsExpression::boILike )
1781  {
1782  QString wildCard;
1783  if ( element.hasAttribute( "wildCard" ) )
1784  {
1785  wildCard = element.attribute( "wildCard" );
1786  }
1787  QString singleChar;
1788  if ( element.hasAttribute( "singleChar" ) )
1789  {
1790  singleChar = element.attribute( "singleChar" );
1791  }
1792  QString escape = "\\";
1793  if ( element.hasAttribute( "escape" ) )
1794  {
1795  escape = element.attribute( "escape" );
1796  }
1797  // replace
1798  QString oprValue = static_cast<const QgsExpression::NodeLiteral*>( opRight )->value().toString();
1799  if ( !wildCard.isEmpty() && wildCard != "%" )
1800  {
1801  oprValue.replace( '%', "\\%" );
1802  if ( oprValue.startsWith( wildCard ) )
1803  {
1804  oprValue.replace( 0, 1, "%" );
1805  }
1806  QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( wildCard ) + ")" );
1807  int pos = 0;
1808  while (( pos = rx.indexIn( oprValue, pos ) ) != -1 )
1809  {
1810  oprValue.replace( pos + 1, 1, "%" );
1811  pos += 1;
1812  }
1813  oprValue.replace( escape + wildCard, wildCard );
1814  }
1815  if ( !singleChar.isEmpty() && singleChar != "_" )
1816  {
1817  oprValue.replace( '_', "\\_" );
1818  if ( oprValue.startsWith( singleChar ) )
1819  {
1820  oprValue.replace( 0, 1, "_" );
1821  }
1822  QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( singleChar ) + ")" );
1823  int pos = 0;
1824  while (( pos = rx.indexIn( oprValue, pos ) ) != -1 )
1825  {
1826  oprValue.replace( pos + 1, 1, "_" );
1827  pos += 1;
1828  }
1829  oprValue.replace( escape + singleChar, singleChar );
1830  }
1831  if ( !escape.isEmpty() && escape != "\\" )
1832  {
1833  oprValue.replace( escape + escape, escape );
1834  }
1835  opRight = new QgsExpression::NodeLiteral( oprValue );
1836  }
1837 
1838  expr = new QgsExpression::NodeBinaryOperator( static_cast< QgsExpression::BinaryOperator >( op ), expr, opRight );
1839  }
1840 
1841  if ( expr == leftOp )
1842  {
1843  if ( errorMessage.isEmpty() )
1844  errorMessage = QString( "only one operand for '%1' binary operator" ).arg( element.tagName() );
1845  delete expr;
1846  return nullptr;
1847  }
1848 
1850  if ( !ret )
1851  delete expr;
1852 
1853  return ret;
1854 }
1855 
1856 
1857 QgsExpression::NodeFunction* QgsOgcUtils::nodeSpatialOperatorFromOgcFilter( QDomElement& element, QString &errorMessage )
1858 {
1859  // we are exploiting the fact that our function names are the same as the XML tag names
1860  int opIdx = QgsExpression::functionIndex( element.tagName().toLower() );
1861 
1863  QDomElement childElem = element.firstChildElement();
1864  QString gml2Str;
1865  while ( !childElem.isNull() && gml2Str.isEmpty() )
1866  {
1867  if ( childElem.tagName() != "PropertyName" )
1868  {
1869  QTextStream gml2Stream( &gml2Str );
1870  childElem.save( gml2Stream, 0 );
1871  }
1872  childElem = childElem.nextSiblingElement();
1873  }
1874  if ( !gml2Str.isEmpty() )
1875  {
1876  gml2Args->append( new QgsExpression::NodeLiteral( QVariant( gml2Str.remove( '\n' ) ) ) );
1877  }
1878  else
1879  {
1880  errorMessage = "No OGC Geometry found";
1881  delete gml2Args;
1882  return nullptr;
1883  }
1884 
1887  opArgs->append( new QgsExpression::NodeFunction( QgsExpression::functionIndex( "geomFromGML" ), gml2Args ) );
1888 
1889  return new QgsExpression::NodeFunction( opIdx, opArgs );
1890 }
1891 
1892 
1893 QgsExpression::NodeUnaryOperator* QgsOgcUtils::nodeNotFromOgcFilter( QDomElement &element, QString &errorMessage )
1894 {
1895  if ( element.tagName() != "Not" )
1896  return nullptr;
1897 
1898  QDomElement operandElem = element.firstChildElement();
1899  QgsExpression::Node* operand = nodeFromOgcFilter( operandElem, errorMessage );
1900  if ( !operand )
1901  {
1902  if ( errorMessage.isEmpty() )
1903  errorMessage = QString( "invalid operand for '%1' unary operator" ).arg( element.tagName() );
1904  return nullptr;
1905  }
1906 
1908 }
1909 
1910 
1911 QgsExpression::NodeFunction* QgsOgcUtils::nodeFunctionFromOgcFilter( QDomElement &element, QString &errorMessage )
1912 {
1913  if ( element.isNull() || element.tagName() != "Function" )
1914  {
1915  errorMessage = QString( "ogc:Function expected, got %1" ).arg( element.tagName() );
1916  return nullptr;
1917  }
1918 
1919  for ( int i = 0; i < QgsExpression::Functions().size(); i++ )
1920  {
1922 
1923  if ( element.attribute( "name" ) != funcDef->name() )
1924  continue;
1925 
1927 
1928  QDomElement operandElem = element.firstChildElement();
1929  while ( !operandElem.isNull() )
1930  {
1931  QgsExpression::Node* op = nodeFromOgcFilter( operandElem, errorMessage );
1932  if ( !op )
1933  {
1934  delete args;
1935  return nullptr;
1936  }
1937  args->append( op );
1938 
1939  operandElem = operandElem.nextSiblingElement();
1940  }
1941 
1942  return new QgsExpression::NodeFunction( i, args );
1943  }
1944 
1945  return nullptr;
1946 }
1947 
1948 
1949 
1950 QgsExpression::Node* QgsOgcUtils::nodeLiteralFromOgcFilter( QDomElement &element, QString &errorMessage )
1951 {
1952  if ( element.isNull() || element.tagName() != "Literal" )
1953  {
1954  errorMessage = QString( "ogc:Literal expected, got %1" ).arg( element.tagName() );
1955  return nullptr;
1956  }
1957 
1958  QgsExpression::Node *root = nullptr;
1959 
1960  // the literal content can have more children (e.g. CDATA section, text, ...)
1961  QDomNode childNode = element.firstChild();
1962  while ( !childNode.isNull() )
1963  {
1964  QgsExpression::Node* operand = nullptr;
1965 
1966  if ( childNode.nodeType() == QDomNode::ElementNode )
1967  {
1968  // found a element node (e.g. PropertyName), convert it
1969  QDomElement operandElem = childNode.toElement();
1970  operand = nodeFromOgcFilter( operandElem, errorMessage );
1971  if ( !operand )
1972  {
1973  if ( root )
1974  delete root;
1975 
1976  errorMessage = QString( "'%1' is an invalid or not supported content for ogc:Literal" ).arg( operandElem.tagName() );
1977  return nullptr;
1978  }
1979  }
1980  else
1981  {
1982  // probably a text/CDATA node
1983  QVariant value = childNode.nodeValue();
1984 
1985  // try to convert the node content to number if possible,
1986  // otherwise let's use it as string
1987  bool ok;
1988  double d = value.toDouble( &ok );
1989  if ( ok )
1990  value = d;
1991 
1992  operand = new QgsExpression::NodeLiteral( value );
1993  if ( !operand )
1994  continue;
1995  }
1996 
1997  // use the concat operator to merge the ogc:Literal children
1998  if ( !root )
1999  {
2000  root = operand;
2001  }
2002  else
2003  {
2004  root = new QgsExpression::NodeBinaryOperator( QgsExpression::boConcat, root, operand );
2005  }
2006 
2007  childNode = childNode.nextSibling();
2008  }
2009 
2010  if ( root )
2011  return root;
2012 
2013  return nullptr;
2014 }
2015 
2016 
2017 QgsExpression::NodeColumnRef* QgsOgcUtils::nodeColumnRefFromOgcFilter( QDomElement &element, QString &errorMessage )
2018 {
2019  if ( element.isNull() || element.tagName() != "PropertyName" )
2020  {
2021  errorMessage = QString( "ogc:PropertyName expected, got %1" ).arg( element.tagName() );
2022  return nullptr;
2023  }
2024 
2025  return new QgsExpression::NodeColumnRef( element.firstChild().nodeValue() );
2026 }
2027 
2028 
2029 QgsExpression::Node* QgsOgcUtils::nodeIsBetweenFromOgcFilter( QDomElement& element, QString& errorMessage )
2030 {
2031  // <ogc:PropertyIsBetween> encode a Range check
2032  QgsExpression::Node *operand = nullptr, *lowerBound = nullptr;
2033  QgsExpression::Node *operand2 = nullptr, *upperBound = nullptr;
2034 
2035  QDomElement operandElem = element.firstChildElement();
2036  while ( !operandElem.isNull() )
2037  {
2038  if ( operandElem.tagName() == "LowerBoundary" )
2039  {
2040  QDomElement lowerBoundElem = operandElem.firstChildElement();
2041  lowerBound = nodeFromOgcFilter( lowerBoundElem, errorMessage );
2042  }
2043  else if ( operandElem.tagName() == "UpperBoundary" )
2044  {
2045  QDomElement upperBoundElem = operandElem.firstChildElement();
2046  upperBound = nodeFromOgcFilter( upperBoundElem, errorMessage );
2047  }
2048  else
2049  {
2050  // <ogc:expression>
2051  // both operand and operand2 contain the same expression,
2052  // they are respectively compared to lower bound and upper bound
2053  operand = nodeFromOgcFilter( operandElem, errorMessage );
2054  operand2 = nodeFromOgcFilter( operandElem, errorMessage );
2055  }
2056 
2057  if ( operand && lowerBound && operand2 && upperBound )
2058  break;
2059 
2060  operandElem = operandElem.nextSiblingElement();
2061  }
2062 
2063  if ( !operand || !lowerBound || !operand2 || !upperBound )
2064  {
2065  if ( operand )
2066  delete operand;
2067 
2068  if ( lowerBound )
2069  delete lowerBound;
2070 
2071  if ( upperBound )
2072  delete upperBound;
2073 
2074  errorMessage = "missing some required sub-elements in ogc:PropertyIsBetween";
2075  return nullptr;
2076  }
2077 
2078  QgsExpression::Node *geOperator = new QgsExpression::NodeBinaryOperator( QgsExpression::boGE, operand, lowerBound );
2079  QgsExpression::Node *leOperator = new QgsExpression::NodeBinaryOperator( QgsExpression::boLE, operand2, upperBound );
2080  return new QgsExpression::NodeBinaryOperator( QgsExpression::boAnd, geOperator, leOperator );
2081 }
2082 
2083 
2084 QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodePropertyIsNullFromOgcFilter( QDomElement& element, QString& errorMessage )
2085 {
2086  // convert ogc:PropertyIsNull to IS operator with NULL right operand
2087  if ( element.tagName() != "PropertyIsNull" )
2088  {
2089  return nullptr;
2090  }
2091 
2092  QDomElement operandElem = element.firstChildElement();
2093  QgsExpression::Node* opLeft = nodeFromOgcFilter( operandElem, errorMessage );
2094  if ( !opLeft )
2095  return nullptr;
2096 
2098  return new QgsExpression::NodeBinaryOperator( QgsExpression::boIs, opLeft, opRight );
2099 }
2100 
2101 
2103 
2104 
2106 {
2107  return expressionToOgcFilter( exp, doc, GML_2_1_2, FILTER_OGC_1_0,
2108  "geometry", QString(), false, false, errorMessage );
2109 }
2110 
2112 {
2113  return expressionToOgcExpression( exp, doc, GML_2_1_2, FILTER_OGC_1_0,
2114  "geometry", QString(), false, false, errorMessage );
2115 }
2116 
2118  QDomDocument& doc,
2119  GMLVersion gmlVersion,
2120  FilterVersion filterVersion,
2121  const QString& geometryName,
2122  const QString& srsName,
2123  bool honourAxisOrientation,
2124  bool invertAxisOrientation,
2125  QString* errorMessage )
2126 {
2127  if ( !exp.rootNode() )
2128  return QDomElement();
2129 
2130  QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
2131  QDomElement exprRootElem = utils.expressionNodeToOgcFilter( exp.rootNode() );
2132  if ( errorMessage )
2133  *errorMessage = utils.errorMessage();
2134  if ( exprRootElem.isNull() )
2135  return QDomElement();
2136 
2137  QDomElement filterElem =
2138  ( filterVersion == FILTER_FES_2_0 ) ?
2139  doc.createElementNS( FES_NAMESPACE, "fes:Filter" ) :
2140  doc.createElementNS( OGC_NAMESPACE, "ogc:Filter" );
2141  if ( utils.GMLNamespaceUsed() )
2142  {
2143  QDomAttr attr = doc.createAttribute( "xmlns:gml" );
2144  if ( gmlVersion == GML_3_2_1 )
2145  attr.setValue( GML32_NAMESPACE );
2146  else
2147  attr.setValue( GML_NAMESPACE );
2148  filterElem.setAttributeNode( attr );
2149  }
2150  filterElem.appendChild( exprRootElem );
2151  return filterElem;
2152 }
2153 
2155  QDomDocument& doc,
2156  GMLVersion gmlVersion,
2157  FilterVersion filterVersion,
2158  const QString& geometryName,
2159  const QString& srsName,
2160  bool honourAxisOrientation,
2161  bool invertAxisOrientation,
2162  QString* errorMessage )
2163 {
2164  const QgsExpression::Node* node = exp.rootNode();
2165  if ( !node )
2166  return QDomElement();
2167 
2168  switch ( node->nodeType() )
2169  {
2173  {
2174  QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
2175  QDomElement exprRootElem = utils.expressionNodeToOgcFilter( node );
2176 
2177  if ( errorMessage )
2178  *errorMessage = utils.errorMessage();
2179 
2180  if ( !exprRootElem.isNull() )
2181  {
2182  return exprRootElem;
2183  }
2184  break;
2185  }
2186  default:
2187  *errorMessage = QObject::tr( "Node type not supported in expression translation: %1" ).arg( node->nodeType() );
2188  }
2189  // got an error
2190  return QDomElement();
2191 }
2192 
2194 {
2195  switch ( node->nodeType() )
2196  {
2198  return expressionUnaryOperatorToOgcFilter( static_cast<const QgsExpression::NodeUnaryOperator*>( node ) );
2200  return expressionBinaryOperatorToOgcFilter( static_cast<const QgsExpression::NodeBinaryOperator*>( node ) );
2202  return expressionInOperatorToOgcFilter( static_cast<const QgsExpression::NodeInOperator*>( node ) );
2204  return expressionFunctionToOgcFilter( static_cast<const QgsExpression::NodeFunction*>( node ) );
2206  return expressionLiteralToOgcFilter( static_cast<const QgsExpression::NodeLiteral*>( node ) );
2208  return expressionColumnRefToOgcFilter( static_cast<const QgsExpression::NodeColumnRef*>( node ) );
2209 
2210  default:
2211  mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
2212  return QDomElement();
2213  }
2214 }
2215 
2216 
2217 QDomElement QgsOgcUtilsExprToFilter::expressionUnaryOperatorToOgcFilter( const QgsExpression::NodeUnaryOperator* node )
2218 {
2219 
2220  QDomElement operandElem = expressionNodeToOgcFilter( node->operand() );
2221  if ( !mErrorMessage.isEmpty() )
2222  return QDomElement();
2223 
2224  QDomElement uoElem;
2225  switch ( node->op() )
2226  {
2228  uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2229  if ( node->operand()->nodeType() == QgsExpression::ntLiteral )
2230  {
2231  // operand expression already created a Literal node:
2232  // take the literal value, prepend - and remove old literal node
2233  uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2234  mDoc.removeChild( operandElem );
2235  }
2236  else
2237  {
2238  mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2239  return QDomElement();
2240  }
2241  break;
2242  case QgsExpression::uoNot:
2243  uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2244  uoElem.appendChild( operandElem );
2245  break;
2246 
2247  default:
2248  mErrorMessage = QObject::tr( "Unary operator %1 not implemented yet" ).arg( QgsExpression::UnaryOperatorText[node->op()] );
2249  return QDomElement();
2250  }
2251 
2252  return uoElem;
2253 }
2254 
2255 
2256 QDomElement QgsOgcUtilsExprToFilter::expressionBinaryOperatorToOgcFilter( const QgsExpression::NodeBinaryOperator* node )
2257 {
2258  QDomElement leftElem = expressionNodeToOgcFilter( node->opLeft() );
2259  if ( !mErrorMessage.isEmpty() )
2260  return QDomElement();
2261 
2262  QgsExpression::BinaryOperator op = node->op();
2263 
2264  // before right operator is parsed: to allow NULL handling
2265  if ( op == QgsExpression::boIs || op == QgsExpression::boIsNot )
2266  {
2267  if ( node->opRight()->nodeType() == QgsExpression::ntLiteral )
2268  {
2269  const QgsExpression::NodeLiteral* rightLit = static_cast<const QgsExpression::NodeLiteral*>( node->opRight() );
2270  if ( rightLit->value().isNull() )
2271  {
2272 
2273  QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2274  elem.appendChild( leftElem );
2275 
2276  if ( op == QgsExpression::boIsNot )
2277  {
2278  QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2279  notElem.appendChild( elem );
2280  return notElem;
2281  }
2282 
2283  return elem;
2284  }
2285 
2286  // continue with equal / not equal operator once the null case is handled
2288  }
2289 
2290  }
2291 
2292  QDomElement rightElem = expressionNodeToOgcFilter( node->opRight() );
2293  if ( !mErrorMessage.isEmpty() )
2294  return QDomElement();
2295 
2296 
2297  QString opText = binaryOperatorToTagName( op );
2298  if ( opText.isEmpty() )
2299  {
2300  // not implemented binary operators
2301  // TODO: regex, % (mod), ^ (pow) are not supported yet
2302  mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( QgsExpression::BinaryOperatorText[op] );
2303  return QDomElement();
2304  }
2305 
2306  QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2307 
2308  if ( op == QgsExpression::boLike || op == QgsExpression::boILike )
2309  {
2310  if ( op == QgsExpression::boILike )
2311  boElem.setAttribute( "matchCase", "false" );
2312 
2313  // setup wildCards to <ogc:PropertyIsLike>
2314  boElem.setAttribute( "wildCard", "%" );
2315  boElem.setAttribute( "singleChar", "_" );
2316  if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2317  boElem.setAttribute( "escape", "\\" );
2318  else
2319  boElem.setAttribute( "escapeChar", "\\" );
2320  }
2321 
2322  boElem.appendChild( leftElem );
2323  boElem.appendChild( rightElem );
2324  return boElem;
2325 }
2326 
2327 
2328 QDomElement QgsOgcUtilsExprToFilter::expressionLiteralToOgcFilter( const QgsExpression::NodeLiteral* node )
2329 {
2330  QString value;
2331  switch ( node->value().type() )
2332  {
2333  case QVariant::Int:
2334  value = QString::number( node->value().toInt() );
2335  break;
2336  case QVariant::Double:
2337  value = QString::number( node->value().toDouble() );
2338  break;
2339  case QVariant::String:
2340  value = node->value().toString();
2341  break;
2342 
2343  default:
2344  mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( node->value().type() );
2345  return QDomElement();
2346  }
2347 
2348  QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2349  litElem.appendChild( mDoc.createTextNode( value ) );
2350  return litElem;
2351 }
2352 
2353 
2354 QDomElement QgsOgcUtilsExprToFilter::expressionColumnRefToOgcFilter( const QgsExpression::NodeColumnRef* node )
2355 {
2356  QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2357  propElem.appendChild( mDoc.createTextNode( node->name() ) );
2358  return propElem;
2359 }
2360 
2361 
2362 
2363 QDomElement QgsOgcUtilsExprToFilter::expressionInOperatorToOgcFilter( const QgsExpression::NodeInOperator* node )
2364 {
2365  if ( node->list()->list().size() == 1 )
2366  return expressionNodeToOgcFilter( node->list()->list()[0] );
2367 
2368  QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2369  QDomElement leftNode = expressionNodeToOgcFilter( node->node() );
2370 
2371  Q_FOREACH ( QgsExpression::Node* n, node->list()->list() )
2372  {
2373  QDomElement listNode = expressionNodeToOgcFilter( n );
2374  if ( !mErrorMessage.isEmpty() )
2375  return QDomElement();
2376 
2377  QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2378  eqElem.appendChild( leftNode.cloneNode() );
2379  eqElem.appendChild( listNode );
2380 
2381  orElem.appendChild( eqElem );
2382  }
2383  return orElem;
2384 }
2385 
2387 {
2388  static QMap<QString, QString> binSpatialOps;
2389  if ( binSpatialOps.isEmpty() )
2390  {
2391  binSpatialOps.insert( "disjoint", "Disjoint" );
2392  binSpatialOps.insert( "intersects", "Intersects" );
2393  binSpatialOps.insert( "touches", "Touches" );
2394  binSpatialOps.insert( "crosses", "Crosses" );
2395  binSpatialOps.insert( "contains", "Contains" );
2396  binSpatialOps.insert( "overlaps", "Overlaps" );
2397  binSpatialOps.insert( "within", "Within" );
2398  }
2399  return binSpatialOps;
2400 }
2401 
2402 static bool isBinarySpatialOperator( const QString& fnName )
2403 {
2404  return binarySpatialOpsMap().contains( fnName );
2405 }
2406 
2408 {
2409  return binarySpatialOpsMap().value( fnName );
2410 }
2411 
2412 static bool isGeometryColumn( const QgsExpression::Node* node )
2413 {
2414  if ( node->nodeType() != QgsExpression::ntFunction )
2415  return false;
2416 
2417  const QgsExpression::NodeFunction* fn = static_cast<const QgsExpression::NodeFunction*>( node );
2419  return fd->name() == "$geometry";
2420 }
2421 
2423 {
2424  // Right now we support only geomFromWKT(' ..... ')
2425  // Ideally we should support any constant sub-expression (not dependent on feature's geometry or attributes)
2426 
2427  if ( node->nodeType() == QgsExpression::ntFunction )
2428  {
2429  const QgsExpression::NodeFunction* fnNode = static_cast<const QgsExpression::NodeFunction*>( node );
2431  if ( fnDef->name() == "geom_from_wkt" )
2432  {
2433  const QList<QgsExpression::Node*>& args = fnNode->args()->list();
2434  if ( args[0]->nodeType() == QgsExpression::ntLiteral )
2435  {
2436  QString wkt = static_cast<const QgsExpression::NodeLiteral*>( args[0] )->value().toString();
2437  return QgsGeometry::fromWkt( wkt );
2438  }
2439  }
2440  }
2441  return nullptr;
2442 }
2443 
2444 
2445 QDomElement QgsOgcUtilsExprToFilter::expressionFunctionToOgcFilter( const QgsExpression::NodeFunction* node )
2446 {
2448 
2449  if ( fd->name() == "intersects_bbox" )
2450  {
2451  QList<QgsExpression::Node*> argNodes = node->args()->list();
2452  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2453 
2454  QgsGeometry* geom = geometryFromConstExpr( argNodes[1] );
2455  if ( geom && isGeometryColumn( argNodes[0] ) )
2456  {
2457  QgsRectangle rect = geom->boundingBox();
2458  delete geom;
2459 
2460  mGMLUsed = true;
2461 
2462  QDomElement elemBox = ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
2463  QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, mSrsName, mInvertAxisOrientation ) :
2464  QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, mSrsName, mInvertAxisOrientation );
2465 
2466  QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2467  geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );
2468 
2469  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":BBOX" );
2470  funcElem.appendChild( geomProperty );
2471  funcElem.appendChild( elemBox );
2472  return funcElem;
2473  }
2474  else
2475  {
2476  delete geom;
2477 
2478  mErrorMessage = QObject::tr( "<BBOX> is currently supported only in form: bbox($geometry, geomFromWKT('...'))" );
2479  return QDomElement();
2480  }
2481  }
2482 
2483  if ( isBinarySpatialOperator( fd->name() ) )
2484  {
2485  QList<QgsExpression::Node*> argNodes = node->args()->list();
2486  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2487 
2488  QgsExpression::Node* otherNode = nullptr;
2489  if ( isGeometryColumn( argNodes[0] ) )
2490  otherNode = argNodes[1];
2491  else if ( isGeometryColumn( argNodes[1] ) )
2492  otherNode = argNodes[0];
2493  else
2494  {
2495  mErrorMessage = QObject::tr( "Unable to translate spatial operator: at least one must refer to geometry." );
2496  return QDomElement();
2497  }
2498 
2499  QDomElement otherGeomElem;
2500 
2501  // the other node must be a geometry constructor
2502  if ( otherNode->nodeType() != QgsExpression::ntFunction )
2503  {
2504  mErrorMessage = QObject::tr( "spatial operator: the other operator must be a geometry constructor function" );
2505  return QDomElement();
2506  }
2507 
2508  const QgsExpression::NodeFunction* otherFn = static_cast<const QgsExpression::NodeFunction*>( otherNode );
2509  QgsExpression::Function* otherFnDef = QgsExpression::Functions()[otherFn->fnIndex()];
2510  if ( otherFnDef->name() == "geom_from_wkt" )
2511  {
2512  QgsExpression::Node* firstFnArg = otherFn->args()->list()[0];
2513  if ( firstFnArg->nodeType() != QgsExpression::ntLiteral )
2514  {
2515  mErrorMessage = QObject::tr( "geom_from_wkt: argument must be string literal" );
2516  return QDomElement();
2517  }
2518  QString wkt = static_cast<const QgsExpression::NodeLiteral*>( firstFnArg )->value().toString();
2519  QgsGeometry* geom = QgsGeometry::fromWkt( wkt );
2520  otherGeomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation,
2521  QString( "qgis_id_geom_%1" ).arg( mGeomId ) );
2522  mGeomId ++;
2523  delete geom;
2524  }
2525  else if ( otherFnDef->name() == "geom_from_gml" )
2526  {
2527  QgsExpression::Node* firstFnArg = otherFn->args()->list()[0];
2528  if ( firstFnArg->nodeType() != QgsExpression::ntLiteral )
2529  {
2530  mErrorMessage = QObject::tr( "geom_from_gml: argument must be string literal" );
2531  return QDomElement();
2532  }
2533 
2534  QDomDocument geomDoc;
2535  QString gml = static_cast<const QgsExpression::NodeLiteral*>( firstFnArg )->value().toString();
2536  if ( !geomDoc.setContent( gml, true ) )
2537  {
2538  mErrorMessage = QObject::tr( "geom_from_gml: unable to parse XML" );
2539  return QDomElement();
2540  }
2541 
2542  QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
2543  otherGeomElem = geomNode.toElement();
2544  }
2545  else
2546  {
2547  mErrorMessage = QObject::tr( "spatial operator: unknown geometry constructor function" );
2548  return QDomElement();
2549  }
2550 
2551  mGMLUsed = true;
2552 
2553  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + tagNameForSpatialOperator( fd->name() ) );
2554  QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2555  geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );
2556  funcElem.appendChild( geomProperty );
2557  funcElem.appendChild( otherGeomElem );
2558  return funcElem;
2559  }
2560 
2561  if ( fd->params() == 0 )
2562  {
2563  mErrorMessage = QObject::tr( "Special columns/constants are not supported." );
2564  return QDomElement();
2565  }
2566 
2567  // this is somehow wrong - we are just hoping that the other side supports the same functions as we do...
2568  QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
2569  funcElem.setAttribute( "name", fd->name() );
2570  Q_FOREACH ( QgsExpression::Node* n, node->args()->list() )
2571  {
2572  QDomElement childElem = expressionNodeToOgcFilter( n );
2573  if ( !mErrorMessage.isEmpty() )
2574  return QDomElement();
2575 
2576  funcElem.appendChild( childElem );
2577  }
2578 
2579  return funcElem;
2580 }
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:245
QGis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
QDomNodeList elementsByTagNameNS(const QString &nsURI, const QString &localName) const
void setValue(const QString &v)
bool contains(const Key &key) const
QDomNode appendChild(const QDomNode &newChild)
QgsOgcUtilsExprToFilter(QDomDocument &doc, QgsOgcUtils::GMLVersion gmlVersion, QgsOgcUtils::FilterVersion filterVersion, const QString &geometryName, const QString &srsName, bool honourAxisOrientation, bool invertAxisOrientation)
Constructor.
Definition: qgsogcutils.cpp: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
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
QString dump() const
Return an expression string, constructed from the internal abstract syntax tree.
QString escape(const QString &str)
const_iterator constEnd() const
const T & at(int i) const
bool contains(const QString &str, Qt::CaseSensitivity cs) const
QDomElement nextSiblingElement(const QString &tagName) const
static bool isGeometryColumn(const QgsExpression::Node *node)
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
WkbType
Used for symbology operations.
Definition: qgis.h:57
static QDomElement rectangleToGMLBox(QgsRectangle *box, QDomDocument &doc, int precision=17)
Exports the rectangle to GML2 Box.
QDomElement documentElement() const
NodeType nodeType() const
QString & remove(int position, int n)
QDomElement createElementNS(const QString &nsURI, const QString &qName)
void setNamedColor(const QString &name)
double toDouble(bool *ok) const
int wkbSize() const
Returns the size of the WKB in asWkb().
QString tr(const char *sourceText, const char *disambiguation, int n)
static QMap< QString, QString > binarySpatialOpsMap()
static QgsRectangle rectangleFromGMLBox(const QDomNode &boxNode)
Read rectangle from GML2 Box.
int size() const
QDomNode nextSibling() const
QDomNode importNode(const QDomNode &importedNode, bool deep)
static QgsRectangle rectangleFromGMLEnvelope(const QDomNode &envelopeNode)
Read rectangle from GML3 Envelope.
const Node * rootNode() const
Returns root node of the expression. Root node is null is parsing has failed.
QDomElement toElement() const
bool GMLNamespaceUsed() const
Return whether the gml: namespace is used.
Definition: qgsogcutils.h:261
static const QList< Function * > & Functions()
static int binaryOperatorFromTagName(const QString &tagName)
int indexIn(const QString &str, int offset, CaretMode caretMode) const
bool isEmpty() const
static bool isBinaryOperator(const QString &tagName)
bool createFromOgcWmsCrs(QString theCrs)
Set up this CRS from the given OGC CRS.
void clear()
QString number(int n, int base)
int count(const T &value) const
static const QString GML_NAMESPACE
Definition: qgsogcutils.cpp:35
QgsExpressionPrivate * d
#define FALLTHROUGH
Definition: qgis.h:439
QString text() const
int toInt(bool *ok) const
bool isNull() const
static int functionIndex(const QString &name)
return index of the function in Functions array
bool hasAttribute(const QString &name) const
void setAttribute(const QString &name, const QString &value)
static QgsGeometry * geometryFromGML(const QString &xmlString)
Static method that creates geometry from GML.
int toInt(bool *ok, int base) const
QString qgsDoubleToString(double a, int precision=17)
Definition: qgis.h:274
bool isEmpty() const
bool isEmpty() const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
const unsigned char * asWkb() const
Returns the buffer containing this geometry in WKB format.
static bool isBinarySpatialOperator(const QString &fnName)
QString name()
The name of the function.
static QString tagNameForSpatialOperator(const QString &fnName)
A class to represent a point.
Definition: qgspoint.h:65
bool hasChildNodes() const
static const QMap< QString, int > & binaryOperatorsTagNamesMap()
FilterVersion
OGC filter version.
Definition: qgsogcutils.h:139
QDomText createTextNode(const QString &value)
NodeList * args() const
iterator end()
QString toLower() const
QDomNode removeChild(const QDomNode &oldChild)
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:202
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:187
bool isNull() const
static const QString OGC_NAMESPACE
Definition: qgsogcutils.cpp:37
const QString & errorMessage() const
Return the error message.
Definition: qgsogcutils.h:264
const Key key(const T &value) const
static bool isSpatialOperator(const QString &tagName)
QString & replace(int position, int n, QChar after)
QString name() const
The name of the column.
const_iterator constBegin() const
void save(QTextStream &str, int indent) const
QDomNode firstChild() const
static const char * UnaryOperatorText[]
BinaryOperator
list of binary operators
QDomElement expressionNodeToOgcFilter(const QgsExpression::Node *node)
Convert an expression to a OGC filter.
QDomNode cloneNode(bool deep) const
static QDomElement expressionToOgcFilter(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=nullptr)
Creates OGC filter XML element.
static QDomElement expressionToOgcExpression(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=nullptr)
Creates an OGC expression XML element.
QDomAttr setAttributeNode(const QDomAttr &newAttr)
int params()
The number of parameters this function takes.
QVariant value() const
The value of the literal.
QDomElement firstChildElement(const QString &tagName) const
Class for storing a coordinate reference system (CRS)
static QgsExpression * expressionFromOgcFilter(const QDomElement &element)
Parse XML with OGC filter into QGIS expression.
QStringList split(const QString &sep, const QString &str, bool allowEmptyEntries)
static QgsGeometry * fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
static QDomElement rectangleToGMLEnvelope(QgsRectangle *env, QDomDocument &doc, int precision=17)
Exports the rectangle to GML3 Envelope.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:192
static QgsGeometry * geometryFromConstExpr(const QgsExpression::Node *node)
QString section(QChar sep, int start, int end, QFlags< QString::SectionFlag > flags) const
static QString binaryOperatorToTagName(QgsExpression::BinaryOperator op)
static QColor colorFromOgcFill(const QDomElement &fillElement)
Parse XML with OGC fill into QColor.
void push_back(const T &value)
GMLVersion
GML version.
Definition: qgsogcutils.h:49
void setAlphaF(qreal alpha)
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:197
double toDouble(bool *ok) const
iterator insert(const Key &key, const T &value)
typedef const_iterator
void append(Node *node)
Takes ownership of the provided node.
bool isEmpty() const
QString tagName() const
QgsWKBTypes::Type readHeader() const
Definition: qgswkbptr.cpp:37
static QgsGeometry * fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
int size() const
void normalize()
Normalize the rectangle so it has non-negative width/height.
const_iterator constEnd() const
QDomElement createElement(const QString &tagName)
const_iterator constBegin() const
Type type() const
int size() const
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
iterator end()
The QgsOgcUtils class provides various utility functions for conversion between OGC (Open Geospatial ...
Definition: qgsogcutils.h:42
iterator begin()
static QDomElement geometryToGML(const QgsGeometry *geometry, QDomDocument &doc, GMLVersion gmlVersion, const QString &srsName, bool invertAxisOrientation, const QString &gmlIdBase, int precision=17)
Exports the geometry to GML.
QList< Node * > list()
QDomNode at(int index) const
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
bool axisInverted() const
Returns whether axis is inverted (eg.
const T value(const Key &key) const
static const char * BinaryOperatorText[]