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