LCOV - code coverage report
Current view: top level - core - qgsogcutils.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 2179 0.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :     qgsogcutils.cpp
       3                 :            :     ---------------------
       4                 :            :     begin                : March 2013
       5                 :            :     copyright            : (C) 2013 by Martin Dobias
       6                 :            :     email                : wonder dot sk at gmail dot com
       7                 :            :  ***************************************************************************
       8                 :            :  *                                                                         *
       9                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      10                 :            :  *   it under the terms of the GNU General Public License as published by  *
      11                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      12                 :            :  *   (at your option) any later version.                                   *
      13                 :            :  *                                                                         *
      14                 :            :  ***************************************************************************/
      15                 :            : #include "qgsogcutils.h"
      16                 :            : 
      17                 :            : #include "qgsexpression.h"
      18                 :            : #include "qgsexpressionnodeimpl.h"
      19                 :            : #include "qgsexpressionfunction.h"
      20                 :            : #include "qgsexpression_p.h"
      21                 :            : #include "qgsgeometry.h"
      22                 :            : #include "qgswkbptr.h"
      23                 :            : #include "qgscoordinatereferencesystem.h"
      24                 :            : #include "qgsrectangle.h"
      25                 :            : #include "qgsvectorlayer.h"
      26                 :            : #include "qgsexpressioncontextutils.h"
      27                 :            : #include "qgslogger.h"
      28                 :            : 
      29                 :            : #include <QColor>
      30                 :            : #include <QStringList>
      31                 :            : #include <QTextStream>
      32                 :            : #include <QObject>
      33                 :            : #include <QRegularExpression>
      34                 :            : 
      35                 :            : #ifndef Q_OS_WIN
      36                 :            : #include <netinet/in.h>
      37                 :            : #else
      38                 :            : #include <winsock.h>
      39                 :            : #endif
      40                 :            : 
      41                 :            : 
      42                 :            : #define GML_NAMESPACE QStringLiteral( "http://www.opengis.net/gml" )
      43                 :            : #define GML32_NAMESPACE QStringLiteral( "http://www.opengis.net/gml/3.2" )
      44                 :            : #define OGC_NAMESPACE QStringLiteral( "http://www.opengis.net/ogc" )
      45                 :            : #define FES_NAMESPACE QStringLiteral( "http://www.opengis.net/fes/2.0" )
      46                 :            : 
      47                 :          0 : QgsOgcUtilsExprToFilter::QgsOgcUtilsExprToFilter( QDomDocument &doc,
      48                 :            :     QgsOgcUtils::GMLVersion gmlVersion,
      49                 :            :     QgsOgcUtils::FilterVersion filterVersion,
      50                 :            :     const QString &geometryName,
      51                 :            :     const QString &srsName,
      52                 :            :     bool honourAxisOrientation,
      53                 :            :     bool invertAxisOrientation )
      54                 :          0 :   : mDoc( doc )
      55                 :          0 :   , mGMLUsed( false )
      56                 :          0 :   , mGMLVersion( gmlVersion )
      57                 :          0 :   , mFilterVersion( filterVersion )
      58                 :          0 :   , mGeometryName( geometryName )
      59                 :          0 :   , mSrsName( srsName )
      60                 :          0 :   , mInvertAxisOrientation( invertAxisOrientation )
      61                 :          0 :   , mFilterPrefix( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "fes" : "ogc" )
      62                 :          0 :   , mPropertyName( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "ValueReference" : "PropertyName" )
      63                 :          0 :   , mGeomId( 1 )
      64                 :            : {
      65                 :          0 :   QgsCoordinateReferenceSystem crs;
      66                 :          0 :   if ( !mSrsName.isEmpty() )
      67                 :          0 :     crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( mSrsName );
      68                 :          0 :   if ( crs.isValid() )
      69                 :            :   {
      70                 :          0 :     if ( honourAxisOrientation && crs.hasAxisInverted() )
      71                 :            :     {
      72                 :          0 :       mInvertAxisOrientation = !mInvertAxisOrientation;
      73                 :          0 :     }
      74                 :          0 :   }
      75                 :          0 : }
      76                 :            : 
      77                 :          0 : QgsGeometry QgsOgcUtils::geometryFromGML( const QDomNode &geometryNode, const Context &context )
      78                 :            : {
      79                 :          0 :   QDomElement geometryTypeElement = geometryNode.toElement();
      80                 :          0 :   QString geomType = geometryTypeElement.tagName();
      81                 :          0 :   QgsGeometry geometry;
      82                 :            : 
      83                 :          0 :   if ( !( geomType == QLatin1String( "Point" ) || geomType == QLatin1String( "LineString" ) || geomType == QLatin1String( "Polygon" ) ||
      84                 :          0 :           geomType == QLatin1String( "MultiPoint" ) || geomType == QLatin1String( "MultiLineString" ) || geomType == QLatin1String( "MultiPolygon" ) ||
      85                 :          0 :           geomType == QLatin1String( "Box" ) || geomType == QLatin1String( "Envelope" ) ) )
      86                 :            :   {
      87                 :          0 :     QDomNode geometryChild = geometryNode.firstChild();
      88                 :          0 :     if ( geometryChild.isNull() )
      89                 :            :     {
      90                 :          0 :       return geometry;
      91                 :            :     }
      92                 :          0 :     geometryTypeElement = geometryChild.toElement();
      93                 :          0 :     geomType = geometryTypeElement.tagName();
      94                 :          0 :   }
      95                 :            : 
      96                 :          0 :   if ( !( geomType == QLatin1String( "Point" ) || geomType == QLatin1String( "LineString" ) || geomType == QLatin1String( "Polygon" ) ||
      97                 :          0 :           geomType == QLatin1String( "MultiPoint" ) || geomType == QLatin1String( "MultiLineString" ) || geomType == QLatin1String( "MultiPolygon" ) ||
      98                 :          0 :           geomType == QLatin1String( "Box" ) || geomType == QLatin1String( "Envelope" ) ) )
      99                 :          0 :     return QgsGeometry();
     100                 :            : 
     101                 :          0 :   if ( geomType == QLatin1String( "Point" ) )
     102                 :            :   {
     103                 :          0 :     geometry = geometryFromGMLPoint( geometryTypeElement );
     104                 :          0 :   }
     105                 :          0 :   else if ( geomType == QLatin1String( "LineString" ) )
     106                 :            :   {
     107                 :          0 :     geometry = geometryFromGMLLineString( geometryTypeElement );
     108                 :          0 :   }
     109                 :          0 :   else if ( geomType == QLatin1String( "Polygon" ) )
     110                 :            :   {
     111                 :          0 :     geometry = geometryFromGMLPolygon( geometryTypeElement );
     112                 :          0 :   }
     113                 :          0 :   else if ( geomType == QLatin1String( "MultiPoint" ) )
     114                 :            :   {
     115                 :          0 :     geometry = geometryFromGMLMultiPoint( geometryTypeElement );
     116                 :          0 :   }
     117                 :          0 :   else if ( geomType == QLatin1String( "MultiLineString" ) )
     118                 :            :   {
     119                 :          0 :     geometry = geometryFromGMLMultiLineString( geometryTypeElement );
     120                 :          0 :   }
     121                 :          0 :   else if ( geomType == QLatin1String( "MultiPolygon" ) )
     122                 :            :   {
     123                 :          0 :     geometry = geometryFromGMLMultiPolygon( geometryTypeElement );
     124                 :          0 :   }
     125                 :          0 :   else if ( geomType == QLatin1String( "Box" ) )
     126                 :            :   {
     127                 :          0 :     geometry = QgsGeometry::fromRect( rectangleFromGMLBox( geometryTypeElement ) );
     128                 :          0 :   }
     129                 :          0 :   else if ( geomType == QLatin1String( "Envelope" ) )
     130                 :            :   {
     131                 :          0 :     geometry = QgsGeometry::fromRect( rectangleFromGMLEnvelope( geometryTypeElement ) );
     132                 :          0 :   }
     133                 :            :   else //unknown type
     134                 :            :   {
     135                 :          0 :     return geometry;
     136                 :            :   }
     137                 :            : 
     138                 :            :   // Handle srsName if context has information about the layer and the transformation context
     139                 :          0 :   if ( context.layer )
     140                 :            :   {
     141                 :          0 :     QgsCoordinateReferenceSystem geomSrs;
     142                 :            : 
     143                 :          0 :     if ( geometryTypeElement.hasAttribute( QStringLiteral( "srsName" ) ) )
     144                 :            :     {
     145                 :          0 :       QString srsName { geometryTypeElement.attribute( QStringLiteral( "srsName" ) ) };
     146                 :            : 
     147                 :            :       // The logic here follows WFS GeoServer conventions from https://docs.geoserver.org/latest/en/user/services/wfs/axis_order.html
     148                 :          0 :       const bool ignoreAxisOrientation { srsName.startsWith( QLatin1String( "http://www.opengis.net/gml/srs/" ) ) || srsName.startsWith( QLatin1String( "EPSG:" ) ) };
     149                 :            : 
     150                 :            :       // GDAL does not recognise http://www.opengis.net/gml/srs/epsg.xml#4326 but it does
     151                 :            :       // http://www.opengis.net/def/crs/EPSG/0/4326 so, let's try that
     152                 :          0 :       if ( srsName.startsWith( QLatin1String( "http://www.opengis.net/gml/srs/" ) ) )
     153                 :            :       {
     154                 :          0 :         const auto parts { srsName.split( QRegularExpression( QStringLiteral( R"raw(/|#|\.)raw" ) ) ) };
     155                 :          0 :         if ( parts.length() == 10 )
     156                 :            :         {
     157                 :          0 :           srsName = QStringLiteral( "http://www.opengis.net/def/crs/%1/0/%2" ).arg( parts[ 7 ].toUpper(), parts[ 9 ] );
     158                 :          0 :         }
     159                 :          0 :       }
     160                 :          0 :       geomSrs.createFromUserInput( srsName );
     161                 :          0 :       if ( geomSrs.isValid() && geomSrs != context.layer->crs() )
     162                 :            :       {
     163                 :          0 :         if ( geomSrs.hasAxisInverted() && ! ignoreAxisOrientation )
     164                 :            :         {
     165                 :          0 :           geometry.get()->swapXy();
     166                 :          0 :         }
     167                 :          0 :         const QgsCoordinateTransform transformer { geomSrs, context.layer->crs(), context.transformContext };
     168                 :            :         try
     169                 :            :         {
     170                 :          0 :           const QgsGeometry::OperationResult result = geometry.transform( transformer );
     171                 :          0 :           if ( result != QgsGeometry::OperationResult::Success )
     172                 :            :           {
     173                 :          0 :             QgsDebugMsgLevel( QStringLiteral( "Error transforming geometry: %1" ).arg( result ), 2 );
     174                 :          0 :           }
     175                 :          0 :         }
     176                 :            :         catch ( QgsCsException & )
     177                 :            :         {
     178                 :          0 :           QgsDebugMsgLevel( QStringLiteral( "CS error transforming geometry" ), 2 );
     179                 :          0 :         }
     180                 :          0 :       }
     181                 :          0 :     }
     182                 :          0 :   }
     183                 :            : 
     184                 :          0 :   return geometry;
     185                 :          0 : }
     186                 :            : 
     187                 :          0 : QgsGeometry QgsOgcUtils::geometryFromGML( const QString &xmlString, const Context &context )
     188                 :            : {
     189                 :            :   // wrap the string into a root tag to have "gml" namespace (and also as a default namespace)
     190                 :          0 :   QString xml = QStringLiteral( "<tmp xmlns=\"%1\" xmlns:gml=\"%1\">%2</tmp>" ).arg( GML_NAMESPACE, xmlString );
     191                 :          0 :   QDomDocument doc;
     192                 :          0 :   if ( !doc.setContent( xml, true ) )
     193                 :          0 :     return QgsGeometry();
     194                 :            : 
     195                 :          0 :   return geometryFromGML( doc.documentElement().firstChildElement(), context );
     196                 :          0 : }
     197                 :            : 
     198                 :            : 
     199                 :          0 : QgsGeometry QgsOgcUtils::geometryFromGMLPoint( const QDomElement &geometryElement )
     200                 :            : {
     201                 :          0 :   QgsPolylineXY pointCoordinate;
     202                 :            : 
     203                 :          0 :   QDomNodeList coordList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
     204                 :          0 :   if ( !coordList.isEmpty() )
     205                 :            :   {
     206                 :          0 :     QDomElement coordElement = coordList.at( 0 ).toElement();
     207                 :          0 :     if ( readGMLCoordinates( pointCoordinate, coordElement ) != 0 )
     208                 :            :     {
     209                 :          0 :       return QgsGeometry();
     210                 :            :     }
     211                 :          0 :   }
     212                 :            :   else
     213                 :            :   {
     214                 :          0 :     QDomNodeList posList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "pos" ) );
     215                 :          0 :     if ( posList.size() < 1 )
     216                 :            :     {
     217                 :          0 :       return QgsGeometry();
     218                 :            :     }
     219                 :          0 :     QDomElement posElement = posList.at( 0 ).toElement();
     220                 :          0 :     if ( readGMLPositions( pointCoordinate, posElement ) != 0 )
     221                 :            :     {
     222                 :          0 :       return QgsGeometry();
     223                 :            :     }
     224                 :          0 :   }
     225                 :            : 
     226                 :          0 :   if ( pointCoordinate.empty() )
     227                 :            :   {
     228                 :          0 :     return QgsGeometry();
     229                 :            :   }
     230                 :            : 
     231                 :          0 :   QgsPolylineXY::const_iterator point_it = pointCoordinate.constBegin();
     232                 :          0 :   char e = htonl( 1 ) != 1;
     233                 :          0 :   double x = point_it->x();
     234                 :          0 :   double y = point_it->y();
     235                 :          0 :   int size = 1 + sizeof( int ) + 2 * sizeof( double );
     236                 :            : 
     237                 :          0 :   QgsWkbTypes::Type type = QgsWkbTypes::Point;
     238                 :          0 :   unsigned char *wkb = new unsigned char[size];
     239                 :            : 
     240                 :          0 :   int wkbPosition = 0; //current offset from wkb beginning (in bytes)
     241                 :          0 :   memcpy( &( wkb )[wkbPosition], &e, 1 );
     242                 :          0 :   wkbPosition += 1;
     243                 :          0 :   memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
     244                 :          0 :   wkbPosition += sizeof( int );
     245                 :          0 :   memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
     246                 :          0 :   wkbPosition += sizeof( double );
     247                 :          0 :   memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
     248                 :            : 
     249                 :          0 :   QgsGeometry g;
     250                 :          0 :   g.fromWkb( wkb, size );
     251                 :          0 :   return g;
     252                 :          0 : }
     253                 :            : 
     254                 :          0 : QgsGeometry QgsOgcUtils::geometryFromGMLLineString( const QDomElement &geometryElement )
     255                 :            : {
     256                 :          0 :   QgsPolylineXY lineCoordinates;
     257                 :            : 
     258                 :          0 :   QDomNodeList coordList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
     259                 :          0 :   if ( !coordList.isEmpty() )
     260                 :            :   {
     261                 :          0 :     QDomElement coordElement = coordList.at( 0 ).toElement();
     262                 :          0 :     if ( readGMLCoordinates( lineCoordinates, coordElement ) != 0 )
     263                 :            :     {
     264                 :          0 :       return QgsGeometry();
     265                 :            :     }
     266                 :          0 :   }
     267                 :            :   else
     268                 :            :   {
     269                 :          0 :     QDomNodeList posList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
     270                 :          0 :     if ( posList.size() < 1 )
     271                 :            :     {
     272                 :          0 :       return QgsGeometry();
     273                 :            :     }
     274                 :          0 :     QDomElement posElement = posList.at( 0 ).toElement();
     275                 :          0 :     if ( readGMLPositions( lineCoordinates, posElement ) != 0 )
     276                 :            :     {
     277                 :          0 :       return QgsGeometry();
     278                 :            :     }
     279                 :          0 :   }
     280                 :            : 
     281                 :          0 :   char e = htonl( 1 ) != 1;
     282                 :          0 :   int size = 1 + 2 * sizeof( int ) + lineCoordinates.size() * 2 * sizeof( double );
     283                 :            : 
     284                 :          0 :   QgsWkbTypes::Type type = QgsWkbTypes::LineString;
     285                 :          0 :   unsigned char *wkb = new unsigned char[size];
     286                 :            : 
     287                 :          0 :   int wkbPosition = 0; //current offset from wkb beginning (in bytes)
     288                 :            :   double x, y;
     289                 :          0 :   int nPoints = lineCoordinates.size();
     290                 :            : 
     291                 :            :   //fill the contents into *wkb
     292                 :          0 :   memcpy( &( wkb )[wkbPosition], &e, 1 );
     293                 :          0 :   wkbPosition += 1;
     294                 :          0 :   memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
     295                 :          0 :   wkbPosition += sizeof( int );
     296                 :          0 :   memcpy( &( wkb )[wkbPosition], &nPoints, sizeof( int ) );
     297                 :          0 :   wkbPosition += sizeof( int );
     298                 :            : 
     299                 :            :   QgsPolylineXY::const_iterator iter;
     300                 :          0 :   for ( iter = lineCoordinates.constBegin(); iter != lineCoordinates.constEnd(); ++iter )
     301                 :            :   {
     302                 :          0 :     x = iter->x();
     303                 :          0 :     y = iter->y();
     304                 :          0 :     memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
     305                 :          0 :     wkbPosition += sizeof( double );
     306                 :          0 :     memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
     307                 :          0 :     wkbPosition += sizeof( double );
     308                 :          0 :   }
     309                 :            : 
     310                 :          0 :   QgsGeometry g;
     311                 :          0 :   g.fromWkb( wkb, size );
     312                 :          0 :   return g;
     313                 :          0 : }
     314                 :            : 
     315                 :          0 : QgsGeometry QgsOgcUtils::geometryFromGMLPolygon( const QDomElement &geometryElement )
     316                 :            : {
     317                 :            :   //read all the coordinates (as QgsPoint) into memory. Each linear ring has an entry in the vector
     318                 :          0 :   QgsMultiPolylineXY ringCoordinates;
     319                 :            : 
     320                 :            :   //read coordinates for outer boundary
     321                 :          0 :   QgsPolylineXY exteriorPointList;
     322                 :          0 :   QDomNodeList outerBoundaryList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "outerBoundaryIs" ) );
     323                 :          0 :   if ( !outerBoundaryList.isEmpty() ) //outer ring is necessary
     324                 :            :   {
     325                 :          0 :     QDomElement coordinatesElement = outerBoundaryList.at( 0 ).firstChild().firstChild().toElement();
     326                 :          0 :     if ( coordinatesElement.isNull() )
     327                 :            :     {
     328                 :          0 :       return QgsGeometry();
     329                 :            :     }
     330                 :          0 :     if ( readGMLCoordinates( exteriorPointList, coordinatesElement ) != 0 )
     331                 :            :     {
     332                 :          0 :       return QgsGeometry();
     333                 :            :     }
     334                 :          0 :     ringCoordinates.push_back( exteriorPointList );
     335                 :            : 
     336                 :            :     //read coordinates for inner boundary
     337                 :          0 :     QDomNodeList innerBoundaryList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "innerBoundaryIs" ) );
     338                 :          0 :     for ( int i = 0; i < innerBoundaryList.size(); ++i )
     339                 :            :     {
     340                 :          0 :       QgsPolylineXY interiorPointList;
     341                 :          0 :       coordinatesElement = innerBoundaryList.at( i ).firstChild().firstChild().toElement();
     342                 :          0 :       if ( coordinatesElement.isNull() )
     343                 :            :       {
     344                 :          0 :         return QgsGeometry();
     345                 :            :       }
     346                 :          0 :       if ( readGMLCoordinates( interiorPointList, coordinatesElement ) != 0 )
     347                 :            :       {
     348                 :          0 :         return QgsGeometry();
     349                 :            :       }
     350                 :          0 :       ringCoordinates.push_back( interiorPointList );
     351                 :          0 :     }
     352                 :          0 :   }
     353                 :            :   else
     354                 :            :   {
     355                 :            :     //read coordinates for exterior
     356                 :          0 :     QDomNodeList exteriorList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "exterior" ) );
     357                 :          0 :     if ( exteriorList.size() < 1 ) //outer ring is necessary
     358                 :            :     {
     359                 :          0 :       return QgsGeometry();
     360                 :            :     }
     361                 :          0 :     QDomElement posElement = exteriorList.at( 0 ).firstChild().firstChild().toElement();
     362                 :          0 :     if ( posElement.isNull() )
     363                 :            :     {
     364                 :          0 :       return QgsGeometry();
     365                 :            :     }
     366                 :          0 :     if ( readGMLPositions( exteriorPointList, posElement ) != 0 )
     367                 :            :     {
     368                 :          0 :       return QgsGeometry();
     369                 :            :     }
     370                 :          0 :     ringCoordinates.push_back( exteriorPointList );
     371                 :            : 
     372                 :            :     //read coordinates for inner boundary
     373                 :          0 :     QDomNodeList interiorList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "interior" ) );
     374                 :          0 :     for ( int i = 0; i < interiorList.size(); ++i )
     375                 :            :     {
     376                 :          0 :       QgsPolylineXY interiorPointList;
     377                 :          0 :       QDomElement posElement = interiorList.at( i ).firstChild().firstChild().toElement();
     378                 :          0 :       if ( posElement.isNull() )
     379                 :            :       {
     380                 :          0 :         return QgsGeometry();
     381                 :            :       }
     382                 :            :       // Note: readGMLPositions returns true on errors and false on success
     383                 :          0 :       if ( readGMLPositions( interiorPointList, posElement ) )
     384                 :            :       {
     385                 :          0 :         return QgsGeometry();
     386                 :            :       }
     387                 :          0 :       ringCoordinates.push_back( interiorPointList );
     388                 :          0 :     }
     389                 :          0 :   }
     390                 :            : 
     391                 :            :   //calculate number of bytes to allocate
     392                 :          0 :   int nrings = ringCoordinates.size();
     393                 :          0 :   if ( nrings < 1 )
     394                 :          0 :     return QgsGeometry();
     395                 :            : 
     396                 :          0 :   int npoints = 0;//total number of points
     397                 :          0 :   for ( QgsMultiPolylineXY::const_iterator it = ringCoordinates.constBegin(); it != ringCoordinates.constEnd(); ++it )
     398                 :            :   {
     399                 :          0 :     npoints += it->size();
     400                 :          0 :   }
     401                 :          0 :   int size = 1 + 2 * sizeof( int ) + nrings * sizeof( int ) + 2 * npoints * sizeof( double );
     402                 :            : 
     403                 :          0 :   QgsWkbTypes::Type type = QgsWkbTypes::Polygon;
     404                 :          0 :   unsigned char *wkb = new unsigned char[size];
     405                 :            : 
     406                 :            :   //char e = QgsApplication::endian();
     407                 :          0 :   char e = htonl( 1 ) != 1;
     408                 :          0 :   int wkbPosition = 0; //current offset from wkb beginning (in bytes)
     409                 :          0 :   int nPointsInRing = 0;
     410                 :            :   double x, y;
     411                 :            : 
     412                 :            :   //fill the contents into *wkb
     413                 :          0 :   memcpy( &( wkb )[wkbPosition], &e, 1 );
     414                 :          0 :   wkbPosition += 1;
     415                 :          0 :   memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
     416                 :          0 :   wkbPosition += sizeof( int );
     417                 :          0 :   memcpy( &( wkb )[wkbPosition], &nrings, sizeof( int ) );
     418                 :          0 :   wkbPosition += sizeof( int );
     419                 :          0 :   for ( QgsMultiPolylineXY::const_iterator it = ringCoordinates.constBegin(); it != ringCoordinates.constEnd(); ++it )
     420                 :            :   {
     421                 :          0 :     nPointsInRing = it->size();
     422                 :          0 :     memcpy( &( wkb )[wkbPosition], &nPointsInRing, sizeof( int ) );
     423                 :          0 :     wkbPosition += sizeof( int );
     424                 :            :     //iterate through the string list converting the strings to x-/y- doubles
     425                 :            :     QgsPolylineXY::const_iterator iter;
     426                 :          0 :     for ( iter = it->begin(); iter != it->end(); ++iter )
     427                 :            :     {
     428                 :          0 :       x = iter->x();
     429                 :          0 :       y = iter->y();
     430                 :            :       //qWarning("currentCoordinate: " + QString::number(x) + " // " + QString::number(y));
     431                 :          0 :       memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
     432                 :          0 :       wkbPosition += sizeof( double );
     433                 :          0 :       memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
     434                 :          0 :       wkbPosition += sizeof( double );
     435                 :          0 :     }
     436                 :          0 :   }
     437                 :            : 
     438                 :          0 :   QgsGeometry g;
     439                 :          0 :   g.fromWkb( wkb, size );
     440                 :          0 :   return g;
     441                 :          0 : }
     442                 :            : 
     443                 :          0 : QgsGeometry QgsOgcUtils::geometryFromGMLMultiPoint( const QDomElement &geometryElement )
     444                 :            : {
     445                 :          0 :   QgsPolylineXY pointList;
     446                 :          0 :   QgsPolylineXY currentPoint;
     447                 :          0 :   QDomNodeList pointMemberList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "pointMember" ) );
     448                 :          0 :   if ( pointMemberList.size() < 1 )
     449                 :            :   {
     450                 :          0 :     return QgsGeometry();
     451                 :            :   }
     452                 :          0 :   QDomNodeList pointNodeList;
     453                 :            :   // coordinates or pos element
     454                 :          0 :   QDomNodeList coordinatesList;
     455                 :          0 :   QDomNodeList posList;
     456                 :          0 :   for ( int i = 0; i < pointMemberList.size(); ++i )
     457                 :            :   {
     458                 :            :     //<Point> element
     459                 :          0 :     pointNodeList = pointMemberList.at( i ).toElement().elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "Point" ) );
     460                 :          0 :     if ( pointNodeList.size() < 1 )
     461                 :            :     {
     462                 :          0 :       continue;
     463                 :            :     }
     464                 :            :     //<coordinates> element
     465                 :          0 :     coordinatesList = pointNodeList.at( 0 ).toElement().elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
     466                 :          0 :     if ( !coordinatesList.isEmpty() )
     467                 :            :     {
     468                 :          0 :       currentPoint.clear();
     469                 :          0 :       if ( readGMLCoordinates( currentPoint, coordinatesList.at( 0 ).toElement() ) != 0 )
     470                 :            :       {
     471                 :          0 :         continue;
     472                 :            :       }
     473                 :          0 :       if ( currentPoint.empty() )
     474                 :            :       {
     475                 :          0 :         continue;
     476                 :            :       }
     477                 :          0 :       pointList.push_back( ( *currentPoint.begin() ) );
     478                 :          0 :       continue;
     479                 :            :     }
     480                 :            :     else
     481                 :            :     {
     482                 :            :       //<pos> element
     483                 :          0 :       posList = pointNodeList.at( 0 ).toElement().elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "pos" ) );
     484                 :          0 :       if ( posList.size() < 1 )
     485                 :            :       {
     486                 :          0 :         continue;
     487                 :            :       }
     488                 :          0 :       currentPoint.clear();
     489                 :          0 :       if ( readGMLPositions( currentPoint, posList.at( 0 ).toElement() ) != 0 )
     490                 :            :       {
     491                 :          0 :         continue;
     492                 :            :       }
     493                 :          0 :       if ( currentPoint.empty() )
     494                 :            :       {
     495                 :          0 :         continue;
     496                 :            :       }
     497                 :          0 :       pointList.push_back( ( *currentPoint.begin() ) );
     498                 :            :     }
     499                 :          0 :   }
     500                 :            : 
     501                 :          0 :   int nPoints = pointList.size(); //number of points
     502                 :          0 :   if ( nPoints < 1 )
     503                 :          0 :     return QgsGeometry();
     504                 :            : 
     505                 :            :   //calculate the required wkb size
     506                 :          0 :   int size = 1 + 2 * sizeof( int ) + pointList.size() * ( 2 * sizeof( double ) + 1 + sizeof( int ) );
     507                 :            : 
     508                 :          0 :   QgsWkbTypes::Type type = QgsWkbTypes::MultiPoint;
     509                 :          0 :   unsigned char *wkb = new unsigned char[size];
     510                 :            : 
     511                 :            :   //fill the wkb content
     512                 :          0 :   char e = htonl( 1 ) != 1;
     513                 :          0 :   int wkbPosition = 0; //current offset from wkb beginning (in bytes)
     514                 :            :   double x, y;
     515                 :          0 :   memcpy( &( wkb )[wkbPosition], &e, 1 );
     516                 :          0 :   wkbPosition += 1;
     517                 :          0 :   memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
     518                 :          0 :   wkbPosition += sizeof( int );
     519                 :          0 :   memcpy( &( wkb )[wkbPosition], &nPoints, sizeof( int ) );
     520                 :          0 :   wkbPosition += sizeof( int );
     521                 :          0 :   type = QgsWkbTypes::Point;
     522                 :          0 :   for ( QgsPolylineXY::const_iterator it = pointList.constBegin(); it != pointList.constEnd(); ++it )
     523                 :            :   {
     524                 :          0 :     memcpy( &( wkb )[wkbPosition], &e, 1 );
     525                 :          0 :     wkbPosition += 1;
     526                 :          0 :     memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
     527                 :          0 :     wkbPosition += sizeof( int );
     528                 :          0 :     x = it->x();
     529                 :          0 :     memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
     530                 :          0 :     wkbPosition += sizeof( double );
     531                 :          0 :     y = it->y();
     532                 :          0 :     memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
     533                 :          0 :     wkbPosition += sizeof( double );
     534                 :          0 :   }
     535                 :            : 
     536                 :          0 :   QgsGeometry g;
     537                 :          0 :   g.fromWkb( wkb, size );
     538                 :          0 :   return g;
     539                 :          0 : }
     540                 :            : 
     541                 :          0 : QgsGeometry QgsOgcUtils::geometryFromGMLMultiLineString( const QDomElement &geometryElement )
     542                 :            : {
     543                 :            :   //geoserver has
     544                 :            :   //<gml:MultiLineString>
     545                 :            :   //<gml:lineStringMember>
     546                 :            :   //<gml:LineString>
     547                 :            : 
     548                 :            :   //mapserver has directly
     549                 :            :   //<gml:MultiLineString
     550                 :            :   //<gml:LineString
     551                 :            : 
     552                 :          0 :   QList< QgsPolylineXY > lineCoordinates; //first list: lines, second list: points of one line
     553                 :          0 :   QDomElement currentLineStringElement;
     554                 :          0 :   QDomNodeList currentCoordList;
     555                 :          0 :   QDomNodeList currentPosList;
     556                 :            : 
     557                 :          0 :   QDomNodeList lineStringMemberList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "lineStringMember" ) );
     558                 :          0 :   if ( !lineStringMemberList.isEmpty() ) //geoserver
     559                 :            :   {
     560                 :          0 :     for ( int i = 0; i < lineStringMemberList.size(); ++i )
     561                 :            :     {
     562                 :          0 :       QDomNodeList lineStringNodeList = lineStringMemberList.at( i ).toElement().elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LineString" ) );
     563                 :          0 :       if ( lineStringNodeList.size() < 1 )
     564                 :            :       {
     565                 :          0 :         return QgsGeometry();
     566                 :            :       }
     567                 :          0 :       currentLineStringElement = lineStringNodeList.at( 0 ).toElement();
     568                 :          0 :       currentCoordList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
     569                 :          0 :       if ( !currentCoordList.isEmpty() )
     570                 :            :       {
     571                 :          0 :         QgsPolylineXY currentPointList;
     572                 :          0 :         if ( readGMLCoordinates( currentPointList, currentCoordList.at( 0 ).toElement() ) != 0 )
     573                 :            :         {
     574                 :          0 :           return QgsGeometry();
     575                 :            :         }
     576                 :          0 :         lineCoordinates.push_back( currentPointList );
     577                 :          0 :       }
     578                 :            :       else
     579                 :            :       {
     580                 :          0 :         currentPosList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
     581                 :          0 :         if ( currentPosList.size() < 1 )
     582                 :            :         {
     583                 :          0 :           return QgsGeometry();
     584                 :            :         }
     585                 :          0 :         QgsPolylineXY currentPointList;
     586                 :          0 :         if ( readGMLPositions( currentPointList, currentPosList.at( 0 ).toElement() ) != 0 )
     587                 :            :         {
     588                 :          0 :           return QgsGeometry();
     589                 :            :         }
     590                 :          0 :         lineCoordinates.push_back( currentPointList );
     591                 :          0 :       }
     592                 :          0 :     }
     593                 :          0 :   }
     594                 :            :   else
     595                 :            :   {
     596                 :          0 :     QDomNodeList lineStringList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LineString" ) );
     597                 :          0 :     if ( !lineStringList.isEmpty() ) //mapserver
     598                 :            :     {
     599                 :          0 :       for ( int i = 0; i < lineStringList.size(); ++i )
     600                 :            :       {
     601                 :          0 :         currentLineStringElement = lineStringList.at( i ).toElement();
     602                 :          0 :         currentCoordList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
     603                 :          0 :         if ( !currentCoordList.isEmpty() )
     604                 :            :         {
     605                 :          0 :           QgsPolylineXY currentPointList;
     606                 :          0 :           if ( readGMLCoordinates( currentPointList, currentCoordList.at( 0 ).toElement() ) != 0 )
     607                 :            :           {
     608                 :          0 :             return QgsGeometry();
     609                 :            :           }
     610                 :          0 :           lineCoordinates.push_back( currentPointList );
     611                 :          0 :           return QgsGeometry();
     612                 :          0 :         }
     613                 :            :         else
     614                 :            :         {
     615                 :          0 :           currentPosList = currentLineStringElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
     616                 :          0 :           if ( currentPosList.size() < 1 )
     617                 :            :           {
     618                 :          0 :             return QgsGeometry();
     619                 :            :           }
     620                 :          0 :           QgsPolylineXY currentPointList;
     621                 :          0 :           if ( readGMLPositions( currentPointList, currentPosList.at( 0 ).toElement() ) != 0 )
     622                 :            :           {
     623                 :          0 :             return QgsGeometry();
     624                 :            :           }
     625                 :          0 :           lineCoordinates.push_back( currentPointList );
     626                 :          0 :         }
     627                 :          0 :       }
     628                 :          0 :     }
     629                 :            :     else
     630                 :            :     {
     631                 :          0 :       return QgsGeometry();
     632                 :            :     }
     633                 :          0 :   }
     634                 :            : 
     635                 :          0 :   int nLines = lineCoordinates.size();
     636                 :          0 :   if ( nLines < 1 )
     637                 :          0 :     return QgsGeometry();
     638                 :            : 
     639                 :            :   //calculate the required wkb size
     640                 :          0 :   int size = ( lineCoordinates.size() + 1 ) * ( 1 + 2 * sizeof( int ) );
     641                 :          0 :   for ( QList< QgsPolylineXY >::const_iterator it = lineCoordinates.constBegin(); it != lineCoordinates.constEnd(); ++it )
     642                 :            :   {
     643                 :          0 :     size += it->size() * 2 * sizeof( double );
     644                 :          0 :   }
     645                 :            : 
     646                 :          0 :   QgsWkbTypes::Type type = QgsWkbTypes::MultiLineString;
     647                 :          0 :   unsigned char *wkb = new unsigned char[size];
     648                 :            : 
     649                 :            :   //fill the wkb content
     650                 :          0 :   char e = htonl( 1 ) != 1;
     651                 :          0 :   int wkbPosition = 0; //current offset from wkb beginning (in bytes)
     652                 :            :   int nPoints; //number of points in a line
     653                 :            :   double x, y;
     654                 :          0 :   memcpy( &( wkb )[wkbPosition], &e, 1 );
     655                 :          0 :   wkbPosition += 1;
     656                 :          0 :   memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
     657                 :          0 :   wkbPosition += sizeof( int );
     658                 :          0 :   memcpy( &( wkb )[wkbPosition], &nLines, sizeof( int ) );
     659                 :          0 :   wkbPosition += sizeof( int );
     660                 :          0 :   type = QgsWkbTypes::LineString;
     661                 :          0 :   for ( QList< QgsPolylineXY >::const_iterator it = lineCoordinates.constBegin(); it != lineCoordinates.constEnd(); ++it )
     662                 :            :   {
     663                 :          0 :     memcpy( &( wkb )[wkbPosition], &e, 1 );
     664                 :          0 :     wkbPosition += 1;
     665                 :          0 :     memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
     666                 :          0 :     wkbPosition += sizeof( int );
     667                 :          0 :     nPoints = it->size();
     668                 :          0 :     memcpy( &( wkb )[wkbPosition], &nPoints, sizeof( int ) );
     669                 :          0 :     wkbPosition += sizeof( int );
     670                 :          0 :     for ( QgsPolylineXY::const_iterator iter = it->begin(); iter != it->end(); ++iter )
     671                 :            :     {
     672                 :          0 :       x = iter->x();
     673                 :          0 :       y = iter->y();
     674                 :            :       // QgsDebugMsg( QStringLiteral( "x, y is %1,%2" ).arg( x, 'f' ).arg( y, 'f' ) );
     675                 :          0 :       memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
     676                 :          0 :       wkbPosition += sizeof( double );
     677                 :          0 :       memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
     678                 :          0 :       wkbPosition += sizeof( double );
     679                 :          0 :     }
     680                 :          0 :   }
     681                 :            : 
     682                 :          0 :   QgsGeometry g;
     683                 :          0 :   g.fromWkb( wkb, size );
     684                 :          0 :   return g;
     685                 :          0 : }
     686                 :            : 
     687                 :          0 : QgsGeometry QgsOgcUtils::geometryFromGMLMultiPolygon( const QDomElement &geometryElement )
     688                 :            : {
     689                 :            :   //first list: different polygons, second list: different rings, third list: different points
     690                 :          0 :   QgsMultiPolygonXY multiPolygonPoints;
     691                 :          0 :   QDomElement currentPolygonMemberElement;
     692                 :          0 :   QDomNodeList polygonList;
     693                 :          0 :   QDomElement currentPolygonElement;
     694                 :            :   // rings in GML2
     695                 :          0 :   QDomNodeList outerBoundaryList;
     696                 :          0 :   QDomElement currentOuterBoundaryElement;
     697                 :          0 :   QDomNodeList innerBoundaryList;
     698                 :          0 :   QDomElement currentInnerBoundaryElement;
     699                 :            :   // rings in GML3
     700                 :          0 :   QDomNodeList exteriorList;
     701                 :          0 :   QDomElement currentExteriorElement;
     702                 :          0 :   QDomElement currentInteriorElement;
     703                 :          0 :   QDomNodeList interiorList;
     704                 :            :   // lienar ring
     705                 :          0 :   QDomNodeList linearRingNodeList;
     706                 :          0 :   QDomElement currentLinearRingElement;
     707                 :            :   // Coordinates or position list
     708                 :          0 :   QDomNodeList currentCoordinateList;
     709                 :          0 :   QDomNodeList currentPosList;
     710                 :            : 
     711                 :          0 :   QDomNodeList polygonMemberList = geometryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "polygonMember" ) );
     712                 :          0 :   QgsPolygonXY currentPolygonList;
     713                 :          0 :   for ( int i = 0; i < polygonMemberList.size(); ++i )
     714                 :            :   {
     715                 :          0 :     currentPolygonList.resize( 0 ); // preserve capacity - don't use clear
     716                 :          0 :     currentPolygonMemberElement = polygonMemberList.at( i ).toElement();
     717                 :          0 :     polygonList = currentPolygonMemberElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "Polygon" ) );
     718                 :          0 :     if ( polygonList.size() < 1 )
     719                 :            :     {
     720                 :          0 :       continue;
     721                 :            :     }
     722                 :          0 :     currentPolygonElement = polygonList.at( 0 ).toElement();
     723                 :            : 
     724                 :            :     //find exterior ring
     725                 :          0 :     outerBoundaryList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "outerBoundaryIs" ) );
     726                 :          0 :     if ( !outerBoundaryList.isEmpty() )
     727                 :            :     {
     728                 :          0 :       currentOuterBoundaryElement = outerBoundaryList.at( 0 ).toElement();
     729                 :          0 :       QgsPolylineXY ringCoordinates;
     730                 :            : 
     731                 :          0 :       linearRingNodeList = currentOuterBoundaryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LinearRing" ) );
     732                 :          0 :       if ( linearRingNodeList.size() < 1 )
     733                 :            :       {
     734                 :          0 :         continue;
     735                 :            :       }
     736                 :          0 :       currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
     737                 :          0 :       currentCoordinateList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
     738                 :          0 :       if ( currentCoordinateList.size() < 1 )
     739                 :            :       {
     740                 :          0 :         continue;
     741                 :            :       }
     742                 :          0 :       if ( readGMLCoordinates( ringCoordinates, currentCoordinateList.at( 0 ).toElement() ) != 0 )
     743                 :            :       {
     744                 :          0 :         continue;
     745                 :            :       }
     746                 :          0 :       currentPolygonList.push_back( ringCoordinates );
     747                 :            : 
     748                 :            :       //find interior rings
     749                 :          0 :       QDomNodeList innerBoundaryList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "innerBoundaryIs" ) );
     750                 :          0 :       for ( int j = 0; j < innerBoundaryList.size(); ++j )
     751                 :            :       {
     752                 :          0 :         QgsPolylineXY ringCoordinates;
     753                 :          0 :         currentInnerBoundaryElement = innerBoundaryList.at( j ).toElement();
     754                 :          0 :         linearRingNodeList = currentInnerBoundaryElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LinearRing" ) );
     755                 :          0 :         if ( linearRingNodeList.size() < 1 )
     756                 :            :         {
     757                 :          0 :           continue;
     758                 :            :         }
     759                 :          0 :         currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
     760                 :          0 :         currentCoordinateList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "coordinates" ) );
     761                 :          0 :         if ( currentCoordinateList.size() < 1 )
     762                 :            :         {
     763                 :          0 :           continue;
     764                 :            :         }
     765                 :          0 :         if ( readGMLCoordinates( ringCoordinates, currentCoordinateList.at( 0 ).toElement() ) != 0 )
     766                 :            :         {
     767                 :          0 :           continue;
     768                 :            :         }
     769                 :          0 :         currentPolygonList.push_back( ringCoordinates );
     770                 :          0 :       }
     771                 :          0 :     }
     772                 :            :     else
     773                 :            :     {
     774                 :            :       //find exterior ring
     775                 :          0 :       exteriorList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "exterior" ) );
     776                 :          0 :       if ( exteriorList.size() < 1 )
     777                 :            :       {
     778                 :          0 :         continue;
     779                 :            :       }
     780                 :            : 
     781                 :          0 :       currentExteriorElement = exteriorList.at( 0 ).toElement();
     782                 :          0 :       QgsPolylineXY ringPositions;
     783                 :            : 
     784                 :          0 :       linearRingNodeList = currentExteriorElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LinearRing" ) );
     785                 :          0 :       if ( linearRingNodeList.size() < 1 )
     786                 :            :       {
     787                 :          0 :         continue;
     788                 :            :       }
     789                 :          0 :       currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
     790                 :          0 :       currentPosList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
     791                 :          0 :       if ( currentPosList.size() < 1 )
     792                 :            :       {
     793                 :          0 :         continue;
     794                 :            :       }
     795                 :          0 :       if ( readGMLPositions( ringPositions, currentPosList.at( 0 ).toElement() ) != 0 )
     796                 :            :       {
     797                 :          0 :         continue;
     798                 :            :       }
     799                 :          0 :       currentPolygonList.push_back( ringPositions );
     800                 :            : 
     801                 :            :       //find interior rings
     802                 :          0 :       QDomNodeList interiorList = currentPolygonElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "interior" ) );
     803                 :          0 :       for ( int j = 0; j < interiorList.size(); ++j )
     804                 :            :       {
     805                 :          0 :         QgsPolylineXY ringPositions;
     806                 :          0 :         currentInteriorElement = interiorList.at( j ).toElement();
     807                 :          0 :         linearRingNodeList = currentInteriorElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "LinearRing" ) );
     808                 :          0 :         if ( linearRingNodeList.size() < 1 )
     809                 :            :         {
     810                 :          0 :           continue;
     811                 :            :         }
     812                 :          0 :         currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
     813                 :          0 :         currentPosList = currentLinearRingElement.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "posList" ) );
     814                 :          0 :         if ( currentPosList.size() < 1 )
     815                 :            :         {
     816                 :          0 :           continue;
     817                 :            :         }
     818                 :          0 :         if ( readGMLPositions( ringPositions, currentPosList.at( 0 ).toElement() ) != 0 )
     819                 :            :         {
     820                 :          0 :           continue;
     821                 :            :         }
     822                 :          0 :         currentPolygonList.push_back( ringPositions );
     823                 :          0 :       }
     824                 :          0 :     }
     825                 :          0 :     multiPolygonPoints.push_back( currentPolygonList );
     826                 :          0 :   }
     827                 :            : 
     828                 :          0 :   int nPolygons = multiPolygonPoints.size();
     829                 :          0 :   if ( nPolygons < 1 )
     830                 :          0 :     return QgsGeometry();
     831                 :            : 
     832                 :          0 :   int size = 1 + 2 * sizeof( int );
     833                 :            :   //calculate the wkb size
     834                 :          0 :   for ( QgsMultiPolygonXY::const_iterator it = multiPolygonPoints.constBegin(); it != multiPolygonPoints.constEnd(); ++it )
     835                 :            :   {
     836                 :          0 :     size += 1 + 2 * sizeof( int );
     837                 :          0 :     for ( QgsPolygonXY::const_iterator iter = it->begin(); iter != it->end(); ++iter )
     838                 :            :     {
     839                 :          0 :       size += sizeof( int ) + 2 * iter->size() * sizeof( double );
     840                 :          0 :     }
     841                 :          0 :   }
     842                 :            : 
     843                 :          0 :   QgsWkbTypes::Type type = QgsWkbTypes::MultiPolygon;
     844                 :          0 :   unsigned char *wkb = new unsigned char[size];
     845                 :            : 
     846                 :          0 :   char e = htonl( 1 ) != 1;
     847                 :          0 :   int wkbPosition = 0; //current offset from wkb beginning (in bytes)
     848                 :            :   double x, y;
     849                 :            :   int nRings;
     850                 :            :   int nPointsInRing;
     851                 :            : 
     852                 :            :   //fill the contents into *wkb
     853                 :          0 :   memcpy( &( wkb )[wkbPosition], &e, 1 );
     854                 :          0 :   wkbPosition += 1;
     855                 :          0 :   memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
     856                 :          0 :   wkbPosition += sizeof( int );
     857                 :          0 :   memcpy( &( wkb )[wkbPosition], &nPolygons, sizeof( int ) );
     858                 :          0 :   wkbPosition += sizeof( int );
     859                 :            : 
     860                 :          0 :   type = QgsWkbTypes::Polygon;
     861                 :            : 
     862                 :          0 :   for ( QgsMultiPolygonXY::const_iterator it = multiPolygonPoints.constBegin(); it != multiPolygonPoints.constEnd(); ++it )
     863                 :            :   {
     864                 :          0 :     memcpy( &( wkb )[wkbPosition], &e, 1 );
     865                 :          0 :     wkbPosition += 1;
     866                 :          0 :     memcpy( &( wkb )[wkbPosition], &type, sizeof( int ) );
     867                 :          0 :     wkbPosition += sizeof( int );
     868                 :          0 :     nRings = it->size();
     869                 :          0 :     memcpy( &( wkb )[wkbPosition], &nRings, sizeof( int ) );
     870                 :          0 :     wkbPosition += sizeof( int );
     871                 :          0 :     for ( QgsPolygonXY::const_iterator iter = it->begin(); iter != it->end(); ++iter )
     872                 :            :     {
     873                 :          0 :       nPointsInRing = iter->size();
     874                 :          0 :       memcpy( &( wkb )[wkbPosition], &nPointsInRing, sizeof( int ) );
     875                 :          0 :       wkbPosition += sizeof( int );
     876                 :          0 :       for ( QgsPolylineXY::const_iterator iterator = iter->begin(); iterator != iter->end(); ++iterator )
     877                 :            :       {
     878                 :          0 :         x = iterator->x();
     879                 :          0 :         y = iterator->y();
     880                 :          0 :         memcpy( &( wkb )[wkbPosition], &x, sizeof( double ) );
     881                 :          0 :         wkbPosition += sizeof( double );
     882                 :          0 :         memcpy( &( wkb )[wkbPosition], &y, sizeof( double ) );
     883                 :          0 :         wkbPosition += sizeof( double );
     884                 :          0 :       }
     885                 :          0 :     }
     886                 :          0 :   }
     887                 :            : 
     888                 :          0 :   QgsGeometry g;
     889                 :          0 :   g.fromWkb( wkb, size );
     890                 :          0 :   return g;
     891                 :          0 : }
     892                 :            : 
     893                 :          0 : bool QgsOgcUtils::readGMLCoordinates( QgsPolylineXY &coords, const QDomElement &elem )
     894                 :            : {
     895                 :          0 :   QString coordSeparator = QStringLiteral( "," );
     896                 :          0 :   QString tupelSeparator = QStringLiteral( " " );
     897                 :            :   //"decimal" has to be "."
     898                 :            : 
     899                 :          0 :   coords.clear();
     900                 :            : 
     901                 :          0 :   if ( elem.hasAttribute( QStringLiteral( "cs" ) ) )
     902                 :            :   {
     903                 :          0 :     coordSeparator = elem.attribute( QStringLiteral( "cs" ) );
     904                 :          0 :   }
     905                 :          0 :   if ( elem.hasAttribute( QStringLiteral( "ts" ) ) )
     906                 :            :   {
     907                 :          0 :     tupelSeparator = elem.attribute( QStringLiteral( "ts" ) );
     908                 :          0 :   }
     909                 :            : 
     910                 :            : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
     911                 :            :   QStringList tupels = elem.text().split( tupelSeparator, QString::SkipEmptyParts );
     912                 :            : #else
     913                 :          0 :   QStringList tupels = elem.text().split( tupelSeparator, Qt::SkipEmptyParts );
     914                 :            : #endif
     915                 :          0 :   QStringList tuple_coords;
     916                 :            :   double x, y;
     917                 :            :   bool conversionSuccess;
     918                 :            : 
     919                 :          0 :   QStringList::const_iterator it;
     920                 :          0 :   for ( it = tupels.constBegin(); it != tupels.constEnd(); ++it )
     921                 :            :   {
     922                 :            : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
     923                 :            :     tuple_coords = ( *it ).split( coordSeparator, QString::SkipEmptyParts );
     924                 :            : #else
     925                 :          0 :     tuple_coords = ( *it ).split( coordSeparator, Qt::SkipEmptyParts );
     926                 :            : #endif
     927                 :          0 :     if ( tuple_coords.size() < 2 )
     928                 :            :     {
     929                 :          0 :       continue;
     930                 :            :     }
     931                 :          0 :     x = tuple_coords.at( 0 ).toDouble( &conversionSuccess );
     932                 :          0 :     if ( !conversionSuccess )
     933                 :            :     {
     934                 :          0 :       return true;
     935                 :            :     }
     936                 :          0 :     y = tuple_coords.at( 1 ).toDouble( &conversionSuccess );
     937                 :          0 :     if ( !conversionSuccess )
     938                 :            :     {
     939                 :          0 :       return true;
     940                 :            :     }
     941                 :          0 :     coords.push_back( QgsPointXY( x, y ) );
     942                 :          0 :   }
     943                 :          0 :   return false;
     944                 :          0 : }
     945                 :            : 
     946                 :          0 : QgsRectangle QgsOgcUtils::rectangleFromGMLBox( const QDomNode &boxNode )
     947                 :            : {
     948                 :          0 :   QgsRectangle rect;
     949                 :            : 
     950                 :          0 :   QDomElement boxElem = boxNode.toElement();
     951                 :          0 :   if ( boxElem.tagName() != QLatin1String( "Box" ) )
     952                 :          0 :     return rect;
     953                 :            : 
     954                 :          0 :   QDomElement bElem = boxElem.firstChild().toElement();
     955                 :          0 :   QString coordSeparator = QStringLiteral( "," );
     956                 :          0 :   QString tupelSeparator = QStringLiteral( " " );
     957                 :          0 :   if ( bElem.hasAttribute( QStringLiteral( "cs" ) ) )
     958                 :            :   {
     959                 :          0 :     coordSeparator = bElem.attribute( QStringLiteral( "cs" ) );
     960                 :          0 :   }
     961                 :          0 :   if ( bElem.hasAttribute( QStringLiteral( "ts" ) ) )
     962                 :            :   {
     963                 :          0 :     tupelSeparator = bElem.attribute( QStringLiteral( "ts" ) );
     964                 :          0 :   }
     965                 :            : 
     966                 :          0 :   QString bString = bElem.text();
     967                 :            :   bool ok1, ok2, ok3, ok4;
     968                 :          0 :   double xmin = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 0, 0 ).toDouble( &ok1 );
     969                 :          0 :   double ymin = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 1, 1 ).toDouble( &ok2 );
     970                 :          0 :   double xmax = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 0, 0 ).toDouble( &ok3 );
     971                 :          0 :   double ymax = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 1, 1 ).toDouble( &ok4 );
     972                 :            : 
     973                 :          0 :   if ( ok1 && ok2 && ok3 && ok4 )
     974                 :            :   {
     975                 :          0 :     rect = QgsRectangle( xmin, ymin, xmax, ymax );
     976                 :          0 :     rect.normalize();
     977                 :          0 :   }
     978                 :            : 
     979                 :            :   return rect;
     980                 :          0 : }
     981                 :            : 
     982                 :          0 : bool QgsOgcUtils::readGMLPositions( QgsPolylineXY &coords, const QDomElement &elem )
     983                 :            : {
     984                 :          0 :   coords.clear();
     985                 :            : 
     986                 :            : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
     987                 :            :   QStringList pos = elem.text().split( ' ', QString::SkipEmptyParts );
     988                 :            : #else
     989                 :          0 :   QStringList pos = elem.text().split( ' ', Qt::SkipEmptyParts );
     990                 :            : #endif
     991                 :            :   double x, y;
     992                 :            :   bool conversionSuccess;
     993                 :          0 :   int posSize = pos.size();
     994                 :            : 
     995                 :          0 :   int srsDimension = 2;
     996                 :          0 :   if ( elem.hasAttribute( QStringLiteral( "srsDimension" ) ) )
     997                 :            :   {
     998                 :          0 :     srsDimension = elem.attribute( QStringLiteral( "srsDimension" ) ).toInt( &conversionSuccess );
     999                 :          0 :     if ( !conversionSuccess )
    1000                 :            :     {
    1001                 :          0 :       srsDimension = 2;
    1002                 :          0 :     }
    1003                 :          0 :   }
    1004                 :          0 :   else if ( elem.hasAttribute( QStringLiteral( "dimension" ) ) )
    1005                 :            :   {
    1006                 :          0 :     srsDimension = elem.attribute( QStringLiteral( "dimension" ) ).toInt( &conversionSuccess );
    1007                 :          0 :     if ( !conversionSuccess )
    1008                 :            :     {
    1009                 :          0 :       srsDimension = 2;
    1010                 :          0 :     }
    1011                 :          0 :   }
    1012                 :            : 
    1013                 :          0 :   for ( int i = 0; i < posSize / srsDimension; i++ )
    1014                 :            :   {
    1015                 :          0 :     x = pos.at( i * srsDimension ).toDouble( &conversionSuccess );
    1016                 :          0 :     if ( !conversionSuccess )
    1017                 :            :     {
    1018                 :          0 :       return true;
    1019                 :            :     }
    1020                 :          0 :     y = pos.at( i * srsDimension + 1 ).toDouble( &conversionSuccess );
    1021                 :          0 :     if ( !conversionSuccess )
    1022                 :            :     {
    1023                 :          0 :       return true;
    1024                 :            :     }
    1025                 :          0 :     coords.push_back( QgsPointXY( x, y ) );
    1026                 :          0 :   }
    1027                 :          0 :   return false;
    1028                 :          0 : }
    1029                 :            : 
    1030                 :            : 
    1031                 :          0 : QgsRectangle QgsOgcUtils::rectangleFromGMLEnvelope( const QDomNode &envelopeNode )
    1032                 :            : {
    1033                 :          0 :   QgsRectangle rect;
    1034                 :            : 
    1035                 :          0 :   QDomElement envelopeElem = envelopeNode.toElement();
    1036                 :          0 :   if ( envelopeElem.tagName() != QLatin1String( "Envelope" ) )
    1037                 :          0 :     return rect;
    1038                 :            : 
    1039                 :          0 :   QDomNodeList lowerCornerList = envelopeElem.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "lowerCorner" ) );
    1040                 :          0 :   if ( lowerCornerList.size() < 1 )
    1041                 :          0 :     return rect;
    1042                 :            : 
    1043                 :          0 :   QDomNodeList upperCornerList = envelopeElem.elementsByTagNameNS( GML_NAMESPACE, QStringLiteral( "upperCorner" ) );
    1044                 :          0 :   if ( upperCornerList.size() < 1 )
    1045                 :          0 :     return rect;
    1046                 :            : 
    1047                 :            :   bool conversionSuccess;
    1048                 :          0 :   int srsDimension = 2;
    1049                 :            : 
    1050                 :          0 :   QDomElement elem = lowerCornerList.at( 0 ).toElement();
    1051                 :          0 :   if ( elem.hasAttribute( QStringLiteral( "srsDimension" ) ) )
    1052                 :            :   {
    1053                 :          0 :     srsDimension = elem.attribute( QStringLiteral( "srsDimension" ) ).toInt( &conversionSuccess );
    1054                 :          0 :     if ( !conversionSuccess )
    1055                 :            :     {
    1056                 :          0 :       srsDimension = 2;
    1057                 :          0 :     }
    1058                 :          0 :   }
    1059                 :          0 :   else if ( elem.hasAttribute( QStringLiteral( "dimension" ) ) )
    1060                 :            :   {
    1061                 :          0 :     srsDimension = elem.attribute( QStringLiteral( "dimension" ) ).toInt( &conversionSuccess );
    1062                 :          0 :     if ( !conversionSuccess )
    1063                 :            :     {
    1064                 :          0 :       srsDimension = 2;
    1065                 :          0 :     }
    1066                 :          0 :   }
    1067                 :          0 :   QString bString = elem.text();
    1068                 :            : 
    1069                 :          0 :   double xmin = bString.section( ' ', 0, 0 ).toDouble( &conversionSuccess );
    1070                 :          0 :   if ( !conversionSuccess )
    1071                 :          0 :     return rect;
    1072                 :          0 :   double ymin = bString.section( ' ', 1, 1 ).toDouble( &conversionSuccess );
    1073                 :          0 :   if ( !conversionSuccess )
    1074                 :          0 :     return rect;
    1075                 :            : 
    1076                 :          0 :   elem = upperCornerList.at( 0 ).toElement();
    1077                 :          0 :   if ( elem.hasAttribute( QStringLiteral( "srsDimension" ) ) )
    1078                 :            :   {
    1079                 :          0 :     srsDimension = elem.attribute( QStringLiteral( "srsDimension" ) ).toInt( &conversionSuccess );
    1080                 :          0 :     if ( !conversionSuccess )
    1081                 :            :     {
    1082                 :          0 :       srsDimension = 2;
    1083                 :          0 :     }
    1084                 :          0 :   }
    1085                 :          0 :   else if ( elem.hasAttribute( QStringLiteral( "dimension" ) ) )
    1086                 :            :   {
    1087                 :          0 :     srsDimension = elem.attribute( QStringLiteral( "dimension" ) ).toInt( &conversionSuccess );
    1088                 :          0 :     if ( !conversionSuccess )
    1089                 :            :     {
    1090                 :          0 :       srsDimension = 2;
    1091                 :          0 :     }
    1092                 :          0 :   }
    1093                 :            : 
    1094                 :            :   Q_UNUSED( srsDimension )
    1095                 :            : 
    1096                 :          0 :   bString = elem.text();
    1097                 :          0 :   double xmax = bString.section( ' ', 0, 0 ).toDouble( &conversionSuccess );
    1098                 :          0 :   if ( !conversionSuccess )
    1099                 :          0 :     return rect;
    1100                 :          0 :   double ymax = bString.section( ' ', 1, 1 ).toDouble( &conversionSuccess );
    1101                 :          0 :   if ( !conversionSuccess )
    1102                 :          0 :     return rect;
    1103                 :            : 
    1104                 :          0 :   rect = QgsRectangle( xmin, ymin, xmax, ymax );
    1105                 :          0 :   rect.normalize();
    1106                 :            : 
    1107                 :          0 :   return rect;
    1108                 :          0 : }
    1109                 :            : 
    1110                 :          0 : QDomElement QgsOgcUtils::rectangleToGMLBox( QgsRectangle *box, QDomDocument &doc, int precision )
    1111                 :            : {
    1112                 :          0 :   return rectangleToGMLBox( box, doc, QString(), false, precision );
    1113                 :          0 : }
    1114                 :            : 
    1115                 :          0 : QDomElement QgsOgcUtils::rectangleToGMLBox( QgsRectangle *box, QDomDocument &doc,
    1116                 :            :     const QString &srsName,
    1117                 :            :     bool invertAxisOrientation,
    1118                 :            :     int precision )
    1119                 :            : {
    1120                 :          0 :   if ( !box )
    1121                 :            :   {
    1122                 :          0 :     return QDomElement();
    1123                 :            :   }
    1124                 :            : 
    1125                 :          0 :   QDomElement boxElem = doc.createElement( QStringLiteral( "gml:Box" ) );
    1126                 :          0 :   if ( !srsName.isEmpty() )
    1127                 :            :   {
    1128                 :          0 :     boxElem.setAttribute( QStringLiteral( "srsName" ), srsName );
    1129                 :          0 :   }
    1130                 :          0 :   QDomElement coordElem = doc.createElement( QStringLiteral( "gml:coordinates" ) );
    1131                 :          0 :   coordElem.setAttribute( QStringLiteral( "cs" ), QStringLiteral( "," ) );
    1132                 :          0 :   coordElem.setAttribute( QStringLiteral( "ts" ), QStringLiteral( " " ) );
    1133                 :            : 
    1134                 :          0 :   QString coordString;
    1135                 :          0 :   coordString += qgsDoubleToString( invertAxisOrientation ? box->yMinimum() : box->xMinimum(), precision );
    1136                 :          0 :   coordString += ',';
    1137                 :          0 :   coordString += qgsDoubleToString( invertAxisOrientation ? box->xMinimum() : box->yMinimum(), precision );
    1138                 :          0 :   coordString += ' ';
    1139                 :          0 :   coordString += qgsDoubleToString( invertAxisOrientation ? box->yMaximum() : box->xMaximum(), precision );
    1140                 :          0 :   coordString += ',';
    1141                 :          0 :   coordString += qgsDoubleToString( invertAxisOrientation ? box->xMaximum() : box->yMaximum(), precision );
    1142                 :            : 
    1143                 :          0 :   QDomText coordText = doc.createTextNode( coordString );
    1144                 :          0 :   coordElem.appendChild( coordText );
    1145                 :          0 :   boxElem.appendChild( coordElem );
    1146                 :            : 
    1147                 :          0 :   return boxElem;
    1148                 :          0 : }
    1149                 :            : 
    1150                 :          0 : QDomElement QgsOgcUtils::rectangleToGMLEnvelope( QgsRectangle *env, QDomDocument &doc, int precision )
    1151                 :            : {
    1152                 :          0 :   return rectangleToGMLEnvelope( env, doc, QString(), false, precision );
    1153                 :          0 : }
    1154                 :            : 
    1155                 :          0 : QDomElement QgsOgcUtils::rectangleToGMLEnvelope( QgsRectangle *env, QDomDocument &doc,
    1156                 :            :     const QString &srsName,
    1157                 :            :     bool invertAxisOrientation,
    1158                 :            :     int precision )
    1159                 :            : {
    1160                 :          0 :   if ( !env )
    1161                 :            :   {
    1162                 :          0 :     return QDomElement();
    1163                 :            :   }
    1164                 :            : 
    1165                 :          0 :   QDomElement envElem = doc.createElement( QStringLiteral( "gml:Envelope" ) );
    1166                 :          0 :   if ( !srsName.isEmpty() )
    1167                 :            :   {
    1168                 :          0 :     envElem.setAttribute( QStringLiteral( "srsName" ), srsName );
    1169                 :          0 :   }
    1170                 :          0 :   QString posList;
    1171                 :            : 
    1172                 :          0 :   QDomElement lowerCornerElem = doc.createElement( QStringLiteral( "gml:lowerCorner" ) );
    1173                 :          0 :   posList = qgsDoubleToString( invertAxisOrientation ? env->yMinimum() : env->xMinimum(), precision );
    1174                 :          0 :   posList += ' ';
    1175                 :          0 :   posList += qgsDoubleToString( invertAxisOrientation ? env->xMinimum() : env->yMinimum(), precision );
    1176                 :          0 :   QDomText lowerCornerText = doc.createTextNode( posList );
    1177                 :          0 :   lowerCornerElem.appendChild( lowerCornerText );
    1178                 :          0 :   envElem.appendChild( lowerCornerElem );
    1179                 :            : 
    1180                 :          0 :   QDomElement upperCornerElem = doc.createElement( QStringLiteral( "gml:upperCorner" ) );
    1181                 :          0 :   posList = qgsDoubleToString( invertAxisOrientation ? env->yMaximum() : env->xMaximum(), precision );
    1182                 :          0 :   posList += ' ';
    1183                 :          0 :   posList += qgsDoubleToString( invertAxisOrientation ? env->xMaximum() : env->yMaximum(), precision );
    1184                 :          0 :   QDomText upperCornerText = doc.createTextNode( posList );
    1185                 :          0 :   upperCornerElem.appendChild( upperCornerText );
    1186                 :          0 :   envElem.appendChild( upperCornerElem );
    1187                 :            : 
    1188                 :          0 :   return envElem;
    1189                 :          0 : }
    1190                 :            : 
    1191                 :          0 : QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry &geometry, QDomDocument &doc, const QString &format, int precision )
    1192                 :            : {
    1193                 :          0 :   return geometryToGML( geometry, doc, ( format == QLatin1String( "GML2" ) ) ? GML_2_1_2 : GML_3_2_1, QString(), false, QString(), precision );
    1194                 :          0 : }
    1195                 :            : 
    1196                 :          0 : QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry &geometry,
    1197                 :            :                                         QDomDocument &doc,
    1198                 :            :                                         GMLVersion gmlVersion,
    1199                 :            :                                         const QString &srsName,
    1200                 :            :                                         bool invertAxisOrientation,
    1201                 :            :                                         const QString &gmlIdBase,
    1202                 :            :                                         int precision )
    1203                 :            : {
    1204                 :          0 :   if ( geometry.isNull() )
    1205                 :          0 :     return QDomElement();
    1206                 :            : 
    1207                 :            :   // coordinate separator
    1208                 :          0 :   QString cs = QStringLiteral( "," );
    1209                 :            :   // tuple separator
    1210                 :          0 :   QString ts = QStringLiteral( " " );
    1211                 :            :   // coord element tagname
    1212                 :          0 :   QDomElement baseCoordElem;
    1213                 :            : 
    1214                 :          0 :   bool hasZValue = false;
    1215                 :            : 
    1216                 :          0 :   QByteArray wkb( geometry.asWkb() );
    1217                 :          0 :   QgsConstWkbPtr wkbPtr( wkb );
    1218                 :            :   try
    1219                 :            :   {
    1220                 :          0 :     wkbPtr.readHeader();
    1221                 :          0 :   }
    1222                 :            :   catch ( const QgsWkbException &e )
    1223                 :            :   {
    1224                 :          0 :     Q_UNUSED( e )
    1225                 :            :     // WKB exception while reading header
    1226                 :          0 :     return QDomElement();
    1227                 :          0 :   }
    1228                 :            : 
    1229                 :          0 :   if ( gmlVersion != GML_2_1_2 )
    1230                 :            :   {
    1231                 :          0 :     switch ( geometry.wkbType() )
    1232                 :            :     {
    1233                 :            :       case QgsWkbTypes::Point25D:
    1234                 :            :       case QgsWkbTypes::Point:
    1235                 :            :       case QgsWkbTypes::MultiPoint25D:
    1236                 :            :       case QgsWkbTypes::MultiPoint:
    1237                 :          0 :         baseCoordElem = doc.createElement( QStringLiteral( "gml:pos" ) );
    1238                 :          0 :         break;
    1239                 :            :       default:
    1240                 :          0 :         baseCoordElem = doc.createElement( QStringLiteral( "gml:posList" ) );
    1241                 :          0 :         break;
    1242                 :            :     }
    1243                 :          0 :     baseCoordElem.setAttribute( QStringLiteral( "srsDimension" ), QStringLiteral( "2" ) );
    1244                 :          0 :     cs = ' ';
    1245                 :          0 :   }
    1246                 :            :   else
    1247                 :            :   {
    1248                 :          0 :     baseCoordElem = doc.createElement( QStringLiteral( "gml:coordinates" ) );
    1249                 :          0 :     baseCoordElem.setAttribute( QStringLiteral( "cs" ), cs );
    1250                 :          0 :     baseCoordElem.setAttribute( QStringLiteral( "ts" ), ts );
    1251                 :            :   }
    1252                 :            : 
    1253                 :            :   try
    1254                 :            :   {
    1255                 :          0 :     switch ( geometry.wkbType() )
    1256                 :            :     {
    1257                 :            :       case QgsWkbTypes::Point25D:
    1258                 :            :       case QgsWkbTypes::Point:
    1259                 :            :       {
    1260                 :          0 :         QDomElement pointElem = doc.createElement( QStringLiteral( "gml:Point" ) );
    1261                 :          0 :         if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
    1262                 :          0 :           pointElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
    1263                 :          0 :         if ( !srsName.isEmpty() )
    1264                 :          0 :           pointElem.setAttribute( QStringLiteral( "srsName" ), srsName );
    1265                 :          0 :         QDomElement coordElem = baseCoordElem.cloneNode().toElement();
    1266                 :            : 
    1267                 :            :         double x, y;
    1268                 :            : 
    1269                 :          0 :         if ( invertAxisOrientation )
    1270                 :          0 :           wkbPtr >> y >> x;
    1271                 :            :         else
    1272                 :          0 :           wkbPtr >> x >> y;
    1273                 :          0 :         QDomText coordText = doc.createTextNode( qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision ) );
    1274                 :            : 
    1275                 :          0 :         coordElem.appendChild( coordText );
    1276                 :          0 :         pointElem.appendChild( coordElem );
    1277                 :          0 :         return pointElem;
    1278                 :          0 :       }
    1279                 :            :       case QgsWkbTypes::MultiPoint25D:
    1280                 :          0 :         hasZValue = true;
    1281                 :            :         //intentional fall-through
    1282                 :            :         FALLTHROUGH
    1283                 :            :       case QgsWkbTypes::MultiPoint:
    1284                 :            :       {
    1285                 :          0 :         QDomElement multiPointElem = doc.createElement( QStringLiteral( "gml:MultiPoint" ) );
    1286                 :          0 :         if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
    1287                 :          0 :           multiPointElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
    1288                 :          0 :         if ( !srsName.isEmpty() )
    1289                 :          0 :           multiPointElem.setAttribute( QStringLiteral( "srsName" ), srsName );
    1290                 :            : 
    1291                 :            :         int nPoints;
    1292                 :          0 :         wkbPtr >> nPoints;
    1293                 :            : 
    1294                 :          0 :         for ( int idx = 0; idx < nPoints; ++idx )
    1295                 :            :         {
    1296                 :          0 :           QDomElement pointMemberElem = doc.createElement( QStringLiteral( "gml:pointMember" ) );
    1297                 :          0 :           QDomElement pointElem = doc.createElement( QStringLiteral( "gml:Point" ) );
    1298                 :          0 :           if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
    1299                 :          0 :             pointElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase + QStringLiteral( ".%1" ).arg( idx + 1 ) );
    1300                 :          0 :           QDomElement coordElem = baseCoordElem.cloneNode().toElement();
    1301                 :            : 
    1302                 :          0 :           wkbPtr.readHeader();
    1303                 :            : 
    1304                 :          0 :           double x = 0;
    1305                 :          0 :           double y = 0;
    1306                 :          0 :           if ( invertAxisOrientation )
    1307                 :          0 :             wkbPtr >> y >> x;
    1308                 :            :           else
    1309                 :          0 :             wkbPtr >> x >> y;
    1310                 :          0 :           QDomText coordText = doc.createTextNode( qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision ) );
    1311                 :            : 
    1312                 :          0 :           coordElem.appendChild( coordText );
    1313                 :          0 :           pointElem.appendChild( coordElem );
    1314                 :            : 
    1315                 :          0 :           if ( hasZValue )
    1316                 :            :           {
    1317                 :          0 :             wkbPtr += sizeof( double );
    1318                 :          0 :           }
    1319                 :          0 :           pointMemberElem.appendChild( pointElem );
    1320                 :          0 :           multiPointElem.appendChild( pointMemberElem );
    1321                 :          0 :         }
    1322                 :          0 :         return multiPointElem;
    1323                 :          0 :       }
    1324                 :            :       case QgsWkbTypes::LineString25D:
    1325                 :          0 :         hasZValue = true;
    1326                 :            :         //intentional fall-through
    1327                 :            :         FALLTHROUGH
    1328                 :            :       case QgsWkbTypes::LineString:
    1329                 :            :       {
    1330                 :          0 :         QDomElement lineStringElem = doc.createElement( QStringLiteral( "gml:LineString" ) );
    1331                 :          0 :         if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
    1332                 :          0 :           lineStringElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
    1333                 :          0 :         if ( !srsName.isEmpty() )
    1334                 :          0 :           lineStringElem.setAttribute( QStringLiteral( "srsName" ), srsName );
    1335                 :            :         // get number of points in the line
    1336                 :            : 
    1337                 :            :         int nPoints;
    1338                 :          0 :         wkbPtr >> nPoints;
    1339                 :            : 
    1340                 :          0 :         QDomElement coordElem = baseCoordElem.cloneNode().toElement();
    1341                 :          0 :         QString coordString;
    1342                 :          0 :         for ( int idx = 0; idx < nPoints; ++idx )
    1343                 :            :         {
    1344                 :          0 :           if ( idx != 0 )
    1345                 :            :           {
    1346                 :          0 :             coordString += ts;
    1347                 :          0 :           }
    1348                 :            : 
    1349                 :          0 :           double x = 0;
    1350                 :          0 :           double y = 0;
    1351                 :          0 :           if ( invertAxisOrientation )
    1352                 :          0 :             wkbPtr >> y >> x;
    1353                 :            :           else
    1354                 :          0 :             wkbPtr >> x >> y;
    1355                 :          0 :           coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
    1356                 :            : 
    1357                 :          0 :           if ( hasZValue )
    1358                 :            :           {
    1359                 :          0 :             wkbPtr += sizeof( double );
    1360                 :          0 :           }
    1361                 :          0 :         }
    1362                 :          0 :         QDomText coordText = doc.createTextNode( coordString );
    1363                 :          0 :         coordElem.appendChild( coordText );
    1364                 :          0 :         lineStringElem.appendChild( coordElem );
    1365                 :          0 :         return lineStringElem;
    1366                 :          0 :       }
    1367                 :            :       case QgsWkbTypes::MultiLineString25D:
    1368                 :          0 :         hasZValue = true;
    1369                 :            :         //intentional fall-through
    1370                 :            :         FALLTHROUGH
    1371                 :            :       case QgsWkbTypes::MultiLineString:
    1372                 :            :       {
    1373                 :          0 :         QDomElement multiLineStringElem = doc.createElement( QStringLiteral( "gml:MultiLineString" ) );
    1374                 :          0 :         if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
    1375                 :          0 :           multiLineStringElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
    1376                 :          0 :         if ( !srsName.isEmpty() )
    1377                 :          0 :           multiLineStringElem.setAttribute( QStringLiteral( "srsName" ), srsName );
    1378                 :            : 
    1379                 :            :         int nLines;
    1380                 :          0 :         wkbPtr >> nLines;
    1381                 :            : 
    1382                 :          0 :         for ( int jdx = 0; jdx < nLines; jdx++ )
    1383                 :            :         {
    1384                 :          0 :           QDomElement lineStringMemberElem = doc.createElement( QStringLiteral( "gml:lineStringMember" ) );
    1385                 :          0 :           QDomElement lineStringElem = doc.createElement( QStringLiteral( "gml:LineString" ) );
    1386                 :          0 :           if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
    1387                 :          0 :             lineStringElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase + QStringLiteral( ".%1" ).arg( jdx + 1 ) );
    1388                 :            : 
    1389                 :          0 :           wkbPtr.readHeader();
    1390                 :            : 
    1391                 :            :           int nPoints;
    1392                 :          0 :           wkbPtr >> nPoints;
    1393                 :            : 
    1394                 :          0 :           QDomElement coordElem = baseCoordElem.cloneNode().toElement();
    1395                 :          0 :           QString coordString;
    1396                 :          0 :           for ( int idx = 0; idx < nPoints; idx++ )
    1397                 :            :           {
    1398                 :          0 :             if ( idx != 0 )
    1399                 :            :             {
    1400                 :          0 :               coordString += ts;
    1401                 :          0 :             }
    1402                 :            : 
    1403                 :          0 :             double x = 0;
    1404                 :          0 :             double y = 0;
    1405                 :          0 :             if ( invertAxisOrientation )
    1406                 :          0 :               wkbPtr >> y >> x;
    1407                 :            :             else
    1408                 :          0 :               wkbPtr >> x >> y;
    1409                 :            : 
    1410                 :          0 :             coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
    1411                 :            : 
    1412                 :          0 :             if ( hasZValue )
    1413                 :            :             {
    1414                 :          0 :               wkbPtr += sizeof( double );
    1415                 :          0 :             }
    1416                 :          0 :           }
    1417                 :          0 :           QDomText coordText = doc.createTextNode( coordString );
    1418                 :          0 :           coordElem.appendChild( coordText );
    1419                 :          0 :           lineStringElem.appendChild( coordElem );
    1420                 :          0 :           lineStringMemberElem.appendChild( lineStringElem );
    1421                 :          0 :           multiLineStringElem.appendChild( lineStringMemberElem );
    1422                 :          0 :         }
    1423                 :          0 :         return multiLineStringElem;
    1424                 :          0 :       }
    1425                 :            :       case QgsWkbTypes::Polygon25D:
    1426                 :          0 :         hasZValue = true;
    1427                 :            :         //intentional fall-through
    1428                 :            :         FALLTHROUGH
    1429                 :            :       case QgsWkbTypes::Polygon:
    1430                 :            :       {
    1431                 :          0 :         QDomElement polygonElem = doc.createElement( QStringLiteral( "gml:Polygon" ) );
    1432                 :          0 :         if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
    1433                 :          0 :           polygonElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
    1434                 :          0 :         if ( !srsName.isEmpty() )
    1435                 :          0 :           polygonElem.setAttribute( QStringLiteral( "srsName" ), srsName );
    1436                 :            : 
    1437                 :            :         // get number of rings in the polygon
    1438                 :            :         int numRings;
    1439                 :          0 :         wkbPtr >> numRings;
    1440                 :            : 
    1441                 :          0 :         if ( numRings == 0 ) // sanity check for zero rings in polygon
    1442                 :          0 :           return QDomElement();
    1443                 :            : 
    1444                 :          0 :         for ( int idx = 0; idx < numRings; idx++ )
    1445                 :            :         {
    1446                 :          0 :           QString boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:outerBoundaryIs" : "gml:exterior";
    1447                 :          0 :           if ( idx != 0 )
    1448                 :            :           {
    1449                 :          0 :             boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:innerBoundaryIs" : "gml:interior";
    1450                 :          0 :           }
    1451                 :          0 :           QDomElement boundaryElem = doc.createElement( boundaryName );
    1452                 :          0 :           QDomElement ringElem = doc.createElement( QStringLiteral( "gml:LinearRing" ) );
    1453                 :            :           // get number of points in the ring
    1454                 :          0 :           int nPoints = 0;
    1455                 :          0 :           wkbPtr >> nPoints;
    1456                 :            : 
    1457                 :          0 :           QDomElement coordElem = baseCoordElem.cloneNode().toElement();
    1458                 :          0 :           QString coordString;
    1459                 :          0 :           for ( int jdx = 0; jdx < nPoints; jdx++ )
    1460                 :            :           {
    1461                 :          0 :             if ( jdx != 0 )
    1462                 :            :             {
    1463                 :          0 :               coordString += ts;
    1464                 :          0 :             }
    1465                 :            : 
    1466                 :          0 :             double x = 0;
    1467                 :          0 :             double y = 0;
    1468                 :          0 :             if ( invertAxisOrientation )
    1469                 :          0 :               wkbPtr >> y >> x;
    1470                 :            :             else
    1471                 :          0 :               wkbPtr >> x >> y;
    1472                 :            : 
    1473                 :          0 :             coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
    1474                 :          0 :             if ( hasZValue )
    1475                 :            :             {
    1476                 :          0 :               wkbPtr += sizeof( double );
    1477                 :          0 :             }
    1478                 :          0 :           }
    1479                 :          0 :           QDomText coordText = doc.createTextNode( coordString );
    1480                 :          0 :           coordElem.appendChild( coordText );
    1481                 :          0 :           ringElem.appendChild( coordElem );
    1482                 :          0 :           boundaryElem.appendChild( ringElem );
    1483                 :          0 :           polygonElem.appendChild( boundaryElem );
    1484                 :          0 :         }
    1485                 :          0 :         return polygonElem;
    1486                 :          0 :       }
    1487                 :            :       case QgsWkbTypes::MultiPolygon25D:
    1488                 :          0 :         hasZValue = true;
    1489                 :            :         //intentional fall-through
    1490                 :            :         FALLTHROUGH
    1491                 :            :       case QgsWkbTypes::MultiPolygon:
    1492                 :            :       {
    1493                 :          0 :         QDomElement multiPolygonElem = doc.createElement( QStringLiteral( "gml:MultiPolygon" ) );
    1494                 :          0 :         if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
    1495                 :          0 :           multiPolygonElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase );
    1496                 :          0 :         if ( !srsName.isEmpty() )
    1497                 :          0 :           multiPolygonElem.setAttribute( QStringLiteral( "srsName" ), srsName );
    1498                 :            : 
    1499                 :            :         int numPolygons;
    1500                 :          0 :         wkbPtr >> numPolygons;
    1501                 :            : 
    1502                 :          0 :         for ( int kdx = 0; kdx < numPolygons; kdx++ )
    1503                 :            :         {
    1504                 :          0 :           QDomElement polygonMemberElem = doc.createElement( QStringLiteral( "gml:polygonMember" ) );
    1505                 :          0 :           QDomElement polygonElem = doc.createElement( QStringLiteral( "gml:Polygon" ) );
    1506                 :          0 :           if ( gmlVersion == GML_3_2_1 && !gmlIdBase.isEmpty() )
    1507                 :          0 :             polygonElem.setAttribute( QStringLiteral( "gml:id" ), gmlIdBase + QStringLiteral( ".%1" ).arg( kdx + 1 ) );
    1508                 :            : 
    1509                 :          0 :           wkbPtr.readHeader();
    1510                 :            : 
    1511                 :            :           int numRings;
    1512                 :          0 :           wkbPtr >> numRings;
    1513                 :            : 
    1514                 :          0 :           for ( int idx = 0; idx < numRings; idx++ )
    1515                 :            :           {
    1516                 :          0 :             QString boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:outerBoundaryIs" : "gml:exterior";
    1517                 :          0 :             if ( idx != 0 )
    1518                 :            :             {
    1519                 :          0 :               boundaryName = ( gmlVersion == GML_2_1_2 ) ? "gml:innerBoundaryIs" : "gml:interior";
    1520                 :          0 :             }
    1521                 :          0 :             QDomElement boundaryElem = doc.createElement( boundaryName );
    1522                 :          0 :             QDomElement ringElem = doc.createElement( QStringLiteral( "gml:LinearRing" ) );
    1523                 :            : 
    1524                 :            :             int nPoints;
    1525                 :          0 :             wkbPtr >> nPoints;
    1526                 :            : 
    1527                 :          0 :             QDomElement coordElem = baseCoordElem.cloneNode().toElement();
    1528                 :          0 :             QString coordString;
    1529                 :          0 :             for ( int jdx = 0; jdx < nPoints; jdx++ )
    1530                 :            :             {
    1531                 :          0 :               if ( jdx != 0 )
    1532                 :            :               {
    1533                 :          0 :                 coordString += ts;
    1534                 :          0 :               }
    1535                 :            : 
    1536                 :          0 :               double x = 0;
    1537                 :          0 :               double y = 0;
    1538                 :          0 :               if ( invertAxisOrientation )
    1539                 :          0 :                 wkbPtr >> y >> x;
    1540                 :            :               else
    1541                 :          0 :                 wkbPtr >> x >> y;
    1542                 :            : 
    1543                 :          0 :               coordString += qgsDoubleToString( x, precision ) + cs + qgsDoubleToString( y, precision );
    1544                 :            : 
    1545                 :          0 :               if ( hasZValue )
    1546                 :            :               {
    1547                 :          0 :                 wkbPtr += sizeof( double );
    1548                 :          0 :               }
    1549                 :          0 :             }
    1550                 :          0 :             QDomText coordText = doc.createTextNode( coordString );
    1551                 :          0 :             coordElem.appendChild( coordText );
    1552                 :          0 :             ringElem.appendChild( coordElem );
    1553                 :          0 :             boundaryElem.appendChild( ringElem );
    1554                 :          0 :             polygonElem.appendChild( boundaryElem );
    1555                 :          0 :             polygonMemberElem.appendChild( polygonElem );
    1556                 :          0 :             multiPolygonElem.appendChild( polygonMemberElem );
    1557                 :          0 :           }
    1558                 :          0 :         }
    1559                 :          0 :         return multiPolygonElem;
    1560                 :          0 :       }
    1561                 :            :       default:
    1562                 :          0 :         return QDomElement();
    1563                 :            :     }
    1564                 :          0 :   }
    1565                 :            :   catch ( const QgsWkbException &e )
    1566                 :            :   {
    1567                 :          0 :     Q_UNUSED( e )
    1568                 :          0 :     return QDomElement();
    1569                 :          0 :   }
    1570                 :          0 : }
    1571                 :            : 
    1572                 :          0 : QDomElement QgsOgcUtils::geometryToGML( const QgsGeometry &geometry, QDomDocument &doc, int precision )
    1573                 :            : {
    1574                 :          0 :   return geometryToGML( geometry, doc, QStringLiteral( "GML2" ), precision );
    1575                 :          0 : }
    1576                 :            : 
    1577                 :          0 : QDomElement QgsOgcUtils::createGMLCoordinates( const QgsPolylineXY &points, QDomDocument &doc )
    1578                 :            : {
    1579                 :          0 :   QDomElement coordElem = doc.createElement( QStringLiteral( "gml:coordinates" ) );
    1580                 :          0 :   coordElem.setAttribute( QStringLiteral( "cs" ), QStringLiteral( "," ) );
    1581                 :          0 :   coordElem.setAttribute( QStringLiteral( "ts" ), QStringLiteral( " " ) );
    1582                 :            : 
    1583                 :          0 :   QString coordString;
    1584                 :          0 :   QVector<QgsPointXY>::const_iterator pointIt = points.constBegin();
    1585                 :          0 :   for ( ; pointIt != points.constEnd(); ++pointIt )
    1586                 :            :   {
    1587                 :          0 :     if ( pointIt != points.constBegin() )
    1588                 :            :     {
    1589                 :          0 :       coordString += ' ';
    1590                 :          0 :     }
    1591                 :          0 :     coordString += qgsDoubleToString( pointIt->x() );
    1592                 :          0 :     coordString += ',';
    1593                 :          0 :     coordString += qgsDoubleToString( pointIt->y() );
    1594                 :          0 :   }
    1595                 :            : 
    1596                 :          0 :   QDomText coordText = doc.createTextNode( coordString );
    1597                 :          0 :   coordElem.appendChild( coordText );
    1598                 :          0 :   return coordElem;
    1599                 :          0 : }
    1600                 :            : 
    1601                 :          0 : QDomElement QgsOgcUtils::createGMLPositions( const QgsPolylineXY &points, QDomDocument &doc )
    1602                 :            : {
    1603                 :          0 :   QDomElement posElem = doc.createElement( QStringLiteral( "gml:pos" ) );
    1604                 :          0 :   if ( points.size() > 1 )
    1605                 :          0 :     posElem = doc.createElement( QStringLiteral( "gml:posList" ) );
    1606                 :          0 :   posElem.setAttribute( QStringLiteral( "srsDimension" ), QStringLiteral( "2" ) );
    1607                 :            : 
    1608                 :          0 :   QString coordString;
    1609                 :          0 :   QVector<QgsPointXY>::const_iterator pointIt = points.constBegin();
    1610                 :          0 :   for ( ; pointIt != points.constEnd(); ++pointIt )
    1611                 :            :   {
    1612                 :          0 :     if ( pointIt != points.constBegin() )
    1613                 :            :     {
    1614                 :          0 :       coordString += ' ';
    1615                 :          0 :     }
    1616                 :          0 :     coordString += qgsDoubleToString( pointIt->x() );
    1617                 :          0 :     coordString += ' ';
    1618                 :          0 :     coordString += qgsDoubleToString( pointIt->y() );
    1619                 :          0 :   }
    1620                 :            : 
    1621                 :          0 :   QDomText coordText = doc.createTextNode( coordString );
    1622                 :          0 :   posElem.appendChild( coordText );
    1623                 :          0 :   return posElem;
    1624                 :          0 : }
    1625                 :            : 
    1626                 :            : 
    1627                 :            : 
    1628                 :            : // -----------------------------------------
    1629                 :            : 
    1630                 :          0 : QColor QgsOgcUtils::colorFromOgcFill( const QDomElement &fillElement )
    1631                 :            : {
    1632                 :          0 :   if ( fillElement.isNull() || !fillElement.hasChildNodes() )
    1633                 :            :   {
    1634                 :          0 :     return QColor();
    1635                 :            :   }
    1636                 :            : 
    1637                 :          0 :   QString cssName;
    1638                 :          0 :   QString elemText;
    1639                 :          0 :   QColor color;
    1640                 :          0 :   QDomElement cssElem = fillElement.firstChildElement( QStringLiteral( "CssParameter" ) );
    1641                 :          0 :   while ( !cssElem.isNull() )
    1642                 :            :   {
    1643                 :          0 :     cssName = cssElem.attribute( QStringLiteral( "name" ), QStringLiteral( "not_found" ) );
    1644                 :          0 :     if ( cssName != QLatin1String( "not_found" ) )
    1645                 :            :     {
    1646                 :          0 :       elemText = cssElem.text();
    1647                 :          0 :       if ( cssName == QLatin1String( "fill" ) )
    1648                 :            :       {
    1649                 :          0 :         color.setNamedColor( elemText );
    1650                 :          0 :       }
    1651                 :          0 :       else if ( cssName == QLatin1String( "fill-opacity" ) )
    1652                 :            :       {
    1653                 :            :         bool ok;
    1654                 :          0 :         double opacity = elemText.toDouble( &ok );
    1655                 :          0 :         if ( ok )
    1656                 :            :         {
    1657                 :          0 :           color.setAlphaF( opacity );
    1658                 :          0 :         }
    1659                 :          0 :       }
    1660                 :          0 :     }
    1661                 :            : 
    1662                 :          0 :     cssElem = cssElem.nextSiblingElement( QStringLiteral( "CssParameter" ) );
    1663                 :            :   }
    1664                 :            : 
    1665                 :          0 :   return color;
    1666                 :          0 : }
    1667                 :            : 
    1668                 :            : 
    1669                 :          0 : QgsExpression *QgsOgcUtils::expressionFromOgcFilter( const QDomElement &element, QgsVectorLayer *layer )
    1670                 :            : {
    1671                 :          0 :   return expressionFromOgcFilter( element, QgsOgcUtils::FILTER_OGC_1_0, layer );
    1672                 :            : }
    1673                 :            : 
    1674                 :          0 : QgsExpression *QgsOgcUtils::expressionFromOgcFilter( const QDomElement &element, const FilterVersion version, QgsVectorLayer *layer )
    1675                 :            : {
    1676                 :          0 :   if ( element.isNull() || !element.hasChildNodes() )
    1677                 :          0 :     return nullptr;
    1678                 :            : 
    1679                 :          0 :   QgsExpression *expr = new QgsExpression();
    1680                 :            : 
    1681                 :            :   // check if it is a single string value not having DOM elements
    1682                 :            :   // that express OGC operators
    1683                 :          0 :   if ( element.firstChild().nodeType() == QDomNode::TextNode )
    1684                 :            :   {
    1685                 :          0 :     expr->setExpression( element.firstChild().nodeValue() );
    1686                 :          0 :     return expr;
    1687                 :            :   }
    1688                 :            : 
    1689                 :          0 :   QgsOgcUtilsExpressionFromFilter utils( version, layer );
    1690                 :            : 
    1691                 :            :   // then check OGC DOM elements that contain OGC tags specifying
    1692                 :            :   // OGC operators.
    1693                 :          0 :   QDomElement childElem = element.firstChildElement();
    1694                 :          0 :   while ( !childElem.isNull() )
    1695                 :            :   {
    1696                 :          0 :     QgsExpressionNode *node = utils.nodeFromOgcFilter( childElem );
    1697                 :            : 
    1698                 :          0 :     if ( !node )
    1699                 :            :     {
    1700                 :            :       // invalid expression, parser error
    1701                 :          0 :       expr->d->mParserErrorString = utils.errorMessage();
    1702                 :          0 :       return expr;
    1703                 :            :     }
    1704                 :            : 
    1705                 :            :     // use the concat binary operator to append to the root node
    1706                 :          0 :     if ( !expr->d->mRootNode )
    1707                 :            :     {
    1708                 :          0 :       expr->d->mRootNode = node;
    1709                 :          0 :     }
    1710                 :            :     else
    1711                 :            :     {
    1712                 :          0 :       expr->d->mRootNode = new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boConcat, expr->d->mRootNode, node );
    1713                 :            :     }
    1714                 :            : 
    1715                 :          0 :     childElem = childElem.nextSiblingElement();
    1716                 :            :   }
    1717                 :            : 
    1718                 :            :   // update expression string
    1719                 :          0 :   expr->d->mExp = expr->dump();
    1720                 :            : 
    1721                 :          0 :   return expr;
    1722                 :          0 : }
    1723                 :            : 
    1724                 :            : typedef QMap<QString, int> IntMap;
    1725                 :          0 : Q_GLOBAL_STATIC_WITH_ARGS( IntMap, BINARY_OPERATORS_TAG_NAMES_MAP, (
    1726                 :            : {
    1727                 :            :   // logical
    1728                 :            :   {  QLatin1String( "Or" ), QgsExpressionNodeBinaryOperator::boOr },
    1729                 :            :   {  QLatin1String( "And" ), QgsExpressionNodeBinaryOperator::boAnd },
    1730                 :            :   // comparison
    1731                 :            :   {  QLatin1String( "PropertyIsEqualTo" ), QgsExpressionNodeBinaryOperator::boEQ },
    1732                 :            :   {  QLatin1String( "PropertyIsNotEqualTo" ), QgsExpressionNodeBinaryOperator::boNE },
    1733                 :            :   {  QLatin1String( "PropertyIsLessThanOrEqualTo" ), QgsExpressionNodeBinaryOperator::boLE },
    1734                 :            :   {  QLatin1String( "PropertyIsGreaterThanOrEqualTo" ), QgsExpressionNodeBinaryOperator::boGE },
    1735                 :            :   {  QLatin1String( "PropertyIsLessThan" ), QgsExpressionNodeBinaryOperator::boLT },
    1736                 :            :   {  QLatin1String( "PropertyIsGreaterThan" ), QgsExpressionNodeBinaryOperator::boGT },
    1737                 :            :   {  QLatin1String( "PropertyIsLike" ), QgsExpressionNodeBinaryOperator::boLike },
    1738                 :            :   // arithmetic
    1739                 :            :   {  QLatin1String( "Add" ), QgsExpressionNodeBinaryOperator::boPlus },
    1740                 :            :   {  QLatin1String( "Sub" ), QgsExpressionNodeBinaryOperator::boMinus },
    1741                 :            :   {  QLatin1String( "Mul" ), QgsExpressionNodeBinaryOperator::boMul },
    1742                 :            :   {  QLatin1String( "Div" ), QgsExpressionNodeBinaryOperator::boDiv },
    1743                 :            : } ) )
    1744                 :            : 
    1745                 :          0 : static int binaryOperatorFromTagName( const QString &tagName )
    1746                 :            : {
    1747                 :            : 
    1748                 :          0 :   return BINARY_OPERATORS_TAG_NAMES_MAP()->value( tagName, -1 );
    1749                 :            : }
    1750                 :            : 
    1751                 :          0 : static QString binaryOperatorToTagName( QgsExpressionNodeBinaryOperator::BinaryOperator op )
    1752                 :            : {
    1753                 :          0 :   if ( op == QgsExpressionNodeBinaryOperator::boILike )
    1754                 :            :   {
    1755                 :          0 :     return QStringLiteral( "PropertyIsLike" );
    1756                 :            :   }
    1757                 :          0 :   return BINARY_OPERATORS_TAG_NAMES_MAP()->key( op, QString() );
    1758                 :          0 : }
    1759                 :            : 
    1760                 :          0 : static bool isBinaryOperator( const QString &tagName )
    1761                 :            : {
    1762                 :          0 :   return binaryOperatorFromTagName( tagName ) >= 0;
    1763                 :            : }
    1764                 :            : 
    1765                 :            : 
    1766                 :          0 : static bool isSpatialOperator( const QString &tagName )
    1767                 :            : {
    1768                 :          0 :   static QStringList spatialOps;
    1769                 :          0 :   if ( spatialOps.isEmpty() )
    1770                 :            :   {
    1771                 :          0 :     spatialOps << QStringLiteral( "BBOX" ) << QStringLiteral( "Intersects" ) << QStringLiteral( "Contains" ) << QStringLiteral( "Crosses" ) << QStringLiteral( "Equals" )
    1772                 :          0 :                << QStringLiteral( "Disjoint" ) << QStringLiteral( "Overlaps" ) << QStringLiteral( "Touches" ) << QStringLiteral( "Within" );
    1773                 :          0 :   }
    1774                 :            : 
    1775                 :          0 :   return spatialOps.contains( tagName );
    1776                 :          0 : }
    1777                 :            : 
    1778                 :          0 : QgsExpressionNode *QgsOgcUtils::nodeFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer )
    1779                 :            : {
    1780                 :          0 :   QgsOgcUtilsExpressionFromFilter utils( QgsOgcUtils::FILTER_OGC_1_0, layer );
    1781                 :          0 :   QgsExpressionNode *node = utils.nodeFromOgcFilter( element );
    1782                 :          0 :   errorMessage = utils.errorMessage();
    1783                 :          0 :   return node;
    1784                 :          0 : }
    1785                 :            : 
    1786                 :          0 : QgsExpressionNodeBinaryOperator *QgsOgcUtils::nodeBinaryOperatorFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer )
    1787                 :            : {
    1788                 :          0 :   QgsOgcUtilsExpressionFromFilter utils( QgsOgcUtils::FILTER_OGC_1_0, layer );
    1789                 :          0 :   QgsExpressionNodeBinaryOperator *node = utils.nodeBinaryOperatorFromOgcFilter( element );
    1790                 :          0 :   errorMessage = utils.errorMessage();
    1791                 :          0 :   return node;
    1792                 :          0 : }
    1793                 :            : 
    1794                 :          0 : QgsExpressionNodeFunction *QgsOgcUtils::nodeSpatialOperatorFromOgcFilter( QDomElement &element, QString &errorMessage )
    1795                 :            : {
    1796                 :          0 :   QgsOgcUtilsExpressionFromFilter utils( QgsOgcUtils::FILTER_OGC_1_0 );
    1797                 :          0 :   QgsExpressionNodeFunction *node = utils.nodeSpatialOperatorFromOgcFilter( element );
    1798                 :          0 :   errorMessage = utils.errorMessage();
    1799                 :          0 :   return node;
    1800                 :          0 : }
    1801                 :            : 
    1802                 :          0 : QgsExpressionNodeUnaryOperator *QgsOgcUtils::nodeNotFromOgcFilter( QDomElement &element, QString &errorMessage )
    1803                 :            : {
    1804                 :          0 :   QgsOgcUtilsExpressionFromFilter utils( QgsOgcUtils::FILTER_OGC_1_0 );
    1805                 :          0 :   QgsExpressionNodeUnaryOperator *node = utils.nodeNotFromOgcFilter( element );
    1806                 :          0 :   errorMessage = utils.errorMessage();
    1807                 :          0 :   return node;
    1808                 :          0 : }
    1809                 :            : 
    1810                 :          0 : QgsExpressionNodeFunction *QgsOgcUtils::nodeFunctionFromOgcFilter( QDomElement &element, QString &errorMessage )
    1811                 :            : {
    1812                 :          0 :   QgsOgcUtilsExpressionFromFilter utils( QgsOgcUtils::FILTER_OGC_1_0 );
    1813                 :          0 :   QgsExpressionNodeFunction *node = utils.nodeFunctionFromOgcFilter( element );
    1814                 :          0 :   errorMessage = utils.errorMessage();
    1815                 :          0 :   return node;
    1816                 :          0 : }
    1817                 :            : 
    1818                 :          0 : QgsExpressionNode *QgsOgcUtils::nodeLiteralFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer )
    1819                 :            : {
    1820                 :          0 :   QgsOgcUtilsExpressionFromFilter utils( QgsOgcUtils::FILTER_OGC_1_0, layer );
    1821                 :          0 :   QgsExpressionNode *node = utils.nodeLiteralFromOgcFilter( element );
    1822                 :          0 :   errorMessage = utils.errorMessage();
    1823                 :          0 :   return node;
    1824                 :          0 : }
    1825                 :            : 
    1826                 :          0 : QgsExpressionNodeColumnRef *QgsOgcUtils::nodeColumnRefFromOgcFilter( QDomElement &element, QString &errorMessage )
    1827                 :            : {
    1828                 :          0 :   QgsOgcUtilsExpressionFromFilter utils( QgsOgcUtils::FILTER_OGC_1_0 );
    1829                 :          0 :   QgsExpressionNodeColumnRef *node = utils.nodeColumnRefFromOgcFilter( element );
    1830                 :          0 :   errorMessage = utils.errorMessage();
    1831                 :          0 :   return node;
    1832                 :          0 : }
    1833                 :            : 
    1834                 :          0 : QgsExpressionNode *QgsOgcUtils::nodeIsBetweenFromOgcFilter( QDomElement &element, QString &errorMessage )
    1835                 :            : {
    1836                 :          0 :   QgsOgcUtilsExpressionFromFilter utils( QgsOgcUtils::FILTER_OGC_1_0 );
    1837                 :          0 :   QgsExpressionNode *node = utils.nodeIsBetweenFromOgcFilter( element );
    1838                 :          0 :   errorMessage = utils.errorMessage();
    1839                 :          0 :   return node;
    1840                 :          0 : }
    1841                 :            : 
    1842                 :          0 : QgsExpressionNodeBinaryOperator *QgsOgcUtils::nodePropertyIsNullFromOgcFilter( QDomElement &element, QString &errorMessage )
    1843                 :            : {
    1844                 :          0 :   QgsOgcUtilsExpressionFromFilter utils( QgsOgcUtils::FILTER_OGC_1_0 );
    1845                 :          0 :   QgsExpressionNodeBinaryOperator *node = utils.nodePropertyIsNullFromOgcFilter( element );
    1846                 :          0 :   errorMessage = utils.errorMessage();
    1847                 :          0 :   return node;
    1848                 :          0 : }
    1849                 :            : 
    1850                 :            : 
    1851                 :            : /////////////////
    1852                 :            : 
    1853                 :            : 
    1854                 :          0 : QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression &exp, QDomDocument &doc, QString *errorMessage )
    1855                 :            : {
    1856                 :          0 :   return expressionToOgcFilter( exp, doc, GML_2_1_2, FILTER_OGC_1_0,
    1857                 :          0 :                                 QStringLiteral( "geometry" ), QString(), false, false, errorMessage );
    1858                 :          0 : }
    1859                 :            : 
    1860                 :          0 : QDomElement QgsOgcUtils::expressionToOgcExpression( const QgsExpression &exp, QDomDocument &doc, QString *errorMessage )
    1861                 :            : {
    1862                 :          0 :   return expressionToOgcExpression( exp, doc, GML_2_1_2, FILTER_OGC_1_0,
    1863                 :          0 :                                     QStringLiteral( "geometry" ), QString(), false, false, errorMessage );
    1864                 :          0 : }
    1865                 :            : 
    1866                 :          0 : QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression &expression,
    1867                 :            :     QDomDocument &doc,
    1868                 :            :     GMLVersion gmlVersion,
    1869                 :            :     FilterVersion filterVersion,
    1870                 :            :     const QString &geometryName,
    1871                 :            :     const QString &srsName,
    1872                 :            :     bool honourAxisOrientation,
    1873                 :            :     bool invertAxisOrientation,
    1874                 :            :     QString *errorMessage )
    1875                 :            : {
    1876                 :          0 :   if ( !expression.rootNode() )
    1877                 :          0 :     return QDomElement();
    1878                 :            : 
    1879                 :          0 :   QgsExpression exp = expression;
    1880                 :            : 
    1881                 :          0 :   QgsExpressionContext context;
    1882                 :          0 :   context << QgsExpressionContextUtils::globalScope();
    1883                 :          0 :   QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
    1884                 :          0 :   QDomElement exprRootElem = utils.expressionNodeToOgcFilter( exp.rootNode(), &exp, &context );
    1885                 :          0 :   if ( errorMessage )
    1886                 :          0 :     *errorMessage = utils.errorMessage();
    1887                 :          0 :   if ( exprRootElem.isNull() )
    1888                 :          0 :     return QDomElement();
    1889                 :            : 
    1890                 :            :   QDomElement filterElem =
    1891                 :          0 :     ( filterVersion == FILTER_FES_2_0 ) ?
    1892                 :          0 :     doc.createElementNS( FES_NAMESPACE, QStringLiteral( "fes:Filter" ) ) :
    1893                 :          0 :     doc.createElementNS( OGC_NAMESPACE, QStringLiteral( "ogc:Filter" ) );
    1894                 :          0 :   if ( utils.GMLNamespaceUsed() )
    1895                 :            :   {
    1896                 :          0 :     QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:gml" ) );
    1897                 :          0 :     if ( gmlVersion == GML_3_2_1 )
    1898                 :          0 :       attr.setValue( GML32_NAMESPACE );
    1899                 :            :     else
    1900                 :          0 :       attr.setValue( GML_NAMESPACE );
    1901                 :          0 :     filterElem.setAttributeNode( attr );
    1902                 :          0 :   }
    1903                 :          0 :   filterElem.appendChild( exprRootElem );
    1904                 :          0 :   return filterElem;
    1905                 :          0 : }
    1906                 :            : 
    1907                 :          0 : QDomElement QgsOgcUtils::expressionToOgcExpression( const QgsExpression &expression,
    1908                 :            :     QDomDocument &doc,
    1909                 :            :     GMLVersion gmlVersion,
    1910                 :            :     FilterVersion filterVersion,
    1911                 :            :     const QString &geometryName,
    1912                 :            :     const QString &srsName,
    1913                 :            :     bool honourAxisOrientation,
    1914                 :            :     bool invertAxisOrientation,
    1915                 :            :     QString *errorMessage )
    1916                 :            : {
    1917                 :          0 :   QgsExpressionContext context;
    1918                 :          0 :   context << QgsExpressionContextUtils::globalScope();
    1919                 :            : 
    1920                 :          0 :   QgsExpression exp = expression;
    1921                 :            : 
    1922                 :          0 :   const QgsExpressionNode *node = exp.rootNode();
    1923                 :          0 :   if ( !node )
    1924                 :          0 :     return QDomElement();
    1925                 :            : 
    1926                 :          0 :   switch ( node->nodeType() )
    1927                 :            :   {
    1928                 :            :     case QgsExpressionNode::ntFunction:
    1929                 :            :     case QgsExpressionNode::ntLiteral:
    1930                 :            :     case QgsExpressionNode::ntColumnRef:
    1931                 :            :     {
    1932                 :          0 :       QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
    1933                 :          0 :       QDomElement exprRootElem = utils.expressionNodeToOgcFilter( node, &exp, &context );
    1934                 :            : 
    1935                 :          0 :       if ( errorMessage )
    1936                 :          0 :         *errorMessage = utils.errorMessage();
    1937                 :            : 
    1938                 :          0 :       if ( !exprRootElem.isNull() )
    1939                 :            :       {
    1940                 :          0 :         return exprRootElem;
    1941                 :            :       }
    1942                 :          0 :       break;
    1943                 :          0 :     }
    1944                 :            :     default:
    1945                 :            :     {
    1946                 :          0 :       if ( errorMessage )
    1947                 :          0 :         *errorMessage = QObject::tr( "Node type not supported in expression translation: %1" ).arg( node->nodeType() );
    1948                 :            :     }
    1949                 :          0 :   }
    1950                 :            :   // got an error
    1951                 :          0 :   return QDomElement();
    1952                 :          0 : }
    1953                 :            : 
    1954                 :          0 : QDomElement QgsOgcUtils::SQLStatementToOgcFilter( const QgsSQLStatement &statement,
    1955                 :            :     QDomDocument &doc,
    1956                 :            :     GMLVersion gmlVersion,
    1957                 :            :     FilterVersion filterVersion,
    1958                 :            :     const QList<LayerProperties> &layerProperties,
    1959                 :            :     bool honourAxisOrientation,
    1960                 :            :     bool invertAxisOrientation,
    1961                 :            :     const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename,
    1962                 :            :     QString *errorMessage )
    1963                 :            : {
    1964                 :          0 :   if ( !statement.rootNode() )
    1965                 :          0 :     return QDomElement();
    1966                 :            : 
    1967                 :          0 :   QgsOgcUtilsSQLStatementToFilter utils( doc, gmlVersion, filterVersion,
    1968                 :          0 :                                          layerProperties, honourAxisOrientation, invertAxisOrientation,
    1969                 :          0 :                                          mapUnprefixedTypenameToPrefixedTypename );
    1970                 :          0 :   QDomElement exprRootElem = utils.toOgcFilter( statement.rootNode() );
    1971                 :          0 :   if ( errorMessage )
    1972                 :          0 :     *errorMessage = utils.errorMessage();
    1973                 :          0 :   if ( exprRootElem.isNull() )
    1974                 :          0 :     return QDomElement();
    1975                 :            : 
    1976                 :            :   QDomElement filterElem =
    1977                 :          0 :     ( filterVersion == FILTER_FES_2_0 ) ?
    1978                 :          0 :     doc.createElementNS( FES_NAMESPACE, QStringLiteral( "fes:Filter" ) ) :
    1979                 :          0 :     doc.createElementNS( OGC_NAMESPACE, QStringLiteral( "ogc:Filter" ) );
    1980                 :          0 :   if ( utils.GMLNamespaceUsed() )
    1981                 :            :   {
    1982                 :          0 :     QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:gml" ) );
    1983                 :          0 :     if ( gmlVersion == GML_3_2_1 )
    1984                 :          0 :       attr.setValue( GML32_NAMESPACE );
    1985                 :            :     else
    1986                 :          0 :       attr.setValue( GML_NAMESPACE );
    1987                 :          0 :     filterElem.setAttributeNode( attr );
    1988                 :          0 :   }
    1989                 :          0 :   filterElem.appendChild( exprRootElem );
    1990                 :          0 :   return filterElem;
    1991                 :          0 : }
    1992                 :            : 
    1993                 :            : //
    1994                 :            : 
    1995                 :            : 
    1996                 :          0 : QDomElement QgsOgcUtilsExprToFilter::expressionNodeToOgcFilter( const QgsExpressionNode *node, QgsExpression *expression, const QgsExpressionContext *context )
    1997                 :            : {
    1998                 :          0 :   switch ( node->nodeType() )
    1999                 :            :   {
    2000                 :            :     case QgsExpressionNode::ntUnaryOperator:
    2001                 :          0 :       return expressionUnaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeUnaryOperator *>( node ), expression, context );
    2002                 :            :     case QgsExpressionNode::ntBinaryOperator:
    2003                 :          0 :       return expressionBinaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeBinaryOperator *>( node ), expression, context );
    2004                 :            :     case QgsExpressionNode::ntInOperator:
    2005                 :          0 :       return expressionInOperatorToOgcFilter( static_cast<const QgsExpressionNodeInOperator *>( node ), expression, context );
    2006                 :            :     case QgsExpressionNode::ntFunction:
    2007                 :          0 :       return expressionFunctionToOgcFilter( static_cast<const QgsExpressionNodeFunction *>( node ), expression, context );
    2008                 :            :     case QgsExpressionNode::ntLiteral:
    2009                 :          0 :       return expressionLiteralToOgcFilter( static_cast<const QgsExpressionNodeLiteral *>( node ), expression, context );
    2010                 :            :     case QgsExpressionNode::ntColumnRef:
    2011                 :          0 :       return expressionColumnRefToOgcFilter( static_cast<const QgsExpressionNodeColumnRef *>( node ), expression, context );
    2012                 :            : 
    2013                 :            :     default:
    2014                 :          0 :       mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
    2015                 :          0 :       return QDomElement();
    2016                 :            :   }
    2017                 :          0 : }
    2018                 :            : 
    2019                 :          0 : QDomElement QgsOgcUtilsExprToFilter::expressionUnaryOperatorToOgcFilter( const QgsExpressionNodeUnaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
    2020                 :            : {
    2021                 :          0 :   QDomElement operandElem = expressionNodeToOgcFilter( node->operand(), expression, context );
    2022                 :          0 :   if ( !mErrorMessage.isEmpty() )
    2023                 :          0 :     return QDomElement();
    2024                 :            : 
    2025                 :          0 :   QDomElement uoElem;
    2026                 :          0 :   switch ( node->op() )
    2027                 :            :   {
    2028                 :            :     case QgsExpressionNodeUnaryOperator::uoMinus:
    2029                 :          0 :       uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
    2030                 :          0 :       if ( node->operand()->nodeType() == QgsExpressionNode::ntLiteral )
    2031                 :            :       {
    2032                 :            :         // operand expression already created a Literal node:
    2033                 :            :         // take the literal value, prepend - and remove old literal node
    2034                 :          0 :         uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
    2035                 :          0 :         mDoc.removeChild( operandElem );
    2036                 :          0 :       }
    2037                 :            :       else
    2038                 :            :       {
    2039                 :          0 :         mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
    2040                 :          0 :         return QDomElement();
    2041                 :            :       }
    2042                 :          0 :       break;
    2043                 :            :     case QgsExpressionNodeUnaryOperator::uoNot:
    2044                 :          0 :       uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
    2045                 :          0 :       uoElem.appendChild( operandElem );
    2046                 :          0 :       break;
    2047                 :            : 
    2048                 :            :     default:
    2049                 :          0 :       mErrorMessage = QObject::tr( "Unary operator '%1' not implemented yet" ).arg( node->text() );
    2050                 :          0 :       return QDomElement();
    2051                 :            :   }
    2052                 :            : 
    2053                 :          0 :   return uoElem;
    2054                 :          0 : }
    2055                 :            : 
    2056                 :            : 
    2057                 :          0 : QDomElement QgsOgcUtilsExprToFilter::expressionBinaryOperatorToOgcFilter( const QgsExpressionNodeBinaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
    2058                 :            : {
    2059                 :          0 :   QDomElement leftElem = expressionNodeToOgcFilter( node->opLeft(), expression, context );
    2060                 :          0 :   if ( !mErrorMessage.isEmpty() )
    2061                 :          0 :     return QDomElement();
    2062                 :            : 
    2063                 :          0 :   QgsExpressionNodeBinaryOperator::BinaryOperator op = node->op();
    2064                 :            : 
    2065                 :            :   // before right operator is parsed: to allow NULL handling
    2066                 :          0 :   if ( op == QgsExpressionNodeBinaryOperator::boIs || op == QgsExpressionNodeBinaryOperator::boIsNot )
    2067                 :            :   {
    2068                 :          0 :     if ( node->opRight()->nodeType() == QgsExpressionNode::ntLiteral )
    2069                 :            :     {
    2070                 :          0 :       const QgsExpressionNodeLiteral *rightLit = static_cast<const QgsExpressionNodeLiteral *>( node->opRight() );
    2071                 :          0 :       if ( rightLit->value().isNull() )
    2072                 :            :       {
    2073                 :            : 
    2074                 :          0 :         QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
    2075                 :          0 :         elem.appendChild( leftElem );
    2076                 :            : 
    2077                 :          0 :         if ( op == QgsExpressionNodeBinaryOperator::boIsNot )
    2078                 :            :         {
    2079                 :          0 :           QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
    2080                 :          0 :           notElem.appendChild( elem );
    2081                 :          0 :           return notElem;
    2082                 :          0 :         }
    2083                 :            : 
    2084                 :          0 :         return elem;
    2085                 :          0 :       }
    2086                 :            : 
    2087                 :            :       // continue with equal / not equal operator once the null case is handled
    2088                 :          0 :       op = ( op == QgsExpressionNodeBinaryOperator::boIs ? QgsExpressionNodeBinaryOperator::boEQ : QgsExpressionNodeBinaryOperator::boNE );
    2089                 :          0 :     }
    2090                 :            : 
    2091                 :          0 :   }
    2092                 :            : 
    2093                 :          0 :   QDomElement rightElem = expressionNodeToOgcFilter( node->opRight(), expression, context );
    2094                 :          0 :   if ( !mErrorMessage.isEmpty() )
    2095                 :          0 :     return QDomElement();
    2096                 :            : 
    2097                 :            : 
    2098                 :          0 :   QString opText = binaryOperatorToTagName( op );
    2099                 :          0 :   if ( opText.isEmpty() )
    2100                 :            :   {
    2101                 :            :     // not implemented binary operators
    2102                 :            :     // TODO: regex, % (mod), ^ (pow) are not supported yet
    2103                 :          0 :     mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( node->text() );
    2104                 :          0 :     return QDomElement();
    2105                 :            :   }
    2106                 :            : 
    2107                 :          0 :   QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
    2108                 :            : 
    2109                 :          0 :   if ( op == QgsExpressionNodeBinaryOperator::boLike || op == QgsExpressionNodeBinaryOperator::boILike )
    2110                 :            :   {
    2111                 :          0 :     if ( op == QgsExpressionNodeBinaryOperator::boILike )
    2112                 :          0 :       boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
    2113                 :            : 
    2114                 :            :     // setup wildCards to <ogc:PropertyIsLike>
    2115                 :          0 :     boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
    2116                 :          0 :     boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
    2117                 :          0 :     if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
    2118                 :          0 :       boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
    2119                 :            :     else
    2120                 :          0 :       boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
    2121                 :          0 :   }
    2122                 :            : 
    2123                 :          0 :   boElem.appendChild( leftElem );
    2124                 :          0 :   boElem.appendChild( rightElem );
    2125                 :          0 :   return boElem;
    2126                 :          0 : }
    2127                 :            : 
    2128                 :            : 
    2129                 :          0 : QDomElement QgsOgcUtilsExprToFilter::expressionLiteralToOgcFilter( const QgsExpressionNodeLiteral *node, QgsExpression *expression, const QgsExpressionContext *context )
    2130                 :            : {
    2131                 :            :   Q_UNUSED( expression )
    2132                 :            :   Q_UNUSED( context )
    2133                 :          0 :   QString value;
    2134                 :          0 :   switch ( node->value().type() )
    2135                 :            :   {
    2136                 :            :     case QVariant::Int:
    2137                 :          0 :       value = QString::number( node->value().toInt() );
    2138                 :          0 :       break;
    2139                 :            :     case QVariant::Double:
    2140                 :          0 :       value = qgsDoubleToString( node->value().toDouble() );
    2141                 :          0 :       break;
    2142                 :            :     case QVariant::String:
    2143                 :          0 :       value = node->value().toString();
    2144                 :          0 :       break;
    2145                 :            :     case QVariant::Date:
    2146                 :          0 :       value = node->value().toDate().toString( Qt::ISODate );
    2147                 :          0 :       break;
    2148                 :            :     case QVariant::DateTime:
    2149                 :          0 :       value = node->value().toDateTime().toString( Qt::ISODate );
    2150                 :          0 :       break;
    2151                 :            : 
    2152                 :            :     default:
    2153                 :          0 :       mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( node->value().type() );
    2154                 :          0 :       return QDomElement();
    2155                 :            :   }
    2156                 :            : 
    2157                 :          0 :   QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
    2158                 :          0 :   litElem.appendChild( mDoc.createTextNode( value ) );
    2159                 :          0 :   return litElem;
    2160                 :          0 : }
    2161                 :            : 
    2162                 :            : 
    2163                 :          0 : QDomElement QgsOgcUtilsExprToFilter::expressionColumnRefToOgcFilter( const QgsExpressionNodeColumnRef *node, QgsExpression *expression, const QgsExpressionContext *context )
    2164                 :            : {
    2165                 :            :   Q_UNUSED( expression )
    2166                 :            :   Q_UNUSED( context )
    2167                 :          0 :   QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
    2168                 :          0 :   propElem.appendChild( mDoc.createTextNode( node->name() ) );
    2169                 :          0 :   return propElem;
    2170                 :          0 : }
    2171                 :            : 
    2172                 :            : 
    2173                 :            : 
    2174                 :          0 : QDomElement QgsOgcUtilsExprToFilter::expressionInOperatorToOgcFilter( const QgsExpressionNodeInOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
    2175                 :            : {
    2176                 :          0 :   if ( node->list()->list().size() == 1 )
    2177                 :          0 :     return expressionNodeToOgcFilter( node->list()->list()[0], expression, context );
    2178                 :            : 
    2179                 :          0 :   QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
    2180                 :          0 :   QDomElement leftNode = expressionNodeToOgcFilter( node->node(), expression, context );
    2181                 :            : 
    2182                 :          0 :   const auto constList = node->list()->list();
    2183                 :          0 :   for ( QgsExpressionNode *n : constList )
    2184                 :            :   {
    2185                 :          0 :     QDomElement listNode = expressionNodeToOgcFilter( n, expression, context );
    2186                 :          0 :     if ( !mErrorMessage.isEmpty() )
    2187                 :          0 :       return QDomElement();
    2188                 :            : 
    2189                 :          0 :     QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
    2190                 :          0 :     eqElem.appendChild( leftNode.cloneNode() );
    2191                 :          0 :     eqElem.appendChild( listNode );
    2192                 :            : 
    2193                 :          0 :     orElem.appendChild( eqElem );
    2194                 :          0 :   }
    2195                 :            : 
    2196                 :          0 :   if ( node->isNotIn() )
    2197                 :            :   {
    2198                 :          0 :     QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
    2199                 :          0 :     notElem.appendChild( orElem );
    2200                 :          0 :     return notElem;
    2201                 :          0 :   }
    2202                 :            : 
    2203                 :          0 :   return orElem;
    2204                 :          0 : }
    2205                 :            : 
    2206                 :          0 : Q_GLOBAL_STATIC_WITH_ARGS( QgsStringMap, BINARY_SPATIAL_OPS_MAP, (
    2207                 :            : {
    2208                 :            :   { QLatin1String( "disjoint" ),   QLatin1String( "Disjoint" ) },
    2209                 :            :   { QLatin1String( "intersects" ), QLatin1String( "Intersects" )},
    2210                 :            :   { QLatin1String( "touches" ),    QLatin1String( "Touches" ) },
    2211                 :            :   { QLatin1String( "crosses" ),    QLatin1String( "Crosses" ) },
    2212                 :            :   { QLatin1String( "contains" ),   QLatin1String( "Contains" ) },
    2213                 :            :   { QLatin1String( "overlaps" ),   QLatin1String( "Overlaps" ) },
    2214                 :            :   { QLatin1String( "within" ),     QLatin1String( "Within" ) }
    2215                 :            : } ) )
    2216                 :            : 
    2217                 :          0 : static bool isBinarySpatialOperator( const QString &fnName )
    2218                 :            : {
    2219                 :          0 :   return BINARY_SPATIAL_OPS_MAP()->contains( fnName );
    2220                 :            : }
    2221                 :            : 
    2222                 :          0 : static QString tagNameForSpatialOperator( const QString &fnName )
    2223                 :            : {
    2224                 :          0 :   return BINARY_SPATIAL_OPS_MAP()->value( fnName );
    2225                 :          0 : }
    2226                 :            : 
    2227                 :          0 : static bool isGeometryColumn( const QgsExpressionNode *node )
    2228                 :            : {
    2229                 :          0 :   if ( node->nodeType() != QgsExpressionNode::ntFunction )
    2230                 :          0 :     return false;
    2231                 :            : 
    2232                 :          0 :   const QgsExpressionNodeFunction *fn = static_cast<const QgsExpressionNodeFunction *>( node );
    2233                 :          0 :   QgsExpressionFunction *fd = QgsExpression::Functions()[fn->fnIndex()];
    2234                 :          0 :   return fd->name() == QLatin1String( "$geometry" );
    2235                 :          0 : }
    2236                 :            : 
    2237                 :          0 : static QgsGeometry geometryFromConstExpr( const QgsExpressionNode *node )
    2238                 :            : {
    2239                 :            :   // Right now we support only geomFromWKT(' ..... ')
    2240                 :            :   // Ideally we should support any constant sub-expression (not dependent on feature's geometry or attributes)
    2241                 :            : 
    2242                 :          0 :   if ( node->nodeType() == QgsExpressionNode::ntFunction )
    2243                 :            :   {
    2244                 :          0 :     const QgsExpressionNodeFunction *fnNode = static_cast<const QgsExpressionNodeFunction *>( node );
    2245                 :          0 :     QgsExpressionFunction *fnDef = QgsExpression::Functions()[fnNode->fnIndex()];
    2246                 :          0 :     if ( fnDef->name() == QLatin1String( "geom_from_wkt" ) )
    2247                 :            :     {
    2248                 :          0 :       const QList<QgsExpressionNode *> &args = fnNode->args()->list();
    2249                 :          0 :       if ( args[0]->nodeType() == QgsExpressionNode::ntLiteral )
    2250                 :            :       {
    2251                 :          0 :         QString wkt = static_cast<const QgsExpressionNodeLiteral *>( args[0] )->value().toString();
    2252                 :          0 :         return QgsGeometry::fromWkt( wkt );
    2253                 :          0 :       }
    2254                 :          0 :     }
    2255                 :          0 :   }
    2256                 :          0 :   return QgsGeometry();
    2257                 :          0 : }
    2258                 :            : 
    2259                 :            : 
    2260                 :          0 : QDomElement QgsOgcUtilsExprToFilter::expressionFunctionToOgcFilter( const QgsExpressionNodeFunction *node, QgsExpression *expression, const QgsExpressionContext *context )
    2261                 :            : {
    2262                 :          0 :   QgsExpressionFunction *fd = QgsExpression::Functions()[node->fnIndex()];
    2263                 :            : 
    2264                 :          0 :   if ( fd->name() == QLatin1String( "intersects_bbox" ) )
    2265                 :            :   {
    2266                 :          0 :     QList<QgsExpressionNode *> argNodes = node->args()->list();
    2267                 :            :     Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
    2268                 :            : 
    2269                 :          0 :     QgsGeometry geom = geometryFromConstExpr( argNodes[1] );
    2270                 :          0 :     if ( !geom.isNull() && isGeometryColumn( argNodes[0] ) )
    2271                 :            :     {
    2272                 :          0 :       QgsRectangle rect = geom.boundingBox();
    2273                 :            : 
    2274                 :          0 :       mGMLUsed = true;
    2275                 :            : 
    2276                 :          0 :       QDomElement elemBox = ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
    2277                 :          0 :                             QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, mSrsName, mInvertAxisOrientation ) :
    2278                 :          0 :                             QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, mSrsName, mInvertAxisOrientation );
    2279                 :            : 
    2280                 :          0 :       QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
    2281                 :          0 :       geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );
    2282                 :            : 
    2283                 :          0 :       QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":BBOX" );
    2284                 :          0 :       funcElem.appendChild( geomProperty );
    2285                 :          0 :       funcElem.appendChild( elemBox );
    2286                 :          0 :       return funcElem;
    2287                 :          0 :     }
    2288                 :            :     else
    2289                 :            :     {
    2290                 :          0 :       mErrorMessage = QObject::tr( "<BBOX> is currently supported only in form: bbox($geometry, geomFromWKT('…'))" );
    2291                 :          0 :       return QDomElement();
    2292                 :            :     }
    2293                 :          0 :   }
    2294                 :            : 
    2295                 :          0 :   if ( isBinarySpatialOperator( fd->name() ) )
    2296                 :            :   {
    2297                 :          0 :     QList<QgsExpressionNode *> argNodes = node->args()->list();
    2298                 :            :     Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
    2299                 :            : 
    2300                 :          0 :     QgsExpressionNode *otherNode = nullptr;
    2301                 :          0 :     if ( isGeometryColumn( argNodes[0] ) )
    2302                 :          0 :       otherNode = argNodes[1];
    2303                 :          0 :     else if ( isGeometryColumn( argNodes[1] ) )
    2304                 :          0 :       otherNode = argNodes[0];
    2305                 :            :     else
    2306                 :            :     {
    2307                 :          0 :       mErrorMessage = QObject::tr( "Unable to translate spatial operator: at least one must refer to geometry." );
    2308                 :          0 :       return QDomElement();
    2309                 :            :     }
    2310                 :            : 
    2311                 :          0 :     QDomElement otherGeomElem;
    2312                 :            : 
    2313                 :            :     // the other node must be a geometry constructor
    2314                 :          0 :     if ( otherNode->nodeType() != QgsExpressionNode::ntFunction )
    2315                 :            :     {
    2316                 :          0 :       mErrorMessage = QObject::tr( "spatial operator: the other operator must be a geometry constructor function" );
    2317                 :          0 :       return QDomElement();
    2318                 :            :     }
    2319                 :            : 
    2320                 :          0 :     const QgsExpressionNodeFunction *otherFn = static_cast<const QgsExpressionNodeFunction *>( otherNode );
    2321                 :          0 :     QgsExpressionFunction *otherFnDef = QgsExpression::Functions()[otherFn->fnIndex()];
    2322                 :          0 :     if ( otherFnDef->name() == QLatin1String( "geom_from_wkt" ) )
    2323                 :            :     {
    2324                 :          0 :       QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
    2325                 :          0 :       if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
    2326                 :            :       {
    2327                 :          0 :         mErrorMessage = QObject::tr( "geom_from_wkt: argument must be string literal" );
    2328                 :          0 :         return QDomElement();
    2329                 :            :       }
    2330                 :          0 :       QString wkt = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
    2331                 :          0 :       QgsGeometry geom = QgsGeometry::fromWkt( wkt );
    2332                 :          0 :       otherGeomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation,
    2333                 :          0 :                       QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
    2334                 :          0 :       mGeomId ++;
    2335                 :          0 :     }
    2336                 :          0 :     else if ( otherFnDef->name() == QLatin1String( "geom_from_gml" ) )
    2337                 :            :     {
    2338                 :          0 :       QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
    2339                 :          0 :       if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
    2340                 :            :       {
    2341                 :          0 :         mErrorMessage = QObject::tr( "geom_from_gml: argument must be string literal" );
    2342                 :          0 :         return QDomElement();
    2343                 :            :       }
    2344                 :            : 
    2345                 :          0 :       QDomDocument geomDoc;
    2346                 :          0 :       QString gml = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
    2347                 :          0 :       if ( !geomDoc.setContent( gml, true ) )
    2348                 :            :       {
    2349                 :          0 :         mErrorMessage = QObject::tr( "geom_from_gml: unable to parse XML" );
    2350                 :          0 :         return QDomElement();
    2351                 :            :       }
    2352                 :            : 
    2353                 :          0 :       QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
    2354                 :          0 :       otherGeomElem = geomNode.toElement();
    2355                 :          0 :     }
    2356                 :            :     else
    2357                 :            :     {
    2358                 :          0 :       mErrorMessage = QObject::tr( "spatial operator: unknown geometry constructor function" );
    2359                 :          0 :       return QDomElement();
    2360                 :            :     }
    2361                 :            : 
    2362                 :          0 :     mGMLUsed = true;
    2363                 :            : 
    2364                 :          0 :     QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + tagNameForSpatialOperator( fd->name() ) );
    2365                 :          0 :     QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
    2366                 :          0 :     geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );
    2367                 :          0 :     funcElem.appendChild( geomProperty );
    2368                 :          0 :     funcElem.appendChild( otherGeomElem );
    2369                 :          0 :     return funcElem;
    2370                 :          0 :   }
    2371                 :            : 
    2372                 :          0 :   if ( fd->isStatic( node, expression, context ) )
    2373                 :            :   {
    2374                 :          0 :     QVariant result = fd->run( node->args(), context, expression, node );
    2375                 :          0 :     QgsExpressionNodeLiteral literal( result );
    2376                 :          0 :     return expressionLiteralToOgcFilter( &literal, expression, context );
    2377                 :          0 :   }
    2378                 :            : 
    2379                 :          0 :   if ( fd->params() == 0 )
    2380                 :            :   {
    2381                 :          0 :     mErrorMessage = QObject::tr( "Special columns/constants are not supported." );
    2382                 :          0 :     return QDomElement();
    2383                 :            :   }
    2384                 :            : 
    2385                 :            :   // this is somehow wrong - we are just hoping that the other side supports the same functions as we do...
    2386                 :          0 :   QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
    2387                 :          0 :   funcElem.setAttribute( QStringLiteral( "name" ), fd->name() );
    2388                 :          0 :   const auto constList = node->args()->list();
    2389                 :          0 :   for ( QgsExpressionNode *n : constList )
    2390                 :            :   {
    2391                 :          0 :     QDomElement childElem = expressionNodeToOgcFilter( n, expression, context );
    2392                 :          0 :     if ( !mErrorMessage.isEmpty() )
    2393                 :          0 :       return QDomElement();
    2394                 :            : 
    2395                 :          0 :     funcElem.appendChild( childElem );
    2396                 :          0 :   }
    2397                 :            : 
    2398                 :          0 :   return funcElem;
    2399                 :          0 : }
    2400                 :            : 
    2401                 :            : //
    2402                 :            : 
    2403                 :          0 : QgsOgcUtilsSQLStatementToFilter::QgsOgcUtilsSQLStatementToFilter( QDomDocument &doc,
    2404                 :            :     QgsOgcUtils::GMLVersion gmlVersion,
    2405                 :            :     QgsOgcUtils::FilterVersion filterVersion,
    2406                 :            :     const QList<QgsOgcUtils::LayerProperties> &layerProperties,
    2407                 :            :     bool honourAxisOrientation,
    2408                 :            :     bool invertAxisOrientation,
    2409                 :            :     const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename )
    2410                 :          0 :   : mDoc( doc )
    2411                 :          0 :   , mGMLUsed( false )
    2412                 :          0 :   , mGMLVersion( gmlVersion )
    2413                 :          0 :   , mFilterVersion( filterVersion )
    2414                 :          0 :   , mLayerProperties( layerProperties )
    2415                 :          0 :   , mHonourAxisOrientation( honourAxisOrientation )
    2416                 :          0 :   , mInvertAxisOrientation( invertAxisOrientation )
    2417                 :          0 :   , mFilterPrefix( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "fes" : "ogc" )
    2418                 :          0 :   , mPropertyName( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "ValueReference" : "PropertyName" )
    2419                 :          0 :   , mGeomId( 1 )
    2420                 :          0 :   , mMapUnprefixedTypenameToPrefixedTypename( mapUnprefixedTypenameToPrefixedTypename )
    2421                 :            : {
    2422                 :          0 : }
    2423                 :            : 
    2424                 :          0 : QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement::Node *node )
    2425                 :            : {
    2426                 :          0 :   switch ( node->nodeType() )
    2427                 :            :   {
    2428                 :            :     case QgsSQLStatement::ntUnaryOperator:
    2429                 :          0 :       return toOgcFilter( static_cast<const QgsSQLStatement::NodeUnaryOperator *>( node ) );
    2430                 :            :     case QgsSQLStatement::ntBinaryOperator:
    2431                 :          0 :       return toOgcFilter( static_cast<const QgsSQLStatement::NodeBinaryOperator *>( node ) );
    2432                 :            :     case QgsSQLStatement::ntInOperator:
    2433                 :          0 :       return toOgcFilter( static_cast<const QgsSQLStatement::NodeInOperator *>( node ) );
    2434                 :            :     case QgsSQLStatement::ntBetweenOperator:
    2435                 :          0 :       return toOgcFilter( static_cast<const QgsSQLStatement::NodeBetweenOperator *>( node ) );
    2436                 :            :     case QgsSQLStatement::ntFunction:
    2437                 :          0 :       return toOgcFilter( static_cast<const QgsSQLStatement::NodeFunction *>( node ) );
    2438                 :            :     case QgsSQLStatement::ntLiteral:
    2439                 :          0 :       return toOgcFilter( static_cast<const QgsSQLStatement::NodeLiteral *>( node ) );
    2440                 :            :     case QgsSQLStatement::ntColumnRef:
    2441                 :          0 :       return toOgcFilter( static_cast<const QgsSQLStatement::NodeColumnRef *>( node ) );
    2442                 :            :     case QgsSQLStatement::ntSelect:
    2443                 :          0 :       return toOgcFilter( static_cast<const QgsSQLStatement::NodeSelect *>( node ) );
    2444                 :            : 
    2445                 :            :     default:
    2446                 :          0 :       mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
    2447                 :          0 :       return QDomElement();
    2448                 :            :   }
    2449                 :          0 : }
    2450                 :            : 
    2451                 :            : 
    2452                 :          0 : QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement::NodeUnaryOperator *node )
    2453                 :            : {
    2454                 :            : 
    2455                 :          0 :   QDomElement operandElem = toOgcFilter( node->operand() );
    2456                 :          0 :   if ( !mErrorMessage.isEmpty() )
    2457                 :          0 :     return QDomElement();
    2458                 :            : 
    2459                 :          0 :   QDomElement uoElem;
    2460                 :          0 :   switch ( node->op() )
    2461                 :            :   {
    2462                 :            :     case QgsSQLStatement::uoMinus:
    2463                 :          0 :       uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
    2464                 :          0 :       if ( node->operand()->nodeType() == QgsSQLStatement::ntLiteral )
    2465                 :            :       {
    2466                 :            :         // operand expression already created a Literal node:
    2467                 :            :         // take the literal value, prepend - and remove old literal node
    2468                 :          0 :         uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
    2469                 :          0 :         mDoc.removeChild( operandElem );
    2470                 :          0 :       }
    2471                 :            :       else
    2472                 :            :       {
    2473                 :          0 :         mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
    2474                 :          0 :         return QDomElement();
    2475                 :            :       }
    2476                 :          0 :       break;
    2477                 :            :     case QgsSQLStatement::uoNot:
    2478                 :          0 :       uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
    2479                 :          0 :       uoElem.appendChild( operandElem );
    2480                 :          0 :       break;
    2481                 :            : 
    2482                 :            :     default:
    2483                 :          0 :       mErrorMessage = QObject::tr( "Unary operator %1 not implemented yet" ).arg( QgsSQLStatement::UNARY_OPERATOR_TEXT[node->op()] );
    2484                 :          0 :       return QDomElement();
    2485                 :            :   }
    2486                 :            : 
    2487                 :          0 :   return uoElem;
    2488                 :          0 : }
    2489                 :            : 
    2490                 :            : 
    2491                 :          0 : QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement::NodeBinaryOperator *node )
    2492                 :            : {
    2493                 :          0 :   QDomElement leftElem = toOgcFilter( node->opLeft() );
    2494                 :          0 :   if ( !mErrorMessage.isEmpty() )
    2495                 :          0 :     return QDomElement();
    2496                 :            : 
    2497                 :          0 :   QgsSQLStatement::BinaryOperator op = node->op();
    2498                 :            : 
    2499                 :            :   // before right operator is parsed: to allow NULL handling
    2500                 :          0 :   if ( op == QgsSQLStatement::boIs || op == QgsSQLStatement::boIsNot )
    2501                 :            :   {
    2502                 :          0 :     if ( node->opRight()->nodeType() == QgsSQLStatement::ntLiteral )
    2503                 :            :     {
    2504                 :          0 :       const QgsSQLStatement::NodeLiteral *rightLit = static_cast<const QgsSQLStatement::NodeLiteral *>( node->opRight() );
    2505                 :          0 :       if ( rightLit->value().isNull() )
    2506                 :            :       {
    2507                 :            : 
    2508                 :          0 :         QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
    2509                 :          0 :         elem.appendChild( leftElem );
    2510                 :            : 
    2511                 :          0 :         if ( op == QgsSQLStatement::boIsNot )
    2512                 :            :         {
    2513                 :          0 :           QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
    2514                 :          0 :           notElem.appendChild( elem );
    2515                 :          0 :           return notElem;
    2516                 :          0 :         }
    2517                 :            : 
    2518                 :          0 :         return elem;
    2519                 :          0 :       }
    2520                 :            : 
    2521                 :            :       // continue with equal / not equal operator once the null case is handled
    2522                 :          0 :       op = ( op == QgsSQLStatement::boIs ? QgsSQLStatement::boEQ : QgsSQLStatement::boNE );
    2523                 :          0 :     }
    2524                 :            : 
    2525                 :          0 :   }
    2526                 :            : 
    2527                 :          0 :   QDomElement rightElem = toOgcFilter( node->opRight() );
    2528                 :          0 :   if ( !mErrorMessage.isEmpty() )
    2529                 :          0 :     return QDomElement();
    2530                 :            : 
    2531                 :            : 
    2532                 :          0 :   QString opText;
    2533                 :          0 :   if ( op == QgsSQLStatement::boOr )
    2534                 :          0 :     opText = QStringLiteral( "Or" );
    2535                 :          0 :   else if ( op == QgsSQLStatement::boAnd )
    2536                 :          0 :     opText = QStringLiteral( "And" );
    2537                 :          0 :   else if ( op == QgsSQLStatement::boEQ )
    2538                 :          0 :     opText = QStringLiteral( "PropertyIsEqualTo" );
    2539                 :          0 :   else if ( op == QgsSQLStatement::boNE )
    2540                 :          0 :     opText = QStringLiteral( "PropertyIsNotEqualTo" );
    2541                 :          0 :   else if ( op == QgsSQLStatement::boLE )
    2542                 :          0 :     opText = QStringLiteral( "PropertyIsLessThanOrEqualTo" );
    2543                 :          0 :   else if ( op == QgsSQLStatement::boGE )
    2544                 :          0 :     opText = QStringLiteral( "PropertyIsGreaterThanOrEqualTo" );
    2545                 :          0 :   else if ( op == QgsSQLStatement::boLT )
    2546                 :          0 :     opText = QStringLiteral( "PropertyIsLessThan" );
    2547                 :          0 :   else if ( op == QgsSQLStatement::boGT )
    2548                 :          0 :     opText = QStringLiteral( "PropertyIsGreaterThan" );
    2549                 :          0 :   else if ( op == QgsSQLStatement::boLike )
    2550                 :          0 :     opText = QStringLiteral( "PropertyIsLike" );
    2551                 :          0 :   else if ( op == QgsSQLStatement::boILike )
    2552                 :          0 :     opText = QStringLiteral( "PropertyIsLike" );
    2553                 :            : 
    2554                 :          0 :   if ( opText.isEmpty() )
    2555                 :            :   {
    2556                 :            :     // not implemented binary operators
    2557                 :          0 :     mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( QgsSQLStatement::BINARY_OPERATOR_TEXT[op] );
    2558                 :          0 :     return QDomElement();
    2559                 :            :   }
    2560                 :            : 
    2561                 :          0 :   QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
    2562                 :            : 
    2563                 :          0 :   if ( op == QgsSQLStatement::boLike || op == QgsSQLStatement::boILike )
    2564                 :            :   {
    2565                 :          0 :     if ( op == QgsSQLStatement::boILike )
    2566                 :          0 :       boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
    2567                 :            : 
    2568                 :            :     // setup wildCards to <ogc:PropertyIsLike>
    2569                 :          0 :     boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
    2570                 :          0 :     boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
    2571                 :          0 :     if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
    2572                 :          0 :       boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
    2573                 :            :     else
    2574                 :          0 :       boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
    2575                 :          0 :   }
    2576                 :            : 
    2577                 :          0 :   boElem.appendChild( leftElem );
    2578                 :          0 :   boElem.appendChild( rightElem );
    2579                 :          0 :   return boElem;
    2580                 :          0 : }
    2581                 :            : 
    2582                 :            : 
    2583                 :          0 : QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement::NodeLiteral *node )
    2584                 :            : {
    2585                 :          0 :   QString value;
    2586                 :          0 :   switch ( node->value().type() )
    2587                 :            :   {
    2588                 :            :     case QVariant::Int:
    2589                 :          0 :       value = QString::number( node->value().toInt() );
    2590                 :          0 :       break;
    2591                 :            :     case QVariant::LongLong:
    2592                 :          0 :       value = QString::number( node->value().toLongLong() );
    2593                 :          0 :       break;
    2594                 :            :     case QVariant::Double:
    2595                 :          0 :       value = qgsDoubleToString( node->value().toDouble() );
    2596                 :          0 :       break;
    2597                 :            :     case QVariant::String:
    2598                 :          0 :       value = node->value().toString();
    2599                 :          0 :       break;
    2600                 :            : 
    2601                 :            :     default:
    2602                 :          0 :       mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( node->value().type() );
    2603                 :          0 :       return QDomElement();
    2604                 :            :   }
    2605                 :            : 
    2606                 :          0 :   QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
    2607                 :          0 :   litElem.appendChild( mDoc.createTextNode( value ) );
    2608                 :          0 :   return litElem;
    2609                 :          0 : }
    2610                 :            : 
    2611                 :            : 
    2612                 :          0 : QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement::NodeColumnRef *node )
    2613                 :            : {
    2614                 :          0 :   QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
    2615                 :          0 :   if ( node->tableName().isEmpty() || mLayerProperties.size() == 1 )
    2616                 :          0 :     propElem.appendChild( mDoc.createTextNode( node->name() ) );
    2617                 :            :   else
    2618                 :            :   {
    2619                 :          0 :     QString tableName( mMapTableAliasToNames[node->tableName()] );
    2620                 :          0 :     if ( mMapUnprefixedTypenameToPrefixedTypename.contains( tableName ) )
    2621                 :          0 :       tableName = mMapUnprefixedTypenameToPrefixedTypename[tableName];
    2622                 :          0 :     propElem.appendChild( mDoc.createTextNode( tableName + "/" + node->name() ) );
    2623                 :          0 :   }
    2624                 :          0 :   return propElem;
    2625                 :          0 : }
    2626                 :            : 
    2627                 :          0 : QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement::NodeInOperator *node )
    2628                 :            : {
    2629                 :          0 :   if ( node->list()->list().size() == 1 )
    2630                 :          0 :     return toOgcFilter( node->list()->list()[0] );
    2631                 :            : 
    2632                 :          0 :   QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
    2633                 :          0 :   QDomElement leftNode = toOgcFilter( node->node() );
    2634                 :            : 
    2635                 :          0 :   const auto constList = node->list()->list();
    2636                 :          0 :   for ( QgsSQLStatement::Node *n : constList )
    2637                 :            :   {
    2638                 :          0 :     QDomElement listNode = toOgcFilter( n );
    2639                 :          0 :     if ( !mErrorMessage.isEmpty() )
    2640                 :          0 :       return QDomElement();
    2641                 :            : 
    2642                 :          0 :     QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
    2643                 :          0 :     eqElem.appendChild( leftNode.cloneNode() );
    2644                 :          0 :     eqElem.appendChild( listNode );
    2645                 :            : 
    2646                 :          0 :     orElem.appendChild( eqElem );
    2647                 :          0 :   }
    2648                 :            : 
    2649                 :          0 :   if ( node->isNotIn() )
    2650                 :            :   {
    2651                 :          0 :     QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
    2652                 :          0 :     notElem.appendChild( orElem );
    2653                 :          0 :     return notElem;
    2654                 :          0 :   }
    2655                 :            : 
    2656                 :          0 :   return orElem;
    2657                 :          0 : }
    2658                 :            : 
    2659                 :          0 : QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement::NodeBetweenOperator *node )
    2660                 :            : {
    2661                 :          0 :   QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsBetween" );
    2662                 :          0 :   elem.appendChild( toOgcFilter( node->node() ) );
    2663                 :          0 :   QDomElement lowerBoundary = mDoc.createElement( mFilterPrefix + ":LowerBoundary" );
    2664                 :          0 :   lowerBoundary.appendChild( toOgcFilter( node->minVal() ) );
    2665                 :          0 :   elem.appendChild( lowerBoundary );
    2666                 :          0 :   QDomElement upperBoundary = mDoc.createElement( mFilterPrefix + ":UpperBoundary" );
    2667                 :          0 :   upperBoundary.appendChild( toOgcFilter( node->maxVal() ) );
    2668                 :          0 :   elem.appendChild( upperBoundary );
    2669                 :            : 
    2670                 :          0 :   if ( node->isNotBetween() )
    2671                 :            :   {
    2672                 :          0 :     QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
    2673                 :          0 :     notElem.appendChild( elem );
    2674                 :          0 :     return notElem;
    2675                 :          0 :   }
    2676                 :            : 
    2677                 :          0 :   return elem;
    2678                 :          0 : }
    2679                 :            : 
    2680                 :          0 : static QString mapBinarySpatialToOgc( const QString &name )
    2681                 :            : {
    2682                 :          0 :   QString nameCompare( name );
    2683                 :            : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 2)
    2684                 :            :   if ( name.size() > 3 && name.midRef( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
    2685                 :            :     nameCompare = name.mid( 3 );
    2686                 :            : #else
    2687                 :          0 :   if ( name.size() > 3 && QStringView {name}.mid( 0, 3 ).toString().compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
    2688                 :          0 :     nameCompare = name.mid( 3 );
    2689                 :            : #endif
    2690                 :          0 :   QStringList spatialOps;
    2691                 :          0 :   spatialOps << QStringLiteral( "BBOX" ) << QStringLiteral( "Intersects" ) << QStringLiteral( "Contains" ) << QStringLiteral( "Crosses" ) << QStringLiteral( "Equals" )
    2692                 :          0 :              << QStringLiteral( "Disjoint" ) << QStringLiteral( "Overlaps" ) << QStringLiteral( "Touches" ) << QStringLiteral( "Within" );
    2693                 :          0 :   const auto constSpatialOps = spatialOps;
    2694                 :          0 :   for ( QString op : constSpatialOps )
    2695                 :            :   {
    2696                 :          0 :     if ( nameCompare.compare( op, Qt::CaseInsensitive ) == 0 )
    2697                 :          0 :       return op;
    2698                 :          0 :   }
    2699                 :          0 :   return QString();
    2700                 :          0 : }
    2701                 :            : 
    2702                 :          0 : static QString mapTernarySpatialToOgc( const QString &name )
    2703                 :            : {
    2704                 :          0 :   QString nameCompare( name );
    2705                 :            : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 2)
    2706                 :            :   if ( name.size() > 3 && name.midRef( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
    2707                 :            :     nameCompare = name.mid( 3 );
    2708                 :            : #else
    2709                 :          0 :   if ( name.size() > 3 && QStringView {name}.mid( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
    2710                 :          0 :     nameCompare = name.mid( 3 );
    2711                 :            : #endif
    2712                 :          0 :   if ( nameCompare.compare( QLatin1String( "DWithin" ), Qt::CaseInsensitive ) == 0 )
    2713                 :          0 :     return QStringLiteral( "DWithin" );
    2714                 :          0 :   if ( nameCompare.compare( QLatin1String( "Beyond" ), Qt::CaseInsensitive ) == 0 )
    2715                 :          0 :     return QStringLiteral( "Beyond" );
    2716                 :          0 :   return QString();
    2717                 :          0 : }
    2718                 :            : 
    2719                 :          0 : QString QgsOgcUtilsSQLStatementToFilter::getGeometryColumnSRSName( const QgsSQLStatement::Node *node )
    2720                 :            : {
    2721                 :          0 :   if ( node->nodeType() != QgsSQLStatement::ntColumnRef )
    2722                 :          0 :     return QString();
    2723                 :            : 
    2724                 :          0 :   const QgsSQLStatement::NodeColumnRef *col = static_cast<const QgsSQLStatement::NodeColumnRef *>( node );
    2725                 :          0 :   if ( !col->tableName().isEmpty() )
    2726                 :            :   {
    2727                 :          0 :     const auto constMLayerProperties = mLayerProperties;
    2728                 :          0 :     for ( QgsOgcUtils::LayerProperties prop : constMLayerProperties )
    2729                 :            :     {
    2730                 :          0 :       if ( prop.mName.compare( mMapTableAliasToNames[col->tableName()], Qt::CaseInsensitive ) == 0 &&
    2731                 :          0 :            prop.mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
    2732                 :            :       {
    2733                 :          0 :         return prop.mSRSName;
    2734                 :            :       }
    2735                 :          0 :     }
    2736                 :          0 :   }
    2737                 :          0 :   if ( !mLayerProperties.empty() &&
    2738                 :          0 :        mLayerProperties.at( 0 ).mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
    2739                 :            :   {
    2740                 :          0 :     return  mLayerProperties.at( 0 ).mSRSName;
    2741                 :            :   }
    2742                 :          0 :   return QString();
    2743                 :          0 : }
    2744                 :            : 
    2745                 :          0 : bool QgsOgcUtilsSQLStatementToFilter::processSRSName( const QgsSQLStatement::NodeFunction *mainNode,
    2746                 :            :     QList<QgsSQLStatement::Node *> args,
    2747                 :            :     bool lastArgIsSRSName,
    2748                 :            :     QString &srsName,
    2749                 :            :     bool &axisInversion )
    2750                 :            : {
    2751                 :          0 :   srsName = mCurrentSRSName;
    2752                 :          0 :   axisInversion = mInvertAxisOrientation;
    2753                 :            : 
    2754                 :          0 :   if ( lastArgIsSRSName )
    2755                 :            :   {
    2756                 :          0 :     QgsSQLStatement::Node *lastArg = args[ args.size() - 1 ];
    2757                 :          0 :     if ( lastArg->nodeType() != QgsSQLStatement::ntLiteral )
    2758                 :            :     {
    2759                 :          0 :       mErrorMessage = QObject::tr( "%1: Last argument must be string or integer literal" ).arg( mainNode->name() );
    2760                 :          0 :       return false;
    2761                 :            :     }
    2762                 :          0 :     const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( lastArg );
    2763                 :          0 :     if ( lit->value().type() == QVariant::Int )
    2764                 :            :     {
    2765                 :          0 :       if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
    2766                 :            :       {
    2767                 :          0 :         srsName = "EPSG:" + QString::number( lit->value().toInt() );
    2768                 :          0 :       }
    2769                 :            :       else
    2770                 :            :       {
    2771                 :          0 :         srsName = "urn:ogc:def:crs:EPSG::" + QString::number( lit->value().toInt() );
    2772                 :            :       }
    2773                 :          0 :     }
    2774                 :            :     else
    2775                 :            :     {
    2776                 :          0 :       srsName = lit->value().toString();
    2777                 :          0 :       if ( srsName.startsWith( QLatin1String( "EPSG:" ), Qt::CaseInsensitive ) )
    2778                 :          0 :         return true;
    2779                 :            :     }
    2780                 :          0 :   }
    2781                 :            : 
    2782                 :          0 :   QgsCoordinateReferenceSystem crs;
    2783                 :          0 :   if ( !srsName.isEmpty() )
    2784                 :          0 :     crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( srsName );
    2785                 :          0 :   if ( crs.isValid() )
    2786                 :            :   {
    2787                 :          0 :     if ( mHonourAxisOrientation && crs.hasAxisInverted() )
    2788                 :            :     {
    2789                 :          0 :       axisInversion = !axisInversion;
    2790                 :          0 :     }
    2791                 :          0 :   }
    2792                 :            : 
    2793                 :          0 :   return true;
    2794                 :          0 : }
    2795                 :            : 
    2796                 :          0 : QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement::NodeFunction *node )
    2797                 :            : {
    2798                 :            :   // ST_GeometryFromText
    2799                 :          0 :   if ( node->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 )
    2800                 :            :   {
    2801                 :          0 :     QList<QgsSQLStatement::Node *> args = node->args()->list();
    2802                 :          0 :     if ( args.size() != 1 && args.size() != 2 )
    2803                 :            :     {
    2804                 :          0 :       mErrorMessage = QObject::tr( "Function %1 should have 1 or 2 arguments" ).arg( node->name() );
    2805                 :          0 :       return QDomElement();
    2806                 :            :     }
    2807                 :            : 
    2808                 :          0 :     QgsSQLStatement::Node *firstFnArg = args[0];
    2809                 :          0 :     if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
    2810                 :            :     {
    2811                 :          0 :       mErrorMessage = QObject::tr( "%1: First argument must be string literal" ).arg( node->name() );
    2812                 :          0 :       return QDomElement();
    2813                 :            :     }
    2814                 :            : 
    2815                 :          0 :     QString srsName;
    2816                 :            :     bool axisInversion;
    2817                 :          0 :     if ( ! processSRSName( node, args, args.size() == 2, srsName, axisInversion ) )
    2818                 :            :     {
    2819                 :          0 :       return QDomElement();
    2820                 :            :     }
    2821                 :            : 
    2822                 :          0 :     QString wkt = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
    2823                 :          0 :     QgsGeometry geom = QgsGeometry::fromWkt( wkt );
    2824                 :          0 :     QDomElement geomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, srsName, axisInversion,
    2825                 :          0 :                            QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
    2826                 :          0 :     mGeomId ++;
    2827                 :          0 :     if ( geomElem.isNull() )
    2828                 :            :     {
    2829                 :          0 :       mErrorMessage = QObject::tr( "%1: invalid WKT" ).arg( node->name() );
    2830                 :          0 :       return QDomElement();
    2831                 :            :     }
    2832                 :          0 :     mGMLUsed = true;
    2833                 :          0 :     return geomElem;
    2834                 :          0 :   }
    2835                 :            : 
    2836                 :            :   // ST_MakeEnvelope
    2837                 :          0 :   if ( node->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 )
    2838                 :            :   {
    2839                 :          0 :     QList<QgsSQLStatement::Node *> args = node->args()->list();
    2840                 :          0 :     if ( args.size() != 4 && args.size() != 5 )
    2841                 :            :     {
    2842                 :          0 :       mErrorMessage = QObject::tr( "Function %1 should have 4 or 5 arguments" ).arg( node->name() );
    2843                 :          0 :       return QDomElement();
    2844                 :            :     }
    2845                 :            : 
    2846                 :          0 :     QgsRectangle rect;
    2847                 :            : 
    2848                 :          0 :     for ( int i = 0; i < 4; i++ )
    2849                 :            :     {
    2850                 :          0 :       QgsSQLStatement::Node *arg = args[i];
    2851                 :          0 :       if ( arg->nodeType() != QgsSQLStatement::ntLiteral )
    2852                 :            :       {
    2853                 :          0 :         mErrorMessage = QObject::tr( "%1: Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
    2854                 :          0 :         return QDomElement();
    2855                 :            :       }
    2856                 :          0 :       const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( arg );
    2857                 :          0 :       double val = 0.0;
    2858                 :          0 :       if ( lit->value().type() == QVariant::Int )
    2859                 :          0 :         val = lit->value().toInt();
    2860                 :          0 :       else if ( lit->value().type() == QVariant::LongLong )
    2861                 :          0 :         val = lit->value().toLongLong();
    2862                 :          0 :       else if ( lit->value().type() == QVariant::Double )
    2863                 :          0 :         val = lit->value().toDouble();
    2864                 :            :       else
    2865                 :            :       {
    2866                 :          0 :         mErrorMessage = QObject::tr( "%1 Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
    2867                 :          0 :         return QDomElement();
    2868                 :            :       }
    2869                 :          0 :       if ( i == 0 )
    2870                 :          0 :         rect.setXMinimum( val );
    2871                 :          0 :       else if ( i == 1 )
    2872                 :          0 :         rect.setYMinimum( val );
    2873                 :          0 :       else if ( i == 2 )
    2874                 :          0 :         rect.setXMaximum( val );
    2875                 :            :       else
    2876                 :          0 :         rect.setYMaximum( val );
    2877                 :          0 :     }
    2878                 :            : 
    2879                 :          0 :     QString srsName;
    2880                 :            :     bool axisInversion;
    2881                 :          0 :     if ( ! processSRSName( node, args, args.size() == 5, srsName, axisInversion ) )
    2882                 :            :     {
    2883                 :          0 :       return QDomElement();
    2884                 :            :     }
    2885                 :            : 
    2886                 :          0 :     mGMLUsed = true;
    2887                 :            : 
    2888                 :          0 :     return ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
    2889                 :          0 :            QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, srsName, axisInversion, 15 ) :
    2890                 :          0 :            QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, srsName, axisInversion, 15 );
    2891                 :          0 :   }
    2892                 :            : 
    2893                 :            :   // ST_GeomFromGML
    2894                 :          0 :   if ( node->name().compare( QLatin1String( "ST_GeomFromGML" ), Qt::CaseInsensitive ) == 0 )
    2895                 :            :   {
    2896                 :          0 :     QList<QgsSQLStatement::Node *> args = node->args()->list();
    2897                 :          0 :     if ( args.size() != 1 )
    2898                 :            :     {
    2899                 :          0 :       mErrorMessage = QObject::tr( "Function %1 should have 1 argument" ).arg( node->name() );
    2900                 :          0 :       return QDomElement();
    2901                 :            :     }
    2902                 :            : 
    2903                 :          0 :     QgsSQLStatement::Node *firstFnArg = args[0];
    2904                 :          0 :     if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
    2905                 :            :     {
    2906                 :          0 :       mErrorMessage = QObject::tr( "%1: Argument must be string literal" ).arg( node->name() );
    2907                 :          0 :       return QDomElement();
    2908                 :            :     }
    2909                 :            : 
    2910                 :          0 :     QDomDocument geomDoc;
    2911                 :          0 :     QString gml = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
    2912                 :          0 :     if ( !geomDoc.setContent( gml, true ) )
    2913                 :            :     {
    2914                 :          0 :       mErrorMessage = QObject::tr( "ST_GeomFromGML: unable to parse XML" );
    2915                 :          0 :       return QDomElement();
    2916                 :            :     }
    2917                 :            : 
    2918                 :          0 :     QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
    2919                 :          0 :     mGMLUsed = true;
    2920                 :          0 :     return geomNode.toElement();
    2921                 :          0 :   }
    2922                 :            : 
    2923                 :            :   // Binary geometry operators
    2924                 :          0 :   QString ogcName( mapBinarySpatialToOgc( node->name() ) );
    2925                 :          0 :   if ( !ogcName.isEmpty() )
    2926                 :            :   {
    2927                 :          0 :     QList<QgsSQLStatement::Node *> args = node->args()->list();
    2928                 :          0 :     if ( args.size() != 2 )
    2929                 :            :     {
    2930                 :          0 :       mErrorMessage = QObject::tr( "Function %1 should have 2 arguments" ).arg( node->name() );
    2931                 :          0 :       return QDomElement();
    2932                 :            :     }
    2933                 :            : 
    2934                 :          0 :     for ( int i = 0; i < 2; i ++ )
    2935                 :            :     {
    2936                 :          0 :       if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
    2937                 :          0 :            ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
    2938                 :          0 :              static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
    2939                 :            :       {
    2940                 :          0 :         mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
    2941                 :          0 :         break;
    2942                 :            :       }
    2943                 :          0 :     }
    2944                 :            : 
    2945                 :            :     //if( ogcName == "Intersects" && mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
    2946                 :            :     //  ogcName = "Intersect";
    2947                 :          0 :     QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + ogcName );
    2948                 :          0 :     const auto constArgs = args;
    2949                 :          0 :     for ( QgsSQLStatement::Node *n : constArgs )
    2950                 :            :     {
    2951                 :          0 :       QDomElement childElem = toOgcFilter( n );
    2952                 :          0 :       if ( !mErrorMessage.isEmpty() )
    2953                 :            :       {
    2954                 :          0 :         mCurrentSRSName.clear();
    2955                 :          0 :         return QDomElement();
    2956                 :            :       }
    2957                 :            : 
    2958                 :          0 :       funcElem.appendChild( childElem );
    2959                 :          0 :     }
    2960                 :            : 
    2961                 :          0 :     mCurrentSRSName.clear();
    2962                 :          0 :     return funcElem;
    2963                 :          0 :   }
    2964                 :            : 
    2965                 :          0 :   ogcName = mapTernarySpatialToOgc( node->name() );
    2966                 :          0 :   if ( !ogcName.isEmpty() )
    2967                 :            :   {
    2968                 :          0 :     QList<QgsSQLStatement::Node *> args = node->args()->list();
    2969                 :          0 :     if ( args.size() != 3 )
    2970                 :            :     {
    2971                 :          0 :       mErrorMessage = QObject::tr( "Function %1 should have 3 arguments" ).arg( node->name() );
    2972                 :          0 :       return QDomElement();
    2973                 :            :     }
    2974                 :            : 
    2975                 :          0 :     for ( int i = 0; i < 2; i ++ )
    2976                 :            :     {
    2977                 :          0 :       if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
    2978                 :          0 :            ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
    2979                 :          0 :              static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
    2980                 :            :       {
    2981                 :          0 :         mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
    2982                 :          0 :         break;
    2983                 :            :       }
    2984                 :          0 :     }
    2985                 :            : 
    2986                 :          0 :     QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + node->name().mid( 3 ) );
    2987                 :          0 :     for ( int i = 0; i < 2; i++ )
    2988                 :            :     {
    2989                 :          0 :       QDomElement childElem = toOgcFilter( args[i] );
    2990                 :          0 :       if ( !mErrorMessage.isEmpty() )
    2991                 :            :       {
    2992                 :          0 :         mCurrentSRSName.clear();
    2993                 :          0 :         return QDomElement();
    2994                 :            :       }
    2995                 :            : 
    2996                 :          0 :       funcElem.appendChild( childElem );
    2997                 :          0 :     }
    2998                 :          0 :     mCurrentSRSName.clear();
    2999                 :            : 
    3000                 :          0 :     QgsSQLStatement::Node *distanceNode = args[2];
    3001                 :          0 :     if ( distanceNode->nodeType() != QgsSQLStatement::ntLiteral )
    3002                 :            :     {
    3003                 :          0 :       mErrorMessage = QObject::tr( "Function %1 3rd argument should be a numeric value or a string made of a numeric value followed by a string" ).arg( node->name() );
    3004                 :          0 :       return QDomElement();
    3005                 :            :     }
    3006                 :          0 :     const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( distanceNode );
    3007                 :          0 :     if ( lit->value().isNull() )
    3008                 :            :     {
    3009                 :          0 :       mErrorMessage = QObject::tr( "Function %1 3rd argument should be a numeric value or a string made of a numeric value followed by a string" ).arg( node->name() );
    3010                 :          0 :       return QDomElement();
    3011                 :            :     }
    3012                 :          0 :     QString distance;
    3013                 :          0 :     QString unit( QStringLiteral( "m" ) );
    3014                 :          0 :     switch ( lit->value().type() )
    3015                 :            :     {
    3016                 :            :       case QVariant::Int:
    3017                 :          0 :         distance = QString::number( lit->value().toInt() );
    3018                 :          0 :         break;
    3019                 :            :       case QVariant::LongLong:
    3020                 :          0 :         distance = QString::number( lit->value().toLongLong() );
    3021                 :          0 :         break;
    3022                 :            :       case QVariant::Double:
    3023                 :          0 :         distance = qgsDoubleToString( lit->value().toDouble() );
    3024                 :          0 :         break;
    3025                 :            :       case QVariant::String:
    3026                 :            :       {
    3027                 :          0 :         distance = lit->value().toString();
    3028                 :          0 :         for ( int i = 0; i < distance.size(); i++ )
    3029                 :            :         {
    3030                 :          0 :           if ( !( ( distance[i] >= '0' && distance[i] <= '9' ) || distance[i] == '-' || distance[i] == '.' || distance[i] == 'e' || distance[i] == 'E' ) )
    3031                 :            :           {
    3032                 :          0 :             unit = distance.mid( i ).trimmed();
    3033                 :          0 :             distance = distance.mid( 0, i );
    3034                 :          0 :             break;
    3035                 :            :           }
    3036                 :          0 :         }
    3037                 :          0 :         break;
    3038                 :            :       }
    3039                 :            : 
    3040                 :            :       default:
    3041                 :          0 :         mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( lit->value().type() );
    3042                 :          0 :         return QDomElement();
    3043                 :            :     }
    3044                 :            : 
    3045                 :          0 :     QDomElement distanceElem = mDoc.createElement( mFilterPrefix + ":Distance" );
    3046                 :          0 :     if ( mFilterVersion == QgsOgcUtils::FILTER_FES_2_0 )
    3047                 :          0 :       distanceElem.setAttribute( QStringLiteral( "uom" ), unit );
    3048                 :            :     else
    3049                 :          0 :       distanceElem.setAttribute( QStringLiteral( "unit" ), unit );
    3050                 :          0 :     distanceElem.appendChild( mDoc.createTextNode( distance ) );
    3051                 :          0 :     funcElem.appendChild( distanceElem );
    3052                 :          0 :     return funcElem;
    3053                 :          0 :   }
    3054                 :            : 
    3055                 :            :   // Other function
    3056                 :          0 :   QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
    3057                 :          0 :   funcElem.setAttribute( QStringLiteral( "name" ), node->name() );
    3058                 :          0 :   const auto constList = node->args()->list();
    3059                 :          0 :   for ( QgsSQLStatement::Node *n : constList )
    3060                 :            :   {
    3061                 :          0 :     QDomElement childElem = toOgcFilter( n );
    3062                 :          0 :     if ( !mErrorMessage.isEmpty() )
    3063                 :          0 :       return QDomElement();
    3064                 :            : 
    3065                 :          0 :     funcElem.appendChild( childElem );
    3066                 :          0 :   }
    3067                 :          0 :   return funcElem;
    3068                 :          0 : }
    3069                 :            : 
    3070                 :          0 : QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement::NodeJoin *node,
    3071                 :            :     const QString &leftTable )
    3072                 :            : {
    3073                 :          0 :   QgsSQLStatement::Node *onExpr = node->onExpr();
    3074                 :          0 :   if ( onExpr )
    3075                 :            :   {
    3076                 :          0 :     return toOgcFilter( onExpr );
    3077                 :            :   }
    3078                 :            : 
    3079                 :          0 :   QList<QDomElement> listElem;
    3080                 :          0 :   const auto constUsingColumns = node->usingColumns();
    3081                 :          0 :   for ( const QString &columnName : constUsingColumns )
    3082                 :            :   {
    3083                 :          0 :     QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
    3084                 :          0 :     QDomElement propElem1 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
    3085                 :          0 :     propElem1.appendChild( mDoc.createTextNode( leftTable + "/" + columnName ) );
    3086                 :          0 :     eqElem.appendChild( propElem1 );
    3087                 :          0 :     QDomElement propElem2 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
    3088                 :          0 :     propElem2.appendChild( mDoc.createTextNode( node->tableDef()->name() + "/" + columnName ) );
    3089                 :          0 :     eqElem.appendChild( propElem2 );
    3090                 :          0 :     listElem.append( eqElem );
    3091                 :          0 :   }
    3092                 :            : 
    3093                 :          0 :   if ( listElem.size() == 1 )
    3094                 :            :   {
    3095                 :          0 :     return listElem[0];
    3096                 :            :   }
    3097                 :          0 :   else if ( listElem.size() > 1 )
    3098                 :            :   {
    3099                 :          0 :     QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
    3100                 :          0 :     const auto constListElem = listElem;
    3101                 :          0 :     for ( const QDomElement &elem : constListElem )
    3102                 :            :     {
    3103                 :          0 :       andElem.appendChild( elem );
    3104                 :            :     }
    3105                 :          0 :     return andElem;
    3106                 :          0 :   }
    3107                 :            : 
    3108                 :          0 :   return QDomElement();
    3109                 :          0 : }
    3110                 :            : 
    3111                 :          0 : void QgsOgcUtilsSQLStatementToFilter::visit( const QgsSQLStatement::NodeTableDef *node )
    3112                 :            : {
    3113                 :          0 :   if ( node->alias().isEmpty() )
    3114                 :            :   {
    3115                 :          0 :     mMapTableAliasToNames[ node->name()] = node->name();
    3116                 :          0 :   }
    3117                 :            :   else
    3118                 :            :   {
    3119                 :          0 :     mMapTableAliasToNames[ node->alias()] = node->name();
    3120                 :            :   }
    3121                 :          0 : }
    3122                 :            : 
    3123                 :          0 : QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement::NodeSelect *node )
    3124                 :            : {
    3125                 :          0 :   QList<QDomElement> listElem;
    3126                 :            : 
    3127                 :          0 :   if ( mFilterVersion != QgsOgcUtils::FILTER_FES_2_0 &&
    3128                 :          0 :        ( node->tables().size() != 1 || !node->joins().empty() ) )
    3129                 :            :   {
    3130                 :          0 :     mErrorMessage = QObject::tr( "Joins are only supported with WFS 2.0" );
    3131                 :          0 :     return QDomElement();
    3132                 :            :   }
    3133                 :            : 
    3134                 :            :   // Register all table name aliases
    3135                 :          0 :   const auto constTables = node->tables();
    3136                 :          0 :   for ( QgsSQLStatement::NodeTableDef *table : constTables )
    3137                 :            :   {
    3138                 :          0 :     visit( table );
    3139                 :            :   }
    3140                 :          0 :   const auto constJoins = node->joins();
    3141                 :          0 :   for ( QgsSQLStatement::NodeJoin *join : constJoins )
    3142                 :            :   {
    3143                 :          0 :     visit( join->tableDef() );
    3144                 :            :   }
    3145                 :            : 
    3146                 :            :   // Process JOIN conditions
    3147                 :          0 :   QList< QgsSQLStatement::NodeTableDef *> nodeTables = node->tables();
    3148                 :          0 :   QString leftTable = nodeTables.at( nodeTables.length() - 1 )->name();
    3149                 :          0 :   for ( QgsSQLStatement::NodeJoin *join : constJoins )
    3150                 :            :   {
    3151                 :          0 :     QDomElement joinElem = toOgcFilter( join, leftTable );
    3152                 :          0 :     if ( !mErrorMessage.isEmpty() )
    3153                 :          0 :       return QDomElement();
    3154                 :          0 :     listElem.append( joinElem );
    3155                 :          0 :     leftTable = join->tableDef()->name();
    3156                 :          0 :   }
    3157                 :            : 
    3158                 :            :   // Process WHERE conditions
    3159                 :          0 :   if ( node->where() )
    3160                 :            :   {
    3161                 :          0 :     QDomElement whereElem = toOgcFilter( node->where() );
    3162                 :          0 :     if ( !mErrorMessage.isEmpty() )
    3163                 :          0 :       return QDomElement();
    3164                 :          0 :     listElem.append( whereElem );
    3165                 :          0 :   }
    3166                 :            : 
    3167                 :            :   // Concatenate all conditions
    3168                 :          0 :   if ( listElem.size() == 1 )
    3169                 :            :   {
    3170                 :          0 :     return listElem[0];
    3171                 :            :   }
    3172                 :          0 :   else if ( listElem.size() > 1 )
    3173                 :            :   {
    3174                 :          0 :     QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
    3175                 :          0 :     const auto constListElem = listElem;
    3176                 :          0 :     for ( const QDomElement &elem : constListElem )
    3177                 :            :     {
    3178                 :          0 :       andElem.appendChild( elem );
    3179                 :            :     }
    3180                 :          0 :     return andElem;
    3181                 :          0 :   }
    3182                 :            : 
    3183                 :          0 :   return QDomElement();
    3184                 :          0 : }
    3185                 :            : 
    3186                 :          0 : QgsOgcUtilsExpressionFromFilter::QgsOgcUtilsExpressionFromFilter( const QgsOgcUtils::FilterVersion version, const QgsVectorLayer *layer )
    3187                 :          0 :   : mLayer( layer )
    3188                 :            : {
    3189                 :          0 :   mPropertyName = QStringLiteral( "PropertyName" );
    3190                 :          0 :   mPrefix = QStringLiteral( "ogc" );
    3191                 :            : 
    3192                 :          0 :   if ( version == QgsOgcUtils::FILTER_FES_2_0 )
    3193                 :            :   {
    3194                 :          0 :     mPropertyName = QStringLiteral( "ValueReference" );
    3195                 :          0 :     mPrefix = QStringLiteral( "fes" );
    3196                 :          0 :   }
    3197                 :          0 : }
    3198                 :            : 
    3199                 :          0 : QgsExpressionNode *QgsOgcUtilsExpressionFromFilter::nodeFromOgcFilter( const QDomElement &element )
    3200                 :            : {
    3201                 :          0 :   if ( element.isNull() )
    3202                 :          0 :     return nullptr;
    3203                 :            : 
    3204                 :            :   // check for binary operators
    3205                 :          0 :   if ( isBinaryOperator( element.tagName() ) )
    3206                 :            :   {
    3207                 :          0 :     return nodeBinaryOperatorFromOgcFilter( element );
    3208                 :            :   }
    3209                 :            : 
    3210                 :            :   // check for spatial operators
    3211                 :          0 :   if ( isSpatialOperator( element.tagName() ) )
    3212                 :            :   {
    3213                 :          0 :     return nodeSpatialOperatorFromOgcFilter( element );
    3214                 :            :   }
    3215                 :            : 
    3216                 :            :   // check for other OGC operators, convert them to expressions
    3217                 :          0 :   if ( element.tagName() == QLatin1String( "Not" ) )
    3218                 :            :   {
    3219                 :          0 :     return nodeNotFromOgcFilter( element );
    3220                 :            :   }
    3221                 :          0 :   else if ( element.tagName() == QLatin1String( "PropertyIsNull" ) )
    3222                 :            :   {
    3223                 :          0 :     return nodePropertyIsNullFromOgcFilter( element );
    3224                 :            :   }
    3225                 :          0 :   else if ( element.tagName() == QLatin1String( "Literal" ) )
    3226                 :            :   {
    3227                 :          0 :     return nodeLiteralFromOgcFilter( element );
    3228                 :            :   }
    3229                 :          0 :   else if ( element.tagName() == QLatin1String( "Function" ) )
    3230                 :            :   {
    3231                 :          0 :     return nodeFunctionFromOgcFilter( element );
    3232                 :            :   }
    3233                 :          0 :   else if ( element.tagName() == mPropertyName )
    3234                 :            :   {
    3235                 :          0 :     return nodeColumnRefFromOgcFilter( element );
    3236                 :            :   }
    3237                 :          0 :   else if ( element.tagName() == QLatin1String( "PropertyIsBetween" ) )
    3238                 :            :   {
    3239                 :          0 :     return nodeIsBetweenFromOgcFilter( element );
    3240                 :            :   }
    3241                 :            : 
    3242                 :          0 :   mErrorMessage += QObject::tr( "unable to convert '%1' element to a valid expression: it is not supported yet or it has invalid arguments" ).arg( element.tagName() );
    3243                 :          0 :   return nullptr;
    3244                 :          0 : }
    3245                 :            : 
    3246                 :          0 : QgsExpressionNodeBinaryOperator *QgsOgcUtilsExpressionFromFilter::nodeBinaryOperatorFromOgcFilter( const QDomElement &element )
    3247                 :            : {
    3248                 :          0 :   if ( element.isNull() )
    3249                 :          0 :     return nullptr;
    3250                 :            : 
    3251                 :          0 :   int op = binaryOperatorFromTagName( element.tagName() );
    3252                 :          0 :   if ( op < 0 )
    3253                 :            :   {
    3254                 :          0 :     mErrorMessage = QObject::tr( "'%1' binary operator not supported." ).arg( element.tagName() );
    3255                 :          0 :     return nullptr;
    3256                 :            :   }
    3257                 :            : 
    3258                 :          0 :   if ( op == QgsExpressionNodeBinaryOperator::boLike && element.hasAttribute( QStringLiteral( "matchCase" ) ) && element.attribute( QStringLiteral( "matchCase" ) ) == QLatin1String( "false" ) )
    3259                 :            :   {
    3260                 :          0 :     op = QgsExpressionNodeBinaryOperator::boILike;
    3261                 :          0 :   }
    3262                 :            : 
    3263                 :          0 :   QDomElement operandElem = element.firstChildElement();
    3264                 :          0 :   std::unique_ptr<QgsExpressionNode> expr( nodeFromOgcFilter( operandElem ) );
    3265                 :            : 
    3266                 :          0 :   if ( !expr )
    3267                 :            :   {
    3268                 :          0 :     mErrorMessage = QObject::tr( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
    3269                 :          0 :     return nullptr;
    3270                 :            :   }
    3271                 :            : 
    3272                 :          0 :   std::unique_ptr<QgsExpressionNode> leftOp( expr->clone() );
    3273                 :          0 :   for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
    3274                 :            :   {
    3275                 :          0 :     std::unique_ptr<QgsExpressionNode> opRight( nodeFromOgcFilter( operandElem ) );
    3276                 :          0 :     if ( !opRight )
    3277                 :            :     {
    3278                 :          0 :       mErrorMessage = QObject::tr( "invalid right operand for '%1' binary operator" ).arg( element.tagName() );
    3279                 :          0 :       return nullptr;
    3280                 :            :     }
    3281                 :            : 
    3282                 :          0 :     if ( op == QgsExpressionNodeBinaryOperator::boLike || op == QgsExpressionNodeBinaryOperator::boILike )
    3283                 :            :     {
    3284                 :          0 :       QString wildCard;
    3285                 :          0 :       if ( element.hasAttribute( QStringLiteral( "wildCard" ) ) )
    3286                 :            :       {
    3287                 :          0 :         wildCard = element.attribute( QStringLiteral( "wildCard" ) );
    3288                 :          0 :       }
    3289                 :          0 :       QString singleChar;
    3290                 :          0 :       if ( element.hasAttribute( QStringLiteral( "singleChar" ) ) )
    3291                 :            :       {
    3292                 :          0 :         singleChar = element.attribute( QStringLiteral( "singleChar" ) );
    3293                 :          0 :       }
    3294                 :          0 :       QString escape = QStringLiteral( "\\" );
    3295                 :          0 :       if ( element.hasAttribute( QStringLiteral( "escape" ) ) )
    3296                 :            :       {
    3297                 :          0 :         escape = element.attribute( QStringLiteral( "escape" ) );
    3298                 :          0 :       }
    3299                 :          0 :       if ( element.hasAttribute( QStringLiteral( "escapeChar" ) ) )
    3300                 :            :       {
    3301                 :          0 :         escape = element.attribute( QStringLiteral( "escapeChar" ) );
    3302                 :          0 :       }
    3303                 :            :       // replace
    3304                 :          0 :       QString oprValue = static_cast<const QgsExpressionNodeLiteral *>( opRight.get() )->value().toString();
    3305                 :          0 :       if ( !wildCard.isEmpty() && wildCard != QLatin1String( "%" ) )
    3306                 :            :       {
    3307                 :          0 :         oprValue.replace( '%', QLatin1String( "\\%" ) );
    3308                 :          0 :         if ( oprValue.startsWith( wildCard ) )
    3309                 :            :         {
    3310                 :          0 :           oprValue.replace( 0, 1, QStringLiteral( "%" ) );
    3311                 :          0 :         }
    3312                 :          0 :         QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( wildCard ) + ")" );
    3313                 :          0 :         int pos = 0;
    3314                 :          0 :         while ( ( pos = rx.indexIn( oprValue, pos ) ) != -1 )
    3315                 :            :         {
    3316                 :          0 :           oprValue.replace( pos + 1, 1, QStringLiteral( "%" ) );
    3317                 :          0 :           pos += 1;
    3318                 :            :         }
    3319                 :          0 :         oprValue.replace( escape + wildCard, wildCard );
    3320                 :          0 :       }
    3321                 :          0 :       if ( !singleChar.isEmpty() && singleChar != QLatin1String( "_" ) )
    3322                 :            :       {
    3323                 :          0 :         oprValue.replace( '_', QLatin1String( "\\_" ) );
    3324                 :          0 :         if ( oprValue.startsWith( singleChar ) )
    3325                 :            :         {
    3326                 :          0 :           oprValue.replace( 0, 1, QStringLiteral( "_" ) );
    3327                 :          0 :         }
    3328                 :          0 :         QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( singleChar ) + ")" );
    3329                 :          0 :         int pos = 0;
    3330                 :          0 :         while ( ( pos = rx.indexIn( oprValue, pos ) ) != -1 )
    3331                 :            :         {
    3332                 :          0 :           oprValue.replace( pos + 1, 1, QStringLiteral( "_" ) );
    3333                 :          0 :           pos += 1;
    3334                 :            :         }
    3335                 :          0 :         oprValue.replace( escape + singleChar, singleChar );
    3336                 :          0 :       }
    3337                 :          0 :       if ( !escape.isEmpty() && escape != QLatin1String( "\\" ) )
    3338                 :            :       {
    3339                 :          0 :         oprValue.replace( escape + escape, escape );
    3340                 :          0 :       }
    3341                 :          0 :       opRight.reset( new QgsExpressionNodeLiteral( oprValue ) );
    3342                 :          0 :     }
    3343                 :            : 
    3344                 :          0 :     expr.reset( new QgsExpressionNodeBinaryOperator( static_cast< QgsExpressionNodeBinaryOperator::BinaryOperator >( op ), expr.release(), opRight.release() ) );
    3345                 :          0 :   }
    3346                 :            : 
    3347                 :          0 :   if ( expr == leftOp )
    3348                 :            :   {
    3349                 :          0 :     mErrorMessage = QObject::tr( "only one operand for '%1' binary operator" ).arg( element.tagName() );
    3350                 :          0 :     return nullptr;
    3351                 :            :   }
    3352                 :            : 
    3353                 :          0 :   return dynamic_cast< QgsExpressionNodeBinaryOperator * >( expr.release() );
    3354                 :          0 : }
    3355                 :            : 
    3356                 :            : 
    3357                 :          0 : QgsExpressionNodeFunction *QgsOgcUtilsExpressionFromFilter::nodeSpatialOperatorFromOgcFilter( const QDomElement &element )
    3358                 :            : {
    3359                 :            :   // we are exploiting the fact that our function names are the same as the XML tag names
    3360                 :          0 :   const int opIdx = QgsExpression::functionIndex( element.tagName().toLower() );
    3361                 :            : 
    3362                 :          0 :   std::unique_ptr<QgsExpressionNode::NodeList> gml2Args( new QgsExpressionNode::NodeList() );
    3363                 :          0 :   QDomElement childElem = element.firstChildElement();
    3364                 :          0 :   QString gml2Str;
    3365                 :          0 :   while ( !childElem.isNull() && gml2Str.isEmpty() )
    3366                 :            :   {
    3367                 :          0 :     if ( childElem.tagName() != mPropertyName )
    3368                 :            :     {
    3369                 :          0 :       QTextStream gml2Stream( &gml2Str );
    3370                 :          0 :       childElem.save( gml2Stream, 0 );
    3371                 :          0 :     }
    3372                 :          0 :     childElem = childElem.nextSiblingElement();
    3373                 :            :   }
    3374                 :          0 :   if ( !gml2Str.isEmpty() )
    3375                 :            :   {
    3376                 :          0 :     gml2Args->append( new QgsExpressionNodeLiteral( QVariant( gml2Str.remove( '\n' ) ) ) );
    3377                 :          0 :   }
    3378                 :            :   else
    3379                 :            :   {
    3380                 :          0 :     mErrorMessage = QObject::tr( "No OGC Geometry found" );
    3381                 :          0 :     return nullptr;
    3382                 :            :   }
    3383                 :            : 
    3384                 :          0 :   std::unique_ptr<QgsExpressionNode::NodeList> opArgs( new QgsExpressionNode::NodeList() );
    3385                 :          0 :   opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "$geometry" ) ), new QgsExpressionNode::NodeList() ) );
    3386                 :          0 :   opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "geomFromGML" ) ), gml2Args.release() ) );
    3387                 :            : 
    3388                 :          0 :   return new QgsExpressionNodeFunction( opIdx, opArgs.release() );
    3389                 :          0 : }
    3390                 :            : 
    3391                 :          0 : QgsExpressionNodeColumnRef *QgsOgcUtilsExpressionFromFilter::nodeColumnRefFromOgcFilter( const QDomElement &element )
    3392                 :            : {
    3393                 :          0 :   if ( element.isNull() || element.tagName() != mPropertyName )
    3394                 :            :   {
    3395                 :          0 :     mErrorMessage = QObject::tr( "%1:PropertyName expected, got %2" ).arg( mPrefix, element.tagName() );
    3396                 :          0 :     return nullptr;
    3397                 :            :   }
    3398                 :            : 
    3399                 :          0 :   return new QgsExpressionNodeColumnRef( element.firstChild().nodeValue() );
    3400                 :          0 : }
    3401                 :            : 
    3402                 :          0 : QgsExpressionNode *QgsOgcUtilsExpressionFromFilter::nodeLiteralFromOgcFilter( const QDomElement &element )
    3403                 :            : {
    3404                 :          0 :   if ( element.isNull() || element.tagName() != QLatin1String( "Literal" ) )
    3405                 :            :   {
    3406                 :          0 :     mErrorMessage = QObject::tr( "%1:Literal expected, got %2" ).arg( mPrefix, element.tagName() );
    3407                 :          0 :     return nullptr;
    3408                 :            :   }
    3409                 :            : 
    3410                 :          0 :   std::unique_ptr<QgsExpressionNode> root;
    3411                 :            : 
    3412                 :            :   // the literal content can have more children (e.g. CDATA section, text, ...)
    3413                 :          0 :   QDomNode childNode = element.firstChild();
    3414                 :          0 :   while ( !childNode.isNull() )
    3415                 :            :   {
    3416                 :          0 :     std::unique_ptr<QgsExpressionNode> operand;
    3417                 :            : 
    3418                 :          0 :     if ( childNode.nodeType() == QDomNode::ElementNode )
    3419                 :            :     {
    3420                 :            :       // found a element node (e.g. PropertyName), convert it
    3421                 :          0 :       const QDomElement operandElem = childNode.toElement();
    3422                 :          0 :       operand.reset( nodeFromOgcFilter( operandElem ) );
    3423                 :          0 :       if ( !operand )
    3424                 :            :       {
    3425                 :          0 :         mErrorMessage = QObject::tr( "'%1' is an invalid or not supported content for %2:Literal" ).arg( operandElem.tagName(), mPrefix );
    3426                 :          0 :         return nullptr;
    3427                 :            :       }
    3428                 :          0 :     }
    3429                 :            :     else
    3430                 :            :     {
    3431                 :            :       // probably a text/CDATA node
    3432                 :          0 :       QVariant value = childNode.nodeValue();
    3433                 :            : 
    3434                 :          0 :       bool converted = false;
    3435                 :            : 
    3436                 :            :       // try to convert the node content to corresponding field type if possible
    3437                 :          0 :       if ( mLayer )
    3438                 :            :       {
    3439                 :          0 :         QDomElement propertyNameElement = element.previousSiblingElement( mPropertyName );
    3440                 :          0 :         if ( propertyNameElement.isNull() || propertyNameElement.tagName() != mPropertyName )
    3441                 :            :         {
    3442                 :          0 :           propertyNameElement = element.nextSiblingElement( mPropertyName );
    3443                 :          0 :         }
    3444                 :          0 :         if ( !propertyNameElement.isNull() || propertyNameElement.tagName() == mPropertyName )
    3445                 :            :         {
    3446                 :          0 :           const int fieldIndex = mLayer->fields().indexOf( propertyNameElement.firstChild().nodeValue() );
    3447                 :          0 :           if ( fieldIndex != -1 )
    3448                 :            :           {
    3449                 :          0 :             QgsField field = mLayer->fields().field( propertyNameElement.firstChild().nodeValue() );
    3450                 :          0 :             field.convertCompatible( value );
    3451                 :          0 :             converted = true;
    3452                 :          0 :           }
    3453                 :          0 :         }
    3454                 :          0 :       }
    3455                 :          0 :       if ( !converted )
    3456                 :            :       {
    3457                 :            :         // try to convert the node content to number if possible,
    3458                 :            :         // otherwise let's use it as string
    3459                 :            :         bool ok;
    3460                 :          0 :         const double d = value.toDouble( &ok );
    3461                 :          0 :         if ( ok )
    3462                 :          0 :           value = d;
    3463                 :          0 :       }
    3464                 :            : 
    3465                 :          0 :       operand.reset( new QgsExpressionNodeLiteral( value ) );
    3466                 :          0 :       if ( !operand )
    3467                 :          0 :         continue;
    3468                 :          0 :     }
    3469                 :            : 
    3470                 :            :     // use the concat operator to merge the ogc:Literal children
    3471                 :          0 :     if ( !root )
    3472                 :            :     {
    3473                 :          0 :       root = std::move( operand );
    3474                 :          0 :     }
    3475                 :            :     else
    3476                 :            :     {
    3477                 :          0 :       root.reset( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boConcat, root.release(), operand.release() ) );
    3478                 :            :     }
    3479                 :            : 
    3480                 :          0 :     childNode = childNode.nextSibling();
    3481                 :          0 :   }
    3482                 :            : 
    3483                 :          0 :   if ( root )
    3484                 :          0 :     return root.release();
    3485                 :            : 
    3486                 :          0 :   return nullptr;
    3487                 :          0 : }
    3488                 :            : 
    3489                 :          0 : QgsExpressionNodeUnaryOperator *QgsOgcUtilsExpressionFromFilter::nodeNotFromOgcFilter( const QDomElement &element )
    3490                 :            : {
    3491                 :          0 :   if ( element.tagName() != QLatin1String( "Not" ) )
    3492                 :          0 :     return nullptr;
    3493                 :            : 
    3494                 :          0 :   const QDomElement operandElem = element.firstChildElement();
    3495                 :          0 :   std::unique_ptr<QgsExpressionNode> operand( nodeFromOgcFilter( operandElem ) );
    3496                 :          0 :   if ( !operand )
    3497                 :            :   {
    3498                 :          0 :     mErrorMessage = QObject::tr( "invalid operand for '%1' unary operator" ).arg( element.tagName() );
    3499                 :          0 :     return nullptr;
    3500                 :            :   }
    3501                 :            : 
    3502                 :          0 :   return new QgsExpressionNodeUnaryOperator( QgsExpressionNodeUnaryOperator::uoNot, operand.release() );
    3503                 :          0 : }
    3504                 :            : 
    3505                 :          0 : QgsExpressionNodeBinaryOperator *QgsOgcUtilsExpressionFromFilter::nodePropertyIsNullFromOgcFilter( const QDomElement &element )
    3506                 :            : {
    3507                 :            :   // convert ogc:PropertyIsNull to IS operator with NULL right operand
    3508                 :          0 :   if ( element.tagName() != QLatin1String( "PropertyIsNull" ) )
    3509                 :            :   {
    3510                 :          0 :     return nullptr;
    3511                 :            :   }
    3512                 :            : 
    3513                 :          0 :   const QDomElement operandElem = element.firstChildElement();
    3514                 :          0 :   std::unique_ptr<QgsExpressionNode> opLeft( nodeFromOgcFilter( operandElem ) );
    3515                 :          0 :   if ( !opLeft )
    3516                 :          0 :     return nullptr;
    3517                 :            : 
    3518                 :          0 :   std::unique_ptr<QgsExpressionNode> opRight( new QgsExpressionNodeLiteral( QVariant() ) );
    3519                 :          0 :   return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boIs, opLeft.release(), opRight.release() );
    3520                 :          0 : }
    3521                 :            : 
    3522                 :          0 : QgsExpressionNodeFunction *QgsOgcUtilsExpressionFromFilter::nodeFunctionFromOgcFilter( const QDomElement &element )
    3523                 :            : {
    3524                 :          0 :   if ( element.isNull() || element.tagName() != QLatin1String( "Function" ) )
    3525                 :            :   {
    3526                 :          0 :     mErrorMessage = QObject::tr( "%1:Function expected, got %2" ).arg( mPrefix, element.tagName() );
    3527                 :          0 :     return nullptr;
    3528                 :            :   }
    3529                 :            : 
    3530                 :          0 :   for ( int i = 0; i < QgsExpression::Functions().size(); i++ )
    3531                 :            :   {
    3532                 :          0 :     const QgsExpressionFunction *funcDef = QgsExpression::Functions()[i];
    3533                 :            : 
    3534                 :          0 :     if ( element.attribute( QStringLiteral( "name" ) ) != funcDef->name() )
    3535                 :          0 :       continue;
    3536                 :            : 
    3537                 :          0 :     std::unique_ptr<QgsExpressionNode::NodeList> args( new QgsExpressionNode::NodeList() );
    3538                 :            : 
    3539                 :          0 :     QDomElement operandElem = element.firstChildElement();
    3540                 :          0 :     while ( !operandElem.isNull() )
    3541                 :            :     {
    3542                 :          0 :       std::unique_ptr<QgsExpressionNode> op( nodeFromOgcFilter( operandElem ) );
    3543                 :          0 :       if ( !op )
    3544                 :            :       {
    3545                 :          0 :         return nullptr;
    3546                 :            :       }
    3547                 :          0 :       args->append( op.release() );
    3548                 :            : 
    3549                 :          0 :       operandElem = operandElem.nextSiblingElement();
    3550                 :          0 :     }
    3551                 :            : 
    3552                 :          0 :     return new QgsExpressionNodeFunction( i, args.release() );
    3553                 :          0 :   }
    3554                 :            : 
    3555                 :          0 :   return nullptr;
    3556                 :          0 : }
    3557                 :            : 
    3558                 :          0 : QgsExpressionNode *QgsOgcUtilsExpressionFromFilter::nodeIsBetweenFromOgcFilter( const QDomElement &element )
    3559                 :            : {
    3560                 :            :   // <ogc:PropertyIsBetween> encode a Range check
    3561                 :          0 :   std::unique_ptr<QgsExpressionNode> operand;
    3562                 :          0 :   std::unique_ptr<QgsExpressionNode> lowerBound;
    3563                 :          0 :   std::unique_ptr<QgsExpressionNode> upperBound;
    3564                 :            : 
    3565                 :          0 :   QDomElement operandElem = element.firstChildElement();
    3566                 :          0 :   while ( !operandElem.isNull() )
    3567                 :            :   {
    3568                 :          0 :     if ( operandElem.tagName() == QLatin1String( "LowerBoundary" ) )
    3569                 :            :     {
    3570                 :          0 :       QDomElement lowerBoundElem = operandElem.firstChildElement();
    3571                 :          0 :       lowerBound.reset( nodeFromOgcFilter( lowerBoundElem ) );
    3572                 :          0 :     }
    3573                 :          0 :     else if ( operandElem.tagName() == QLatin1String( "UpperBoundary" ) )
    3574                 :            :     {
    3575                 :          0 :       QDomElement upperBoundElem = operandElem.firstChildElement();
    3576                 :          0 :       upperBound.reset( nodeFromOgcFilter( upperBoundElem ) );
    3577                 :          0 :     }
    3578                 :            :     else
    3579                 :            :     {
    3580                 :            :       // <ogc:expression>
    3581                 :          0 :       operand.reset( nodeFromOgcFilter( operandElem ) );
    3582                 :            :     }
    3583                 :            : 
    3584                 :          0 :     if ( operand && lowerBound && upperBound )
    3585                 :          0 :       break;
    3586                 :            : 
    3587                 :          0 :     operandElem = operandElem.nextSiblingElement();
    3588                 :            :   }
    3589                 :            : 
    3590                 :          0 :   if ( !operand || !lowerBound || !upperBound )
    3591                 :            :   {
    3592                 :          0 :     mErrorMessage = QObject::tr( "missing some required sub-elements in %1:PropertyIsBetween" ).arg( mPrefix );
    3593                 :          0 :     return nullptr;
    3594                 :            :   }
    3595                 :            : 
    3596                 :          0 :   std::unique_ptr<QgsExpressionNode> leOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boLE, operand->clone(), upperBound.release() ) );
    3597                 :          0 :   std::unique_ptr<QgsExpressionNode> geOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boGE, operand.release(), lowerBound.release() ) );
    3598                 :          0 :   return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boAnd, geOperator.release(), leOperator.release() );
    3599                 :          0 : }
    3600                 :            : 
    3601                 :          0 : QString QgsOgcUtilsExpressionFromFilter::errorMessage() const
    3602                 :            : {
    3603                 :          0 :   return mErrorMessage;
    3604                 :            : }

Generated by: LCOV version 1.14