QGIS API Documentation  2.9.0-Master
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_OS_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( const 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  //intentional fall-through
1112  case QGis::WKBMultiPoint:
1113  {
1114  QDomElement multiPointElem = doc.createElement( "gml:MultiPoint" );
1115 
1116  int nPoints;
1117  wkbPtr >> nPoints;
1118 
1119  for ( int idx = 0; idx < nPoints; ++idx )
1120  {
1121  wkbPtr += 1 + sizeof( int );
1122  QDomElement pointMemberElem = doc.createElement( "gml:pointMember" );
1123  QDomElement pointElem = doc.createElement( "gml:Point" );
1124  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1125 
1126  double x, y;
1127  wkbPtr >> x >> y;
1128  QDomText coordText = doc.createTextNode( qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision ) );
1129 
1130  coordElem.appendChild( coordText );
1131  pointElem.appendChild( coordElem );
1132 
1133  if ( hasZValue )
1134  {
1135  wkbPtr += sizeof( double );
1136  }
1137  pointMemberElem.appendChild( pointElem );
1138  multiPointElem.appendChild( pointMemberElem );
1139  }
1140  return multiPointElem;
1141  }
1143  hasZValue = true;
1144  //intentional fall-through
1145  case QGis::WKBLineString:
1146  {
1147  QDomElement lineStringElem = doc.createElement( "gml:LineString" );
1148  // get number of points in the line
1149 
1150  int nPoints;
1151  wkbPtr >> nPoints;
1152 
1153  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1154  QString coordString;
1155  for ( int idx = 0; idx < nPoints; ++idx )
1156  {
1157  if ( idx != 0 )
1158  {
1159  coordString += ts;
1160  }
1161 
1162  double x, y;
1163  wkbPtr >> x >> y;
1164  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1165 
1166  if ( hasZValue )
1167  {
1168  wkbPtr += sizeof( double );
1169  }
1170  }
1171  QDomText coordText = doc.createTextNode( coordString );
1172  coordElem.appendChild( coordText );
1173  lineStringElem.appendChild( coordElem );
1174  return lineStringElem;
1175  }
1177  hasZValue = true;
1178  //intentional fall-through
1180  {
1181  QDomElement multiLineStringElem = doc.createElement( "gml:MultiLineString" );
1182 
1183  int nLines;
1184  wkbPtr >> nLines;
1185 
1186  for ( int jdx = 0; jdx < nLines; jdx++ )
1187  {
1188  QDomElement lineStringMemberElem = doc.createElement( "gml:lineStringMember" );
1189  QDomElement lineStringElem = doc.createElement( "gml:LineString" );
1190  wkbPtr += 1 + sizeof( int ); // skip type since we know its 2
1191 
1192  int nPoints;
1193  wkbPtr >> nPoints;
1194 
1195  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1196  QString coordString;
1197  for ( int idx = 0; idx < nPoints; idx++ )
1198  {
1199  if ( idx != 0 )
1200  {
1201  coordString += ts;
1202  }
1203 
1204  double x, y;
1205  wkbPtr >> x >> y;
1206 
1207  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1208 
1209  if ( hasZValue )
1210  {
1211  wkbPtr += sizeof( double );
1212  }
1213  }
1214  QDomText coordText = doc.createTextNode( coordString );
1215  coordElem.appendChild( coordText );
1216  lineStringElem.appendChild( coordElem );
1217  lineStringMemberElem.appendChild( lineStringElem );
1218  multiLineStringElem.appendChild( lineStringMemberElem );
1219  }
1220  return multiLineStringElem;
1221  }
1222  case QGis::WKBPolygon25D:
1223  hasZValue = true;
1224  //intentional fall-through
1225  case QGis::WKBPolygon:
1226  {
1227  QDomElement polygonElem = doc.createElement( "gml:Polygon" );
1228 
1229  // get number of rings in the polygon
1230  int numRings;
1231  wkbPtr >> numRings;
1232 
1233  if ( numRings == 0 ) // sanity check for zero rings in polygon
1234  return QDomElement();
1235 
1236  int *ringNumPoints = new int[numRings]; // number of points in each ring
1237 
1238  for ( int idx = 0; idx < numRings; idx++ )
1239  {
1240  QString boundaryName = "gml:outerBoundaryIs";
1241  if ( idx != 0 )
1242  {
1243  boundaryName = "gml:innerBoundaryIs";
1244  }
1245  QDomElement boundaryElem = doc.createElement( boundaryName );
1246  QDomElement ringElem = doc.createElement( "gml:LinearRing" );
1247  // get number of points in the ring
1248  int nPoints;
1249  wkbPtr >> nPoints;
1250  ringNumPoints[idx] = nPoints;
1251 
1252  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1253  QString coordString;
1254  for ( int jdx = 0; jdx < nPoints; jdx++ )
1255  {
1256  if ( jdx != 0 )
1257  {
1258  coordString += ts;
1259  }
1260 
1261  double x, y;
1262  wkbPtr >> x >> y;
1263 
1264  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1265  if ( hasZValue )
1266  {
1267  wkbPtr += sizeof( double );
1268  }
1269  }
1270  QDomText coordText = doc.createTextNode( coordString );
1271  coordElem.appendChild( coordText );
1272  ringElem.appendChild( coordElem );
1273  boundaryElem.appendChild( ringElem );
1274  polygonElem.appendChild( boundaryElem );
1275  }
1276  delete [] ringNumPoints;
1277  return polygonElem;
1278  }
1280  hasZValue = true;
1281  //intentional fall-through
1282  case QGis::WKBMultiPolygon:
1283  {
1284  QDomElement multiPolygonElem = doc.createElement( "gml:MultiPolygon" );
1285 
1286  int numPolygons;
1287  wkbPtr >> numPolygons;
1288 
1289  for ( int kdx = 0; kdx < numPolygons; kdx++ )
1290  {
1291  QDomElement polygonMemberElem = doc.createElement( "gml:polygonMember" );
1292  QDomElement polygonElem = doc.createElement( "gml:Polygon" );
1293 
1294  wkbPtr += 1 + sizeof( int );
1295 
1296  int numRings;
1297  wkbPtr >> numRings;
1298 
1299  for ( int idx = 0; idx < numRings; idx++ )
1300  {
1301  QString boundaryName = "gml:outerBoundaryIs";
1302  if ( idx != 0 )
1303  {
1304  boundaryName = "gml:innerBoundaryIs";
1305  }
1306  QDomElement boundaryElem = doc.createElement( boundaryName );
1307  QDomElement ringElem = doc.createElement( "gml:LinearRing" );
1308 
1309  int nPoints;
1310  wkbPtr >> nPoints;
1311 
1312  QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1313  QString coordString;
1314  for ( int jdx = 0; jdx < nPoints; jdx++ )
1315  {
1316  if ( jdx != 0 )
1317  {
1318  coordString += ts;
1319  }
1320 
1321  double x, y;
1322  wkbPtr >> x >> y;
1323 
1324  coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
1325 
1326  if ( hasZValue )
1327  {
1328  wkbPtr += sizeof( double );
1329  }
1330  }
1331  QDomText coordText = doc.createTextNode( coordString );
1332  coordElem.appendChild( coordText );
1333  ringElem.appendChild( coordElem );
1334  boundaryElem.appendChild( ringElem );
1335  polygonElem.appendChild( boundaryElem );
1336  polygonMemberElem.appendChild( polygonElem );
1337  multiPolygonElem.appendChild( polygonMemberElem );
1338  }
1339  }
1340  return multiPolygonElem;
1341  }
1342  default:
1343  return QDomElement();
1344  }
1345 }
1346 
1347 QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry *geometry, QDomDocument &doc, const int &precision )
1348 {
1349  return geometryToGML( geometry, doc, "GML2", precision );
1350 }
1351 
1352 QDomElement QgsOgcUtils::createGMLCoordinates( const QgsPolyline &points, QDomDocument &doc )
1353 {
1354  QDomElement coordElem = doc.createElement( "gml:coordinates" );
1355  coordElem.setAttribute( "cs", "," );
1356  coordElem.setAttribute( "ts", " " );
1357 
1358  QString coordString;
1359  QVector<QgsPoint>::const_iterator pointIt = points.constBegin();
1360  for ( ; pointIt != points.constEnd(); ++pointIt )
1361  {
1362  if ( pointIt != points.constBegin() )
1363  {
1364  coordString += " ";
1365  }
1366  coordString += qgsDoubleToString( pointIt->x() );
1367  coordString += ",";
1368  coordString += qgsDoubleToString( pointIt->y() );
1369  }
1370 
1371  QDomText coordText = doc.createTextNode( coordString );
1372  coordElem.appendChild( coordText );
1373  return coordElem;
1374 }
1375 
1376 QDomElement QgsOgcUtils::createGMLPositions( const QgsPolyline &points, QDomDocument& doc )
1377 {
1378  QDomElement posElem = doc.createElement( "gml:pos" );
1379  if ( points.size() > 1 )
1380  posElem = doc.createElement( "gml:posList" );
1381  posElem.setAttribute( "srsDimension", "2" );
1382 
1383  QString coordString;
1384  QVector<QgsPoint>::const_iterator pointIt = points.constBegin();
1385  for ( ; pointIt != points.constEnd(); ++pointIt )
1386  {
1387  if ( pointIt != points.constBegin() )
1388  {
1389  coordString += " ";
1390  }
1391  coordString += qgsDoubleToString( pointIt->x() );
1392  coordString += " ";
1393  coordString += qgsDoubleToString( pointIt->y() );
1394  }
1395 
1396  QDomText coordText = doc.createTextNode( coordString );
1397  posElem.appendChild( coordText );
1398  return posElem;
1399 }
1400 
1401 
1402 
1403 // -----------------------------------------
1404 
1405 QColor QgsOgcUtils::colorFromOgcFill( const QDomElement& fillElement )
1406 {
1407  if ( fillElement.isNull() || !fillElement.hasChildNodes() )
1408  {
1409  return QColor();
1410  }
1411 
1412  QString cssName;
1413  QString elemText;
1414  QColor color;
1415  QDomElement cssElem = fillElement.firstChildElement( "CssParameter" );
1416  while ( !cssElem.isNull() )
1417  {
1418  cssName = cssElem.attribute( "name", "not_found" );
1419  if ( cssName != "not_found" )
1420  {
1421  elemText = cssElem.text();
1422  if ( cssName == "fill" )
1423  {
1424  color.setNamedColor( elemText );
1425  }
1426  else if ( cssName == "fill-opacity" )
1427  {
1428  bool ok;
1429  double opacity = elemText.toDouble( &ok );
1430  if ( ok )
1431  {
1432  color.setAlphaF( opacity );
1433  }
1434  }
1435  }
1436 
1437  cssElem = cssElem.nextSiblingElement( "CssParameter" );
1438  }
1439 
1440  return color;
1441 }
1442 
1443 
1445 {
1446  if ( element.isNull() || !element.hasChildNodes() )
1447  return NULL;
1448 
1449  QgsExpression *expr = new QgsExpression();
1450 
1451  QDomElement childElem = element.firstChildElement();
1452  while ( !childElem.isNull() )
1453  {
1454  QString errorMsg;
1455  QgsExpression::Node *node = nodeFromOgcFilter( childElem, errorMsg );
1456  if ( !node )
1457  {
1458  // invalid expression, parser error
1459  expr->mParserErrorString = errorMsg;
1460  return expr;
1461  }
1462 
1463  // use the concat binary operator to append to the root node
1464  if ( !expr->mRootNode )
1465  {
1466  expr->mRootNode = node;
1467  }
1468  else
1469  {
1471  }
1472 
1473  childElem = childElem.nextSiblingElement();
1474  }
1475 
1476  // update expression string
1477  expr->mExp = expr->dump();
1478 
1479  return expr;
1480 }
1481 
1482 
1483 static const QMap<QString, int>& binaryOperatorsTagNamesMap()
1484 {
1485  static QMap<QString, int> binOps;
1486  if ( binOps.isEmpty() )
1487  {
1488  // logical
1489  binOps.insert( "Or", QgsExpression::boOr );
1490  binOps.insert( "And", QgsExpression::boAnd );
1491  // comparison
1492  binOps.insert( "PropertyIsEqualTo", QgsExpression::boEQ );
1493  binOps.insert( "PropertyIsNotEqualTo", QgsExpression::boNE );
1494  binOps.insert( "PropertyIsLessThanOrEqualTo", QgsExpression::boLE );
1495  binOps.insert( "PropertyIsGreaterThanOrEqualTo", QgsExpression::boGE );
1496  binOps.insert( "PropertyIsLessThan", QgsExpression::boLT );
1497  binOps.insert( "PropertyIsGreaterThan", QgsExpression::boGT );
1498  binOps.insert( "PropertyIsLike", QgsExpression::boLike );
1499  // arithmetics
1500  binOps.insert( "Add", QgsExpression::boPlus );
1501  binOps.insert( "Sub", QgsExpression::boMinus );
1502  binOps.insert( "Mul", QgsExpression::boMul );
1503  binOps.insert( "Div", QgsExpression::boDiv );
1504  }
1505  return binOps;
1506 }
1507 
1508 static int binaryOperatorFromTagName( const QString& tagName )
1509 {
1510 
1511  return binaryOperatorsTagNamesMap().value( tagName, -1 );
1512 }
1513 
1515 {
1516  return binaryOperatorsTagNamesMap().key( op, QString() );
1517 }
1518 
1519 static bool isBinaryOperator( const QString& tagName )
1520 {
1521  return binaryOperatorFromTagName( tagName ) >= 0;
1522 }
1523 
1524 
1525 static bool isSpatialOperator( const QString& tagName )
1526 {
1527  static QStringList spatialOps;
1528  if ( spatialOps.isEmpty() )
1529  {
1530  spatialOps << "BBOX" << "Intersects" << "Contians" << "Crosses" << "Equals"
1531  << "Disjoint" << "Overlaps" << "Touches" << "Within";
1532  }
1533 
1534  return spatialOps.contains( tagName );
1535 }
1536 
1537 
1538 
1539 QgsExpression::Node* QgsOgcUtils::nodeFromOgcFilter( QDomElement &element, QString &errorMessage )
1540 {
1541  if ( element.isNull() )
1542  return NULL;
1543 
1544  // check for binary operators
1545  if ( isBinaryOperator( element.tagName() ) )
1546  {
1547  return nodeBinaryOperatorFromOgcFilter( element, errorMessage );
1548  }
1549 
1550  // check for spatial operators
1551  if ( isSpatialOperator( element.tagName() ) )
1552  {
1553  return nodeSpatialOperatorFromOgcFilter( element, errorMessage );
1554  }
1555 
1556  // check for other OGC operators, convert them to expressions
1557 
1558  if ( element.tagName() == "Not" )
1559  {
1560  return nodeNotFromOgcFilter( element, errorMessage );
1561  }
1562  else if ( element.tagName() == "PropertyIsNull" )
1563  {
1564  return nodePropertyIsNullFromOgcFilter( element, errorMessage );
1565  }
1566  else if ( element.tagName() == "Literal" )
1567  {
1568  return nodeLiteralFromOgcFilter( element, errorMessage );
1569  }
1570  else if ( element.tagName() == "Function" )
1571  {
1572  return nodeFunctionFromOgcFilter( element, errorMessage );
1573  }
1574  else if ( element.tagName() == "PropertyName" )
1575  {
1576  return nodeColumnRefFromOgcFilter( element, errorMessage );
1577  }
1578  else if ( element.tagName() == "PropertyIsBetween" )
1579  {
1580  return nodeIsBetweenFromOgcFilter( element, errorMessage );
1581  }
1582 
1583  errorMessage += QString( "unable to convert '%1' element to a valid expression: it is not supported yet or it has invalid arguments" ).arg( element.tagName() );
1584  return NULL;
1585 }
1586 
1587 
1588 
1589 QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodeBinaryOperatorFromOgcFilter( QDomElement &element, QString &errorMessage )
1590 {
1591  if ( element.isNull() )
1592  return NULL;
1593 
1594  int op = binaryOperatorFromTagName( element.tagName() );
1595  if ( op < 0 )
1596  {
1597  if ( errorMessage.isEmpty() )
1598  errorMessage = QString( "'%1' binary operator not supported." ).arg( element.tagName() );
1599  return NULL;
1600  }
1601 
1602  QDomElement operandElem = element.firstChildElement();
1603  QgsExpression::Node *expr = nodeFromOgcFilter( operandElem, errorMessage ), *leftOp = expr;
1604  if ( !expr )
1605  {
1606  if ( errorMessage.isEmpty() )
1607  errorMessage = QString( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
1608  return NULL;
1609  }
1610 
1611  for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
1612  {
1613  QgsExpression::Node* opRight = nodeFromOgcFilter( operandElem, errorMessage );
1614  if ( !opRight )
1615  {
1616  if ( errorMessage.isEmpty() )
1617  errorMessage = QString( "invalid right operand for '%1' binary operator" ).arg( element.tagName() );
1618  delete expr;
1619  return NULL;
1620  }
1621 
1622  expr = new QgsExpression::NodeBinaryOperator(( QgsExpression::BinaryOperator ) op, expr, opRight );
1623  }
1624 
1625  if ( expr == leftOp )
1626  {
1627  if ( errorMessage.isEmpty() )
1628  errorMessage = QString( "only one operand for '%1' binary operator" ).arg( element.tagName() );
1629  delete expr;
1630  return NULL;
1631  }
1632 
1634  if ( !ret )
1635  delete expr;
1636 
1637  return ret;
1638 }
1639 
1640 
1641 QgsExpression::NodeFunction* QgsOgcUtils::nodeSpatialOperatorFromOgcFilter( QDomElement& element, QString& errorMessage )
1642 {
1643  // we are exploiting the fact that our function names are the same as the XML tag names
1644  int opIdx = QgsExpression::functionIndex( element.tagName().toLower() );
1645 
1647  QDomElement childElem = element.firstChildElement();
1648  QString gml2Str;
1649  while ( !childElem.isNull() && gml2Str.isEmpty() )
1650  {
1651  if ( childElem.tagName() != "PropertyName" )
1652  {
1653  QTextStream gml2Stream( &gml2Str );
1654  childElem.save( gml2Stream, 0 );
1655  }
1656  childElem = childElem.nextSiblingElement();
1657  }
1658  if ( !gml2Str.isEmpty() )
1659  {
1660  gml2Args->append( new QgsExpression::NodeLiteral( QVariant( gml2Str.remove( "\n" ) ) ) );
1661  }
1662  else
1663  {
1664  errorMessage = "No OGC Geometry found";
1665  delete gml2Args;
1666  return NULL;
1667  }
1668 
1671  opArgs->append( new QgsExpression::NodeFunction( QgsExpression::functionIndex( "geomFromGML" ), gml2Args ) );
1672 
1673  return new QgsExpression::NodeFunction( opIdx, opArgs );
1674 }
1675 
1676 
1677 QgsExpression::NodeUnaryOperator* QgsOgcUtils::nodeNotFromOgcFilter( QDomElement &element, QString &errorMessage )
1678 {
1679  if ( element.tagName() != "Not" )
1680  return NULL;
1681 
1682  QDomElement operandElem = element.firstChildElement();
1683  QgsExpression::Node* operand = nodeFromOgcFilter( operandElem, errorMessage );
1684  if ( !operand )
1685  {
1686  if ( errorMessage.isEmpty() )
1687  errorMessage = QString( "invalid operand for '%1' unary operator" ).arg( element.tagName() );
1688  return NULL;
1689  }
1690 
1692 }
1693 
1694 
1695 QgsExpression::NodeFunction* QgsOgcUtils::nodeFunctionFromOgcFilter( QDomElement &element, QString &errorMessage )
1696 {
1697  if ( element.isNull() || element.tagName() != "Function" )
1698  {
1699  errorMessage = QString( "ogc:Function expected, got %1" ).arg( element.tagName() );
1700  return NULL;
1701  }
1702 
1703  for ( int i = 0; i < QgsExpression::Functions().size(); i++ )
1704  {
1706 
1707  if ( element.attribute( "name" ) != funcDef->name() )
1708  continue;
1709 
1711 
1712  QDomElement operandElem = element.firstChildElement();
1713  while ( !operandElem.isNull() )
1714  {
1715  QgsExpression::Node* op = nodeFromOgcFilter( operandElem, errorMessage );
1716  if ( !op )
1717  {
1718  delete args;
1719  return NULL;
1720  }
1721  args->append( op );
1722 
1723  operandElem = operandElem.nextSiblingElement();
1724  }
1725 
1726  return new QgsExpression::NodeFunction( i, args );
1727  }
1728 
1729  return NULL;
1730 }
1731 
1732 
1733 
1734 QgsExpression::Node* QgsOgcUtils::nodeLiteralFromOgcFilter( QDomElement &element, QString &errorMessage )
1735 {
1736  if ( element.isNull() || element.tagName() != "Literal" )
1737  {
1738  errorMessage = QString( "ogc:Literal expected, got %1" ).arg( element.tagName() );
1739  return NULL;
1740  }
1741 
1742  QgsExpression::Node *root = 0;
1743 
1744  // the literal content can have more children (e.g. CDATA section, text, ...)
1745  QDomNode childNode = element.firstChild();
1746  while ( !childNode.isNull() )
1747  {
1748  QgsExpression::Node* operand = 0;
1749 
1750  if ( childNode.nodeType() == QDomNode::ElementNode )
1751  {
1752  // found a element node (e.g. PropertyName), convert it
1753  QDomElement operandElem = childNode.toElement();
1754  operand = nodeFromOgcFilter( operandElem, errorMessage );
1755  if ( !operand )
1756  {
1757  if ( root )
1758  delete root;
1759 
1760  errorMessage = QString( "'%1' is an invalid or not supported content for ogc:Literal" ).arg( operandElem.tagName() );
1761  return NULL;
1762  }
1763  }
1764  else
1765  {
1766  // probably a text/CDATA node
1767  QVariant value = childNode.nodeValue();
1768 
1769  // try to convert the node content to number if possible,
1770  // otherwise let's use it as string
1771  bool ok;
1772  double d = value.toDouble( &ok );
1773  if ( ok )
1774  value = d;
1775 
1776  operand = new QgsExpression::NodeLiteral( value );
1777  if ( !operand )
1778  continue;
1779  }
1780 
1781  // use the concat operator to merge the ogc:Literal children
1782  if ( !root )
1783  {
1784  root = operand;
1785  }
1786  else
1787  {
1788  root = new QgsExpression::NodeBinaryOperator( QgsExpression::boConcat, root, operand );
1789  }
1790 
1791  childNode = childNode.nextSibling();
1792  }
1793 
1794  if ( root )
1795  return root;
1796 
1797  return NULL;
1798 }
1799 
1800 
1801 QgsExpression::NodeColumnRef* QgsOgcUtils::nodeColumnRefFromOgcFilter( QDomElement &element, QString &errorMessage )
1802 {
1803  if ( element.isNull() || element.tagName() != "PropertyName" )
1804  {
1805  errorMessage = QString( "ogc:PropertyName expected, got %1" ).arg( element.tagName() );
1806  return NULL;
1807  }
1808 
1809  return new QgsExpression::NodeColumnRef( element.firstChild().nodeValue() );
1810 }
1811 
1812 
1813 QgsExpression::Node* QgsOgcUtils::nodeIsBetweenFromOgcFilter( QDomElement& element, QString& errorMessage )
1814 {
1815  // <ogc:PropertyIsBetween> encode a Range check
1816  QgsExpression::Node *operand = 0, *lowerBound = 0;
1817  QgsExpression::Node *operand2 = 0, *upperBound = 0;
1818 
1819  QDomElement operandElem = element.firstChildElement();
1820  while ( !operandElem.isNull() )
1821  {
1822  if ( operandElem.tagName() == "LowerBoundary" )
1823  {
1824  QDomElement lowerBoundElem = operandElem.firstChildElement();
1825  lowerBound = nodeFromOgcFilter( lowerBoundElem, errorMessage );
1826  }
1827  else if ( operandElem.tagName() == "UpperBoundary" )
1828  {
1829  QDomElement upperBoundElem = operandElem.firstChildElement();
1830  upperBound = nodeFromOgcFilter( upperBoundElem, errorMessage );
1831  }
1832  else
1833  {
1834  // <ogc:expression>
1835  // both operand and operand2 contain the same expression,
1836  // they are respectively compared to lower bound and upper bound
1837  operand = nodeFromOgcFilter( operandElem, errorMessage );
1838  operand2 = nodeFromOgcFilter( operandElem, errorMessage );
1839  }
1840 
1841  if ( operand && lowerBound && operand2 && upperBound )
1842  break;
1843 
1844  operandElem = operandElem.nextSiblingElement();
1845  }
1846 
1847  if ( !operand || !lowerBound || !operand2 || !upperBound )
1848  {
1849  if ( operand )
1850  delete operand;
1851 
1852  if ( lowerBound )
1853  delete lowerBound;
1854 
1855  if ( upperBound )
1856  delete upperBound;
1857 
1858  errorMessage = "missing some required sub-elements in ogc:PropertyIsBetween";
1859  return NULL;
1860  }
1861 
1862  QgsExpression::Node *geOperator = new QgsExpression::NodeBinaryOperator( QgsExpression::boGE, operand, lowerBound );
1863  QgsExpression::Node *leOperator = new QgsExpression::NodeBinaryOperator( QgsExpression::boLE, operand2, upperBound );
1864  return new QgsExpression::NodeBinaryOperator( QgsExpression::boAnd, geOperator, leOperator );
1865 }
1866 
1867 
1868 QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodePropertyIsNullFromOgcFilter( QDomElement& element, QString& errorMessage )
1869 {
1870  // convert ogc:PropertyIsNull to IS operator with NULL right operand
1871  if ( element.tagName() != "PropertyIsNull" )
1872  {
1873  return NULL;
1874  }
1875 
1876  QDomElement operandElem = element.firstChildElement();
1877  QgsExpression::Node* opLeft = nodeFromOgcFilter( operandElem, errorMessage );
1878  if ( !opLeft )
1879  return NULL;
1880 
1881  QgsExpression::Node* opRight = new QgsExpression::NodeLiteral( QVariant() );
1882  return new QgsExpression::NodeBinaryOperator( QgsExpression::boIs, opLeft, opRight );
1883 }
1884 
1885 
1887 
1888 
1889 QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression& exp, QDomDocument& doc, QString* errorMessage )
1890 {
1891  if ( !exp.rootNode() )
1892  return QDomElement();
1893 
1894  QString localErrorMessage; // temporary that will be thrown away unused
1895  QString& refErrorMessage = ( errorMessage ? *errorMessage : localErrorMessage );
1896  refErrorMessage.clear();
1897 
1898  QDomElement exprRootElem = expressionNodeToOgcFilter( exp.rootNode(), doc, refErrorMessage );
1899  if ( exprRootElem.isNull() )
1900  return QDomElement();
1901 
1902  QDomElement filterElem = doc.createElementNS( OGC_NAMESPACE, "ogc:Filter" );
1903  filterElem.appendChild( exprRootElem );
1904  return filterElem;
1905 }
1906 
1907 
1908 QDomElement QgsOgcUtils::expressionNodeToOgcFilter( const QgsExpression::Node* node, QDomDocument& doc, QString& errorMessage )
1909 {
1910  switch ( node->nodeType() )
1911  {
1913  return expressionUnaryOperatorToOgcFilter( static_cast<const QgsExpression::NodeUnaryOperator*>( node ), doc, errorMessage );
1915  return expressionBinaryOperatorToOgcFilter( static_cast<const QgsExpression::NodeBinaryOperator*>( node ), doc, errorMessage );
1917  return expressionInOperatorToOgcFilter( static_cast<const QgsExpression::NodeInOperator*>( node ), doc, errorMessage );
1919  return expressionFunctionToOgcFilter( static_cast<const QgsExpression::NodeFunction*>( node ), doc, errorMessage );
1921  return expressionLiteralToOgcFilter( static_cast<const QgsExpression::NodeLiteral*>( node ), doc, errorMessage );
1923  return expressionColumnRefToOgcFilter( static_cast<const QgsExpression::NodeColumnRef*>( node ), doc, errorMessage );
1924 
1925  default:
1926  errorMessage = QString( "Node type not supported: %1" ).arg( node->nodeType() );
1927  return QDomElement();
1928  }
1929 }
1930 
1931 
1932 QDomElement QgsOgcUtils::expressionUnaryOperatorToOgcFilter( const QgsExpression::NodeUnaryOperator* node, QDomDocument& doc, QString& errorMessage )
1933 {
1934 
1935  QDomElement operandElem = expressionNodeToOgcFilter( node->operand(), doc, errorMessage );
1936  if ( !errorMessage.isEmpty() )
1937  return QDomElement();
1938 
1939  QDomElement uoElem;
1940  switch ( node->op() )
1941  {
1943  uoElem = doc.createElement( "ogc:Literal" );
1944  if ( node->operand()->nodeType() == QgsExpression::ntLiteral )
1945  {
1946  // operand expression already created a Literal node:
1947  // take the literal value, prepend - and remove old literal node
1948  uoElem.appendChild( doc.createTextNode( "-" + operandElem.text() ) );
1949  doc.removeChild( operandElem );
1950  }
1951  else
1952  {
1953  errorMessage = QString( "This use of unary operator not implemented yet" );
1954  return QDomElement();
1955  }
1956  break;
1957  case QgsExpression::uoNot:
1958  uoElem = doc.createElement( "ogc:Not" );
1959  uoElem.appendChild( operandElem );
1960  break;
1961 
1962  default:
1963  errorMessage = QString( "Unary operator %1 not implemented yet" ).arg( QgsExpression::UnaryOperatorText[node->op()] );
1964  return QDomElement();
1965  }
1966 
1967  return uoElem;
1968 }
1969 
1970 
1971 QDomElement QgsOgcUtils::expressionBinaryOperatorToOgcFilter( const QgsExpression::NodeBinaryOperator* node, QDomDocument& doc, QString& errorMessage )
1972 {
1973  QDomElement leftElem = expressionNodeToOgcFilter( node->opLeft(), doc, errorMessage );
1974  if ( !errorMessage.isEmpty() )
1975  return QDomElement();
1976 
1977  QgsExpression::BinaryOperator op = node->op();
1978 
1979  // before right operator is parsed: to allow NULL handling
1980  if ( op == QgsExpression::boIs || op == QgsExpression::boIsNot )
1981  {
1982  if ( node->opRight()->nodeType() == QgsExpression::ntLiteral )
1983  {
1984  const QgsExpression::NodeLiteral* rightLit = static_cast<const QgsExpression::NodeLiteral*>( node->opRight() );
1985  if ( rightLit->value().isNull() )
1986  {
1987 
1988  QDomElement elem = doc.createElement( "ogc:PropertyIsNull" );
1989  elem.appendChild( leftElem );
1990 
1991  if ( op == QgsExpression::boIsNot )
1992  {
1993  QDomElement notElem = doc.createElement( "ogc:Not" );
1994  notElem.appendChild( elem );
1995  return notElem;
1996  }
1997 
1998  return elem;
1999  }
2000 
2001  // continue with equal / not equal operator once the null case is handled
2003  }
2004 
2005  }
2006 
2007  QDomElement rightElem = expressionNodeToOgcFilter( node->opRight(), doc, errorMessage );
2008  if ( !errorMessage.isEmpty() )
2009  return QDomElement();
2010 
2011 
2012  QString opText = binaryOperatorToTagName( op );
2013  if ( opText.isEmpty() )
2014  {
2015  // not implemented binary operators
2016  // TODO: regex, % (mod), ^ (pow) are not supported yet
2017  errorMessage = QString( "Binary operator %1 not implemented yet" ).arg( QgsExpression::BinaryOperatorText[op] );
2018  return QDomElement();
2019  }
2020 
2021  QDomElement boElem = doc.createElement( "ogc:" + opText );
2022 
2023  if ( op == QgsExpression::boLike || op == QgsExpression::boILike )
2024  {
2025  if ( op == QgsExpression::boILike )
2026  boElem.setAttribute( "matchCase", "false" );
2027 
2028  // setup wildcards to <ogc:PropertyIsLike>
2029  boElem.setAttribute( "wildCard", "%" );
2030  boElem.setAttribute( "singleChar", "?" );
2031  boElem.setAttribute( "escapeChar", "!" );
2032  }
2033 
2034  boElem.appendChild( leftElem );
2035  boElem.appendChild( rightElem );
2036  return boElem;
2037 }
2038 
2039 
2040 QDomElement QgsOgcUtils::expressionLiteralToOgcFilter( const QgsExpression::NodeLiteral* node, QDomDocument& doc, QString& errorMessage )
2041 {
2042  QString value;
2043  switch ( node->value().type() )
2044  {
2045  case QVariant::Int:
2046  value = QString::number( node->value().toInt() );
2047  break;
2048  case QVariant::Double:
2049  value = QString::number( node->value().toDouble() );
2050  break;
2051  case QVariant::String:
2052  value = node->value().toString();
2053  break;
2054 
2055  default:
2056  errorMessage = QString( "Literal type not supported: %1" ).arg( node->value().type() );
2057  return QDomElement();
2058  }
2059 
2060  QDomElement litElem = doc.createElement( "ogc:Literal" );
2061  litElem.appendChild( doc.createTextNode( value ) );
2062  return litElem;
2063 }
2064 
2065 
2066 QDomElement QgsOgcUtils::expressionColumnRefToOgcFilter( const QgsExpression::NodeColumnRef* node, QDomDocument& doc, QString& /*errorMessage*/ )
2067 {
2068  QDomElement propElem = doc.createElement( "ogc:PropertyName" );
2069  propElem.appendChild( doc.createTextNode( node->name() ) );
2070  return propElem;
2071 }
2072 
2073 
2074 
2075 QDomElement QgsOgcUtils::expressionInOperatorToOgcFilter( const QgsExpression::NodeInOperator* node, QDomDocument& doc, QString& errorMessage )
2076 {
2077  if ( node->list()->list().size() == 1 )
2078  return expressionNodeToOgcFilter( node->list()->list()[0], doc, errorMessage );
2079 
2080  QDomElement orElem = doc.createElement( "ogc:Or" );
2081  QDomElement leftNode = expressionNodeToOgcFilter( node->node(), doc, errorMessage );
2082 
2083  foreach ( QgsExpression::Node* n, node->list()->list() )
2084  {
2085  QDomElement listNode = expressionNodeToOgcFilter( n, doc, errorMessage );
2086  if ( !errorMessage.isEmpty() )
2087  return QDomElement();
2088 
2089  QDomElement eqElem = doc.createElement( "ogc:PropertyIsEqualTo" );
2090  eqElem.appendChild( leftNode.cloneNode() );
2091  eqElem.appendChild( listNode );
2092 
2093  orElem.appendChild( eqElem );
2094  }
2095  return orElem;
2096 }
2097 
2098 static QMap<QString, QString> binarySpatialOpsMap()
2099 {
2100  static QMap<QString, QString> binSpatialOps;
2101  if ( binSpatialOps.isEmpty() )
2102  {
2103  binSpatialOps.insert( "disjoint", "Disjoint" );
2104  binSpatialOps.insert( "intersects", "Intersects" );
2105  binSpatialOps.insert( "touches", "Touches" );
2106  binSpatialOps.insert( "crosses", "Crosses" );
2107  binSpatialOps.insert( "contains", "Contains" );
2108  binSpatialOps.insert( "overlaps", "Overlaps" );
2109  binSpatialOps.insert( "within", "Within" );
2110  }
2111  return binSpatialOps;
2112 }
2113 
2114 static bool isBinarySpatialOperator( const QString& fnName )
2115 {
2116  return binarySpatialOpsMap().contains( fnName );
2117 }
2118 
2119 static QString tagNameForSpatialOperator( const QString& fnName )
2120 {
2121  return binarySpatialOpsMap().value( fnName );
2122 }
2123 
2124 static bool isGeometryColumn( const QgsExpression::Node* node )
2125 {
2126  if ( node->nodeType() != QgsExpression::ntFunction )
2127  return false;
2128 
2129  const QgsExpression::NodeFunction* fn = static_cast<const QgsExpression::NodeFunction*>( node );
2131  return fd->name() == "$geometry";
2132 }
2133 
2135 {
2136  // Right now we support only geomFromWKT(' ..... ')
2137  // Ideally we should support any constant sub-expression (not dependent on feature's geometry or attributes)
2138 
2139  if ( node->nodeType() == QgsExpression::ntFunction )
2140  {
2141  const QgsExpression::NodeFunction* fnNode = static_cast<const QgsExpression::NodeFunction*>( node );
2143  if ( fnDef->name() == "geom_from_wkt" )
2144  {
2145  const QList<QgsExpression::Node*>& args = fnNode->args()->list();
2146  if ( args[0]->nodeType() == QgsExpression::ntLiteral )
2147  {
2148  QString wkt = static_cast<const QgsExpression::NodeLiteral*>( args[0] )->value().toString();
2149  return QgsGeometry::fromWkt( wkt );
2150  }
2151  }
2152  }
2153  return 0;
2154 }
2155 
2156 
2157 QDomElement QgsOgcUtils::expressionFunctionToOgcFilter( const QgsExpression::NodeFunction* node, QDomDocument& doc, QString& errorMessage )
2158 {
2160 
2161  if ( fd->name() == "intersects_bbox" )
2162  {
2163  QList<QgsExpression::Node*> argNodes = node->args()->list();
2164  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2165 
2166  QgsGeometry* geom = geometryFromConstExpr( argNodes[1] );
2167  if ( geom && isGeometryColumn( argNodes[0] ) )
2168  {
2169  QgsRectangle rect = geom->boundingBox();
2170  delete geom;
2171 
2172  QDomElement elemBox = rectangleToGMLBox( &rect, doc );
2173 
2174  QDomElement geomProperty = doc.createElement( "ogc:PropertyName" );
2175  geomProperty.appendChild( doc.createTextNode( "geometry" ) );
2176 
2177  QDomElement funcElem = doc.createElement( "ogr:BBOX" );
2178  funcElem.appendChild( geomProperty );
2179  funcElem.appendChild( elemBox );
2180  return funcElem;
2181  }
2182  else
2183  {
2184  delete geom;
2185 
2186  errorMessage = QString( "<BBOX> is currently supported only in form: bbox($geometry, geomFromWKT('...'))" );
2187  return QDomElement();
2188  }
2189  }
2190 
2191  if ( isBinarySpatialOperator( fd->name() ) )
2192  {
2193  QList<QgsExpression::Node*> argNodes = node->args()->list();
2194  Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2195 
2196  QgsExpression::Node* otherNode = 0;
2197  if ( isGeometryColumn( argNodes[0] ) )
2198  otherNode = argNodes[1];
2199  else if ( isGeometryColumn( argNodes[1] ) )
2200  otherNode = argNodes[0];
2201  else
2202  {
2203  errorMessage = QString( "Unable to translate spatial operator: at least one must refer to geometry." );
2204  return QDomElement();
2205  }
2206 
2207  QDomElement otherGeomElem;
2208 
2209  // the other node must be a geometry constructor
2210  if ( otherNode->nodeType() != QgsExpression::ntFunction )
2211  {
2212  errorMessage = "spatial operator: the other operator must be a geometry constructor function";
2213  return QDomElement();
2214  }
2215 
2216  const QgsExpression::NodeFunction* otherFn = static_cast<const QgsExpression::NodeFunction*>( otherNode );
2217  QgsExpression::Function* otherFnDef = QgsExpression::Functions()[otherFn->fnIndex()];
2218  if ( otherFnDef->name() == "geom_from_wkt" )
2219  {
2220  QgsExpression::Node* firstFnArg = otherFn->args()->list()[0];
2221  if ( firstFnArg->nodeType() != QgsExpression::ntLiteral )
2222  {
2223  errorMessage = "geom_from_wkt: argument must be string literal";
2224  return QDomElement();
2225  }
2226  QString wkt = static_cast<const QgsExpression::NodeLiteral*>( firstFnArg )->value().toString();
2227  QgsGeometry* geom = QgsGeometry::fromWkt( wkt );
2228  otherGeomElem = QgsOgcUtils::geometryToGML( geom, doc );
2229  delete geom;
2230  }
2231  else if ( otherFnDef->name() == "geom_from_gml" )
2232  {
2233  QgsExpression::Node* firstFnArg = otherFn->args()->list()[0];
2234  if ( firstFnArg->nodeType() != QgsExpression::ntLiteral )
2235  {
2236  errorMessage = "geom_from_gml: argument must be string literal";
2237  return QDomElement();
2238  }
2239 
2240  QDomDocument geomDoc;
2241  QString gml = static_cast<const QgsExpression::NodeLiteral*>( firstFnArg )->value().toString();
2242  if ( !geomDoc.setContent( gml, true ) )
2243  {
2244  errorMessage = "geom_from_gml: unable to parse XML";
2245  return QDomElement();
2246  }
2247 
2248  QDomNode geomNode = doc.importNode( geomDoc.documentElement(), true );
2249  otherGeomElem = geomNode.toElement();
2250  }
2251  else
2252  {
2253  errorMessage = "spatial operator: unknown geometry constructor function";
2254  return QDomElement();
2255  }
2256 
2257  QDomElement funcElem = doc.createElement( "ogc:" + tagNameForSpatialOperator( fd->name() ) );
2258  QDomElement geomProperty = doc.createElement( "ogc:PropertyName" );
2259  geomProperty.appendChild( doc.createTextNode( "geometry" ) );
2260  funcElem.appendChild( geomProperty );
2261  funcElem.appendChild( otherGeomElem );
2262  return funcElem;
2263  }
2264 
2265  if ( fd->params() == 0 )
2266  {
2267  errorMessage = QString( "Special columns / constants are not supported." );
2268  return QDomElement();
2269  }
2270 
2271  // this is somehow wrong - we are just hoping that the other side supports the same functions as we do...
2272  QDomElement funcElem = doc.createElement( "ogc:Function" );
2273  funcElem.setAttribute( "name", fd->name() );
2274  foreach ( QgsExpression::Node* n, node->args()->list() )
2275  {
2276  QDomElement childElem = expressionNodeToOgcFilter( n, doc, errorMessage );
2277  if ( !errorMessage.isEmpty() )
2278  return QDomElement();
2279 
2280  funcElem.appendChild( childElem );
2281  }
2282 
2283  return funcElem;
2284 }
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:86
static QDomElement geometryToGML(const QgsGeometry *geometry, QDomDocument &doc, QString format, const int &precision=17)
Exports the geometry to GML2 or GML3.
virtual NodeType nodeType() const =0
A rectangle specified with double values.
Definition: qgsrectangle.h:35
A abstract base class for defining QgsExpression functions.
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:192
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:197
static int functionIndex(const QString &name)
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:182
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
QString qgsDoubleToString(const double &a, const int &precision=17)
Definition: qgis.h:339
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)
Takes ownership of the provided 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:187
static QDomElement rectangleToGMLEnvelope(QgsRectangle *env, QDomDocument &doc, const int &precision=17)
Exports the rectangle to GML2 Envelope.
QList< Node * > list()
static const char * BinaryOperatorText[]