QGIS API Documentation  2.13.0-Master
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() + 1 + sizeof( int ) );
1084 
1085  if ( format == "GML3" )
1086  {
1087  switch ( geometry->wkbType() )
1088  {
1089  case QGis::WKBPoint25D:
1090  case QGis::WKBPoint:
1092  case QGis::WKBMultiPoint:
1093  baseCoordElem = doc.createElement( "gml:pos" );
1094  break;
1095  default:
1096  baseCoordElem = doc.createElement( "gml:posList" );
1097  break;
1098  }
1099  baseCoordElem.setAttribute( "srsDimension", "2" );
1100  cs = ' ';
1101  }
1102  else
1103  {
1104  baseCoordElem = doc.createElement( "gml:coordinates" );
1105  baseCoordElem.setAttribute( "cs", cs );
1106  baseCoordElem.setAttribute( "ts", ts );
1107  }
1108 
1109  switch ( geometry->wkbType() )
1110  {
1111  case QGis::WKBPoint25D:
1112  case QGis::WKBPoint:
1113  {
1114  QDomElement pointElem = doc.createElement( "gml:Point" );
1115  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1116 
1117  double x, y;
1118  wkbPtr >> x >> y;
1119  QDomText coordText = doc.createTextNode( qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision ) );
1120 
1121  coordElem.appendChild( coordText );
1122  pointElem.appendChild( coordElem );
1123  return pointElem;
1124  }
1126  hasZValue = true;
1127  //intentional fall-through
1128  FALLTHROUGH;
1129  case QGis::WKBMultiPoint:
1130  {
1131  QDomElement multiPointElem = doc.createElement( "gml:MultiPoint" );
1132 
1133  int nPoints;
1134  wkbPtr >> nPoints;
1135 
1136  for ( int idx = 0; idx < nPoints; ++idx )
1137  {
1138  wkbPtr += 1 + sizeof( int );
1139  QDomElement pointMemberElem = doc.createElement( "gml:pointMember" );
1140  QDomElement pointElem = doc.createElement( "gml:Point" );
1141  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1142 
1143  double x, y;
1144  wkbPtr >> x >> y;
1145  QDomText coordText = doc.createTextNode( qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision ) );
1146 
1147  coordElem.appendChild( coordText );
1148  pointElem.appendChild( coordElem );
1149 
1150  if ( hasZValue )
1151  {
1152  wkbPtr += sizeof( double );
1153  }
1154  pointMemberElem.appendChild( pointElem );
1155  multiPointElem.appendChild( pointMemberElem );
1156  }
1157  return multiPointElem;
1158  }
1160  hasZValue = true;
1161  //intentional fall-through
1162  FALLTHROUGH;
1163  case QGis::WKBLineString:
1164  {
1165  QDomElement lineStringElem = doc.createElement( "gml:LineString" );
1166  // get number of points in the line
1167 
1168  int nPoints;
1169  wkbPtr >> nPoints;
1170 
1171  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1172  QString coordString;
1173  for ( int idx = 0; idx < nPoints; ++idx )
1174  {
1175  if ( idx != 0 )
1176  {
1177  coordString += ts;
1178  }
1179 
1180  double x, y;
1181  wkbPtr >> x >> y;
1182  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1183 
1184  if ( hasZValue )
1185  {
1186  wkbPtr += sizeof( double );
1187  }
1188  }
1189  QDomText coordText = doc.createTextNode( coordString );
1190  coordElem.appendChild( coordText );
1191  lineStringElem.appendChild( coordElem );
1192  return lineStringElem;
1193  }
1195  hasZValue = true;
1196  //intentional fall-through
1197  FALLTHROUGH;
1199  {
1200  QDomElement multiLineStringElem = doc.createElement( "gml:MultiLineString" );
1201 
1202  int nLines;
1203  wkbPtr >> nLines;
1204 
1205  for ( int jdx = 0; jdx < nLines; jdx++ )
1206  {
1207  QDomElement lineStringMemberElem = doc.createElement( "gml:lineStringMember" );
1208  QDomElement lineStringElem = doc.createElement( "gml:LineString" );
1209  wkbPtr += 1 + sizeof( int ); // skip type since we know its 2
1210 
1211  int nPoints;
1212  wkbPtr >> nPoints;
1213 
1214  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1215  QString coordString;
1216  for ( int idx = 0; idx < nPoints; idx++ )
1217  {
1218  if ( idx != 0 )
1219  {
1220  coordString += ts;
1221  }
1222 
1223  double x, y;
1224  wkbPtr >> x >> y;
1225 
1226  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1227 
1228  if ( hasZValue )
1229  {
1230  wkbPtr += sizeof( double );
1231  }
1232  }
1233  QDomText coordText = doc.createTextNode( coordString );
1234  coordElem.appendChild( coordText );
1235  lineStringElem.appendChild( coordElem );
1236  lineStringMemberElem.appendChild( lineStringElem );
1237  multiLineStringElem.appendChild( lineStringMemberElem );
1238  }
1239  return multiLineStringElem;
1240  }
1241  case QGis::WKBPolygon25D:
1242  hasZValue = true;
1243  //intentional fall-through
1244  FALLTHROUGH;
1245  case QGis::WKBPolygon:
1246  {
1247  QDomElement polygonElem = doc.createElement( "gml:Polygon" );
1248 
1249  // get number of rings in the polygon
1250  int numRings;
1251  wkbPtr >> numRings;
1252 
1253  if ( numRings == 0 ) // sanity check for zero rings in polygon
1254  return QDomElement();
1255 
1256  int *ringNumPoints = new int[numRings]; // number of points in each ring
1257 
1258  for ( int idx = 0; idx < numRings; idx++ )
1259  {
1260  QString boundaryName = "gml:outerBoundaryIs";
1261  if ( idx != 0 )
1262  {
1263  boundaryName = "gml:innerBoundaryIs";
1264  }
1265  QDomElement boundaryElem = doc.createElement( boundaryName );
1266  QDomElement ringElem = doc.createElement( "gml:LinearRing" );
1267  // get number of points in the ring
1268  int nPoints;
1269  wkbPtr >> nPoints;
1270  ringNumPoints[idx] = nPoints;
1271 
1272  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1273  QString coordString;
1274  for ( int jdx = 0; jdx < nPoints; jdx++ )
1275  {
1276  if ( jdx != 0 )
1277  {
1278  coordString += ts;
1279  }
1280 
1281  double x, y;
1282  wkbPtr >> x >> y;
1283 
1284  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1285  if ( hasZValue )
1286  {
1287  wkbPtr += sizeof( double );
1288  }
1289  }
1290  QDomText coordText = doc.createTextNode( coordString );
1291  coordElem.appendChild( coordText );
1292  ringElem.appendChild( coordElem );
1293  boundaryElem.appendChild( ringElem );
1294  polygonElem.appendChild( boundaryElem );
1295  }
1296  delete [] ringNumPoints;
1297  return polygonElem;
1298  }
1300  hasZValue = true;
1301  //intentional fall-through
1302  FALLTHROUGH;
1303  case QGis::WKBMultiPolygon:
1304  {
1305  QDomElement multiPolygonElem = doc.createElement( "gml:MultiPolygon" );
1306 
1307  int numPolygons;
1308  wkbPtr >> numPolygons;
1309 
1310  for ( int kdx = 0; kdx < numPolygons; kdx++ )
1311  {
1312  QDomElement polygonMemberElem = doc.createElement( "gml:polygonMember" );
1313  QDomElement polygonElem = doc.createElement( "gml:Polygon" );
1314 
1315  wkbPtr += 1 + sizeof( int );
1316 
1317  int numRings;
1318  wkbPtr >> numRings;
1319 
1320  for ( int idx = 0; idx < numRings; idx++ )
1321  {
1322  QString boundaryName = "gml:outerBoundaryIs";
1323  if ( idx != 0 )
1324  {
1325  boundaryName = "gml:innerBoundaryIs";
1326  }
1327  QDomElement boundaryElem = doc.createElement( boundaryName );
1328  QDomElement ringElem = doc.createElement( "gml:LinearRing" );
1329 
1330  int nPoints;
1331  wkbPtr >> nPoints;
1332 
1333  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1334  QString coordString;
1335  for ( int jdx = 0; jdx < nPoints; jdx++ )
1336  {
1337  if ( jdx != 0 )
1338  {
1339  coordString += ts;
1340  }
1341 
1342  double x, y;
1343  wkbPtr >> x >> y;
1344 
1345  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1346 
1347  if ( hasZValue )
1348  {
1349  wkbPtr += sizeof( double );
1350  }
1351  }
1352  QDomText coordText = doc.createTextNode( coordString );
1353  coordElem.appendChild( coordText );
1354  ringElem.appendChild( coordElem );
1355  boundaryElem.appendChild( ringElem );
1356  polygonElem.appendChild( boundaryElem );
1357  polygonMemberElem.appendChild( polygonElem );
1358  multiPolygonElem.appendChild( polygonMemberElem );
1359  }
1360  }
1361  return multiPolygonElem;
1362  }
1363  default:
1364  return QDomElement();
1365  }
1366 }
1367 
1368 QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry *geometry, QDomDocument &doc, int precision )
1369 {
1370  return geometryToGML( geometry, doc, "GML2", precision );
1371 }
1372 
1373 QDomElement QgsOgcUtils::createGMLCoordinates( const QgsPolyline &points, QDomDocument &doc )
1374 {
1375  QDomElement coordElem = doc.createElement( "gml:coordinates" );
1376  coordElem.setAttribute( "cs", "," );
1377  coordElem.setAttribute( "ts", " " );
1378 
1379  QString coordString;
1380  QVector<QgsPoint>::const_iterator pointIt = points.constBegin();
1381  for ( ; pointIt != points.constEnd(); ++pointIt )
1382  {
1383  if ( pointIt != points.constBegin() )
1384  {
1385  coordString += ' ';
1386  }
1387  coordString += qgsDoubleToString( pointIt->x() );
1388  coordString += ',';
1389  coordString += qgsDoubleToString( pointIt->y() );
1390  }
1391 
1392  QDomText coordText = doc.createTextNode( coordString );
1393  coordElem.appendChild( coordText );
1394  return coordElem;
1395 }
1396 
1397 QDomElement QgsOgcUtils::createGMLPositions( const QgsPolyline &points, QDomDocument& doc )
1398 {
1399  QDomElement posElem = doc.createElement( "gml:pos" );
1400  if ( points.size() > 1 )
1401  posElem = doc.createElement( "gml:posList" );
1402  posElem.setAttribute( "srsDimension", "2" );
1403 
1404  QString coordString;
1405  QVector<QgsPoint>::const_iterator pointIt = points.constBegin();
1406  for ( ; pointIt != points.constEnd(); ++pointIt )
1407  {
1408  if ( pointIt != points.constBegin() )
1409  {
1410  coordString += ' ';
1411  }
1412  coordString += qgsDoubleToString( pointIt->x() );
1413  coordString += ' ';
1414  coordString += qgsDoubleToString( pointIt->y() );
1415  }
1416 
1417  QDomText coordText = doc.createTextNode( coordString );
1418  posElem.appendChild( coordText );
1419  return posElem;
1420 }
1421 
1422 
1423 
1424 // -----------------------------------------
1425 
1427 {
1428  if ( fillElement.isNull() || !fillElement.hasChildNodes() )
1429  {
1430  return QColor();
1431  }
1432 
1433  QString cssName;
1434  QString elemText;
1435  QColor color;
1436  QDomElement cssElem = fillElement.firstChildElement( "CssParameter" );
1437  while ( !cssElem.isNull() )
1438  {
1439  cssName = cssElem.attribute( "name", "not_found" );
1440  if ( cssName != "not_found" )
1441  {
1442  elemText = cssElem.text();
1443  if ( cssName == "fill" )
1444  {
1445  color.setNamedColor( elemText );
1446  }
1447  else if ( cssName == "fill-opacity" )
1448  {
1449  bool ok;
1450  double opacity = elemText.toDouble( &ok );
1451  if ( ok )
1452  {
1453  color.setAlphaF( opacity );
1454  }
1455  }
1456  }
1457 
1458  cssElem = cssElem.nextSiblingElement( "CssParameter" );
1459  }
1460 
1461  return color;
1462 }
1463 
1464 
1466 {
1467  if ( element.isNull() || !element.hasChildNodes() )
1468  return nullptr;
1469 
1470  QgsExpression *expr = new QgsExpression();
1471 
1472  QDomElement childElem = element.firstChildElement();
1473  while ( !childElem.isNull() )
1474  {
1475  QString errorMsg;
1476  QgsExpression::Node *node = nodeFromOgcFilter( childElem, errorMsg );
1477  if ( !node )
1478  {
1479  // invalid expression, parser error
1480  expr->d->mParserErrorString = errorMsg;
1481  return expr;
1482  }
1483 
1484  // use the concat binary operator to append to the root node
1485  if ( !expr->d->mRootNode )
1486  {
1487  expr->d->mRootNode = node;
1488  }
1489  else
1490  {
1491  expr->d->mRootNode = new QgsExpression::NodeBinaryOperator( QgsExpression::boConcat, expr->d->mRootNode, node );
1492  }
1493 
1494  childElem = childElem.nextSiblingElement();
1495  }
1496 
1497  // update expression string
1498  expr->d->mExp = expr->dump();
1499 
1500  return expr;
1501 }
1502 
1503 
1505 {
1506  static QMap<QString, int> binOps;
1507  if ( binOps.isEmpty() )
1508  {
1509  // logical
1510  binOps.insert( "Or", QgsExpression::boOr );
1511  binOps.insert( "And", QgsExpression::boAnd );
1512  // comparison
1513  binOps.insert( "PropertyIsEqualTo", QgsExpression::boEQ );
1514  binOps.insert( "PropertyIsNotEqualTo", QgsExpression::boNE );
1515  binOps.insert( "PropertyIsLessThanOrEqualTo", QgsExpression::boLE );
1516  binOps.insert( "PropertyIsGreaterThanOrEqualTo", QgsExpression::boGE );
1517  binOps.insert( "PropertyIsLessThan", QgsExpression::boLT );
1518  binOps.insert( "PropertyIsGreaterThan", QgsExpression::boGT );
1519  binOps.insert( "PropertyIsLike", QgsExpression::boLike );
1520  // arithmetics
1521  binOps.insert( "Add", QgsExpression::boPlus );
1522  binOps.insert( "Sub", QgsExpression::boMinus );
1523  binOps.insert( "Mul", QgsExpression::boMul );
1524  binOps.insert( "Div", QgsExpression::boDiv );
1525  }
1526  return binOps;
1527 }
1528 
1529 static int binaryOperatorFromTagName( const QString& tagName )
1530 {
1531 
1532  return binaryOperatorsTagNamesMap().value( tagName, -1 );
1533 }
1534 
1536 {
1537  return binaryOperatorsTagNamesMap().key( op, QString() );
1538 }
1539 
1540 static bool isBinaryOperator( const QString& tagName )
1541 {
1542  return binaryOperatorFromTagName( tagName ) >= 0;
1543 }
1544 
1545 
1546 static bool isSpatialOperator( const QString& tagName )
1547 {
1548  static QStringList spatialOps;
1549  if ( spatialOps.isEmpty() )
1550  {
1551  spatialOps << "BBOX" << "Intersects" << "Contians" << "Crosses" << "Equals"
1552  << "Disjoint" << "Overlaps" << "Touches" << "Within";
1553  }
1554 
1555  return spatialOps.contains( tagName );
1556 }
1557 
1558 
1559 
1560 QgsExpression::Node* QgsOgcUtils::nodeFromOgcFilter( QDomElement &element, QString &errorMessage )
1561 {
1562  if ( element.isNull() )
1563  return nullptr;
1564 
1565  // check for binary operators
1566  if ( isBinaryOperator( element.tagName() ) )
1567  {
1568  return nodeBinaryOperatorFromOgcFilter( element, errorMessage );
1569  }
1570 
1571  // check for spatial operators
1572  if ( isSpatialOperator( element.tagName() ) )
1573  {
1574  return nodeSpatialOperatorFromOgcFilter( element, errorMessage );
1575  }
1576 
1577  // check for other OGC operators, convert them to expressions
1578 
1579  if ( element.tagName() == "Not" )
1580  {
1581  return nodeNotFromOgcFilter( element, errorMessage );
1582  }
1583  else if ( element.tagName() == "PropertyIsNull" )
1584  {
1585  return nodePropertyIsNullFromOgcFilter( element, errorMessage );
1586  }
1587  else if ( element.tagName() == "Literal" )
1588  {
1589  return nodeLiteralFromOgcFilter( element, errorMessage );
1590  }
1591  else if ( element.tagName() == "Function" )
1592  {
1593  return nodeFunctionFromOgcFilter( element, errorMessage );
1594  }
1595  else if ( element.tagName() == "PropertyName" )
1596  {
1597  return nodeColumnRefFromOgcFilter( element, errorMessage );
1598  }
1599  else if ( element.tagName() == "PropertyIsBetween" )
1600  {
1601  return nodeIsBetweenFromOgcFilter( element, errorMessage );
1602  }
1603 
1604  errorMessage += QString( "unable to convert '%1' element to a valid expression: it is not supported yet or it has invalid arguments" ).arg( element.tagName() );
1605  return nullptr;
1606 }
1607 
1608 
1609 
1610 QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodeBinaryOperatorFromOgcFilter( QDomElement &element, QString &errorMessage )
1611 {
1612  if ( element.isNull() )
1613  return nullptr;
1614 
1615  int op = binaryOperatorFromTagName( element.tagName() );
1616  if ( op < 0 )
1617  {
1618  if ( errorMessage.isEmpty() )
1619  errorMessage = QString( "'%1' binary operator not supported." ).arg( element.tagName() );
1620  return nullptr;
1621  }
1622 
1623  QDomElement operandElem = element.firstChildElement();
1624  QgsExpression::Node *expr = nodeFromOgcFilter( operandElem, errorMessage ), *leftOp = expr;
1625  if ( !expr )
1626  {
1627  if ( errorMessage.isEmpty() )
1628  errorMessage = QString( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
1629  return nullptr;
1630  }
1631 
1632  for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
1633  {
1634  QgsExpression::Node* opRight = nodeFromOgcFilter( operandElem, errorMessage );
1635  if ( !opRight )
1636  {
1637  if ( errorMessage.isEmpty() )
1638  errorMessage = QString( "invalid right operand for '%1' binary operator" ).arg( element.tagName() );
1639  delete expr;
1640  return nullptr;
1641  }
1642 
1643  expr = new QgsExpression::NodeBinaryOperator( static_cast< QgsExpression::BinaryOperator >( op ), expr, opRight );
1644  }
1645 
1646  if ( expr == leftOp )
1647  {
1648  if ( errorMessage.isEmpty() )
1649  errorMessage = QString( "only one operand for '%1' binary operator" ).arg( element.tagName() );
1650  delete expr;
1651  return nullptr;
1652  }
1653 
1655  if ( !ret )
1656  delete expr;
1657 
1658  return ret;
1659 }
1660 
1661 
1662 QgsExpression::NodeFunction* QgsOgcUtils::nodeSpatialOperatorFromOgcFilter( QDomElement& element, QString& errorMessage )
1663 {
1664  // we are exploiting the fact that our function names are the same as the XML tag names
1665  int opIdx = QgsExpression::functionIndex( element.tagName().toLower() );
1666 
1668  QDomElement childElem = element.firstChildElement();
1669  QString gml2Str;
1670  while ( !childElem.isNull() && gml2Str.isEmpty() )
1671  {
1672  if ( childElem.tagName() != "PropertyName" )
1673  {
1674  QTextStream gml2Stream( &gml2Str );
1675  childElem.save( gml2Stream, 0 );
1676  }
1677  childElem = childElem.nextSiblingElement();
1678  }
1679  if ( !gml2Str.isEmpty() )
1680  {
1681  gml2Args->append( new QgsExpression::NodeLiteral( QVariant( gml2Str.remove( '\n' ) ) ) );
1682  }
1683  else
1684  {
1685  errorMessage = "No OGC Geometry found";
1686  delete gml2Args;
1687  return nullptr;
1688  }
1689 
1692  opArgs->append( new QgsExpression::NodeFunction( QgsExpression::functionIndex( "geomFromGML" ), gml2Args ) );
1693 
1694  return new QgsExpression::NodeFunction( opIdx, opArgs );
1695 }
1696 
1697 
1698 QgsExpression::NodeUnaryOperator* QgsOgcUtils::nodeNotFromOgcFilter( QDomElement &element, QString &errorMessage )
1699 {
1700  if ( element.tagName() != "Not" )
1701  return nullptr;
1702 
1703  QDomElement operandElem = element.firstChildElement();
1704  QgsExpression::Node* operand = nodeFromOgcFilter( operandElem, errorMessage );
1705  if ( !operand )
1706  {
1707  if ( errorMessage.isEmpty() )
1708  errorMessage = QString( "invalid operand for '%1' unary operator" ).arg( element.tagName() );
1709  return nullptr;
1710  }
1711 
1713 }
1714 
1715 
1716 QgsExpression::NodeFunction* QgsOgcUtils::nodeFunctionFromOgcFilter( QDomElement &element, QString &errorMessage )
1717 {
1718  if ( element.isNull() || element.tagName() != "Function" )
1719  {
1720  errorMessage = QString( "ogc:Function expected, got %1" ).arg( element.tagName() );
1721  return nullptr;
1722  }
1723 
1724  for ( int i = 0; i < QgsExpression::Functions().size(); i++ )
1725  {
1727 
1728  if ( element.attribute( "name" ) != funcDef->name() )
1729  continue;
1730 
1732 
1733  QDomElement operandElem = element.firstChildElement();
1734  while ( !operandElem.isNull() )
1735  {
1736  QgsExpression::Node* op = nodeFromOgcFilter( operandElem, errorMessage );
1737  if ( !op )
1738  {
1739  delete args;
1740  return nullptr;
1741  }
1742  args->append( op );
1743 
1744  operandElem = operandElem.nextSiblingElement();
1745  }
1746 
1747  return new QgsExpression::NodeFunction( i, args );
1748  }
1749 
1750  return nullptr;
1751 }
1752 
1753 
1754 
1755 QgsExpression::Node* QgsOgcUtils::nodeLiteralFromOgcFilter( QDomElement &element, QString &errorMessage )
1756 {
1757  if ( element.isNull() || element.tagName() != "Literal" )
1758  {
1759  errorMessage = QString( "ogc:Literal expected, got %1" ).arg( element.tagName() );
1760  return nullptr;
1761  }
1762 
1763  QgsExpression::Node *root = nullptr;
1764 
1765  // the literal content can have more children (e.g. CDATA section, text, ...)
1766  QDomNode childNode = element.firstChild();
1767  while ( !childNode.isNull() )
1768  {
1769  QgsExpression::Node* operand = nullptr;
1770 
1771  if ( childNode.nodeType() == QDomNode::ElementNode )
1772  {
1773  // found a element node (e.g. PropertyName), convert it
1774  QDomElement operandElem = childNode.toElement();
1775  operand = nodeFromOgcFilter( operandElem, errorMessage );
1776  if ( !operand )
1777  {
1778  if ( root )
1779  delete root;
1780 
1781  errorMessage = QString( "'%1' is an invalid or not supported content for ogc:Literal" ).arg( operandElem.tagName() );
1782  return nullptr;
1783  }
1784  }
1785  else
1786  {
1787  // probably a text/CDATA node
1788  QVariant value = childNode.nodeValue();
1789 
1790  // try to convert the node content to number if possible,
1791  // otherwise let's use it as string
1792  bool ok;
1793  double d = value.toDouble( &ok );
1794  if ( ok )
1795  value = d;
1796 
1797  operand = new QgsExpression::NodeLiteral( value );
1798  if ( !operand )
1799  continue;
1800  }
1801 
1802  // use the concat operator to merge the ogc:Literal children
1803  if ( !root )
1804  {
1805  root = operand;
1806  }
1807  else
1808  {
1809  root = new QgsExpression::NodeBinaryOperator( QgsExpression::boConcat, root, operand );
1810  }
1811 
1812  childNode = childNode.nextSibling();
1813  }
1814 
1815  if ( root )
1816  return root;
1817 
1818  return nullptr;
1819 }
1820 
1821 
1822 QgsExpression::NodeColumnRef* QgsOgcUtils::nodeColumnRefFromOgcFilter( QDomElement &element, QString &errorMessage )
1823 {
1824  if ( element.isNull() || element.tagName() != "PropertyName" )
1825  {
1826  errorMessage = QString( "ogc:PropertyName expected, got %1" ).arg( element.tagName() );
1827  return nullptr;
1828  }
1829 
1830  return new QgsExpression::NodeColumnRef( element.firstChild().nodeValue() );
1831 }
1832 
1833 
1834 QgsExpression::Node* QgsOgcUtils::nodeIsBetweenFromOgcFilter( QDomElement& element, QString& errorMessage )
1835 {
1836  // <ogc:PropertyIsBetween> encode a Range check
1837  QgsExpression::Node *operand = nullptr, *lowerBound = nullptr;
1838  QgsExpression::Node *operand2 = nullptr, *upperBound = nullptr;
1839 
1840  QDomElement operandElem = element.firstChildElement();
1841  while ( !operandElem.isNull() )
1842  {
1843  if ( operandElem.tagName() == "LowerBoundary" )
1844  {
1845  QDomElement lowerBoundElem = operandElem.firstChildElement();
1846  lowerBound = nodeFromOgcFilter( lowerBoundElem, errorMessage );
1847  }
1848  else if ( operandElem.tagName() == "UpperBoundary" )
1849  {
1850  QDomElement upperBoundElem = operandElem.firstChildElement();
1851  upperBound = nodeFromOgcFilter( upperBoundElem, errorMessage );
1852  }
1853  else
1854  {
1855  // <ogc:expression>
1856  // both operand and operand2 contain the same expression,
1857  // they are respectively compared to lower bound and upper bound
1858  operand = nodeFromOgcFilter( operandElem, errorMessage );
1859  operand2 = nodeFromOgcFilter( operandElem, errorMessage );
1860  }
1861 
1862  if ( operand && lowerBound && operand2 && upperBound )
1863  break;
1864 
1865  operandElem = operandElem.nextSiblingElement();
1866  }
1867 
1868  if ( !operand || !lowerBound || !operand2 || !upperBound )
1869  {
1870  if ( operand )
1871  delete operand;
1872 
1873  if ( lowerBound )
1874  delete lowerBound;
1875 
1876  if ( upperBound )
1877  delete upperBound;
1878 
1879  errorMessage = "missing some required sub-elements in ogc:PropertyIsBetween";
1880  return nullptr;
1881  }
1882 
1883  QgsExpression::Node *geOperator = new QgsExpression::NodeBinaryOperator( QgsExpression::boGE, operand, lowerBound );
1884  QgsExpression::Node *leOperator = new QgsExpression::NodeBinaryOperator( QgsExpression::boLE, operand2, upperBound );
1885  return new QgsExpression::NodeBinaryOperator( QgsExpression::boAnd, geOperator, leOperator );
1886 }
1887 
1888 
1889 QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodePropertyIsNullFromOgcFilter( QDomElement& element, QString& errorMessage )
1890 {
1891  // convert ogc:PropertyIsNull to IS operator with NULL right operand
1892  if ( element.tagName() != "PropertyIsNull" )
1893  {
1894  return nullptr;
1895  }
1896 
1897  QDomElement operandElem = element.firstChildElement();
1898  QgsExpression::Node* opLeft = nodeFromOgcFilter( operandElem, errorMessage );
1899  if ( !opLeft )
1900  return nullptr;
1901 
1903  return new QgsExpression::NodeBinaryOperator( QgsExpression::boIs, opLeft, opRight );
1904 }
1905 
1906 
1908 
1909 
1911 {
1912  if ( !exp.rootNode() )
1913  return QDomElement();
1914 
1915  QString localErrorMessage; // temporary that will be thrown away unused
1916  QString& refErrorMessage = ( errorMessage ? *errorMessage : localErrorMessage );
1917  refErrorMessage.clear();
1918 
1919  QDomElement exprRootElem = expressionNodeToOgcFilter( exp.rootNode(), doc, refErrorMessage );
1920  if ( exprRootElem.isNull() )
1921  return QDomElement();
1922 
1923  QDomElement filterElem = doc.createElementNS( OGC_NAMESPACE, "ogc:Filter" );
1924  filterElem.appendChild( exprRootElem );
1925  return filterElem;
1926 }
1927 
1928 
1929 QDomElement QgsOgcUtils::expressionNodeToOgcFilter( const QgsExpression::Node* node, QDomDocument& doc, QString& errorMessage )
1930 {
1931  switch ( node->nodeType() )
1932  {
1934  return expressionUnaryOperatorToOgcFilter( static_cast<const QgsExpression::NodeUnaryOperator*>( node ), doc, errorMessage );
1936  return expressionBinaryOperatorToOgcFilter( static_cast<const QgsExpression::NodeBinaryOperator*>( node ), doc, errorMessage );
1938  return expressionInOperatorToOgcFilter( static_cast<const QgsExpression::NodeInOperator*>( node ), doc, errorMessage );
1940  return expressionFunctionToOgcFilter( static_cast<const QgsExpression::NodeFunction*>( node ), doc, errorMessage );
1942  return expressionLiteralToOgcFilter( static_cast<const QgsExpression::NodeLiteral*>( node ), doc, errorMessage );
1944  return expressionColumnRefToOgcFilter( static_cast<const QgsExpression::NodeColumnRef*>( node ), doc, errorMessage );
1945 
1946  default:
1947  errorMessage = QString( "Node type not supported: %1" ).arg( node->nodeType() );
1948  return QDomElement();
1949  }
1950 }
1951 
1952 
1953 QDomElement QgsOgcUtils::expressionUnaryOperatorToOgcFilter( const QgsExpression::NodeUnaryOperator* node, QDomDocument& doc, QString& errorMessage )
1954 {
1955 
1956  QDomElement operandElem = expressionNodeToOgcFilter( node->operand(), doc, errorMessage );
1957  if ( !errorMessage.isEmpty() )
1958  return QDomElement();
1959 
1960  QDomElement uoElem;
1961  switch ( node->op() )
1962  {
1964  uoElem = doc.createElement( "ogc:Literal" );
1965  if ( node->operand()->nodeType() == QgsExpression::ntLiteral )
1966  {
1967  // operand expression already created a Literal node:
1968  // take the literal value, prepend - and remove old literal node
1969  uoElem.appendChild( doc.createTextNode( "-" + operandElem.text() ) );
1970  doc.removeChild( operandElem );
1971  }
1972  else
1973  {
1974  errorMessage = QString( "This use of unary operator not implemented yet" );
1975  return QDomElement();
1976  }
1977  break;
1978  case QgsExpression::uoNot:
1979  uoElem = doc.createElement( "ogc:Not" );
1980  uoElem.appendChild( operandElem );
1981  break;
1982 
1983  default:
1984  errorMessage = QString( "Unary operator %1 not implemented yet" ).arg( QgsExpression::UnaryOperatorText[node->op()] );
1985  return QDomElement();
1986  }
1987 
1988  return uoElem;
1989 }
1990 
1991 
1992 QDomElement QgsOgcUtils::expressionBinaryOperatorToOgcFilter( const QgsExpression::NodeBinaryOperator* node, QDomDocument& doc, QString& errorMessage )
1993 {
1994  QDomElement leftElem = expressionNodeToOgcFilter( node->opLeft(), doc, errorMessage );
1995  if ( !errorMessage.isEmpty() )
1996  return QDomElement();
1997 
1998  QgsExpression::BinaryOperator op = node->op();
1999 
2000  // before right operator is parsed: to allow NULL handling
2001  if ( op == QgsExpression::boIs || op == QgsExpression::boIsNot )
2002  {
2003  if ( node->opRight()->nodeType() == QgsExpression::ntLiteral )
2004  {
2005  const QgsExpression::NodeLiteral* rightLit = static_cast<const QgsExpression::NodeLiteral*>( node->opRight() );
2006  if ( rightLit->value().isNull() )
2007  {
2008 
2009  QDomElement elem = doc.createElement( "ogc:PropertyIsNull" );
2010  elem.appendChild( leftElem );
2011 
2012  if ( op == QgsExpression::boIsNot )
2013  {
2014  QDomElement notElem = doc.createElement( "ogc:Not" );
2015  notElem.appendChild( elem );
2016  return notElem;
2017  }
2018 
2019  return elem;
2020  }
2021 
2022  // continue with equal / not equal operator once the null case is handled
2024  }
2025 
2026  }
2027 
2028  QDomElement rightElem = expressionNodeToOgcFilter( node->opRight(), doc, errorMessage );
2029  if ( !errorMessage.isEmpty() )
2030  return QDomElement();
2031 
2032 
2033  QString opText = binaryOperatorToTagName( op );
2034  if ( opText.isEmpty() )
2035  {
2036  // not implemented binary operators
2037  // TODO: regex, % (mod), ^ (pow) are not supported yet
2038  errorMessage = QString( "Binary operator %1 not implemented yet" ).arg( QgsExpression::BinaryOperatorText[op] );
2039  return QDomElement();
2040  }
2041 
2042  QDomElement boElem = doc.createElement( "ogc:" + opText );
2043 
2044  if ( op == QgsExpression::boLike || op == QgsExpression::boILike )
2045  {
2046  if ( op == QgsExpression::boILike )
2047  boElem.setAttribute( "matchCase", "false" );
2048 
2049  // setup wildcards to <ogc:PropertyIsLike>
2050  boElem.setAttribute( "wildCard", "%" );
2051  boElem.setAttribute( "singleChar", "?" );
2052  boElem.setAttribute( "escapeChar", "!" );
2053  }
2054 
2055  boElem.appendChild( leftElem );
2056  boElem.appendChild( rightElem );
2057  return boElem;
2058 }
2059 
2060 
2061 QDomElement QgsOgcUtils::expressionLiteralToOgcFilter( const QgsExpression::NodeLiteral* node, QDomDocument& doc, QString& errorMessage )
2062 {
2063  QString value;
2064  switch ( node->value().type() )
2065  {
2066  case QVariant::Int:
2067  value = QString::number( node->value().toInt() );
2068  break;
2069  case QVariant::Double:
2070  value = QString::number( node->value().toDouble() );
2071  break;
2072  case QVariant::String:
2073  value = node->value().toString();
2074  break;
2075 
2076  default:
2077  errorMessage = QString( "Literal type not supported: %1" ).arg( node->value().type() );
2078  return QDomElement();
2079  }
2080 
2081  QDomElement litElem = doc.createElement( "ogc:Literal" );
2082  litElem.appendChild( doc.createTextNode( value ) );
2083  return litElem;
2084 }
2085 
2086 
2087 QDomElement QgsOgcUtils::expressionColumnRefToOgcFilter( const QgsExpression::NodeColumnRef* node, QDomDocument& doc, QString& /*errorMessage*/ )
2088 {
2089  QDomElement propElem = doc.createElement( "ogc:PropertyName" );
2090  propElem.appendChild( doc.createTextNode( node->name() ) );
2091  return propElem;
2092 }
2093 
2094 
2095 
2096 QDomElement QgsOgcUtils::expressionInOperatorToOgcFilter( const QgsExpression::NodeInOperator* node, QDomDocument& doc, QString& errorMessage )
2097 {
2098  if ( node->list()->list().size() == 1 )
2099  return expressionNodeToOgcFilter( node->list()->list()[0], doc, errorMessage );
2100 
2101  QDomElement orElem = doc.createElement( "ogc:Or" );
2102  QDomElement leftNode = expressionNodeToOgcFilter( node->node(), doc, errorMessage );
2103 
2104  Q_FOREACH ( QgsExpression::Node* n, node->list()->list() )
2105  {
2106  QDomElement listNode = expressionNodeToOgcFilter( n, doc, errorMessage );
2107  if ( !errorMessage.isEmpty() )
2108  return QDomElement();
2109 
2110  QDomElement eqElem = doc.createElement( "ogc:PropertyIsEqualTo" );
2111  eqElem.appendChild( leftNode.cloneNode() );
2112  eqElem.appendChild( listNode );
2113 
2114  orElem.appendChild( eqElem );
2115  }
2116  return orElem;
2117 }
2118 
2120 {
2121  static QMap<QString, QString> binSpatialOps;
2122  if ( binSpatialOps.isEmpty() )
2123  {
2124  binSpatialOps.insert( "disjoint", "Disjoint" );
2125  binSpatialOps.insert( "intersects", "Intersects" );
2126  binSpatialOps.insert( "touches", "Touches" );
2127  binSpatialOps.insert( "crosses", "Crosses" );
2128  binSpatialOps.insert( "contains", "Contains" );
2129  binSpatialOps.insert( "overlaps", "Overlaps" );
2130  binSpatialOps.insert( "within", "Within" );
2131  }
2132  return binSpatialOps;
2133 }
2134 
2135 static bool isBinarySpatialOperator( const QString& fnName )
2136 {
2137  return binarySpatialOpsMap().contains( fnName );
2138 }
2139 
2141 {
2142  return binarySpatialOpsMap().value( fnName );
2143 }
2144 
2145 static bool isGeometryColumn( const QgsExpression::Node* node )
2146 {
2147  if ( node->nodeType() != QgsExpression::ntFunction )
2148  return false;
2149 
2150  const QgsExpression::NodeFunction* fn = static_cast<const QgsExpression::NodeFunction*>( node );
2152  return fd->name() == "$geometry";
2153 }
2154 
2156 {
2157  // Right now we support only geomFromWKT(' ..... ')
2158  // Ideally we should support any constant sub-expression (not dependent on feature's geometry or attributes)
2159 
2160  if ( node->nodeType() == QgsExpression::ntFunction )
2161  {
2162  const QgsExpression::NodeFunction* fnNode = static_cast<const QgsExpression::NodeFunction*>( node );
2164  if ( fnDef->name() == "geom_from_wkt" )
2165  {
2166  const QList<QgsExpression::Node*>& args = fnNode->args()->list();
2167  if ( args[0]->nodeType() == QgsExpression::ntLiteral )
2168  {
2169  QString wkt = static_cast<const QgsExpression::NodeLiteral*>( args[0] )->value().toString();
2170  return QgsGeometry::fromWkt( wkt );
2171  }
2172  }
2173  }
2174  return nullptr;
2175 }
2176 
2177 
2178 QDomElement QgsOgcUtils::expressionFunctionToOgcFilter( const QgsExpression::NodeFunction* node, QDomDocument& doc, QString& errorMessage )
2179 {
2181 
2182  if ( fd->name() == "intersects_bbox" )
2183  {
2184  QList<QgsExpression::Node*> argNodes = node->args()->list();
2185  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2186 
2187  QgsGeometry* geom = geometryFromConstExpr( argNodes[1] );
2188  if ( geom && isGeometryColumn( argNodes[0] ) )
2189  {
2190  QgsRectangle rect = geom->boundingBox();
2191  delete geom;
2192 
2193  QDomElement elemBox = rectangleToGMLBox( &rect, doc );
2194 
2195  QDomElement geomProperty = doc.createElement( "ogc:PropertyName" );
2196  geomProperty.appendChild( doc.createTextNode( "geometry" ) );
2197 
2198  QDomElement funcElem = doc.createElement( "ogr:BBOX" );
2199  funcElem.appendChild( geomProperty );
2200  funcElem.appendChild( elemBox );
2201  return funcElem;
2202  }
2203  else
2204  {
2205  delete geom;
2206 
2207  errorMessage = QString( "<BBOX> is currently supported only in form: bbox($geometry, geomFromWKT('...'))" );
2208  return QDomElement();
2209  }
2210  }
2211 
2212  if ( isBinarySpatialOperator( fd->name() ) )
2213  {
2214  QList<QgsExpression::Node*> argNodes = node->args()->list();
2215  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2216 
2217  QgsExpression::Node* otherNode = nullptr;
2218  if ( isGeometryColumn( argNodes[0] ) )
2219  otherNode = argNodes[1];
2220  else if ( isGeometryColumn( argNodes[1] ) )
2221  otherNode = argNodes[0];
2222  else
2223  {
2224  errorMessage = QString( "Unable to translate spatial operator: at least one must refer to geometry." );
2225  return QDomElement();
2226  }
2227 
2228  QDomElement otherGeomElem;
2229 
2230  // the other node must be a geometry constructor
2231  if ( otherNode->nodeType() != QgsExpression::ntFunction )
2232  {
2233  errorMessage = "spatial operator: the other operator must be a geometry constructor function";
2234  return QDomElement();
2235  }
2236 
2237  const QgsExpression::NodeFunction* otherFn = static_cast<const QgsExpression::NodeFunction*>( otherNode );
2238  QgsExpression::Function* otherFnDef = QgsExpression::Functions()[otherFn->fnIndex()];
2239  if ( otherFnDef->name() == "geom_from_wkt" )
2240  {
2241  QgsExpression::Node* firstFnArg = otherFn->args()->list()[0];
2242  if ( firstFnArg->nodeType() != QgsExpression::ntLiteral )
2243  {
2244  errorMessage = "geom_from_wkt: argument must be string literal";
2245  return QDomElement();
2246  }
2247  QString wkt = static_cast<const QgsExpression::NodeLiteral*>( firstFnArg )->value().toString();
2248  QgsGeometry* geom = QgsGeometry::fromWkt( wkt );
2249  otherGeomElem = QgsOgcUtils::geometryToGML( geom, doc );
2250  delete geom;
2251  }
2252  else if ( otherFnDef->name() == "geom_from_gml" )
2253  {
2254  QgsExpression::Node* firstFnArg = otherFn->args()->list()[0];
2255  if ( firstFnArg->nodeType() != QgsExpression::ntLiteral )
2256  {
2257  errorMessage = "geom_from_gml: argument must be string literal";
2258  return QDomElement();
2259  }
2260 
2261  QDomDocument geomDoc;
2262  QString gml = static_cast<const QgsExpression::NodeLiteral*>( firstFnArg )->value().toString();
2263  if ( !geomDoc.setContent( gml, true ) )
2264  {
2265  errorMessage = "geom_from_gml: unable to parse XML";
2266  return QDomElement();
2267  }
2268 
2269  QDomNode geomNode = doc.importNode( geomDoc.documentElement(), true );
2270  otherGeomElem = geomNode.toElement();
2271  }
2272  else
2273  {
2274  errorMessage = "spatial operator: unknown geometry constructor function";
2275  return QDomElement();
2276  }
2277 
2278  QDomElement funcElem = doc.createElement( "ogc:" + tagNameForSpatialOperator( fd->name() ) );
2279  QDomElement geomProperty = doc.createElement( "ogc:PropertyName" );
2280  geomProperty.appendChild( doc.createTextNode( "geometry" ) );
2281  funcElem.appendChild( geomProperty );
2282  funcElem.appendChild( otherGeomElem );
2283  return funcElem;
2284  }
2285 
2286  if ( fd->params() == 0 )
2287  {
2288  errorMessage = QString( "Special columns / constants are not supported." );
2289  return QDomElement();
2290  }
2291 
2292  // this is somehow wrong - we are just hoping that the other side supports the same functions as we do...
2293  QDomElement funcElem = doc.createElement( "ogc:Function" );
2294  funcElem.setAttribute( "name", fd->name() );
2295  Q_FOREACH ( QgsExpression::Node* n, node->args()->list() )
2296  {
2297  QDomElement childElem = expressionNodeToOgcFilter( n, doc, errorMessage );
2298  if ( !errorMessage.isEmpty() )
2299  return QDomElement();
2300 
2301  funcElem.appendChild( childElem );
2302  }
2303 
2304  return funcElem;
2305 }
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'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:196
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.
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
WkbType
Used for symbology operations.
Definition: qgis.h:57
QString & remove(int position, int n)
QDomElement createElementNS(const QString &nsURI, const QString &qName)
QVariant value() const
The value of the literal.
void setNamedColor(const QString &name)
double toDouble(bool *ok) const
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)
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:201
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:186
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
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:191
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[]