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&#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: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.
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)
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[]