QGIS API Documentation  2.7.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
qgsogcutils.cpp
Go to the documentation of this file.
1 #include "qgsogcutils.h"
2 
3 #include "qgsexpression.h"
4 #include "qgsgeometry.h"
5 
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, const int &precision )
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(), precision );
1010  coordString += ",";
1011  coordString += qgsDoubleToString( box->yMinimum(), precision );
1012  coordString += " ";
1013  coordString += qgsDoubleToString( box->xMaximum(), precision );
1014  coordString += ",";
1015  coordString += qgsDoubleToString( box->yMaximum(), precision );
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, const int &precision )
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(), precision );
1036  posList += " ";
1037  posList += qgsDoubleToString( env->yMinimum(), precision );
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(), precision );
1044  posList += " ";
1045  posList += qgsDoubleToString( env->yMaximum(), precision );
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, const int &precision )
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, precision ) + cs + qgsDoubleToString( y, precision ) );
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, precision ) + cs + qgsDoubleToString( y, precision ) );
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, precision ) + cs + qgsDoubleToString( y, precision );
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, precision ) + cs + qgsDoubleToString( y, precision );
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, precision ) + cs + qgsDoubleToString( y, precision );
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, precision ) + cs + qgsDoubleToString( y, precision );
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, const int &precision )
1343 {
1344  return geometryToGML( geometry, doc, "GML2", precision );
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 
1584 QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodeBinaryOperatorFromOgcFilter( QDomElement &element, QString &errorMessage )
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 *expr = nodeFromOgcFilter( operandElem, errorMessage ), *leftOp = expr;
1599  if ( !expr )
1600  {
1601  if ( errorMessage.isEmpty() )
1602  errorMessage = QString( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
1603  return NULL;
1604  }
1605 
1606  for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
1607  {
1608  QgsExpression::Node* opRight = nodeFromOgcFilter( operandElem, errorMessage );
1609  if ( !opRight )
1610  {
1611  if ( errorMessage.isEmpty() )
1612  errorMessage = QString( "invalid right operand for '%1' binary operator" ).arg( element.tagName() );
1613  delete expr;
1614  return NULL;
1615  }
1616 
1617  expr = new QgsExpression::NodeBinaryOperator(( QgsExpression::BinaryOperator ) op, expr, opRight );
1618  }
1619 
1620  if ( expr == leftOp )
1621  {
1622  if ( errorMessage.isEmpty() )
1623  errorMessage = QString( "only one operand for '%1' binary operator" ).arg( element.tagName() );
1624  delete expr;
1625  return NULL;
1626  }
1627 
1628  return dynamic_cast< QgsExpression::NodeBinaryOperator * >( expr );
1629 }
1630 
1631 
1632 QgsExpression::NodeFunction* QgsOgcUtils::nodeSpatialOperatorFromOgcFilter( QDomElement& element, QString& errorMessage )
1633 {
1634  // we are exploiting the fact that our function names are the same as the XML tag names
1635  int opIdx = QgsExpression::functionIndex( element.tagName().toLower() );
1636 
1638  QDomElement childElem = element.firstChildElement();
1639  QString gml2Str;
1640  while ( !childElem.isNull() && gml2Str.isEmpty() )
1641  {
1642  if ( childElem.tagName() != "PropertyName" )
1643  {
1644  QTextStream gml2Stream( &gml2Str );
1645  childElem.save( gml2Stream, 0 );
1646  }
1647  childElem = childElem.nextSiblingElement();
1648  }
1649  if ( !gml2Str.isEmpty() )
1650  {
1651  gml2Args->append( new QgsExpression::NodeLiteral( QVariant( gml2Str.remove( "\n" ) ) ) );
1652  }
1653  else
1654  {
1655  errorMessage = QString( "No OGC Geometry found" );
1656  return NULL;
1657  }
1658 
1661  opArgs->append( new QgsExpression::NodeFunction( QgsExpression::functionIndex( "geomFromGML" ), gml2Args ) );
1662 
1663  return new QgsExpression::NodeFunction( opIdx, opArgs );
1664 }
1665 
1666 
1667 QgsExpression::NodeUnaryOperator* QgsOgcUtils::nodeNotFromOgcFilter( QDomElement &element, QString &errorMessage )
1668 {
1669  if ( element.tagName() != "Not" )
1670  return NULL;
1671 
1672  QDomElement operandElem = element.firstChildElement();
1673  QgsExpression::Node* operand = nodeFromOgcFilter( operandElem, errorMessage );
1674  if ( !operand )
1675  {
1676  if ( errorMessage.isEmpty() )
1677  errorMessage = QString( "invalid operand for '%1' unary operator" ).arg( element.tagName() );
1678  return NULL;
1679  }
1680 
1682 }
1683 
1684 
1685 QgsExpression::NodeFunction* QgsOgcUtils::nodeFunctionFromOgcFilter( QDomElement &element, QString &errorMessage )
1686 {
1687  if ( element.isNull() || element.tagName() != "Function" )
1688  {
1689  errorMessage = QString( "ogc:Function expected, got %1" ).arg( element.tagName() );
1690  return NULL;
1691  }
1692 
1693  for ( int i = 0; i < QgsExpression::Functions().size(); i++ )
1694  {
1696 
1697  if ( element.attribute( "name" ) != funcDef->name() )
1698  continue;
1699 
1701 
1702  QDomElement operandElem = element.firstChildElement();
1703  while ( !operandElem.isNull() )
1704  {
1705  QgsExpression::Node* op = nodeFromOgcFilter( operandElem, errorMessage );
1706  if ( !op )
1707  {
1708  delete args;
1709  return NULL;
1710  }
1711  args->append( op );
1712 
1713  operandElem = operandElem.nextSiblingElement();
1714  }
1715 
1716  return new QgsExpression::NodeFunction( i, args );
1717  }
1718 
1719  return NULL;
1720 }
1721 
1722 
1723 
1724 QgsExpression::Node* QgsOgcUtils::nodeLiteralFromOgcFilter( QDomElement &element, QString &errorMessage )
1725 {
1726  if ( element.isNull() || element.tagName() != "Literal" )
1727  {
1728  errorMessage = QString( "ogc:Literal expected, got %1" ).arg( element.tagName() );
1729  return NULL;
1730  }
1731 
1732  QgsExpression::Node *root = 0;
1733 
1734  // the literal content can have more children (e.g. CDATA section, text, ...)
1735  QDomNode childNode = element.firstChild();
1736  while ( !childNode.isNull() )
1737  {
1738  QgsExpression::Node* operand = 0;
1739 
1740  if ( childNode.nodeType() == QDomNode::ElementNode )
1741  {
1742  // found a element node (e.g. PropertyName), convert it
1743  QDomElement operandElem = childNode.toElement();
1744  operand = nodeFromOgcFilter( operandElem, errorMessage );
1745  if ( !operand )
1746  {
1747  if ( root )
1748  delete root;
1749 
1750  errorMessage = QString( "'%1' is an invalid or not supported content for ogc:Literal" ).arg( operandElem.tagName() );
1751  return NULL;
1752  }
1753  }
1754  else
1755  {
1756  // probably a text/CDATA node
1757  QVariant value = childNode.nodeValue();
1758 
1759  // try to convert the node content to number if possible,
1760  // otherwise let's use it as string
1761  bool ok;
1762  double d = value.toDouble( &ok );
1763  if ( ok )
1764  value = d;
1765 
1766  operand = new QgsExpression::NodeLiteral( value );
1767  if ( !operand )
1768  continue;
1769  }
1770 
1771  // use the concat operator to merge the ogc:Literal children
1772  if ( !root )
1773  {
1774  root = operand;
1775  }
1776  else
1777  {
1778  root = new QgsExpression::NodeBinaryOperator( QgsExpression::boConcat, root, operand );
1779  }
1780 
1781  childNode = childNode.nextSibling();
1782  }
1783 
1784  if ( root )
1785  return root;
1786 
1787  return NULL;
1788 }
1789 
1790 
1791 QgsExpression::NodeColumnRef* QgsOgcUtils::nodeColumnRefFromOgcFilter( QDomElement &element, QString &errorMessage )
1792 {
1793  if ( element.isNull() || element.tagName() != "PropertyName" )
1794  {
1795  errorMessage = QString( "ogc:PropertyName expected, got %1" ).arg( element.tagName() );
1796  return NULL;
1797  }
1798 
1799  return new QgsExpression::NodeColumnRef( element.firstChild().nodeValue() );
1800 }
1801 
1802 
1803 QgsExpression::Node* QgsOgcUtils::nodeIsBetweenFromOgcFilter( QDomElement& element, QString& errorMessage )
1804 {
1805  // <ogc:PropertyIsBetween> encode a Range check
1806  QgsExpression::Node *operand = 0, *lowerBound = 0;
1807  QgsExpression::Node *operand2 = 0, *upperBound = 0;
1808 
1809  QDomElement operandElem = element.firstChildElement();
1810  while ( !operandElem.isNull() )
1811  {
1812  if ( operandElem.tagName() == "LowerBoundary" )
1813  {
1814  QDomElement lowerBoundElem = operandElem.firstChildElement();
1815  lowerBound = nodeFromOgcFilter( lowerBoundElem, errorMessage );
1816  }
1817  else if ( operandElem.tagName() == "UpperBoundary" )
1818  {
1819  QDomElement upperBoundElem = operandElem.firstChildElement();
1820  upperBound = nodeFromOgcFilter( upperBoundElem, errorMessage );
1821  }
1822  else
1823  {
1824  // <ogc:expression>
1825  // both operand and operand2 contain the same expression,
1826  // they are respectively compared to lower bound and upper bound
1827  operand = nodeFromOgcFilter( operandElem, errorMessage );
1828  operand2 = nodeFromOgcFilter( operandElem, errorMessage );
1829  }
1830 
1831  if ( operand && lowerBound && operand2 && upperBound )
1832  break;
1833 
1834  operandElem = operandElem.nextSiblingElement();
1835  }
1836 
1837  if ( !operand || !lowerBound || !operand2 || !upperBound )
1838  {
1839  if ( operand )
1840  delete operand;
1841 
1842  if ( lowerBound )
1843  delete lowerBound;
1844 
1845  if ( upperBound )
1846  delete upperBound;
1847 
1848  errorMessage = "missing some required sub-elements in ogc:PropertyIsBetween";
1849  return NULL;
1850  }
1851 
1852  QgsExpression::Node *geOperator = new QgsExpression::NodeBinaryOperator( QgsExpression::boGE, operand, lowerBound );
1853  QgsExpression::Node *leOperator = new QgsExpression::NodeBinaryOperator( QgsExpression::boLE, operand2, upperBound );
1854  return new QgsExpression::NodeBinaryOperator( QgsExpression::boAnd, geOperator, leOperator );
1855 }
1856 
1857 
1858 QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodePropertyIsNullFromOgcFilter( QDomElement& element, QString& errorMessage )
1859 {
1860  // convert ogc:PropertyIsNull to IS operator with NULL right operand
1861  if ( element.tagName() != "PropertyIsNull" )
1862  {
1863  return NULL;
1864  }
1865 
1866  QDomElement operandElem = element.firstChildElement();
1867  QgsExpression::Node* opLeft = nodeFromOgcFilter( operandElem, errorMessage );
1868  if ( !opLeft )
1869  return NULL;
1870 
1871  QgsExpression::Node* opRight = new QgsExpression::NodeLiteral( QVariant() );
1872  return new QgsExpression::NodeBinaryOperator( QgsExpression::boIs, opLeft, opRight );
1873 }
1874 
1875 
1877 
1878 
1879 QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression& exp, QDomDocument& doc, QString* errorMessage )
1880 {
1881  if ( !exp.rootNode() )
1882  return QDomElement();
1883 
1884  QString localErrorMessage; // temporary that will be thrown away unused
1885  QString& refErrorMessage = ( errorMessage ? *errorMessage : localErrorMessage );
1886  refErrorMessage.clear();
1887 
1888  QDomElement exprRootElem = expressionNodeToOgcFilter( exp.rootNode(), doc, refErrorMessage );
1889  if ( exprRootElem.isNull() )
1890  return QDomElement();
1891 
1892  QDomElement filterElem = doc.createElementNS( OGC_NAMESPACE, "ogc:Filter" );
1893  filterElem.appendChild( exprRootElem );
1894  return filterElem;
1895 }
1896 
1897 
1898 QDomElement QgsOgcUtils::expressionNodeToOgcFilter( const QgsExpression::Node* node, QDomDocument& doc, QString& errorMessage )
1899 {
1900  switch ( node->nodeType() )
1901  {
1903  return expressionUnaryOperatorToOgcFilter( static_cast<const QgsExpression::NodeUnaryOperator*>( node ), doc, errorMessage );
1905  return expressionBinaryOperatorToOgcFilter( static_cast<const QgsExpression::NodeBinaryOperator*>( node ), doc, errorMessage );
1907  return expressionInOperatorToOgcFilter( static_cast<const QgsExpression::NodeInOperator*>( node ), doc, errorMessage );
1909  return expressionFunctionToOgcFilter( static_cast<const QgsExpression::NodeFunction*>( node ), doc, errorMessage );
1911  return expressionLiteralToOgcFilter( static_cast<const QgsExpression::NodeLiteral*>( node ), doc, errorMessage );
1913  return expressionColumnRefToOgcFilter( static_cast<const QgsExpression::NodeColumnRef*>( node ), doc, errorMessage );
1914 
1915  default:
1916  errorMessage = QString( "Node type not supported: %1" ).arg( node->nodeType() );
1917  return QDomElement();
1918  }
1919 }
1920 
1921 
1922 QDomElement QgsOgcUtils::expressionUnaryOperatorToOgcFilter( const QgsExpression::NodeUnaryOperator* node, QDomDocument& doc, QString& errorMessage )
1923 {
1924  QDomElement uoElem;
1925  switch ( node->op() )
1926  {
1928  uoElem = doc.createElement( "ogc:Literal" );
1929  uoElem.appendChild( doc.createTextNode( "-" ) );
1930  break;
1931  case QgsExpression::uoNot:
1932  uoElem = doc.createElement( "ogc:Not" );
1933  break;
1934 
1935  default:
1936  errorMessage = QString( "Unary operator %1 not implemented yet" ).arg( QgsExpression::UnaryOperatorText[node->op()] );
1937  return QDomElement();
1938  }
1939 
1940  QDomElement operandElem = expressionNodeToOgcFilter( node->operand(), doc, errorMessage );
1941  if ( !errorMessage.isEmpty() )
1942  return QDomElement();
1943 
1944  uoElem.appendChild( operandElem );
1945  return uoElem;
1946 }
1947 
1948 
1949 QDomElement QgsOgcUtils::expressionBinaryOperatorToOgcFilter( const QgsExpression::NodeBinaryOperator* node, QDomDocument& doc, QString& errorMessage )
1950 {
1951  QDomElement leftElem = expressionNodeToOgcFilter( node->opLeft(), doc, errorMessage );
1952  if ( !errorMessage.isEmpty() )
1953  return QDomElement();
1954 
1955  QgsExpression::BinaryOperator op = node->op();
1956 
1957  // before right operator is parsed: to allow NULL handling
1958  if ( op == QgsExpression::boIs || op == QgsExpression::boIsNot )
1959  {
1960  if ( node->opRight()->nodeType() == QgsExpression::ntLiteral )
1961  {
1962  const QgsExpression::NodeLiteral* rightLit = static_cast<const QgsExpression::NodeLiteral*>( node->opRight() );
1963  if ( rightLit->value().isNull() )
1964  {
1965 
1966  QDomElement elem = doc.createElement( "ogc:PropertyIsNull" );
1967  elem.appendChild( leftElem );
1968 
1969  if ( op == QgsExpression::boIsNot )
1970  {
1971  QDomElement notElem = doc.createElement( "ogc:Not" );
1972  notElem.appendChild( elem );
1973  return notElem;
1974  }
1975 
1976  return elem;
1977  }
1978 
1979  // continue with equal / not equal operator once the null case is handled
1981  }
1982 
1983  }
1984 
1985  QDomElement rightElem = expressionNodeToOgcFilter( node->opRight(), doc, errorMessage );
1986  if ( !errorMessage.isEmpty() )
1987  return QDomElement();
1988 
1989 
1990  QString opText = binaryOperatorToTagName( op );
1991  if ( opText.isEmpty() )
1992  {
1993  // not implemented binary operators
1994  // TODO: regex, % (mod), ^ (pow) are not supported yet
1995  errorMessage = QString( "Binary operator %1 not implemented yet" ).arg( QgsExpression::BinaryOperatorText[op] );
1996  return QDomElement();
1997  }
1998 
1999  QDomElement boElem = doc.createElement( "ogc:" + opText );
2000 
2001  if ( op == QgsExpression::boLike || op == QgsExpression::boILike )
2002  {
2003  if ( op == QgsExpression::boILike )
2004  boElem.setAttribute( "matchCase", "false" );
2005 
2006  // setup wildcards to <ogc:PropertyIsLike>
2007  boElem.setAttribute( "wildCard", "%" );
2008  boElem.setAttribute( "singleChar", "?" );
2009  boElem.setAttribute( "escapeChar", "!" );
2010  }
2011 
2012  boElem.appendChild( leftElem );
2013  boElem.appendChild( rightElem );
2014  return boElem;
2015 }
2016 
2017 
2018 QDomElement QgsOgcUtils::expressionLiteralToOgcFilter( const QgsExpression::NodeLiteral* node, QDomDocument& doc, QString& errorMessage )
2019 {
2020  QString value;
2021  switch ( node->value().type() )
2022  {
2023  case QVariant::Int:
2024  value = QString::number( node->value().toInt() );
2025  break;
2026  case QVariant::Double:
2027  value = QString::number( node->value().toDouble() );
2028  break;
2029  case QVariant::String:
2030  value = node->value().toString();
2031  break;
2032 
2033  default:
2034  errorMessage = QString( "Literal type not supported: %1" ).arg( node->value().type() );
2035  return QDomElement();
2036  }
2037 
2038  QDomElement litElem = doc.createElement( "ogc:Literal" );
2039  litElem.appendChild( doc.createTextNode( value ) );
2040  return litElem;
2041 }
2042 
2043 
2044 QDomElement QgsOgcUtils::expressionColumnRefToOgcFilter( const QgsExpression::NodeColumnRef* node, QDomDocument& doc, QString& /*errorMessage*/ )
2045 {
2046  QDomElement propElem = doc.createElement( "ogc:PropertyName" );
2047  propElem.appendChild( doc.createTextNode( node->name() ) );
2048  return propElem;
2049 }
2050 
2051 
2052 
2053 QDomElement QgsOgcUtils::expressionInOperatorToOgcFilter( const QgsExpression::NodeInOperator* node, QDomDocument& doc, QString& errorMessage )
2054 {
2055  if ( node->list()->list().size() == 1 )
2056  return expressionNodeToOgcFilter( node->list()->list()[0], doc, errorMessage );
2057 
2058  QDomElement orElem = doc.createElement( "ogc:Or" );
2059  QDomElement leftNode = expressionNodeToOgcFilter( node->node(), doc, errorMessage );
2060 
2061  foreach ( QgsExpression::Node* n, node->list()->list() )
2062  {
2063  QDomElement listNode = expressionNodeToOgcFilter( n, doc, errorMessage );
2064  if ( !errorMessage.isEmpty() )
2065  return QDomElement();
2066 
2067  QDomElement eqElem = doc.createElement( "ogc:PropertyIsEqualTo" );
2068  eqElem.appendChild( leftNode.cloneNode() );
2069  eqElem.appendChild( listNode );
2070 
2071  orElem.appendChild( eqElem );
2072  }
2073  return orElem;
2074 }
2075 
2076 static QMap<QString, QString> binarySpatialOpsMap()
2077 {
2078  static QMap<QString, QString> binSpatialOps;
2079  if ( binSpatialOps.isEmpty() )
2080  {
2081  binSpatialOps.insert( "disjoint", "Disjoint" );
2082  binSpatialOps.insert( "intersects", "Intersects" );
2083  binSpatialOps.insert( "touches", "Touches" );
2084  binSpatialOps.insert( "crosses", "Crosses" );
2085  binSpatialOps.insert( "contains", "Contains" );
2086  binSpatialOps.insert( "overlaps", "Overlaps" );
2087  binSpatialOps.insert( "within", "Within" );
2088  }
2089  return binSpatialOps;
2090 }
2091 
2092 static bool isBinarySpatialOperator( const QString& fnName )
2093 {
2094  return binarySpatialOpsMap().contains( fnName );
2095 }
2096 
2097 static QString tagNameForSpatialOperator( const QString& fnName )
2098 {
2099  return binarySpatialOpsMap().value( fnName );
2100 }
2101 
2102 static bool isGeometryColumn( const QgsExpression::Node* node )
2103 {
2104  if ( node->nodeType() != QgsExpression::ntFunction )
2105  return false;
2106 
2107  const QgsExpression::NodeFunction* fn = static_cast<const QgsExpression::NodeFunction*>( node );
2109  return fd->name() == "$geometry";
2110 }
2111 
2113 {
2114  // Right now we support only geomFromWKT(' ..... ')
2115  // Ideally we should support any constant sub-expression (not dependent on feature's geometry or attributes)
2116 
2117  if ( node->nodeType() == QgsExpression::ntFunction )
2118  {
2119  const QgsExpression::NodeFunction* fnNode = static_cast<const QgsExpression::NodeFunction*>( node );
2121  if ( fnDef->name() == "geomFromWKT" )
2122  {
2123  const QList<QgsExpression::Node*>& args = fnNode->args()->list();
2124  if ( args[0]->nodeType() == QgsExpression::ntLiteral )
2125  {
2126  QString wkt = static_cast<const QgsExpression::NodeLiteral*>( args[0] )->value().toString();
2127  return QgsGeometry::fromWkt( wkt );
2128  }
2129  }
2130  }
2131  return 0;
2132 }
2133 
2134 
2135 QDomElement QgsOgcUtils::expressionFunctionToOgcFilter( const QgsExpression::NodeFunction* node, QDomDocument& doc, QString& errorMessage )
2136 {
2138 
2139  if ( fd->name() == "bbox" )
2140  {
2141  QList<QgsExpression::Node*> argNodes = node->args()->list();
2142  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2143 
2144  QgsGeometry* geom = geometryFromConstExpr( argNodes[1] );
2145  if ( geom && isGeometryColumn( argNodes[0] ) )
2146  {
2147  QgsRectangle rect = geom->boundingBox();
2148  delete geom;
2149 
2150  QDomElement elemBox = rectangleToGMLBox( &rect, doc );
2151 
2152  QDomElement geomProperty = doc.createElement( "ogc:PropertyName" );
2153  geomProperty.appendChild( doc.createTextNode( "geometry" ) );
2154 
2155  QDomElement funcElem = doc.createElement( "ogr:BBOX" );
2156  funcElem.appendChild( geomProperty );
2157  funcElem.appendChild( elemBox );
2158  return funcElem;
2159  }
2160  else
2161  {
2162  delete geom;
2163 
2164  errorMessage = QString( "<BBOX> is currently supported only in form: bbox($geometry, geomFromWKT('...'))" );
2165  return QDomElement();
2166  }
2167  }
2168 
2169  if ( isBinarySpatialOperator( fd->name() ) )
2170  {
2171  QList<QgsExpression::Node*> argNodes = node->args()->list();
2172  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2173 
2174  QgsExpression::Node* otherNode = 0;
2175  if ( isGeometryColumn( argNodes[0] ) )
2176  otherNode = argNodes[1];
2177  else if ( isGeometryColumn( argNodes[1] ) )
2178  otherNode = argNodes[0];
2179  else
2180  {
2181  errorMessage = QString( "Unable to translate spatial operator: at least one must refer to geometry." );
2182  return QDomElement();
2183  }
2184 
2185  QDomElement otherGeomElem;
2186 
2187  // the other node must be a geometry constructor
2188  if ( otherNode->nodeType() != QgsExpression::ntFunction )
2189  {
2190  errorMessage = "spatial operator: the other operator must be a geometry constructor function";
2191  return QDomElement();
2192  }
2193 
2194  const QgsExpression::NodeFunction* otherFn = static_cast<const QgsExpression::NodeFunction*>( otherNode );
2195  QgsExpression::Function* otherFnDef = QgsExpression::Functions()[otherFn->fnIndex()];
2196  if ( otherFnDef->name() == "geomFromWKT" )
2197  {
2198  QgsExpression::Node* firstFnArg = otherFn->args()->list()[0];
2199  if ( firstFnArg->nodeType() != QgsExpression::ntLiteral )
2200  {
2201  errorMessage = "geomFromWKT: argument must be string literal";
2202  return QDomElement();
2203  }
2204  QString wkt = static_cast<const QgsExpression::NodeLiteral*>( firstFnArg )->value().toString();
2205  QgsGeometry* geom = QgsGeometry::fromWkt( wkt );
2206  otherGeomElem = QgsOgcUtils::geometryToGML( geom, doc );
2207  delete geom;
2208  }
2209  else if ( otherFnDef->name() == "geomFromGML" )
2210  {
2211  QgsExpression::Node* firstFnArg = otherFn->args()->list()[0];
2212  if ( firstFnArg->nodeType() != QgsExpression::ntLiteral )
2213  {
2214  errorMessage = "geomFromGML: argument must be string literal";
2215  return QDomElement();
2216  }
2217 
2218  QDomDocument geomDoc;
2219  QString gml = static_cast<const QgsExpression::NodeLiteral*>( firstFnArg )->value().toString();
2220  if ( !geomDoc.setContent( gml, true ) )
2221  {
2222  errorMessage = "geomFromGML: unable to parse XML";
2223  return QDomElement();
2224  }
2225 
2226  QDomNode geomNode = doc.importNode( geomDoc.documentElement(), true );
2227  otherGeomElem = geomNode.toElement();
2228  }
2229  else
2230  {
2231  errorMessage = "spatial operator: unknown geometry constructor function";
2232  return QDomElement();
2233  }
2234 
2235  QDomElement funcElem = doc.createElement( "ogc:" + tagNameForSpatialOperator( fd->name() ) );
2236  QDomElement geomProperty = doc.createElement( "ogc:PropertyName" );
2237  geomProperty.appendChild( doc.createTextNode( "geometry" ) );
2238  funcElem.appendChild( geomProperty );
2239  funcElem.appendChild( otherGeomElem );
2240  return funcElem;
2241  }
2242 
2243  if ( fd->params() == 0 )
2244  {
2245  errorMessage = QString( "Special columns / constants are not supported." );
2246  return QDomElement();
2247  }
2248 
2249  // this is somehow wrong - we are just hoping that the other side supports the same functions as we do...
2250  QDomElement funcElem = doc.createElement( "ogc:Function" );
2251  funcElem.setAttribute( "name", fd->name() );
2252  foreach ( QgsExpression::Node* n, node->args()->list() )
2253  {
2254  QDomElement childElem = expressionNodeToOgcFilter( n, doc, errorMessage );
2255  if ( !errorMessage.isEmpty() )
2256  return QDomElement();
2257 
2258  funcElem.appendChild( childElem );
2259  }
2260 
2261  return funcElem;
2262 }
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:87
static int functionIndex(QString name)
virtual NodeType nodeType() const =0
A rectangle specified with double values.
Definition: qgsrectangle.h:35
A abstract base class for defining QgsExpression functions.
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:188
QVector< QgsPoint > QgsPolyline
polyline is represented as a vector of points
Definition: qgsgeometry.h:33
static bool isGeometryColumn(const QgsExpression::Node *node)
NodeList * list() const
QString dump() const
Return the expression string that represents this QgsExpression.
WkbType
Used for symbology operations.
Definition: qgis.h:53
QVariant value() const
static QMap< QString, QString > binarySpatialOpsMap()
static QgsRectangle rectangleFromGMLBox(const QDomNode &boxNode)
read rectangle from GML2 Box
QString mParserErrorString
static QgsRectangle rectangleFromGMLEnvelope(const QDomNode &envelopeNode)
read rectangle from GML3 Envelope
static const QList< Function * > & Functions()
static int binaryOperatorFromTagName(const QString &tagName)
static QDomElement expressionToOgcFilter(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=0)
Creates OGC filter XML element.
static bool isBinaryOperator(const QString &tagName)
static const QString GML_NAMESPACE
Definition: qgsogcutils.cpp:17
NodeList * args() const
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:193
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:178
static QgsGeometry * geometryFromGML(const QString &xmlString)
static method that creates geometry from GML
Definition: qgsogcutils.cpp:81
const Node * rootNode() const
Returns root node of the expression. Root node is null is parsing has failed.
QVector< QgsPolygon > QgsMultiPolygon
a collection of QgsPolygons that share a common collection of attributes
Definition: qgsgeometry.h:48
QGis::WkbType wkbType() const
Returns type of wkb (point / linestring / polygon etc.)
static bool isBinarySpatialOperator(const QString &fnName)
QVector< QgsPolyline > QgsPolygon
polygon: first item of the list is outer ring, inner rings (if any) start from second item ...
Definition: qgsgeometry.h:39
QString name()
The name of the function.
static QString tagNameForSpatialOperator(const QString &fnName)
A class to represent a point.
Definition: qgspoint.h:63
static const QMap< QString, int > & binaryOperatorsTagNamesMap()
BinaryOperator op() const
static QDomElement geometryToGML(QgsGeometry *geometry, QDomDocument &doc, QString format, const int &precision=17)
Exports the geometry to GML2 or GML3.
QString qgsDoubleToString(const double &a, const int &precision=17)
Definition: qgis.h:317
QVector< QgsPolyline > QgsMultiPolyline
a collection of QgsPolylines that share a common collection of attributes
Definition: qgsgeometry.h:45
static const QString OGC_NAMESPACE
Definition: qgsogcutils.cpp:18
static bool isSpatialOperator(const QString &tagName)
static const char * UnaryOperatorText[]
BinaryOperator
list of binary operators
void fromWkb(unsigned char *wkb, size_t length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length...
int params()
The number of parameters this function takes.
static QgsExpression * expressionFromOgcFilter(const QDomElement &element)
Parse XML with OGC filter into QGIS expression.
static QgsGeometry * fromRect(const QgsRectangle &rect)
construct geometry from a rectangle
static QgsGeometry * geometryFromConstExpr(const QgsExpression::Node *node)
static QString binaryOperatorToTagName(QgsExpression::BinaryOperator op)
static QgsGeometry * fromWkt(QString wkt)
static method that creates geometry from Wkt
static QColor colorFromOgcFill(const QDomElement &fillElement)
Parse XML with OGC fill into QColor.
void append(Node *node)
UnaryOperator op() const
void normalize()
Normalize the rectangle so it has non-negative width/height.
static QDomElement rectangleToGMLBox(QgsRectangle *box, QDomDocument &doc, const int &precision=17)
Exports the rectangle to GML2 Box.
double size
Definition: qgssvgcache.cpp:77
const unsigned char * asWkb() const
Returns the buffer containing this geometry in WKB format.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:183
static QDomElement rectangleToGMLEnvelope(QgsRectangle *env, QDomDocument &doc, const int &precision=17)
Exports the rectangle to GML2 Envelope.
QList< Node * > list()
static const char * BinaryOperatorText[]