QGIS API Documentation  master-6227475
src/core/qgsogcutils.cpp
Go to the documentation of this file.
00001 #include "qgsogcutils.h"
00002 
00003 #include "qgsexpression.h"
00004 #include "qgsgeometry.h"
00005 
00006 #include <QStringList>
00007 #include <QTextStream>
00008 
00009 #ifndef Q_WS_WIN
00010 #include <netinet/in.h>
00011 #else
00012 #include <winsock.h>
00013 #endif
00014 
00015 
00016 static const QString GML_NAMESPACE = "http://www.opengis.net/gml";
00017 
00018 QgsGeometry* QgsOgcUtils::geometryFromGML( const QDomNode& geometryNode )
00019 {
00020   QDomElement geometryTypeElement = geometryNode.toElement();
00021   QString geomType = geometryTypeElement.tagName();
00022 
00023   if ( !( geomType == "Point" || geomType == "LineString" || geomType == "Polygon" || geomType == "MultiPoint" || geomType == "MultiLineString" || geomType == "MultiPolygon" || geomType == "Box" || geomType == "Envelope" ) )
00024   {
00025     QDomNode geometryChild = geometryNode.firstChild();
00026     if ( geometryChild.isNull() )
00027     {
00028       return 0;
00029     }
00030     geometryTypeElement = geometryChild.toElement();
00031     geomType = geometryTypeElement.tagName();
00032   }
00033 
00034   if ( !( geomType == "Point" || geomType == "LineString" || geomType == "Polygon" || geomType == "MultiPoint" || geomType == "MultiLineString" || geomType == "MultiPolygon" || geomType == "Box" || geomType == "Envelope" ) )
00035     return 0;
00036 
00037   if ( geomType == "Point" )
00038   {
00039     return geometryFromGMLPoint( geometryTypeElement );
00040   }
00041   else if ( geomType == "LineString" )
00042   {
00043     return geometryFromGMLLineString( geometryTypeElement );
00044   }
00045   else if ( geomType == "Polygon" )
00046   {
00047     return geometryFromGMLPolygon( geometryTypeElement );
00048   }
00049   else if ( geomType == "MultiPoint" )
00050   {
00051     return geometryFromGMLMultiPoint( geometryTypeElement );
00052   }
00053   else if ( geomType == "MultiLineString" )
00054   {
00055     return geometryFromGMLMultiLineString( geometryTypeElement );
00056   }
00057   else if ( geomType == "MultiPolygon" )
00058   {
00059     return geometryFromGMLMultiPolygon( geometryTypeElement );
00060   }
00061   else if ( geomType == "Box" )
00062   {
00063     return QgsGeometry::fromRect( rectangleFromGMLBox( geometryTypeElement ) );
00064   }
00065   else if ( geomType == "Envelope" )
00066   {
00067     return QgsGeometry::fromRect( rectangleFromGMLEnvelope( geometryTypeElement ) );
00068   }
00069   else //unknown type
00070   {
00071     return 0;
00072   }
00073 }
00074 
00075 QgsGeometry* QgsOgcUtils::geometryFromGML( const QString& xmlString )
00076 {
00077   // wrap the string into a root tag to have "gml" namespace (and also as a default namespace)
00078   QString xml = QString( "<tmp xmlns=\"%1\" xmlns:gml=\"%1\">%2</tmp>" ).arg( GML_NAMESPACE ).arg( xmlString );
00079   QDomDocument doc;
00080   if ( !doc.setContent( xml, true ) )
00081     return 0;
00082 
00083   return geometryFromGML( doc.documentElement().firstChildElement() );
00084 }
00085 
00086 
00087 QgsGeometry* QgsOgcUtils::geometryFromGMLPoint( const QDomElement& geometryElement )
00088 {
00089   std::list<QgsPoint> pointCoordinate;
00090 
00091   QDomNodeList coordList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, "coordinates" );
00092   if ( coordList.size() > 0 )
00093   {
00094     QDomElement coordElement = coordList.at( 0 ).toElement();
00095     if ( readGMLCoordinates( pointCoordinate, coordElement ) != 0 )
00096     {
00097       return 0;
00098     }
00099   }
00100   else
00101   {
00102     QDomNodeList posList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, "pos" );
00103     if ( posList.size() < 1 )
00104     {
00105       return 0;
00106     }
00107     QDomElement posElement = posList.at( 0 ).toElement();
00108     if ( readGMLPositions( pointCoordinate, posElement ) != 0 )
00109     {
00110       return 0;
00111     }
00112   }
00113 
00114   if ( pointCoordinate.size() < 1 )
00115   {
00116     return 0;
00117   }
00118 
00119   std::list<QgsPoint>::const_iterator point_it = pointCoordinate.begin();
00120   //char e = QgsApplication::endian();
00121   char e = ( htonl( 1 ) == 1 ) ? 0 : 1 ;
00122   double x = point_it->x();
00123   double y = point_it->y();
00124   int size = 1 + sizeof( int ) + 2 * sizeof( double );
00125 
00126   QGis::WkbType type = QGis::WKBPoint;
00127   unsigned char* wkb = new unsigned char[size];
00128 
00129   int wkbPosition = 0; //current offset from wkb beginning (in bytes)
00130   memcpy( &( wkb )[wkbPosition], &e, 1 );
00131   wkbPosition += 1;
00132   memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
00133   wkbPosition += sizeof( int );
00134   memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
00135   wkbPosition += sizeof( double );
00136   memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
00137 
00138   QgsGeometry* g = new QgsGeometry();
00139   g->fromWkb( wkb, size );
00140   return g;
00141 }
00142 
00143 QgsGeometry* QgsOgcUtils::geometryFromGMLLineString( const QDomElement& geometryElement )
00144 {
00145   std::list<QgsPoint> lineCoordinates;
00146 
00147   QDomNodeList coordList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, "coordinates" );
00148   if ( coordList.size() > 0 )
00149   {
00150     QDomElement coordElement = coordList.at( 0 ).toElement();
00151     if ( readGMLCoordinates( lineCoordinates, coordElement ) != 0 )
00152     {
00153       return 0;
00154     }
00155   }
00156   else
00157   {
00158     QDomNodeList posList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, "posList" );
00159     if ( posList.size() < 1 )
00160     {
00161       return 0;
00162     }
00163     QDomElement posElement = posList.at( 0 ).toElement();
00164     if ( readGMLPositions( lineCoordinates, posElement ) != 0 )
00165     {
00166       return 0;
00167     }
00168   }
00169 
00170   //char e = QgsApplication::endian();
00171   char e = ( htonl( 1 ) == 1 ) ? 0 : 1 ;
00172   int size = 1 + 2 * sizeof( int ) + lineCoordinates.size() * 2 * sizeof( double );
00173 
00174   QGis::WkbType type = QGis::WKBLineString;
00175   unsigned char* wkb = new unsigned char[size];
00176 
00177   int wkbPosition = 0; //current offset from wkb beginning (in bytes)
00178   double x, y;
00179   int nPoints = lineCoordinates.size();
00180 
00181   //fill the contents into *wkb
00182   memcpy( &( wkb )[wkbPosition], &e, 1 );
00183   wkbPosition += 1;
00184   memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
00185   wkbPosition += sizeof( int );
00186   memcpy( &( wkb )[wkbPosition], &nPoints, sizeof( int ) );
00187   wkbPosition += sizeof( int );
00188 
00189   std::list<QgsPoint>::const_iterator iter;
00190   for ( iter = lineCoordinates.begin(); iter != lineCoordinates.end(); ++iter )
00191   {
00192     x = iter->x();
00193     y = iter->y();
00194     memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
00195     wkbPosition += sizeof( double );
00196     memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
00197     wkbPosition += sizeof( double );
00198   }
00199 
00200   QgsGeometry* g = new QgsGeometry();
00201   g->fromWkb( wkb, size );
00202   return g;
00203 }
00204 
00205 QgsGeometry* QgsOgcUtils::geometryFromGMLPolygon( const QDomElement& geometryElement )
00206 {
00207   //read all the coordinates (as QgsPoint) into memory. Each linear ring has an entry in the vector
00208   std::vector<std::list<QgsPoint> > ringCoordinates;
00209 
00210   //read coordinates for outer boundary
00211   std::list<QgsPoint> exteriorPointList;
00212   QDomNodeList outerBoundaryList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, "outerBoundaryIs" );
00213   if ( outerBoundaryList.size() > 0 ) //outer ring is necessary
00214   {
00215     QDomElement coordinatesElement = outerBoundaryList.at( 0 ).firstChild().firstChild().toElement();
00216     if ( coordinatesElement.isNull() )
00217     {
00218       return 0;
00219     }
00220     if ( readGMLCoordinates( exteriorPointList, coordinatesElement ) != 0 )
00221     {
00222       return 0;
00223     }
00224     ringCoordinates.push_back( exteriorPointList );
00225 
00226     //read coordinates for inner boundary
00227     QDomNodeList innerBoundaryList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, "innerBoundaryIs" );
00228     for ( int i = 0; i < innerBoundaryList.size(); ++i )
00229     {
00230       std::list<QgsPoint> interiorPointList;
00231       coordinatesElement = innerBoundaryList.at( i ).firstChild().firstChild().toElement();
00232       if ( coordinatesElement.isNull() )
00233       {
00234         return 0;
00235       }
00236       if ( readGMLCoordinates( interiorPointList, coordinatesElement ) != 0 )
00237       {
00238         return 0;
00239       }
00240       ringCoordinates.push_back( interiorPointList );
00241     }
00242   }
00243   else
00244   {
00245     //read coordinates for exterior
00246     QDomNodeList exteriorList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, "exterior" );
00247     if ( exteriorList.size() < 1 ) //outer ring is necessary
00248     {
00249       return 0;
00250     }
00251     QDomElement posElement = exteriorList.at( 0 ).firstChild().firstChild().toElement();
00252     if ( posElement.isNull() )
00253     {
00254       return 0;
00255     }
00256     if ( readGMLPositions( exteriorPointList, posElement ) != 0 )
00257     {
00258       return 0;
00259     }
00260     ringCoordinates.push_back( exteriorPointList );
00261 
00262     //read coordinates for inner boundary
00263     QDomNodeList interiorList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, "interior" );
00264     for ( int i = 0; i < interiorList.size(); ++i )
00265     {
00266       std::list<QgsPoint> interiorPointList;
00267       QDomElement posElement = interiorList.at( i ).firstChild().firstChild().toElement();
00268       if ( posElement.isNull() )
00269       {
00270         return 0;
00271       }
00272       if ( readGMLPositions( interiorPointList, posElement ) != 0 )
00273       {
00274         return 0;
00275       }
00276       ringCoordinates.push_back( interiorPointList );
00277     }
00278   }
00279 
00280   //calculate number of bytes to allocate
00281   int nrings = ringCoordinates.size();
00282   if ( nrings < 1 )
00283     return 0;
00284 
00285   int npoints = 0;//total number of points
00286   for ( std::vector<std::list<QgsPoint> >::const_iterator it = ringCoordinates.begin(); it != ringCoordinates.end(); ++it )
00287   {
00288     npoints += it->size();
00289   }
00290   int size = 1 + 2 * sizeof( int ) + nrings * sizeof( int ) + 2 * npoints * sizeof( double );
00291 
00292   QGis::WkbType type = QGis::WKBPolygon;
00293   unsigned char* wkb = new unsigned char[size];
00294 
00295   //char e = QgsApplication::endian();
00296   char e = ( htonl( 1 ) == 1 ) ? 0 : 1 ;
00297   int wkbPosition = 0; //current offset from wkb beginning (in bytes)
00298   int nPointsInRing = 0;
00299   double x, y;
00300 
00301   //fill the contents into *wkb
00302   memcpy( &( wkb )[wkbPosition], &e, 1 );
00303   wkbPosition += 1;
00304   memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
00305   wkbPosition += sizeof( int );
00306   memcpy( &( wkb )[wkbPosition], &nrings, sizeof( int ) );
00307   wkbPosition += sizeof( int );
00308   for ( std::vector<std::list<QgsPoint> >::const_iterator it = ringCoordinates.begin(); it != ringCoordinates.end(); ++it )
00309   {
00310     nPointsInRing = it->size();
00311     memcpy( &( wkb )[wkbPosition], &nPointsInRing, sizeof( int ) );
00312     wkbPosition += sizeof( int );
00313     //iterate through the string list converting the strings to x-/y- doubles
00314     std::list<QgsPoint>::const_iterator iter;
00315     for ( iter = it->begin(); iter != it->end(); ++iter )
00316     {
00317       x = iter->x();
00318       y = iter->y();
00319       //qWarning("currentCoordinate: " + QString::number(x) + " // " + QString::number(y));
00320       memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
00321       wkbPosition += sizeof( double );
00322       memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
00323       wkbPosition += sizeof( double );
00324     }
00325   }
00326 
00327   QgsGeometry* g = new QgsGeometry();
00328   g->fromWkb( wkb, size );
00329   return g;
00330 }
00331 
00332 QgsGeometry* QgsOgcUtils::geometryFromGMLMultiPoint( const QDomElement& geometryElement )
00333 {
00334   std::list<QgsPoint> pointList;
00335   std::list<QgsPoint> currentPoint;
00336   QDomNodeList pointMemberList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, "pointMember" );
00337   if ( pointMemberList.size() < 1 )
00338   {
00339     return 0;
00340   }
00341   QDomNodeList pointNodeList;
00342   // coordinates or pos element
00343   QDomNodeList coordinatesList;
00344   QDomNodeList posList;
00345   for ( int i = 0; i < pointMemberList.size(); ++i )
00346   {
00347     //<Point> element
00348     pointNodeList = pointMemberList.at( i ).toElement().elementsByTagNameNS( GML_NAMESPACE, "Point" );
00349     if ( pointNodeList.size() < 1 )
00350     {
00351       continue;
00352     }
00353     //<coordinates> element
00354     coordinatesList = pointNodeList.at( 0 ).toElement().elementsByTagNameNS( GML_NAMESPACE, "coordinates" );
00355     if ( coordinatesList.size() > 0 )
00356     {
00357       currentPoint.clear();
00358       if ( readGMLCoordinates( currentPoint, coordinatesList.at( 0 ).toElement() ) != 0 )
00359       {
00360         continue;
00361       }
00362       if ( currentPoint.size() < 1 )
00363       {
00364         continue;
00365       }
00366       pointList.push_back(( *currentPoint.begin() ) );
00367       continue;
00368     }
00369     else
00370     {
00371       //<pos> element
00372       posList = pointNodeList.at( 0 ).toElement().elementsByTagNameNS( GML_NAMESPACE, "pos" );
00373       if ( posList.size() < 1 )
00374       {
00375         continue;
00376       }
00377       currentPoint.clear();
00378       if ( readGMLPositions( currentPoint, posList.at( 0 ).toElement() ) != 0 )
00379       {
00380         continue;
00381       }
00382       if ( currentPoint.size() < 1 )
00383       {
00384         continue;
00385       }
00386       pointList.push_back(( *currentPoint.begin() ) );
00387     }
00388   }
00389 
00390   int nPoints = pointList.size(); //number of points
00391   if ( nPoints < 1 )
00392     return 0;
00393 
00394   //calculate the required wkb size
00395   int size = 1 + 2 * sizeof( int ) + pointList.size() * ( 2 * sizeof( double ) + 1 + sizeof( int ) );
00396 
00397   QGis::WkbType type = QGis::WKBMultiPoint;
00398   unsigned char* wkb = new unsigned char[size];
00399 
00400   //fill the wkb content
00401   //char e = QgsApplication::endian();
00402   char e = ( htonl( 1 ) == 1 ) ? 0 : 1 ;
00403   int wkbPosition = 0; //current offset from wkb beginning (in bytes)
00404   double x, y;
00405   memcpy( &( wkb )[wkbPosition], &e, 1 );
00406   wkbPosition += 1;
00407   memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
00408   wkbPosition += sizeof( int );
00409   memcpy( &( wkb )[wkbPosition], &nPoints, sizeof( int ) );
00410   wkbPosition += sizeof( int );
00411   for ( std::list<QgsPoint>::const_iterator it = pointList.begin(); it != pointList.end(); ++it )
00412   {
00413     memcpy( &( wkb )[wkbPosition], &e, 1 );
00414     wkbPosition += 1;
00415     memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
00416     wkbPosition += sizeof( int );
00417     x = it->x();
00418     memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
00419     wkbPosition += sizeof( double );
00420     y = it->y();
00421     memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
00422     wkbPosition += sizeof( double );
00423   }
00424 
00425   QgsGeometry* g = new QgsGeometry();
00426   g->fromWkb( wkb, size );
00427   return g;
00428 }
00429 
00430 QgsGeometry* QgsOgcUtils::geometryFromGMLMultiLineString( const QDomElement& geometryElement )
00431 {
00432   //geoserver has
00433   //<gml:MultiLineString>
00434   //<gml:lineStringMember>
00435   //<gml:LineString>
00436 
00437   //mapserver has directly
00438   //<gml:MultiLineString
00439   //<gml:LineString
00440 
00441   std::list<std::list<QgsPoint> > lineCoordinates; //first list: lines, second list: points of one line
00442   QDomElement currentLineStringElement;
00443   QDomNodeList currentCoordList;
00444   QDomNodeList currentPosList;
00445 
00446   QDomNodeList lineStringMemberList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, "lineStringMember" );
00447   if ( lineStringMemberList.size() > 0 ) //geoserver
00448   {
00449     for ( int i = 0; i < lineStringMemberList.size(); ++i )
00450     {
00451       QDomNodeList lineStringNodeList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, "LineString" );
00452       if ( lineStringNodeList.size() < 1 )
00453       {
00454         return 0;
00455       }
00456       currentLineStringElement = lineStringNodeList.at( 0 ).toElement();
00457       currentCoordList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, "coordinates" );
00458       if ( currentCoordList.size() > 0 )
00459       {
00460         std::list<QgsPoint> currentPointList;
00461         if ( readGMLCoordinates( currentPointList, currentCoordList.at( 0 ).toElement() ) != 0 )
00462         {
00463           return 0;
00464         }
00465         lineCoordinates.push_back( currentPointList );
00466       }
00467       else
00468       {
00469         currentPosList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, "posList" );
00470         if ( currentPosList.size() < 1 )
00471         {
00472           return 0;
00473         }
00474         std::list<QgsPoint> currentPointList;
00475         if ( readGMLPositions( currentPointList, currentPosList.at( 0 ).toElement() ) != 0 )
00476         {
00477           return 0;
00478         }
00479         lineCoordinates.push_back( currentPointList );
00480       }
00481     }
00482   }
00483   else
00484   {
00485     QDomNodeList lineStringList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, "LineString" );
00486     if ( lineStringList.size() > 0 ) //mapserver
00487     {
00488       for ( int i = 0; i < lineStringList.size(); ++i )
00489       {
00490         currentLineStringElement = lineStringList.at( i ).toElement();
00491         currentCoordList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, "coordinates" );
00492         if ( currentCoordList.size() > 0 )
00493         {
00494           std::list<QgsPoint> currentPointList;
00495           if ( readGMLCoordinates( currentPointList, currentCoordList.at( 0 ).toElement() ) != 0 )
00496           {
00497             return 0;
00498           }
00499           lineCoordinates.push_back( currentPointList );
00500           return 0;
00501         }
00502         else
00503         {
00504           currentPosList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, "posList" );
00505           if ( currentPosList.size() < 1 )
00506           {
00507             return 0;
00508           }
00509           std::list<QgsPoint> currentPointList;
00510           if ( readGMLPositions( currentPointList, currentPosList.at( 0 ).toElement() ) != 0 )
00511           {
00512             return 0;
00513           }
00514           lineCoordinates.push_back( currentPointList );
00515         }
00516       }
00517     }
00518     else
00519     {
00520       return 0;
00521     }
00522   }
00523 
00524   int nLines = lineCoordinates.size();
00525   if ( nLines < 1 )
00526     return 0;
00527 
00528 
00529   //calculate the required wkb size
00530   int size = ( lineCoordinates.size() + 1 ) * ( 1 + 2 * sizeof( int ) );
00531   for ( std::list<std::list<QgsPoint> >::const_iterator it = lineCoordinates.begin(); it != lineCoordinates.end(); ++it )
00532   {
00533     size += it->size() * 2 * sizeof( double );
00534   }
00535 
00536   QGis::WkbType type = QGis::WKBMultiLineString;
00537   unsigned char* wkb = new unsigned char[size];
00538 
00539   //fill the wkb content
00540   //char e = QgsApplication::endian();
00541   char e = ( htonl( 1 ) == 1 ) ? 0 : 1 ;
00542   int wkbPosition = 0; //current offset from wkb beginning (in bytes)
00543   int nPoints; //number of points in a line
00544   double x, y;
00545   memcpy( &( wkb )[wkbPosition], &e, 1 );
00546   wkbPosition += 1;
00547   memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
00548   wkbPosition += sizeof( int );
00549   memcpy( &( wkb )[wkbPosition], &nLines, sizeof( int ) );
00550   wkbPosition += sizeof( int );
00551   for ( std::list<std::list<QgsPoint> >::const_iterator it = lineCoordinates.begin(); it != lineCoordinates.end(); ++it )
00552   {
00553     memcpy( &( wkb )[wkbPosition], &e, 1 );
00554     wkbPosition += 1;
00555     memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
00556     wkbPosition += sizeof( int );
00557     nPoints = it->size();
00558     memcpy( &( wkb )[wkbPosition], &nPoints, sizeof( int ) );
00559     wkbPosition += sizeof( int );
00560     for ( std::list<QgsPoint>::const_iterator iter = it->begin(); iter != it->end(); ++iter )
00561     {
00562       x = iter->x();
00563       //qWarning("x is: " + QString::number(x));
00564       y = iter->y();
00565       //qWarning("y is: " + QString::number(y));
00566       memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
00567       wkbPosition += sizeof( double );
00568       memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
00569       wkbPosition += sizeof( double );
00570     }
00571   }
00572 
00573   QgsGeometry* g = new QgsGeometry();
00574   g->fromWkb( wkb, size );
00575   return g;
00576 }
00577 
00578 QgsGeometry* QgsOgcUtils::geometryFromGMLMultiPolygon( const QDomElement& geometryElement )
00579 {
00580   //first list: different polygons, second list: different rings, third list: different points
00581   std::list<std::list<std::list<QgsPoint> > > multiPolygonPoints;
00582   QDomElement currentPolygonMemberElement;
00583   QDomNodeList polygonList;
00584   QDomElement currentPolygonElement;
00585   // rings in GML2
00586   QDomNodeList outerBoundaryList;
00587   QDomElement currentOuterBoundaryElement;
00588   QDomNodeList innerBoundaryList;
00589   QDomElement currentInnerBoundaryElement;
00590   // rings in GML3
00591   QDomNodeList exteriorList;
00592   QDomElement currentExteriorElement;
00593   QDomElement currentInteriorElement;
00594   QDomNodeList interiorList;
00595   // lienar ring
00596   QDomNodeList linearRingNodeList;
00597   QDomElement currentLinearRingElement;
00598   // Coordinates or position list
00599   QDomNodeList currentCoordinateList;
00600   QDomNodeList currentPosList;
00601 
00602   QDomNodeList polygonMemberList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, "polygonMember" );
00603   for ( int i = 0; i < polygonMemberList.size(); ++i )
00604   {
00605     std::list<std::list<QgsPoint> > currentPolygonList;
00606     currentPolygonMemberElement = polygonMemberList.at( i ).toElement();
00607     polygonList = currentPolygonMemberElement.elementsByTagNameNS( GML_NAMESPACE, "Polygon" );
00608     if ( polygonList.size() < 1 )
00609     {
00610       continue;
00611     }
00612     currentPolygonElement = polygonList.at( 0 ).toElement();
00613 
00614     //find exterior ring
00615     outerBoundaryList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, "outerBoundaryIs" );
00616     if ( outerBoundaryList.size() > 0 )
00617     {
00618       currentOuterBoundaryElement = outerBoundaryList.at( 0 ).toElement();
00619       std::list<QgsPoint> ringCoordinates;
00620 
00621       linearRingNodeList = currentOuterBoundaryElement.elementsByTagNameNS( GML_NAMESPACE, "LinearRing" );
00622       if ( linearRingNodeList.size() < 1 )
00623       {
00624         continue;
00625       }
00626       currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
00627       currentCoordinateList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, "coordinates" );
00628       if ( currentCoordinateList.size() < 1 )
00629       {
00630         continue;
00631       }
00632       if ( readGMLCoordinates( ringCoordinates, currentCoordinateList.at( 0 ).toElement() ) != 0 )
00633       {
00634         continue;
00635       }
00636       currentPolygonList.push_back( ringCoordinates );
00637 
00638       //find interior rings
00639       QDomNodeList innerBoundaryList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, "innerBoundaryIs" );
00640       for ( int j = 0; j < innerBoundaryList.size(); ++j )
00641       {
00642         std::list<QgsPoint> ringCoordinates;
00643         currentInnerBoundaryElement = innerBoundaryList.at( j ).toElement();
00644         linearRingNodeList = currentInnerBoundaryElement.elementsByTagNameNS( GML_NAMESPACE, "LinearRing" );
00645         if ( linearRingNodeList.size() < 1 )
00646         {
00647           continue;
00648         }
00649         currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
00650         currentCoordinateList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, "coordinates" );
00651         if ( currentCoordinateList.size() < 1 )
00652         {
00653           continue;
00654         }
00655         if ( readGMLCoordinates( ringCoordinates, currentCoordinateList.at( 0 ).toElement() ) != 0 )
00656         {
00657           continue;
00658         }
00659         currentPolygonList.push_back( ringCoordinates );
00660       }
00661     }
00662     else
00663     {
00664       //find exterior ring
00665       exteriorList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, "exterior" );
00666       if ( exteriorList.size() < 1 )
00667       {
00668         continue;
00669       }
00670 
00671       currentExteriorElement = exteriorList.at( 0 ).toElement();
00672       std::list<QgsPoint> ringPositions;
00673 
00674       linearRingNodeList = currentExteriorElement.elementsByTagNameNS( GML_NAMESPACE, "LinearRing" );
00675       if ( linearRingNodeList.size() < 1 )
00676       {
00677         continue;
00678       }
00679       currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
00680       currentPosList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, "posList" );
00681       if ( currentPosList.size() < 1 )
00682       {
00683         continue;
00684       }
00685       if ( readGMLPositions( ringPositions, currentPosList.at( 0 ).toElement() ) != 0 )
00686       {
00687         continue;
00688       }
00689       currentPolygonList.push_back( ringPositions );
00690 
00691       //find interior rings
00692       QDomNodeList interiorList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, "interior" );
00693       for ( int j = 0; j < interiorList.size(); ++j )
00694       {
00695         std::list<QgsPoint> ringPositions;
00696         currentInteriorElement = interiorList.at( j ).toElement();
00697         linearRingNodeList = currentInteriorElement.elementsByTagNameNS( GML_NAMESPACE, "LinearRing" );
00698         if ( linearRingNodeList.size() < 1 )
00699         {
00700           continue;
00701         }
00702         currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
00703         currentPosList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, "posList" );
00704         if ( currentPosList.size() < 1 )
00705         {
00706           continue;
00707         }
00708         if ( readGMLPositions( ringPositions, currentPosList.at( 0 ).toElement() ) != 0 )
00709         {
00710           continue;
00711         }
00712         currentPolygonList.push_back( ringPositions );
00713       }
00714     }
00715     multiPolygonPoints.push_back( currentPolygonList );
00716   }
00717 
00718   int nPolygons = multiPolygonPoints.size();
00719   if ( nPolygons < 1 )
00720     return 0;
00721 
00722   int size = 1 + 2 * sizeof( int );
00723   //calculate the wkb size
00724   for ( std::list<std::list<std::list<QgsPoint> > >::const_iterator it = multiPolygonPoints.begin(); it != multiPolygonPoints.end(); ++it )
00725   {
00726     size += 1 + 2 * sizeof( int );
00727     for ( std::list<std::list<QgsPoint> >::const_iterator iter = it->begin(); iter != it->end(); ++iter )
00728     {
00729       size += sizeof( int ) + 2 * iter->size() * sizeof( double );
00730     }
00731   }
00732 
00733   QGis::WkbType type = QGis::WKBMultiPolygon;
00734   unsigned char* wkb = new unsigned char[size];
00735 
00736   int polygonType = QGis::WKBPolygon;
00737   //char e = QgsApplication::endian();
00738   char e = ( htonl( 1 ) == 1 ) ? 0 : 1 ;
00739   int wkbPosition = 0; //current offset from wkb beginning (in bytes)
00740   double x, y;
00741   int nRings;
00742   int nPointsInRing;
00743 
00744   //fill the contents into *wkb
00745   memcpy( &( wkb )[wkbPosition], &e, 1 );
00746   wkbPosition += 1;
00747   memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
00748   wkbPosition += sizeof( int );
00749   memcpy( &( wkb )[wkbPosition], &nPolygons, sizeof( int ) );
00750   wkbPosition += sizeof( int );
00751 
00752   for ( std::list<std::list<std::list<QgsPoint> > >::const_iterator it = multiPolygonPoints.begin(); it != multiPolygonPoints.end(); ++it )
00753   {
00754     memcpy( &( wkb )[wkbPosition], &e, 1 );
00755     wkbPosition += 1;
00756     memcpy( &( wkb )[wkbPosition], &polygonType, sizeof( int ) );
00757     wkbPosition += sizeof( int );
00758     nRings = it->size();
00759     memcpy( &( wkb )[wkbPosition], &nRings, sizeof( int ) );
00760     wkbPosition += sizeof( int );
00761     for ( std::list<std::list<QgsPoint> >::const_iterator iter = it->begin(); iter != it->end(); ++iter )
00762     {
00763       nPointsInRing = iter->size();
00764       memcpy( &( wkb )[wkbPosition], &nPointsInRing, sizeof( int ) );
00765       wkbPosition += sizeof( int );
00766       for ( std::list<QgsPoint>::const_iterator iterator = iter->begin(); iterator != iter->end(); ++iterator )
00767       {
00768         x = iterator->x();
00769         y = iterator->y();
00770         memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
00771         wkbPosition += sizeof( double );
00772         memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
00773         wkbPosition += sizeof( double );
00774       }
00775     }
00776   }
00777 
00778   QgsGeometry* g = new QgsGeometry();
00779   g->fromWkb( wkb, size );
00780   return g;
00781 }
00782 
00783 bool QgsOgcUtils::readGMLCoordinates( std::list<QgsPoint>& coords, const QDomElement elem )
00784 {
00785   QString coordSeparator = ",";
00786   QString tupelSeparator = " ";
00787   //"decimal" has to be "."
00788 
00789   coords.clear();
00790 
00791   if ( elem.hasAttribute( "cs" ) )
00792   {
00793     coordSeparator = elem.attribute( "cs" );
00794   }
00795   if ( elem.hasAttribute( "ts" ) )
00796   {
00797     tupelSeparator = elem.attribute( "ts" );
00798   }
00799 
00800   QStringList tupels = elem.text().split( tupelSeparator, QString::SkipEmptyParts );
00801   QStringList tupel_coords;
00802   double x, y;
00803   bool conversionSuccess;
00804 
00805   QStringList::const_iterator it;
00806   for ( it = tupels.constBegin(); it != tupels.constEnd(); ++it )
00807   {
00808     tupel_coords = ( *it ).split( coordSeparator, QString::SkipEmptyParts );
00809     if ( tupel_coords.size() < 2 )
00810     {
00811       continue;
00812     }
00813     x = tupel_coords.at( 0 ).toDouble( &conversionSuccess );
00814     if ( !conversionSuccess )
00815     {
00816       return 1;
00817     }
00818     y = tupel_coords.at( 1 ).toDouble( &conversionSuccess );
00819     if ( !conversionSuccess )
00820     {
00821       return 1;
00822     }
00823     coords.push_back( QgsPoint( x, y ) );
00824   }
00825   return 0;
00826 }
00827 
00828 QgsRectangle QgsOgcUtils::rectangleFromGMLBox( const QDomNode& boxNode )
00829 {
00830   QgsRectangle rect;
00831 
00832   QDomElement boxElem = boxNode.toElement();
00833   if ( boxElem.tagName() != "Box" )
00834     return rect;
00835 
00836   QDomElement bElem = boxElem.firstChild().toElement();
00837   QString coordSeparator = ",";
00838   QString tupelSeparator = " ";
00839   if ( bElem.hasAttribute( "cs" ) )
00840   {
00841     coordSeparator = bElem.attribute( "cs" );
00842   }
00843   if ( bElem.hasAttribute( "ts" ) )
00844   {
00845     tupelSeparator = bElem.attribute( "ts" );
00846   }
00847 
00848   QString bString = bElem.text();
00849   bool ok1, ok2, ok3, ok4;
00850   double xmin = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 0, 0 ).toDouble( &ok1 );
00851   double ymin = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 1, 1 ).toDouble( &ok2 );
00852   double xmax = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 0, 0 ).toDouble( &ok3 );
00853   double ymax = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 1, 1 ).toDouble( &ok4 );
00854 
00855   if ( ok1 && ok2 && ok3 && ok4 )
00856   {
00857     rect = QgsRectangle( xmin, ymin, xmax, ymax );
00858     rect.normalize();
00859   }
00860 
00861   return rect;
00862 }
00863 
00864 bool QgsOgcUtils::readGMLPositions( std::list<QgsPoint>& coords, const QDomElement elem )
00865 {
00866   //tupel and coord separator are the same
00867   QString coordSeparator = " ";
00868   QString tupelSeparator = " ";
00869   //"decimal" has to be "."
00870 
00871 
00872   coords.clear();
00873 
00874   QStringList pos = elem.text().split( " ", QString::SkipEmptyParts );
00875   double x, y;
00876   bool conversionSuccess;
00877   int posSize = pos.size();
00878 
00879   int srsDimension = 2;
00880   if ( elem.hasAttribute( "srsDimension" ) )
00881   {
00882     srsDimension = elem.attribute( "srsDimension" ).toInt( &conversionSuccess );
00883     if ( !conversionSuccess )
00884     {
00885       srsDimension = 2;
00886     }
00887   }
00888   else if ( elem.hasAttribute( "dimension" ) )
00889   {
00890     srsDimension = elem.attribute( "dimension" ).toInt( &conversionSuccess );
00891     if ( !conversionSuccess )
00892     {
00893       srsDimension = 2;
00894     }
00895   }
00896 
00897   for ( int i = 0; i < posSize / srsDimension; i++ )
00898   {
00899     x = pos.at( i * srsDimension ).toDouble( &conversionSuccess );
00900     if ( !conversionSuccess )
00901     {
00902       return 1;
00903     }
00904     y = pos.at( i * srsDimension + 1 ).toDouble( &conversionSuccess );
00905     if ( !conversionSuccess )
00906     {
00907       return 1;
00908     }
00909     coords.push_back( QgsPoint( x, y ) );
00910   }
00911   return 0;
00912 }
00913 
00914 
00915 QgsRectangle QgsOgcUtils::rectangleFromGMLEnvelope( const QDomNode& envelopeNode )
00916 {
00917   QgsRectangle rect;
00918 
00919   QDomElement envelopeElem = envelopeNode.toElement();
00920   if ( envelopeElem.tagName() != "Envelope" )
00921     return rect;
00922 
00923   QDomNodeList lowerCornerList = envelopeElem.elementsByTagNameNS( GML_NAMESPACE, "lowerCorner" );
00924   if ( lowerCornerList.size() < 1 )
00925     return rect;
00926 
00927   QDomNodeList upperCornerList = envelopeElem.elementsByTagNameNS( GML_NAMESPACE, "upperCorner" );
00928   if ( upperCornerList.size() < 1 )
00929     return rect;
00930 
00931   bool conversionSuccess;
00932   int srsDimension = 2;
00933 
00934   QDomElement elem = lowerCornerList.at( 0 ).toElement();
00935   if ( elem.hasAttribute( "srsDimension" ) )
00936   {
00937     srsDimension = elem.attribute( "srsDimension" ).toInt( &conversionSuccess );
00938     if ( !conversionSuccess )
00939     {
00940       srsDimension = 2;
00941     }
00942   }
00943   else if ( elem.hasAttribute( "dimension" ) )
00944   {
00945     srsDimension = elem.attribute( "dimension" ).toInt( &conversionSuccess );
00946     if ( !conversionSuccess )
00947     {
00948       srsDimension = 2;
00949     }
00950   }
00951   QString bString = elem.text();
00952 
00953   double xmin = bString.section( " ", 0, 0 ).toDouble( &conversionSuccess );
00954   if ( !conversionSuccess )
00955     return rect;
00956   double ymin = bString.section( " ", 1, 1 ).toDouble( &conversionSuccess );
00957   if ( !conversionSuccess )
00958     return rect;
00959 
00960   elem = upperCornerList.at( 0 ).toElement();
00961   if ( elem.hasAttribute( "srsDimension" ) )
00962   {
00963     srsDimension = elem.attribute( "srsDimension" ).toInt( &conversionSuccess );
00964     if ( !conversionSuccess )
00965     {
00966       srsDimension = 2;
00967     }
00968   }
00969   else if ( elem.hasAttribute( "dimension" ) )
00970   {
00971     srsDimension = elem.attribute( "dimension" ).toInt( &conversionSuccess );
00972     if ( !conversionSuccess )
00973     {
00974       srsDimension = 2;
00975     }
00976   }
00977 
00978   Q_UNUSED( srsDimension );
00979 
00980   bString = elem.text();
00981   double xmax = bString.section( " ", 0, 0 ).toDouble( &conversionSuccess );
00982   if ( !conversionSuccess )
00983     return rect;
00984   double ymax = bString.section( " ", 1, 1 ).toDouble( &conversionSuccess );
00985   if ( !conversionSuccess )
00986     return rect;
00987 
00988   rect = QgsRectangle( xmin, ymin, xmax, ymax );
00989   rect.normalize();
00990 
00991   return rect;
00992 }
00993 
00994 QDomElement QgsOgcUtils::rectangleToGMLBox( QgsRectangle* box, QDomDocument& doc )
00995 {
00996   if ( !box )
00997   {
00998     return QDomElement();
00999   }
01000 
01001   QDomElement boxElem = doc.createElement( "gml:Box" );
01002   QDomElement coordElem = doc.createElement( "gml:coordinates" );
01003   coordElem.setAttribute( "cs", "," );
01004   coordElem.setAttribute( "ts", " " );
01005 
01006   QString coordString;
01007   coordString += QString::number( box->xMinimum(), 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01008   coordString += ",";
01009   coordString += QString::number( box->yMinimum(), 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01010   coordString += " ";
01011   coordString += QString::number( box->xMaximum(), 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01012   coordString += ",";
01013   coordString += QString::number( box->yMaximum(), 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01014 
01015   QDomText coordText = doc.createTextNode( coordString );
01016   coordElem.appendChild( coordText );
01017   boxElem.appendChild( coordElem );
01018 
01019   return boxElem;
01020 }
01021 
01022 QDomElement QgsOgcUtils::rectangleToGMLEnvelope( QgsRectangle* env, QDomDocument& doc )
01023 {
01024   if ( !env )
01025   {
01026     return QDomElement();
01027   }
01028 
01029   QDomElement envElem = doc.createElement( "gml:Envelope" );
01030   QString posList;
01031 
01032   QDomElement lowerCornerElem = doc.createElement( "gml:lowerCorner" );
01033   posList = QString::number( env->xMinimum(), 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01034   posList += " ";
01035   posList += QString::number( env->yMinimum(), 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01036   QDomText lowerCornerText = doc.createTextNode( posList );
01037   lowerCornerElem.appendChild( lowerCornerText );
01038   envElem.appendChild( lowerCornerElem );
01039 
01040   QDomElement upperCornerElem = doc.createElement( "gml:upperCorner" );
01041   posList = QString::number( env->xMaximum(), 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01042   posList += " ";
01043   posList += QString::number( env->yMaximum(), 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01044   QDomText upperCornerText = doc.createTextNode( posList );
01045   upperCornerElem.appendChild( upperCornerText );
01046   envElem.appendChild( upperCornerElem );
01047 
01048   return envElem;
01049 }
01050 
01051 QDomElement QgsOgcUtils::geometryToGML( QgsGeometry* geometry, QDomDocument& doc, QString format )
01052 {
01053   if ( !geometry || !geometry->asWkb() )
01054     return QDomElement();
01055 
01056   // coordinate separator
01057   QString cs = ",";
01058   // tupel separator
01059   QString ts = " ";
01060   // coord element tagname
01061   QDomElement baseCoordElem;
01062 
01063   bool hasZValue = false;
01064   double *x, *y;
01065   unsigned char* wkb = geometry->asWkb();
01066 
01067   if ( format == "GML3" )
01068   {
01069     switch ( geometry->wkbType() )
01070     {
01071       case QGis::WKBPoint25D:
01072       case QGis::WKBPoint:
01073       case QGis::WKBMultiPoint25D:
01074       case QGis::WKBMultiPoint:
01075         baseCoordElem = doc.createElement( "gml:pos" );;
01076         break;
01077       default:
01078         baseCoordElem = doc.createElement( "gml:posList" );;
01079         break;
01080     }
01081     baseCoordElem.setAttribute( "srsDimension", "2" );
01082     cs = " ";
01083   }
01084   else
01085   {
01086     baseCoordElem = doc.createElement( "gml:coordinates" );;
01087     baseCoordElem.setAttribute( "cs", cs );
01088     baseCoordElem.setAttribute( "ts", ts );
01089   }
01090 
01091   switch ( geometry->wkbType() )
01092   {
01093     case QGis::WKBPoint25D:
01094     case QGis::WKBPoint:
01095     {
01096       QDomElement pointElem = doc.createElement( "gml:Point" );
01097       QDomElement coordElem = baseCoordElem.cloneNode().toElement();
01098       QString coordString;
01099       x = ( double * )( wkb + 5 );
01100       coordString += QString::number( *x, 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01101       coordString += cs;
01102       y = ( double * )( wkb + 5 + sizeof( double ) );
01103       coordString += QString::number( *y, 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01104       QDomText coordText = doc.createTextNode( coordString );
01105       coordElem.appendChild( coordText );
01106       pointElem.appendChild( coordElem );
01107       return pointElem;
01108     }
01109     case QGis::WKBMultiPoint25D:
01110       hasZValue = true;
01111     case QGis::WKBMultiPoint:
01112     {
01113       unsigned char *ptr;
01114       int idx;
01115       int *nPoints;
01116 
01117       QDomElement multiPointElem = doc.createElement( "gml:MultiPoint" );
01118       nPoints = ( int* )( wkb + 5 );
01119       ptr = wkb + 5 + sizeof( int );
01120       for ( idx = 0; idx < *nPoints; ++idx )
01121       {
01122         ptr += ( 1 + sizeof( int ) );
01123         QDomElement pointMemberElem = doc.createElement( "gml:pointMember" );
01124         QDomElement pointElem = doc.createElement( "gml:Point" );
01125         QDomElement coordElem = baseCoordElem.cloneNode().toElement();
01126         QString coordString;
01127         x = ( double * )( ptr );
01128         coordString += QString::number( *x, 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01129         coordString += cs;
01130         ptr += sizeof( double );
01131         y = ( double * )( ptr );
01132         coordString += QString::number( *y, 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01133         QDomText coordText = doc.createTextNode( coordString );
01134         coordElem.appendChild( coordText );
01135         pointElem.appendChild( coordElem );
01136 
01137         ptr += sizeof( double );
01138         if ( hasZValue )
01139         {
01140           ptr += sizeof( double );
01141         }
01142         pointMemberElem.appendChild( pointElem );
01143         multiPointElem.appendChild( pointMemberElem );
01144       }
01145       return multiPointElem;
01146     }
01147     case QGis::WKBLineString25D:
01148       hasZValue = true;
01149     case QGis::WKBLineString:
01150     {
01151       unsigned char *ptr;
01152       int *nPoints;
01153       int idx;
01154 
01155       QDomElement lineStringElem = doc.createElement( "gml:LineString" );
01156       // get number of points in the line
01157       ptr = wkb + 5;
01158       nPoints = ( int * ) ptr;
01159       ptr = wkb + 1 + 2 * sizeof( int );
01160       QDomElement coordElem = baseCoordElem.cloneNode().toElement();
01161       QString coordString;
01162       for ( idx = 0; idx < *nPoints; ++idx )
01163       {
01164         if ( idx != 0 )
01165         {
01166           coordString += ts;
01167         }
01168         x = ( double * ) ptr;
01169         coordString += QString::number( *x, 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01170         coordString += cs;
01171         ptr += sizeof( double );
01172         y = ( double * ) ptr;
01173         coordString += QString::number( *y, 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01174         ptr += sizeof( double );
01175         if ( hasZValue )
01176         {
01177           ptr += sizeof( double );
01178         }
01179       }
01180       QDomText coordText = doc.createTextNode( coordString );
01181       coordElem.appendChild( coordText );
01182       lineStringElem.appendChild( coordElem );
01183       return lineStringElem;
01184     }
01185     case QGis::WKBMultiLineString25D:
01186       hasZValue = true;
01187     case QGis::WKBMultiLineString:
01188     {
01189       unsigned char *ptr;
01190       int idx, jdx, numLineStrings;
01191       int *nPoints;
01192 
01193       QDomElement multiLineStringElem = doc.createElement( "gml:MultiLineString" );
01194       numLineStrings = ( int )( wkb[5] );
01195       ptr = wkb + 9;
01196       for ( jdx = 0; jdx < numLineStrings; jdx++ )
01197       {
01198         QDomElement lineStringMemberElem = doc.createElement( "gml:lineStringMember" );
01199         QDomElement lineStringElem = doc.createElement( "gml:LineString" );
01200         ptr += 5; // skip type since we know its 2
01201         nPoints = ( int * ) ptr;
01202         ptr += sizeof( int );
01203         QDomElement coordElem = baseCoordElem.cloneNode().toElement();
01204         QString coordString;
01205         for ( idx = 0; idx < *nPoints; idx++ )
01206         {
01207           if ( idx != 0 )
01208           {
01209             coordString += ts;
01210           }
01211           x = ( double * ) ptr;
01212           coordString += QString::number( *x, 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01213           ptr += sizeof( double );
01214           coordString += cs;
01215           y = ( double * ) ptr;
01216           coordString += QString::number( *y, 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01217           ptr += sizeof( double );
01218           if ( hasZValue )
01219           {
01220             ptr += sizeof( double );
01221           }
01222         }
01223         QDomText coordText = doc.createTextNode( coordString );
01224         coordElem.appendChild( coordText );
01225         lineStringElem.appendChild( coordElem );
01226         lineStringMemberElem.appendChild( lineStringElem );
01227         multiLineStringElem.appendChild( lineStringMemberElem );
01228       }
01229       return multiLineStringElem;
01230     }
01231     case QGis::WKBPolygon25D:
01232       hasZValue = true;
01233     case QGis::WKBPolygon:
01234     {
01235       unsigned char *ptr;
01236       int idx, jdx;
01237       int *numRings, *nPoints;
01238 
01239       QDomElement polygonElem = doc.createElement( "gml:Polygon" );
01240       // get number of rings in the polygon
01241       numRings = ( int * )( wkb + 1 + sizeof( int ) );
01242       if ( !( *numRings ) )  // sanity check for zero rings in polygon
01243       {
01244         return QDomElement();
01245       }
01246       int *ringStart; // index of first point for each ring
01247       int *ringNumPoints; // number of points in each ring
01248       ringStart = new int[*numRings];
01249       ringNumPoints = new int[*numRings];
01250       ptr = wkb + 1 + 2 * sizeof( int ); // set pointer to the first ring
01251       for ( idx = 0; idx < *numRings; idx++ )
01252       {
01253         QString boundaryName = "gml:outerBoundaryIs";
01254         if ( idx != 0 )
01255         {
01256           boundaryName = "gml:innerBoundaryIs";
01257         }
01258         QDomElement boundaryElem = doc.createElement( boundaryName );
01259         QDomElement ringElem = doc.createElement( "gml:LinearRing" );
01260         // get number of points in the ring
01261         nPoints = ( int * ) ptr;
01262         ringNumPoints[idx] = *nPoints;
01263         ptr += 4;
01264         QDomElement coordElem = baseCoordElem.cloneNode().toElement();
01265         QString coordString;
01266         for ( jdx = 0; jdx < *nPoints; jdx++ )
01267         {
01268           if ( jdx != 0 )
01269           {
01270             coordString += ts;
01271           }
01272           x = ( double * ) ptr;
01273           coordString += QString::number( *x, 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01274           coordString += cs;
01275           ptr += sizeof( double );
01276           y = ( double * ) ptr;
01277           coordString += QString::number( *y, 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01278           ptr += sizeof( double );
01279           if ( hasZValue )
01280           {
01281             ptr += sizeof( double );
01282           }
01283         }
01284         QDomText coordText = doc.createTextNode( coordString );
01285         coordElem.appendChild( coordText );
01286         ringElem.appendChild( coordElem );
01287         boundaryElem.appendChild( ringElem );
01288         polygonElem.appendChild( boundaryElem );
01289       }
01290       delete [] ringStart;
01291       delete [] ringNumPoints;
01292       return polygonElem;
01293     }
01294     case QGis::WKBMultiPolygon25D:
01295       hasZValue = true;
01296     case QGis::WKBMultiPolygon:
01297     {
01298       unsigned char *ptr;
01299       int idx, jdx, kdx;
01300       int *numPolygons, *numRings, *nPoints;
01301 
01302       QDomElement multiPolygonElem = doc.createElement( "gml:MultiPolygon" );
01303       ptr = wkb + 5;
01304       numPolygons = ( int * ) ptr;
01305       ptr = wkb + 9;
01306       for ( kdx = 0; kdx < *numPolygons; kdx++ )
01307       {
01308         QDomElement polygonMemberElem = doc.createElement( "gml:polygonMember" );
01309         QDomElement polygonElem = doc.createElement( "gml:Polygon" );
01310         ptr += 5;
01311         numRings = ( int * ) ptr;
01312         ptr += 4;
01313         for ( idx = 0; idx < *numRings; idx++ )
01314         {
01315           QString boundaryName = "gml:outerBoundaryIs";
01316           if ( idx != 0 )
01317           {
01318             boundaryName = "gml:innerBoundaryIs";
01319           }
01320           QDomElement boundaryElem = doc.createElement( boundaryName );
01321           QDomElement ringElem = doc.createElement( "gml:LinearRing" );
01322           nPoints = ( int * ) ptr;
01323           ptr += 4;
01324           QDomElement coordElem = baseCoordElem.cloneNode().toElement();
01325           QString coordString;
01326           for ( jdx = 0; jdx < *nPoints; jdx++ )
01327           {
01328             if ( jdx != 0 )
01329             {
01330               coordString += ts;
01331             }
01332             x = ( double * ) ptr;
01333             coordString += QString::number( *x, 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01334             ptr += sizeof( double );
01335             coordString += cs;
01336             y = ( double * ) ptr;
01337             coordString += QString::number( *y, 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01338             ptr += sizeof( double );
01339             if ( hasZValue )
01340             {
01341               ptr += sizeof( double );
01342             }
01343           }
01344           QDomText coordText = doc.createTextNode( coordString );
01345           coordElem.appendChild( coordText );
01346           ringElem.appendChild( coordElem );
01347           boundaryElem.appendChild( ringElem );
01348           polygonElem.appendChild( boundaryElem );
01349           polygonMemberElem.appendChild( polygonElem );
01350           multiPolygonElem.appendChild( polygonMemberElem );
01351         }
01352       }
01353       return multiPolygonElem;
01354     }
01355     default:
01356       return QDomElement();
01357   }
01358 }
01359 
01360 QDomElement QgsOgcUtils::geometryToGML( QgsGeometry* geometry, QDomDocument& doc )
01361 {
01362   return geometryToGML( geometry, doc, "GML2" );
01363 }
01364 
01365 QDomElement QgsOgcUtils::createGMLCoordinates( const QVector<QgsPoint> points, QDomDocument& doc )
01366 {
01367   QDomElement coordElem = doc.createElement( "gml:coordinates" );
01368   coordElem.setAttribute( "cs", "," );
01369   coordElem.setAttribute( "ts", " " );
01370 
01371   QString coordString;
01372   QVector<QgsPoint>::const_iterator pointIt = points.constBegin();
01373   for ( ; pointIt != points.constEnd(); ++pointIt )
01374   {
01375     if ( pointIt != points.constBegin() )
01376     {
01377       coordString += " ";
01378     }
01379     coordString += QString::number( pointIt->x(), 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01380     coordString += ",";
01381     coordString += QString::number( pointIt->y(), 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01382   }
01383 
01384   QDomText coordText = doc.createTextNode( coordString );
01385   coordElem.appendChild( coordText );
01386   return coordElem;
01387 }
01388 
01389 QDomElement QgsOgcUtils::createGMLPositions( const QVector<QgsPoint> points, QDomDocument& doc )
01390 {
01391   QDomElement posElem = doc.createElement( "gml:pos" );
01392   if ( points.size() > 1 )
01393     posElem = doc.createElement( "gml:posList" );
01394   posElem.setAttribute( "srsDimension", "2" );
01395 
01396   QString coordString;
01397   QVector<QgsPoint>::const_iterator pointIt = points.constBegin();
01398   for ( ; pointIt != points.constEnd(); ++pointIt )
01399   {
01400     if ( pointIt != points.constBegin() )
01401     {
01402       coordString += " ";
01403     }
01404     coordString += QString::number( pointIt->x(), 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01405     coordString += " ";
01406     coordString += QString::number( pointIt->y(), 'f', 8 ).remove( QRegExp( "[0]{1,7}$" ) );
01407   }
01408 
01409   QDomText coordText = doc.createTextNode( coordString );
01410   posElem.appendChild( coordText );
01411   return posElem;
01412 }
01413 
01414 
01415 
01416 // -----------------------------------------
01417 
01418 
01419 QgsExpression* QgsOgcUtils::expressionFromOgcFilter( const QDomElement& element )
01420 {
01421   if ( element.isNull() || !element.hasChildNodes() )
01422     return NULL;
01423 
01424   QgsExpression *expr = new QgsExpression();
01425 
01426   QDomElement childElem = element.firstChildElement();
01427   while ( !childElem.isNull() )
01428   {
01429     QString errorMsg;
01430     QgsExpression::Node *node = nodeFromOgcFilter( childElem, errorMsg );
01431     if ( !node )
01432     {
01433       // invalid expression, parser error
01434       expr->mParserErrorString = errorMsg;
01435       return expr;
01436     }
01437 
01438     // use the concat binary operator to append to the root node
01439     if ( !expr->mRootNode )
01440     {
01441       expr->mRootNode = node;
01442     }
01443     else
01444     {
01445       expr->mRootNode = new QgsExpression::NodeBinaryOperator( QgsExpression::boConcat, expr->mRootNode, node );
01446     }
01447 
01448     childElem = childElem.nextSiblingElement();
01449   }
01450 
01451   return expr;
01452 }
01453 
01454 
01455 static const QMap<QString, int>& binaryOperatorsTagNamesMap()
01456 {
01457   static QMap<QString, int> binOps;
01458   if ( binOps.isEmpty() )
01459   {
01460     // logical
01461     binOps.insert( "Or", QgsExpression::boOr );
01462     binOps.insert( "And", QgsExpression::boAnd );
01463     // comparison
01464     binOps.insert( "PropertyIsEqualTo", QgsExpression::boEQ );
01465     binOps.insert( "PropertyIsNotEqualTo", QgsExpression::boNE );
01466     binOps.insert( "PropertyIsLessThanOrEqualTo", QgsExpression::boLE );
01467     binOps.insert( "PropertyIsGreaterThanOrEqualTo", QgsExpression::boGE );
01468     binOps.insert( "PropertyIsLessThan", QgsExpression::boLT );
01469     binOps.insert( "PropertyIsGreaterThan", QgsExpression::boGT );
01470     binOps.insert( "PropertyIsLike", QgsExpression::boLike );
01471     // arithmetics
01472     binOps.insert( "Add", QgsExpression::boPlus );
01473     binOps.insert( "Sub", QgsExpression::boMinus );
01474     binOps.insert( "Mul", QgsExpression::boMul );
01475     binOps.insert( "Div", QgsExpression::boDiv );
01476   }
01477   return binOps;
01478 }
01479 
01480 static int binaryOperatorFromTagName( const QString& tagName )
01481 {
01482 
01483   return binaryOperatorsTagNamesMap().value( tagName, -1 );
01484 }
01485 
01486 static QString binaryOperatorToTagName( QgsExpression::BinaryOperator op )
01487 {
01488   return binaryOperatorsTagNamesMap().key( op, QString() );
01489 }
01490 
01491 static bool isBinaryOperator( const QString& tagName )
01492 {
01493   return binaryOperatorFromTagName( tagName ) >= 0;
01494 }
01495 
01496 
01497 static bool isSpatialOperator( const QString& tagName )
01498 {
01499   static QStringList spatialOps;
01500   if ( spatialOps.isEmpty() )
01501   {
01502     spatialOps << "BBOX" << "Intersects" << "Contians" << "Crosses" << "Equals"
01503     << "Disjoint" << "Overlaps" << "Touches" << "Within";
01504   }
01505 
01506   return spatialOps.contains( tagName );
01507 }
01508 
01509 
01510 
01511 QgsExpression::Node* QgsOgcUtils::nodeFromOgcFilter( QDomElement &element, QString &errorMessage )
01512 {
01513   if ( element.isNull() )
01514     return NULL;
01515 
01516   // check for binary operators
01517   if ( isBinaryOperator( element.tagName() ) )
01518   {
01519     return nodeBinaryOperatorFromOgcFilter( element, errorMessage );
01520   }
01521 
01522   // check for spatial operators
01523   if ( isSpatialOperator( element.tagName() ) )
01524   {
01525     return nodeSpatialOperatorFromOgcFilter( element, errorMessage );
01526   }
01527 
01528   // check for other OGC operators, convert them to expressions
01529 
01530   if ( element.tagName() == "Not" )
01531   {
01532     return nodeNotFromOgcFilter( element, errorMessage );
01533   }
01534   else if ( element.tagName() == "PropertyIsNull" )
01535   {
01536     return nodePropertyIsNullFromOgcFilter( element, errorMessage );
01537   }
01538   else if ( element.tagName() == "Literal" )
01539   {
01540     return nodeLiteralFromOgcFilter( element, errorMessage );
01541   }
01542   else if ( element.tagName() == "Function" )
01543   {
01544     return nodeFunctionFromOgcFilter( element, errorMessage );
01545   }
01546   else if ( element.tagName() == "PropertyName" )
01547   {
01548     return nodeColumnRefFromOgcFilter( element, errorMessage );
01549   }
01550   else if ( element.tagName() == "PropertyIsBetween" )
01551   {
01552     return nodeIsBetweenFromOgcFilter( element, errorMessage );
01553   }
01554 
01555   errorMessage += QString( "unable to convert '%1' element to a valid expression: it is not supported yet or it has invalid arguments" ).arg( element.tagName() );
01556   return NULL;
01557 }
01558 
01559 
01560 
01561 QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodeBinaryOperatorFromOgcFilter( QDomElement &element, QString &errorMessage )
01562 {
01563   if ( element.isNull() )
01564     return NULL;
01565 
01566   int op = binaryOperatorFromTagName( element.tagName() );
01567   if ( op < 0 )
01568   {
01569     if ( errorMessage.isEmpty() )
01570       errorMessage = QString( "'%1' binary operator not supported." ).arg( element.tagName() );
01571     return NULL;
01572   }
01573 
01574   QDomElement operandElem = element.firstChildElement();
01575   QgsExpression::Node* opLeft = nodeFromOgcFilter( operandElem, errorMessage );
01576   if ( !opLeft )
01577   {
01578     if ( errorMessage.isEmpty() )
01579       errorMessage = QString( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
01580     return NULL;
01581   }
01582 
01583   operandElem = operandElem.nextSiblingElement();
01584   QgsExpression::Node* opRight = nodeFromOgcFilter( operandElem, errorMessage );
01585   if ( !opRight )
01586   {
01587     if ( errorMessage.isEmpty() )
01588       errorMessage = QString( "invalid right operand for '%1' binary operator" ).arg( element.tagName() );
01589     delete opLeft;
01590     return NULL;
01591   }
01592 
01593   return new QgsExpression::NodeBinaryOperator(( QgsExpression::BinaryOperator ) op, opLeft, opRight );
01594 }
01595 
01596 
01597 QgsExpression::NodeFunction* QgsOgcUtils::nodeSpatialOperatorFromOgcFilter( QDomElement& element, QString& errorMessage )
01598 {
01599   // we are exploiting the fact that our function names are the same as the XML tag names
01600   int opIdx = QgsExpression::functionIndex( element.tagName().toLower() );
01601 
01602   QgsExpression::NodeList *gml2Args = new QgsExpression::NodeList();
01603   QDomElement childElem = element.firstChildElement();
01604   QString gml2Str;
01605   while ( !childElem.isNull() && gml2Str.isEmpty() )
01606   {
01607     if ( childElem.tagName() != "PropertyName" )
01608     {
01609       QTextStream gml2Stream( &gml2Str );
01610       childElem.save( gml2Stream, 0 );
01611     }
01612     childElem = childElem.nextSiblingElement();
01613   }
01614   if ( !gml2Str.isEmpty() )
01615   {
01616     gml2Args->append( new QgsExpression::NodeLiteral( QVariant( gml2Str.remove( "\n" ) ) ) );
01617   }
01618   else
01619   {
01620     errorMessage = QString( "No OGC Geometry found" );
01621     return NULL;
01622   }
01623 
01624   QgsExpression::NodeList *opArgs = new QgsExpression::NodeList();
01625   opArgs->append( new QgsExpression::NodeFunction( QgsExpression::functionIndex( "$geometry" ), new QgsExpression::NodeList() ) );
01626   opArgs->append( new QgsExpression::NodeFunction( QgsExpression::functionIndex( "geomFromGML" ), gml2Args ) );
01627 
01628   return new QgsExpression::NodeFunction( opIdx, opArgs );
01629 }
01630 
01631 
01632 QgsExpression::NodeUnaryOperator* QgsOgcUtils::nodeNotFromOgcFilter( QDomElement &element, QString &errorMessage )
01633 {
01634   if ( element.tagName() != "Not" )
01635     return NULL;
01636 
01637   QDomElement operandElem = element.firstChildElement();
01638   QgsExpression::Node* operand = nodeFromOgcFilter( operandElem, errorMessage );
01639   if ( !operand )
01640   {
01641     if ( errorMessage.isEmpty() )
01642       errorMessage = QString( "invalid operand for '%1' unary operator" ).arg( element.tagName() );
01643     return NULL;
01644   }
01645 
01646   return new QgsExpression::NodeUnaryOperator( QgsExpression::uoNot, operand );
01647 }
01648 
01649 
01650 QgsExpression::NodeFunction* QgsOgcUtils::nodeFunctionFromOgcFilter( QDomElement &element, QString &errorMessage )
01651 {
01652   if ( element.isNull() || element.tagName() != "Function" )
01653   {
01654     errorMessage = QString( "ogc:Function expected, got %1" ).arg( element.tagName() );
01655     return NULL;
01656   }
01657 
01658   for ( int i = 0; i < QgsExpression::Functions().size(); i++ )
01659   {
01660     QgsExpression::Function* funcDef = QgsExpression::Functions()[i];
01661 
01662     if ( element.attribute( "name" ) != funcDef->name() )
01663       continue;
01664 
01665     QgsExpression::NodeList *args = new QgsExpression::NodeList();
01666 
01667     QDomElement operandElem = element.firstChildElement();
01668     while ( !operandElem.isNull() )
01669     {
01670       QgsExpression::Node* op = nodeFromOgcFilter( operandElem, errorMessage );
01671       if ( !op )
01672       {
01673         delete args;
01674         return NULL;
01675       }
01676       args->append( op );
01677 
01678       operandElem = operandElem.nextSiblingElement();
01679     }
01680 
01681     return new QgsExpression::NodeFunction( i, args );
01682   }
01683 
01684   return NULL;
01685 }
01686 
01687 
01688 
01689 QgsExpression::Node* QgsOgcUtils::nodeLiteralFromOgcFilter( QDomElement &element, QString &errorMessage )
01690 {
01691   if ( element.isNull() || element.tagName() != "Literal" )
01692   {
01693     errorMessage = QString( "ogc:Literal expected, got %1" ).arg( element.tagName() );
01694     return NULL;
01695   }
01696 
01697   QgsExpression::Node *root = 0;
01698 
01699   // the literal content can have more children (e.g. CDATA section, text, ...)
01700   QDomNode childNode = element.firstChild();
01701   while ( !childNode.isNull() )
01702   {
01703     QgsExpression::Node* operand = 0;
01704 
01705     if ( childNode.nodeType() == QDomNode::ElementNode )
01706     {
01707       // found a element node (e.g. PropertyName), convert it
01708       QDomElement operandElem = childNode.toElement();
01709       operand = nodeFromOgcFilter( operandElem, errorMessage );
01710       if ( !operand )
01711       {
01712         if ( root )
01713           delete root;
01714 
01715         errorMessage = QString( "'%1' is an invalid or not supported content for ogc:Literal" ).arg( operandElem.tagName() );
01716         return NULL;
01717       }
01718     }
01719     else
01720     {
01721       // probably a text/CDATA node
01722       QVariant value = childNode.nodeValue();
01723 
01724       // try to convert the node content to number if possible,
01725       // otherwise let's use it as string
01726       bool ok;
01727       double d = value.toDouble( &ok );
01728       if ( ok )
01729         value = d;
01730 
01731       operand = new QgsExpression::NodeLiteral( value );
01732       if ( !operand )
01733         continue;
01734     }
01735 
01736     // use the concat operator to merge the ogc:Literal children
01737     if ( !root )
01738     {
01739       root = operand;
01740     }
01741     else
01742     {
01743       root = new QgsExpression::NodeBinaryOperator( QgsExpression::boConcat, root, operand );
01744     }
01745 
01746     childNode = childNode.nextSibling();
01747   }
01748 
01749   if ( root )
01750     return root;
01751 
01752   return NULL;
01753 }
01754 
01755 
01756 QgsExpression::NodeColumnRef* QgsOgcUtils::nodeColumnRefFromOgcFilter( QDomElement &element, QString &errorMessage )
01757 {
01758   if ( element.isNull() || element.tagName() != "PropertyName" )
01759   {
01760     errorMessage = QString( "ogc:PropertyName expected, got %1" ).arg( element.tagName() );
01761     return NULL;
01762   }
01763 
01764   return new QgsExpression::NodeColumnRef( element.firstChild().nodeValue() );
01765 }
01766 
01767 
01768 QgsExpression::Node* QgsOgcUtils::nodeIsBetweenFromOgcFilter( QDomElement& element, QString& errorMessage )
01769 {
01770   // <ogc:PropertyIsBetween> encode a Range check
01771   QgsExpression::Node *operand = 0, *lowerBound = 0;
01772   QgsExpression::Node *operand2 = 0, *upperBound = 0;
01773 
01774   QDomElement operandElem = element.firstChildElement();
01775   while ( !operandElem.isNull() )
01776   {
01777     if ( operandElem.tagName() == "LowerBoundary" )
01778     {
01779       QDomElement lowerBoundElem = operandElem.firstChildElement();
01780       lowerBound = nodeFromOgcFilter( lowerBoundElem, errorMessage );
01781     }
01782     else if ( operandElem.tagName() ==  "UpperBoundary" )
01783     {
01784       QDomElement upperBoundElem = operandElem.firstChildElement();
01785       upperBound = nodeFromOgcFilter( upperBoundElem, errorMessage );
01786     }
01787     else
01788     {
01789       // <ogc:expression>
01790       // both operand and operand2 contain the same expression,
01791       // they are respectively compared to lower bound and upper bound
01792       operand = nodeFromOgcFilter( operandElem, errorMessage );
01793       operand2 = nodeFromOgcFilter( operandElem, errorMessage );
01794     }
01795 
01796     if ( operand && lowerBound && operand2 && upperBound )
01797       break;
01798 
01799     operandElem = operandElem.nextSiblingElement();
01800   }
01801 
01802   if ( !operand || !lowerBound || !operand2 || !upperBound )
01803   {
01804     if ( operand )
01805       delete operand;
01806 
01807     if ( lowerBound )
01808       delete lowerBound;
01809 
01810     if ( upperBound )
01811       delete upperBound;
01812 
01813     errorMessage = "missing some required sub-elements in ogc:PropertyIsBetween";
01814     return NULL;
01815   }
01816 
01817   QgsExpression::Node *geOperator = new QgsExpression::NodeBinaryOperator( QgsExpression::boGE, operand, lowerBound );
01818   QgsExpression::Node *leOperator = new QgsExpression::NodeBinaryOperator( QgsExpression::boLE, operand2, upperBound );
01819   return new QgsExpression::NodeBinaryOperator( QgsExpression::boAnd, geOperator, leOperator );
01820 }
01821 
01822 
01823 
01824 QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodePropertyIsNullFromOgcFilter( QDomElement& element, QString& errorMessage )
01825 {
01826   // convert ogc:PropertyIsNull to IS operator with NULL right operand
01827   if ( element.tagName() != "PropertyIsNull" )
01828   {
01829     return NULL;
01830   }
01831 
01832   QDomElement operandElem = element.firstChildElement();
01833   QgsExpression::Node* opLeft = nodeFromOgcFilter( operandElem, errorMessage );
01834   if ( !opLeft )
01835     return NULL;
01836 
01837   QgsExpression::Node* opRight = new QgsExpression::NodeLiteral( QVariant() );
01838   return new QgsExpression::NodeBinaryOperator( QgsExpression::boIs, opLeft, opRight );
01839 }
01840 
01841 
01843 
01844 
01845 
01846 
01847 QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression& exp, QDomDocument& doc, QString* errorMessage )
01848 {
01849   if ( !exp.rootNode() )
01850     return QDomElement();
01851 
01852   QString localErrorMessage; // temporary that will be thrown away unused
01853   QString& refErrorMessage = ( errorMessage ? *errorMessage : localErrorMessage );
01854   refErrorMessage.clear();
01855 
01856   QDomElement exprRootElem = expressionNodeToOgcFilter( exp.rootNode(), doc, refErrorMessage );
01857   if ( exprRootElem.isNull() )
01858     return QDomElement();
01859 
01860   QDomElement filterElem = doc.createElement( "ogc:Filter" );
01861   filterElem.appendChild( exprRootElem );
01862   return filterElem;
01863 }
01864 
01865 
01866 QDomElement QgsOgcUtils::expressionNodeToOgcFilter( const QgsExpression::Node* node, QDomDocument& doc, QString& errorMessage )
01867 {
01868   switch ( node->nodeType() )
01869   {
01870     case QgsExpression::ntUnaryOperator:
01871       return expressionUnaryOperatorToOgcFilter( static_cast<const QgsExpression::NodeUnaryOperator*>( node ), doc, errorMessage );
01872     case QgsExpression::ntBinaryOperator:
01873       return expressionBinaryOperatorToOgcFilter( static_cast<const QgsExpression::NodeBinaryOperator*>( node ), doc, errorMessage );
01874     case QgsExpression::ntInOperator:
01875       return expressionInOperatorToOgcFilter( static_cast<const QgsExpression::NodeInOperator*>( node ), doc, errorMessage );
01876     case QgsExpression::ntFunction:
01877       return expressionFunctionToOgcFilter( static_cast<const QgsExpression::NodeFunction*>( node ), doc, errorMessage );
01878     case QgsExpression::ntLiteral:
01879       return expressionLiteralToOgcFilter( static_cast<const QgsExpression::NodeLiteral*>( node ), doc, errorMessage );
01880     case QgsExpression::ntColumnRef:
01881       return expressionColumnRefToOgcFilter( static_cast<const QgsExpression::NodeColumnRef*>( node ), doc, errorMessage );
01882 
01883     default:
01884       errorMessage = QString( "Node type not supported: %1" ).arg( node->nodeType() );
01885       return QDomElement();
01886   }
01887 }
01888 
01889 
01890 QDomElement QgsOgcUtils::expressionUnaryOperatorToOgcFilter( const QgsExpression::NodeUnaryOperator* node, QDomDocument& doc, QString& errorMessage )
01891 {
01892   QDomElement uoElem;
01893   switch ( node->op() )
01894   {
01895     case QgsExpression::uoMinus:
01896       uoElem = doc.createElement( "ogc:Literal" );
01897       uoElem.appendChild( doc.createTextNode( "-" ) );
01898       break;
01899     case QgsExpression::uoNot:
01900       uoElem = doc.createElement( "ogc:Not" );
01901       break;
01902 
01903     default:
01904       errorMessage = QString( "Unary operator %1 not implemented yet" ).arg( QgsExpression::UnaryOperatorText[node->op()] );
01905       return QDomElement();
01906   }
01907 
01908   QDomElement operandElem = expressionNodeToOgcFilter( node->operand(), doc, errorMessage );
01909   if ( !errorMessage.isEmpty() )
01910     return QDomElement();
01911 
01912   uoElem.appendChild( operandElem );
01913   return uoElem;
01914 }
01915 
01916 
01917 QDomElement QgsOgcUtils::expressionBinaryOperatorToOgcFilter( const QgsExpression::NodeBinaryOperator* node, QDomDocument& doc, QString& errorMessage )
01918 {
01919   QDomElement leftElem = expressionNodeToOgcFilter( node->opLeft(), doc, errorMessage );
01920   if ( !errorMessage.isEmpty() )
01921     return QDomElement();
01922 
01923   QgsExpression::BinaryOperator op = node->op();
01924 
01925   // before right operator is parsed: to allow NULL handling
01926   if ( op == QgsExpression::boIs || op == QgsExpression::boIsNot )
01927   {
01928     if ( node->opRight()->nodeType() == QgsExpression::ntLiteral )
01929     {
01930       const QgsExpression::NodeLiteral* rightLit = static_cast<const QgsExpression::NodeLiteral*>( node->opRight() );
01931       if ( rightLit->value().isNull() )
01932       {
01933 
01934         QDomElement elem = doc.createElement( "ogc:PropertyIsNull" );
01935         elem.appendChild( leftElem );
01936 
01937         if ( op == QgsExpression::boIsNot )
01938         {
01939           QDomElement notElem = doc.createElement( "ogc:Not" );
01940           notElem.appendChild( elem );
01941           return notElem;
01942         }
01943 
01944         return elem;
01945       }
01946 
01947       // continue with equal / not equal operator once the null case is handled
01948       op = ( op == QgsExpression::boIs ? QgsExpression::boEQ : QgsExpression::boNE );
01949     }
01950 
01951   }
01952 
01953   QDomElement rightElem = expressionNodeToOgcFilter( node->opRight(), doc, errorMessage );
01954   if ( !errorMessage.isEmpty() )
01955     return QDomElement();
01956 
01957 
01958   QString opText = binaryOperatorToTagName( op );
01959   if ( opText.isEmpty() )
01960   {
01961     // not implemented binary operators
01962     // TODO: regex, % (mod), ^ (pow) are not supported yet
01963     errorMessage = QString( "Binary operator %1 not implemented yet" ).arg( QgsExpression::BinaryOperatorText[op] );
01964     return QDomElement();
01965   }
01966 
01967   QDomElement boElem = doc.createElement( "ogc:" + opText );
01968 
01969   if ( op == QgsExpression::boLike || op == QgsExpression::boILike )
01970   {
01971     if ( op == QgsExpression::boILike )
01972       boElem.setAttribute( "matchCase", "false" );
01973 
01974     // setup wildcards to <ogc:PropertyIsLike>
01975     boElem.setAttribute( "wildCard", "%" );
01976     boElem.setAttribute( "singleChar", "?" );
01977     boElem.setAttribute( "escapeChar", "!" );
01978   }
01979 
01980   boElem.appendChild( leftElem );
01981   boElem.appendChild( rightElem );
01982   return boElem;
01983 }
01984 
01985 
01986 QDomElement QgsOgcUtils::expressionLiteralToOgcFilter( const QgsExpression::NodeLiteral* node, QDomDocument& doc, QString& errorMessage )
01987 {
01988   QString value;
01989   switch ( node->value().type() )
01990   {
01991     case QVariant::Int:
01992       value = QString::number( node->value().toInt() );
01993       break;
01994     case QVariant::Double:
01995       value = QString::number( node->value().toDouble() );
01996       break;
01997     case QVariant::String:
01998       value = node->value().toString();
01999       break;
02000 
02001     default:
02002       errorMessage = QString( "Literal type not supported: %1" ).arg( node->value().type() );
02003       return QDomElement();
02004   }
02005 
02006   QDomElement litElem = doc.createElement( "ogc:Literal" );
02007   litElem.appendChild( doc.createTextNode( value ) );
02008   return litElem;
02009 }
02010 
02011 
02012 QDomElement QgsOgcUtils::expressionColumnRefToOgcFilter( const QgsExpression::NodeColumnRef* node, QDomDocument& doc, QString& /*errorMessage*/ )
02013 {
02014   QDomElement propElem = doc.createElement( "ogc:PropertyName" );
02015   propElem.appendChild( doc.createTextNode( node->name() ) );
02016   return propElem;
02017 }
02018 
02019 
02020 
02021 QDomElement QgsOgcUtils::expressionInOperatorToOgcFilter( const QgsExpression::NodeInOperator* node, QDomDocument& doc, QString& errorMessage )
02022 {
02023   if ( node->list()->list().size() == 1 )
02024     return expressionNodeToOgcFilter( node->list()->list()[0], doc, errorMessage );
02025 
02026   QDomElement orElem = doc.createElement( "ogc:Or" );
02027   QDomElement leftNode = expressionNodeToOgcFilter( node->node(), doc, errorMessage );
02028 
02029   foreach ( QgsExpression::Node* n, node->list()->list() )
02030   {
02031     QDomElement listNode = expressionNodeToOgcFilter( n, doc, errorMessage );
02032     if ( !errorMessage.isEmpty() )
02033       return QDomElement();
02034 
02035     QDomElement eqElem = doc.createElement( "ogc:PropertyIsEqualTo" );
02036     eqElem.appendChild( leftNode.cloneNode() );
02037     eqElem.appendChild( listNode );
02038 
02039     orElem.appendChild( eqElem );
02040   }
02041   return orElem;
02042 }
02043 
02044 static QMap<QString, QString> binarySpatialOpsMap()
02045 {
02046   static QMap<QString, QString> binSpatialOps;
02047   if ( binSpatialOps.isEmpty() )
02048   {
02049     binSpatialOps.insert( "disjoint", "Disjoint" );
02050     binSpatialOps.insert( "intersects", "Intersects" );
02051     binSpatialOps.insert( "touches", "Touches" );
02052     binSpatialOps.insert( "crosses", "Crosses" );
02053     binSpatialOps.insert( "contains", "Contains" );
02054     binSpatialOps.insert( "overlaps", "Overlaps" );
02055     binSpatialOps.insert( "within", "Within" );
02056   }
02057   return binSpatialOps;
02058 }
02059 
02060 static bool isBinarySpatialOperator( const QString& fnName )
02061 {
02062   return binarySpatialOpsMap().contains( fnName );
02063 }
02064 
02065 static QString tagNameForSpatialOperator( const QString& fnName )
02066 {
02067   return binarySpatialOpsMap().value( fnName );
02068 }
02069 
02070 static bool isGeometryColumn( const QgsExpression::Node* node )
02071 {
02072   if ( node->nodeType() != QgsExpression::ntFunction )
02073     return false;
02074 
02075   const QgsExpression::NodeFunction* fn = static_cast<const QgsExpression::NodeFunction*>( node );
02076   QgsExpression::Function* fd = QgsExpression::Functions()[fn->fnIndex()];
02077   return fd->name() == "$geometry";
02078 }
02079 
02080 
02081 QDomElement QgsOgcUtils::expressionFunctionToOgcFilter( const QgsExpression::NodeFunction* node, QDomDocument& doc, QString& errorMessage )
02082 {
02083   QgsExpression::Function* fd = QgsExpression::Functions()[node->fnIndex()];
02084 
02085   if ( fd->name() == "bbox" )
02086   {
02087     errorMessage = QString( "<BBOX> is currently not supported." );
02088     return QDomElement();
02089   }
02090 
02091   if ( isBinarySpatialOperator( fd->name() ) )
02092   {
02093     QList<QgsExpression::Node*> argNodes = node->args()->list();
02094     Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
02095 
02096     QgsExpression::Node* otherNode = 0;
02097     if ( isGeometryColumn( argNodes[0] ) )
02098       otherNode = argNodes[1];
02099     else if ( isGeometryColumn( argNodes[1] ) )
02100       otherNode = argNodes[0];
02101     else
02102     {
02103       errorMessage = QString( "Unable to translate spatial operator: at least one must refer to geometry." );
02104       return QDomElement();
02105     }
02106 
02107     QDomElement otherGeomElem;
02108 
02109     // the other node must be a geometry constructor
02110     if ( otherNode->nodeType() != QgsExpression::ntFunction )
02111     {
02112       errorMessage = "spatial operator: the other operator must be a geometry constructor function";
02113       return QDomElement();
02114     }
02115 
02116     const QgsExpression::NodeFunction* otherFn = static_cast<const QgsExpression::NodeFunction*>( otherNode );
02117     QgsExpression::Function* otherFnDef = QgsExpression::Functions()[otherFn->fnIndex()];
02118     if ( otherFnDef->name() == "geomFromWKT" )
02119     {
02120       QgsExpression::Node* firstFnArg = otherFn->args()->list()[0];
02121       if ( firstFnArg->nodeType() != QgsExpression::ntLiteral )
02122       {
02123         errorMessage = "geomFromWKT: argument must be string literal";
02124         return QDomElement();
02125       }
02126       QString wkt = static_cast<const QgsExpression::NodeLiteral*>( firstFnArg )->value().toString();
02127       QgsGeometry* geom = QgsGeometry::fromWkt( wkt );
02128       otherGeomElem = QgsOgcUtils::geometryToGML( geom, doc );
02129       delete geom;
02130     }
02131     else if ( otherFnDef->name() == "geomFromGML" )
02132     {
02133       QgsExpression::Node* firstFnArg = otherFn->args()->list()[0];
02134       if ( firstFnArg->nodeType() != QgsExpression::ntLiteral )
02135       {
02136         errorMessage = "geomFromGML: argument must be string literal";
02137         return QDomElement();
02138       }
02139 
02140       QDomDocument geomDoc;
02141       QString gml = static_cast<const QgsExpression::NodeLiteral*>( firstFnArg )->value().toString();
02142       if ( !geomDoc.setContent( gml, true ) )
02143       {
02144         errorMessage = "geomFromGML: unable to parse XML";
02145         return QDomElement();
02146       }
02147 
02148       QDomNode geomNode = doc.importNode( geomDoc.documentElement(), true );
02149       otherGeomElem = geomNode.toElement();
02150     }
02151     else
02152     {
02153       errorMessage = "spatial operator: unknown geometry constructor function";
02154       return QDomElement();
02155     }
02156 
02157     QDomElement funcElem = doc.createElement( "ogc:" + tagNameForSpatialOperator( fd->name() ) );
02158     QDomElement geomProperty = doc.createElement( "ogc:PropertyName" );
02159     geomProperty.appendChild( doc.createTextNode( "geometry" ) );
02160     funcElem.appendChild( geomProperty );
02161     funcElem.appendChild( otherGeomElem );
02162     return funcElem;
02163   }
02164 
02165   if ( fd->params() == 0 )
02166   {
02167     errorMessage = QString( "Special columns / constants are not supported." );
02168     return QDomElement();
02169   }
02170 
02171   // this is somehow wrong - we are just hoping that the other side supports the same functions as we do...
02172   QDomElement funcElem = doc.createElement( "ogc:Function" );
02173   funcElem.setAttribute( "name", fd->name() );
02174   foreach ( QgsExpression::Node* n, node->args()->list() )
02175   {
02176     QDomElement childElem = expressionNodeToOgcFilter( n, doc, errorMessage );
02177     if ( !errorMessage.isEmpty() )
02178       return QDomElement();
02179 
02180     funcElem.appendChild( childElem );
02181   }
02182 
02183   return funcElem;
02184 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines