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