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