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