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