QGIS API Documentation 3.40.0-Bratislava (b56115d8743)
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 {
1958 {
1959 QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, QString(), QString(), geometryName, srsName, honourAxisOrientation, invertAxisOrientation, fieldNameToXPathMap, namespacePrefixToUriMap );
1960 const QDomElement exprRootElem = utils.expressionNodeToOgcFilter( node, &exp, &context );
1961
1962 if ( errorMessage )
1963 *errorMessage = utils.errorMessage();
1964
1965 if ( !exprRootElem.isNull() )
1966 {
1967 if ( requiresFilterElement )
1968 {
1969 QDomElement filterElem = filterElement( doc, gmlVersion, filterVersion, utils.GMLNamespaceUsed() );
1970
1971 filterElem.appendChild( exprRootElem );
1972 return filterElem;
1973 }
1974 return exprRootElem;
1975 }
1976 break;
1977 }
1978 default:
1979 {
1980 if ( errorMessage )
1981 *errorMessage = QObject::tr( "Node type not supported in expression translation: %1" ).arg( node->nodeType() );
1982 }
1983 }
1984 // got an error
1985 return QDomElement();
1986}
1987
1989 QDomDocument &doc,
1990 GMLVersion gmlVersion,
1991 FilterVersion filterVersion,
1992 const QList<LayerProperties> &layerProperties,
1993 bool honourAxisOrientation,
1994 bool invertAxisOrientation,
1995 const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename,
1996 QString *errorMessage,
1997 const QMap<QString, QString> &fieldNameToXPathMap,
1998 const QMap<QString, QString> &namespacePrefixToUriMap )
1999{
2000 if ( !statement.rootNode() )
2001 return QDomElement();
2002
2003 QgsOgcUtilsSQLStatementToFilter utils( doc, gmlVersion, filterVersion,
2004 layerProperties, honourAxisOrientation, invertAxisOrientation,
2005 mapUnprefixedTypenameToPrefixedTypename, fieldNameToXPathMap, namespacePrefixToUriMap );
2006 const QDomElement exprRootElem = utils.toOgcFilter( statement.rootNode() );
2007 if ( errorMessage )
2008 *errorMessage = utils.errorMessage();
2009 if ( exprRootElem.isNull() )
2010 return QDomElement();
2011
2012 QDomElement filterElem = filterElement( doc, gmlVersion, filterVersion, utils.GMLNamespaceUsed() );
2013
2014 QSet<QString> setNamespaceURI;
2015 for ( const LayerProperties &props : layerProperties )
2016 {
2017 if ( !props.mNamespacePrefix.isEmpty() && !props.mNamespaceURI.isEmpty() &&
2018 !setNamespaceURI.contains( props.mNamespaceURI ) )
2019 {
2020 setNamespaceURI.insert( props.mNamespaceURI );
2021 QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:" ) + props.mNamespacePrefix );
2022 attr.setValue( props.mNamespaceURI );
2023 filterElem.setAttributeNode( attr );
2024 }
2025 }
2026 filterElem.appendChild( exprRootElem );
2027 return filterElem;
2028}
2029
2030//
2031
2032
2034{
2035 switch ( node->nodeType() )
2036 {
2038 return expressionUnaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeUnaryOperator *>( node ), expression, context );
2040 return expressionBinaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeBinaryOperator *>( node ), expression, context );
2042 return expressionInOperatorToOgcFilter( static_cast<const QgsExpressionNodeInOperator *>( node ), expression, context );
2044 return expressionFunctionToOgcFilter( static_cast<const QgsExpressionNodeFunction *>( node ), expression, context );
2046 return expressionLiteralToOgcFilter( static_cast<const QgsExpressionNodeLiteral *>( node ), expression, context );
2048 return expressionColumnRefToOgcFilter( static_cast<const QgsExpressionNodeColumnRef *>( node ), expression, context );
2049
2050 default:
2051 mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
2052 return QDomElement();
2053 }
2054}
2055
2056QDomElement QgsOgcUtilsExprToFilter::expressionUnaryOperatorToOgcFilter( const QgsExpressionNodeUnaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
2057{
2058 const QDomElement operandElem = expressionNodeToOgcFilter( node->operand(), expression, context );
2059 if ( !mErrorMessage.isEmpty() )
2060 return QDomElement();
2061
2062 QDomElement uoElem;
2063 switch ( node->op() )
2064 {
2066 uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2067 if ( node->operand()->nodeType() == QgsExpressionNode::ntLiteral )
2068 {
2069 // operand expression already created a Literal node:
2070 // take the literal value, prepend - and remove old literal node
2071 uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2072 mDoc.removeChild( operandElem );
2073 }
2074 else
2075 {
2076 mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2077 return QDomElement();
2078 }
2079 break;
2081 uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2082 uoElem.appendChild( operandElem );
2083 break;
2084
2085 default:
2086 mErrorMessage = QObject::tr( "Unary operator '%1' not implemented yet" ).arg( node->text() );
2087 return QDomElement();
2088 }
2089
2090 return uoElem;
2091}
2092
2093
2094QDomElement QgsOgcUtilsExprToFilter::expressionBinaryOperatorToOgcFilter( const QgsExpressionNodeBinaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
2095{
2096 const QDomElement leftElem = expressionNodeToOgcFilter( node->opLeft(), expression, context );
2097 if ( !mErrorMessage.isEmpty() )
2098 return QDomElement();
2099
2101
2102 // before right operator is parsed: to allow NULL handling
2104 {
2105 if ( node->opRight()->nodeType() == QgsExpressionNode::ntLiteral )
2106 {
2107 const QgsExpressionNodeLiteral *rightLit = static_cast<const QgsExpressionNodeLiteral *>( node->opRight() );
2108 if ( QgsVariantUtils::isNull( rightLit->value() ) )
2109 {
2110
2111 QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2112 elem.appendChild( leftElem );
2113
2115 {
2116 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2117 notElem.appendChild( elem );
2118 return notElem;
2119 }
2120
2121 return elem;
2122 }
2123
2124 // continue with equal / not equal operator once the null case is handled
2126 }
2127
2128 }
2129
2130 const QDomElement rightElem = expressionNodeToOgcFilter( node->opRight(), expression, context );
2131 if ( !mErrorMessage.isEmpty() )
2132 return QDomElement();
2133
2134
2135 const QString opText = binaryOperatorToTagName( op );
2136 if ( opText.isEmpty() )
2137 {
2138 // not implemented binary operators
2139 // TODO: regex, % (mod), ^ (pow) are not supported yet
2140 mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( node->text() );
2141 return QDomElement();
2142 }
2143
2144 QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2145
2147 {
2149 boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2150
2151 // setup wildCards to <ogc:PropertyIsLike>
2152 boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2153 boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2154 if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2155 boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2156 else
2157 boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2158 }
2159
2160 boElem.appendChild( leftElem );
2161 boElem.appendChild( rightElem );
2162 return boElem;
2163}
2164
2165
2166QDomElement QgsOgcUtilsExprToFilter::expressionLiteralToOgcFilter( const QgsExpressionNodeLiteral *node, QgsExpression *expression, const QgsExpressionContext *context )
2167{
2168 Q_UNUSED( expression )
2169 Q_UNUSED( context )
2170 QString value;
2171 switch ( node->value().userType() )
2172 {
2173 case QMetaType::Type::Int:
2174 value = QString::number( node->value().toInt() );
2175 break;
2176 case QMetaType::Type::Double:
2177 value = qgsDoubleToString( node->value().toDouble() );
2178 break;
2179 case QMetaType::Type::QString:
2180 value = node->value().toString();
2181 break;
2182 case QMetaType::Type::QDate:
2183 value = node->value().toDate().toString( Qt::ISODate );
2184 break;
2185 case QMetaType::Type::QDateTime:
2186 value = node->value().toDateTime().toString( Qt::ISODate );
2187 break;
2188
2189 default:
2190 mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( static_cast<QMetaType::Type>( node->value().userType() ) );
2191 return QDomElement();
2192 }
2193
2194 QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2195 litElem.appendChild( mDoc.createTextNode( value ) );
2196 return litElem;
2197}
2198
2199
2200QDomElement QgsOgcUtilsExprToFilter::expressionColumnRefToOgcFilter( const QgsExpressionNodeColumnRef *node, QgsExpression *expression, const QgsExpressionContext *context )
2201{
2202 Q_UNUSED( expression )
2203 Q_UNUSED( context )
2204 QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2205 if ( !mFieldNameToXPathMap.isEmpty() )
2206 {
2207 const auto iterFieldName = mFieldNameToXPathMap.constFind( node->name() );
2208 if ( iterFieldName != mFieldNameToXPathMap.constEnd() )
2209 {
2210 const QString xpath( *iterFieldName );
2211
2212 if ( !mNamespacePrefixToUriMap.isEmpty() )
2213 {
2214 const QStringList parts = xpath.split( '/' );
2215 QSet<QString> setNamespacePrefix;
2216 for ( const QString &part : std::as_const( parts ) )
2217 {
2218 const QStringList subparts = part.split( ':' );
2219 if ( subparts.size() == 2 && !setNamespacePrefix.contains( subparts[0] ) )
2220 {
2221 const auto iterNamespacePrefix = mNamespacePrefixToUriMap.constFind( subparts[0] );
2222 if ( iterNamespacePrefix != mNamespacePrefixToUriMap.constEnd() )
2223 {
2224 setNamespacePrefix.insert( subparts[0] );
2225 QDomAttr attr = mDoc.createAttribute( QStringLiteral( "xmlns:" ) + subparts[0] );
2226 attr.setValue( *iterNamespacePrefix );
2227 propElem.setAttributeNode( attr );
2228 }
2229 }
2230 }
2231 }
2232
2233 propElem.appendChild( mDoc.createTextNode( xpath ) );
2234
2235 return propElem;
2236 }
2237 }
2238 QString columnRef( node->name() );
2239 if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
2240 columnRef = mNamespacePrefix + QStringLiteral( ":" ) + columnRef;
2241 propElem.appendChild( mDoc.createTextNode( columnRef ) );
2242 return propElem;
2243}
2244
2245
2246
2247QDomElement QgsOgcUtilsExprToFilter::expressionInOperatorToOgcFilter( const QgsExpressionNodeInOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
2248{
2249 if ( node->list()->list().size() == 1 )
2250 {
2251 const QDomElement leftNode = expressionNodeToOgcFilter( node->node(), expression, context );
2252 const QDomElement firstListNode = expressionNodeToOgcFilter( node->list()->list().first(), expression, context );
2253 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2254 eqElem.appendChild( leftNode );
2255 eqElem.appendChild( firstListNode );
2256 if ( node->isNotIn() )
2257 {
2258 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2259 notElem.appendChild( eqElem );
2260 return notElem;
2261 }
2262 return eqElem;
2263 }
2264
2265 QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2266 const QDomElement leftNode = expressionNodeToOgcFilter( node->node(), expression, context );
2267
2268 const auto constList = node->list()->list();
2269 for ( QgsExpressionNode *n : constList )
2270 {
2271 const QDomElement listNode = expressionNodeToOgcFilter( n, expression, context );
2272 if ( !mErrorMessage.isEmpty() )
2273 return QDomElement();
2274
2275 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2276 eqElem.appendChild( leftNode.cloneNode() );
2277 eqElem.appendChild( listNode );
2278
2279 orElem.appendChild( eqElem );
2280 }
2281
2282 if ( node->isNotIn() )
2283 {
2284 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2285 notElem.appendChild( orElem );
2286 return notElem;
2287 }
2288
2289 return orElem;
2290}
2291
2292Q_GLOBAL_STATIC_WITH_ARGS( QgsStringMap, BINARY_SPATIAL_OPS_MAP, (
2293{
2294 { QLatin1String( "disjoint" ), QLatin1String( "Disjoint" ) },
2295 { QLatin1String( "intersects" ), QLatin1String( "Intersects" )},
2296 { QLatin1String( "touches" ), QLatin1String( "Touches" ) },
2297 { QLatin1String( "crosses" ), QLatin1String( "Crosses" ) },
2298 { QLatin1String( "contains" ), QLatin1String( "Contains" ) },
2299 { QLatin1String( "overlaps" ), QLatin1String( "Overlaps" ) },
2300 { QLatin1String( "within" ), QLatin1String( "Within" ) }
2301} ) )
2302
2303static bool isBinarySpatialOperator( const QString &fnName )
2304{
2305 return BINARY_SPATIAL_OPS_MAP()->contains( fnName );
2306}
2307
2308static QString tagNameForSpatialOperator( const QString &fnName )
2309{
2310 return BINARY_SPATIAL_OPS_MAP()->value( fnName );
2311}
2312
2313static bool isGeometryColumn( const QgsExpressionNode *node )
2314{
2315 if ( node->nodeType() != QgsExpressionNode::ntFunction )
2316 return false;
2317
2318 const QgsExpressionNodeFunction *fn = static_cast<const QgsExpressionNodeFunction *>( node );
2320 return fd->name() == QLatin1String( "$geometry" );
2321}
2322
2323static QgsGeometry geometryFromConstExpr( const QgsExpressionNode *node )
2324{
2325 // Right now we support only geomFromWKT(' ..... ')
2326 // Ideally we should support any constant sub-expression (not dependent on feature's geometry or attributes)
2327
2328 if ( node->nodeType() == QgsExpressionNode::ntFunction )
2329 {
2330 const QgsExpressionNodeFunction *fnNode = static_cast<const QgsExpressionNodeFunction *>( node );
2332 if ( fnDef->name() == QLatin1String( "geom_from_wkt" ) )
2333 {
2334 const QList<QgsExpressionNode *> &args = fnNode->args()->list();
2335 if ( args[0]->nodeType() == QgsExpressionNode::ntLiteral )
2336 {
2337 const QString wkt = static_cast<const QgsExpressionNodeLiteral *>( args[0] )->value().toString();
2338 return QgsGeometry::fromWkt( wkt );
2339 }
2340 }
2341 }
2342 return QgsGeometry();
2343}
2344
2345
2346QDomElement QgsOgcUtilsExprToFilter::expressionFunctionToOgcFilter( const QgsExpressionNodeFunction *node, QgsExpression *expression, const QgsExpressionContext *context )
2347{
2349
2350 if ( fd->name() == QLatin1String( "intersects_bbox" ) )
2351 {
2352 QList<QgsExpressionNode *> argNodes = node->args()->list();
2353 Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2354
2355 const QgsGeometry geom = geometryFromConstExpr( argNodes[1] );
2356 if ( !geom.isNull() && isGeometryColumn( argNodes[0] ) )
2357 {
2358 QgsRectangle rect = geom.boundingBox();
2359
2360 mGMLUsed = true;
2361
2362 const QDomElement elemBox = ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
2363 QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, mSrsName, mInvertAxisOrientation ) :
2364 QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, mSrsName, mInvertAxisOrientation );
2365
2366 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":BBOX" );
2367
2368 if ( !mGeometryName.isEmpty() )
2369 {
2370 // Geometry column is optional for a BBOX filter.
2371 QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2372 QString columnRef( mGeometryName );
2373 if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
2374 columnRef = mNamespacePrefix + QStringLiteral( ":" ) + columnRef;
2375 geomProperty.appendChild( mDoc.createTextNode( columnRef ) );
2376
2377 funcElem.appendChild( geomProperty );
2378 }
2379 funcElem.appendChild( elemBox );
2380 return funcElem;
2381 }
2382 else
2383 {
2384 mErrorMessage = QObject::tr( "<BBOX> is currently supported only in form: bbox($geometry, geomFromWKT('…'))" );
2385 return QDomElement();
2386 }
2387 }
2388
2389 if ( isBinarySpatialOperator( fd->name() ) )
2390 {
2391 QList<QgsExpressionNode *> argNodes = node->args()->list();
2392 Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2393
2394 QgsExpressionNode *otherNode = nullptr;
2395 if ( isGeometryColumn( argNodes[0] ) )
2396 otherNode = argNodes[1];
2397 else if ( isGeometryColumn( argNodes[1] ) )
2398 otherNode = argNodes[0];
2399 else
2400 {
2401 mErrorMessage = QObject::tr( "Unable to translate spatial operator: at least one must refer to geometry." );
2402 return QDomElement();
2403 }
2404
2405 QDomElement otherGeomElem;
2406
2407 // the other node must be a geometry constructor
2408 if ( otherNode->nodeType() != QgsExpressionNode::ntFunction )
2409 {
2410 mErrorMessage = QObject::tr( "spatial operator: the other operator must be a geometry constructor function" );
2411 return QDomElement();
2412 }
2413
2414 const QgsExpressionNodeFunction *otherFn = static_cast<const QgsExpressionNodeFunction *>( otherNode );
2415 QgsExpressionFunction *otherFnDef = QgsExpression::Functions()[otherFn->fnIndex()];
2416 if ( otherFnDef->name() == QLatin1String( "geom_from_wkt" ) )
2417 {
2418 QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2419 if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2420 {
2421 mErrorMessage = QObject::tr( "geom_from_wkt: argument must be string literal" );
2422 return QDomElement();
2423 }
2424 const QString wkt = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2425 const QgsGeometry geom = QgsGeometry::fromWkt( wkt );
2426 otherGeomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation,
2427 QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2428 if ( otherGeomElem.isNull() )
2429 {
2430 mErrorMessage = QObject::tr( "geom_from_wkt: unable to generate GML from wkt geometry" );
2431 return QDomElement();
2432 }
2433 mGeomId ++;
2434 }
2435 else if ( otherFnDef->name() == QLatin1String( "geom_from_gml" ) )
2436 {
2437 QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2438 if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2439 {
2440 mErrorMessage = QObject::tr( "geom_from_gml: argument must be string literal" );
2441 return QDomElement();
2442 }
2443
2444 QDomDocument geomDoc;
2445 const QString gml = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2446 if ( !geomDoc.setContent( gml, true ) )
2447 {
2448 mErrorMessage = QObject::tr( "geom_from_gml: unable to parse XML" );
2449 return QDomElement();
2450 }
2451
2452 const QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
2453 otherGeomElem = geomNode.toElement();
2454 }
2455 else if ( otherNode->hasCachedStaticValue() && otherNode->cachedStaticValue().userType() == qMetaTypeId< QgsGeometry>() )
2456 {
2457 QgsGeometry geom = otherNode->cachedStaticValue().value<QgsGeometry>();
2458 otherGeomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation,
2459 QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2460 if ( otherGeomElem.isNull() )
2461 {
2462 mErrorMessage = QObject::tr( "geom from static value: unable to generate GML from static variable" );
2463 return QDomElement();
2464 }
2465 mGeomId ++;
2466 }
2467 else
2468 {
2469 mErrorMessage = QObject::tr( "spatial operator: unknown geometry constructor function" );
2470 return QDomElement();
2471 }
2472
2473 mGMLUsed = true;
2474
2475 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + tagNameForSpatialOperator( fd->name() ) );
2476 QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2477 QString columnRef( mGeometryName );
2478 if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
2479 columnRef = mNamespacePrefix + QStringLiteral( ":" ) + columnRef;
2480 geomProperty.appendChild( mDoc.createTextNode( columnRef ) );
2481 funcElem.appendChild( geomProperty );
2482 funcElem.appendChild( otherGeomElem );
2483 return funcElem;
2484 }
2485
2486 if ( fd->isStatic( node, expression, context ) )
2487 {
2488 const QVariant result = fd->run( node->args(), context, expression, node );
2489 const QgsExpressionNodeLiteral literal( result );
2490 return expressionLiteralToOgcFilter( &literal, expression, context );
2491 }
2492
2493 if ( fd->params() == 0 )
2494 {
2495 mErrorMessage = QObject::tr( "Special columns/constants are not supported." );
2496 return QDomElement();
2497 }
2498
2499 // this is somehow wrong - we are just hoping that the other side supports the same functions as we do...
2500 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
2501 funcElem.setAttribute( QStringLiteral( "name" ), fd->name() );
2502 const auto constList = node->args()->list();
2503 for ( QgsExpressionNode *n : constList )
2504 {
2505 const QDomElement childElem = expressionNodeToOgcFilter( n, expression, context );
2506 if ( !mErrorMessage.isEmpty() )
2507 return QDomElement();
2508
2509 funcElem.appendChild( childElem );
2510 }
2511
2512 return funcElem;
2513}
2514
2515//
2516
2518 QgsOgcUtils::GMLVersion gmlVersion,
2519 QgsOgcUtils::FilterVersion filterVersion,
2520 const QList<QgsOgcUtils::LayerProperties> &layerProperties,
2521 bool honourAxisOrientation,
2522 bool invertAxisOrientation,
2523 const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename,
2524 const QMap<QString, QString> &fieldNameToXPathMap,
2525 const QMap<QString, QString> &namespacePrefixToUriMap )
2526 : mDoc( doc )
2527 , mGMLUsed( false )
2528 , mGMLVersion( gmlVersion )
2529 , mFilterVersion( filterVersion )
2530 , mLayerProperties( layerProperties )
2531 , mHonourAxisOrientation( honourAxisOrientation )
2532 , mInvertAxisOrientation( invertAxisOrientation )
2533 , mFilterPrefix( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "fes" : "ogc" )
2534 , mPropertyName( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "ValueReference" : "PropertyName" )
2535 , mGeomId( 1 )
2536 , mMapUnprefixedTypenameToPrefixedTypename( mapUnprefixedTypenameToPrefixedTypename )
2537 , mFieldNameToXPathMap( fieldNameToXPathMap )
2538 , mNamespacePrefixToUriMap( namespacePrefixToUriMap )
2539{
2540}
2541
2543{
2544 switch ( node->nodeType() )
2545 {
2547 return toOgcFilter( static_cast<const QgsSQLStatement::NodeUnaryOperator *>( node ) );
2549 return toOgcFilter( static_cast<const QgsSQLStatement::NodeBinaryOperator *>( node ) );
2551 return toOgcFilter( static_cast<const QgsSQLStatement::NodeInOperator *>( node ) );
2553 return toOgcFilter( static_cast<const QgsSQLStatement::NodeBetweenOperator *>( node ) );
2555 return toOgcFilter( static_cast<const QgsSQLStatement::NodeFunction *>( node ) );
2557 return toOgcFilter( static_cast<const QgsSQLStatement::NodeLiteral *>( node ) );
2559 return toOgcFilter( static_cast<const QgsSQLStatement::NodeColumnRef *>( node ) );
2561 return toOgcFilter( static_cast<const QgsSQLStatement::NodeSelect *>( node ) );
2562
2563 default:
2564 mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
2565 return QDomElement();
2566 }
2567}
2568
2569
2571{
2572
2573 const QDomElement operandElem = toOgcFilter( node->operand() );
2574 if ( !mErrorMessage.isEmpty() )
2575 return QDomElement();
2576
2577 QDomElement uoElem;
2578 switch ( node->op() )
2579 {
2581 uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2582 if ( node->operand()->nodeType() == QgsSQLStatement::ntLiteral )
2583 {
2584 // operand expression already created a Literal node:
2585 // take the literal value, prepend - and remove old literal node
2586 uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2587 mDoc.removeChild( operandElem );
2588 }
2589 else
2590 {
2591 mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2592 return QDomElement();
2593 }
2594 break;
2596 uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2597 uoElem.appendChild( operandElem );
2598 break;
2599
2600 default:
2601 mErrorMessage = QObject::tr( "Unary operator %1 not implemented yet" ).arg( QgsSQLStatement::UNARY_OPERATOR_TEXT[node->op()] );
2602 return QDomElement();
2603 }
2604
2605 return uoElem;
2606}
2607
2608
2610{
2611 const QDomElement leftElem = toOgcFilter( node->opLeft() );
2612 if ( !mErrorMessage.isEmpty() )
2613 return QDomElement();
2614
2616
2617 // before right operator is parsed: to allow NULL handling
2619 {
2620 if ( node->opRight()->nodeType() == QgsSQLStatement::ntLiteral )
2621 {
2622 const QgsSQLStatement::NodeLiteral *rightLit = static_cast<const QgsSQLStatement::NodeLiteral *>( node->opRight() );
2623 if ( QgsVariantUtils::isNull( rightLit->value() ) )
2624 {
2625
2626 QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2627 elem.appendChild( leftElem );
2628
2629 if ( op == QgsSQLStatement::boIsNot )
2630 {
2631 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2632 notElem.appendChild( elem );
2633 return notElem;
2634 }
2635
2636 return elem;
2637 }
2638
2639 // continue with equal / not equal operator once the null case is handled
2641 }
2642
2643 }
2644
2645 const QDomElement rightElem = toOgcFilter( node->opRight() );
2646 if ( !mErrorMessage.isEmpty() )
2647 return QDomElement();
2648
2649
2650 QString opText;
2651 if ( op == QgsSQLStatement::boOr )
2652 opText = QStringLiteral( "Or" );
2653 else if ( op == QgsSQLStatement::boAnd )
2654 opText = QStringLiteral( "And" );
2655 else if ( op == QgsSQLStatement::boEQ )
2656 opText = QStringLiteral( "PropertyIsEqualTo" );
2657 else if ( op == QgsSQLStatement::boNE )
2658 opText = QStringLiteral( "PropertyIsNotEqualTo" );
2659 else if ( op == QgsSQLStatement::boLE )
2660 opText = QStringLiteral( "PropertyIsLessThanOrEqualTo" );
2661 else if ( op == QgsSQLStatement::boGE )
2662 opText = QStringLiteral( "PropertyIsGreaterThanOrEqualTo" );
2663 else if ( op == QgsSQLStatement::boLT )
2664 opText = QStringLiteral( "PropertyIsLessThan" );
2665 else if ( op == QgsSQLStatement::boGT )
2666 opText = QStringLiteral( "PropertyIsGreaterThan" );
2667 else if ( op == QgsSQLStatement::boLike )
2668 opText = QStringLiteral( "PropertyIsLike" );
2669 else if ( op == QgsSQLStatement::boILike )
2670 opText = QStringLiteral( "PropertyIsLike" );
2671
2672 if ( opText.isEmpty() )
2673 {
2674 // not implemented binary operators
2675 mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( QgsSQLStatement::BINARY_OPERATOR_TEXT[op] );
2676 return QDomElement();
2677 }
2678
2679 QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2680
2682 {
2683 if ( op == QgsSQLStatement::boILike )
2684 boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2685
2686 // setup wildCards to <ogc:PropertyIsLike>
2687 boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2688 boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2689 if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2690 boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2691 else
2692 boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2693 }
2694
2695 boElem.appendChild( leftElem );
2696 boElem.appendChild( rightElem );
2697 return boElem;
2698}
2699
2700
2702{
2703 QString value;
2704 switch ( node->value().userType() )
2705 {
2706 case QMetaType::Type::Int:
2707 value = QString::number( node->value().toInt() );
2708 break;
2709 case QMetaType::Type::LongLong:
2710 value = QString::number( node->value().toLongLong() );
2711 break;
2712 case QMetaType::Type::Double:
2713 value = qgsDoubleToString( node->value().toDouble() );
2714 break;
2715 case QMetaType::Type::QString:
2716 value = node->value().toString();
2717 break;
2718
2719 default:
2720 mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( static_cast<QMetaType::Type>( node->value().userType() ) );
2721 return QDomElement();
2722 }
2723
2724 QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2725 litElem.appendChild( mDoc.createTextNode( value ) );
2726 return litElem;
2727}
2728
2729
2731{
2732 QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2733 if ( node->tableName().isEmpty() || mLayerProperties.size() == 1 )
2734 {
2735 if ( !mFieldNameToXPathMap.isEmpty() )
2736 {
2737 const auto iterFieldName = mFieldNameToXPathMap.constFind( node->name() );
2738 if ( iterFieldName != mFieldNameToXPathMap.constEnd() )
2739 {
2740 const QString xpath( *iterFieldName );
2741
2742 if ( !mNamespacePrefixToUriMap.isEmpty() )
2743 {
2744 const QStringList parts = xpath.split( '/' );
2745 QSet<QString> setNamespacePrefix;
2746 for ( const QString &part : std::as_const( parts ) )
2747 {
2748 const QStringList subparts = part.split( ':' );
2749 if ( subparts.size() == 2 && !setNamespacePrefix.contains( subparts[0] ) )
2750 {
2751 const auto iterNamespacePrefix = mNamespacePrefixToUriMap.constFind( subparts[0] );
2752 if ( iterNamespacePrefix != mNamespacePrefixToUriMap.constEnd() )
2753 {
2754 setNamespacePrefix.insert( subparts[0] );
2755 QDomAttr attr = mDoc.createAttribute( QStringLiteral( "xmlns:" ) + subparts[0] );
2756 attr.setValue( *iterNamespacePrefix );
2757 propElem.setAttributeNode( attr );
2758 }
2759 }
2760 }
2761 }
2762
2763 propElem.appendChild( mDoc.createTextNode( xpath ) );
2764
2765 return propElem;
2766 }
2767 }
2768 if ( mLayerProperties.size() == 1 && !mLayerProperties[0].mNamespacePrefix.isEmpty() && !mLayerProperties[0].mNamespaceURI.isEmpty() )
2769 propElem.appendChild( mDoc.createTextNode(
2770 mLayerProperties[0].mNamespacePrefix + QStringLiteral( ":" ) + node->name() ) );
2771 else
2772 propElem.appendChild( mDoc.createTextNode( node->name() ) );
2773 }
2774 else
2775 {
2776 QString tableName( mMapTableAliasToNames[node->tableName()] );
2777 if ( mMapUnprefixedTypenameToPrefixedTypename.contains( tableName ) )
2778 tableName = mMapUnprefixedTypenameToPrefixedTypename[tableName];
2779 propElem.appendChild( mDoc.createTextNode( tableName + "/" + node->name() ) );
2780 }
2781 return propElem;
2782}
2783
2785{
2786 if ( node->list()->list().size() == 1 )
2787 {
2788 const QDomElement leftNode = toOgcFilter( node->node() );
2789 const QDomElement firstListNode = toOgcFilter( node->list()->list().first() );
2790 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2791 eqElem.appendChild( leftNode );
2792 eqElem.appendChild( firstListNode );
2793 if ( node->isNotIn() )
2794 {
2795 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2796 notElem.appendChild( eqElem );
2797 return notElem;
2798 }
2799 return eqElem;
2800 }
2801
2802 QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2803 const QDomElement leftNode = toOgcFilter( node->node() );
2804
2805 const auto constList = node->list()->list();
2806 for ( QgsSQLStatement::Node *n : constList )
2807 {
2808 const QDomElement listNode = toOgcFilter( n );
2809 if ( !mErrorMessage.isEmpty() )
2810 return QDomElement();
2811
2812 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2813 eqElem.appendChild( leftNode.cloneNode() );
2814 eqElem.appendChild( listNode );
2815
2816 orElem.appendChild( eqElem );
2817 }
2818
2819 if ( node->isNotIn() )
2820 {
2821 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2822 notElem.appendChild( orElem );
2823 return notElem;
2824 }
2825
2826 return orElem;
2827}
2828
2830{
2831 QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsBetween" );
2832 elem.appendChild( toOgcFilter( node->node() ) );
2833 QDomElement lowerBoundary = mDoc.createElement( mFilterPrefix + ":LowerBoundary" );
2834 lowerBoundary.appendChild( toOgcFilter( node->minVal() ) );
2835 elem.appendChild( lowerBoundary );
2836 QDomElement upperBoundary = mDoc.createElement( mFilterPrefix + ":UpperBoundary" );
2837 upperBoundary.appendChild( toOgcFilter( node->maxVal() ) );
2838 elem.appendChild( upperBoundary );
2839
2840 if ( node->isNotBetween() )
2841 {
2842 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2843 notElem.appendChild( elem );
2844 return notElem;
2845 }
2846
2847 return elem;
2848}
2849
2850static QString mapBinarySpatialToOgc( const QString &name )
2851{
2852 QString nameCompare( name );
2853 if ( name.size() > 3 && QStringView {name} .mid( 0, 3 ).toString().compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2854 nameCompare = name.mid( 3 );
2855 QStringList spatialOps;
2856 spatialOps << QStringLiteral( "BBOX" ) << QStringLiteral( "Intersects" ) << QStringLiteral( "Contains" ) << QStringLiteral( "Crosses" ) << QStringLiteral( "Equals" )
2857 << QStringLiteral( "Disjoint" ) << QStringLiteral( "Overlaps" ) << QStringLiteral( "Touches" ) << QStringLiteral( "Within" );
2858 const auto constSpatialOps = spatialOps;
2859 for ( QString op : constSpatialOps )
2860 {
2861 if ( nameCompare.compare( op, Qt::CaseInsensitive ) == 0 )
2862 return op;
2863 }
2864 return QString();
2865}
2866
2867static QString mapTernarySpatialToOgc( const QString &name )
2868{
2869 QString nameCompare( name );
2870 if ( name.size() > 3 && QStringView {name} .mid( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2871 nameCompare = name.mid( 3 );
2872 if ( nameCompare.compare( QLatin1String( "DWithin" ), Qt::CaseInsensitive ) == 0 )
2873 return QStringLiteral( "DWithin" );
2874 if ( nameCompare.compare( QLatin1String( "Beyond" ), Qt::CaseInsensitive ) == 0 )
2875 return QStringLiteral( "Beyond" );
2876 return QString();
2877}
2878
2879QString QgsOgcUtilsSQLStatementToFilter::getGeometryColumnSRSName( const QgsSQLStatement::Node *node )
2880{
2881 if ( node->nodeType() != QgsSQLStatement::ntColumnRef )
2882 return QString();
2883
2884 const QgsSQLStatement::NodeColumnRef *col = static_cast<const QgsSQLStatement::NodeColumnRef *>( node );
2885 if ( !col->tableName().isEmpty() )
2886 {
2887 const auto constMLayerProperties = mLayerProperties;
2888 for ( const QgsOgcUtils::LayerProperties &prop : constMLayerProperties )
2889 {
2890 if ( prop.mName.compare( mMapTableAliasToNames[col->tableName()], Qt::CaseInsensitive ) == 0 &&
2891 prop.mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
2892 {
2893 return prop.mSRSName;
2894 }
2895 }
2896 }
2897 if ( !mLayerProperties.empty() &&
2898 mLayerProperties.at( 0 ).mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
2899 {
2900 return mLayerProperties.at( 0 ).mSRSName;
2901 }
2902 return QString();
2903}
2904
2905bool QgsOgcUtilsSQLStatementToFilter::processSRSName( const QgsSQLStatement::NodeFunction *mainNode,
2906 QList<QgsSQLStatement::Node *> args,
2907 bool lastArgIsSRSName,
2908 QString &srsName,
2909 bool &axisInversion )
2910{
2911 srsName = mCurrentSRSName;
2912 axisInversion = mInvertAxisOrientation;
2913
2914 if ( lastArgIsSRSName )
2915 {
2916 QgsSQLStatement::Node *lastArg = args[ args.size() - 1 ];
2917 if ( lastArg->nodeType() != QgsSQLStatement::ntLiteral )
2918 {
2919 mErrorMessage = QObject::tr( "%1: Last argument must be string or integer literal" ).arg( mainNode->name() );
2920 return false;
2921 }
2922 const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( lastArg );
2923 if ( lit->value().userType() == QMetaType::Type::Int )
2924 {
2925 if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2926 {
2927 srsName = "EPSG:" + QString::number( lit->value().toInt() );
2928 }
2929 else
2930 {
2931 srsName = "urn:ogc:def:crs:EPSG::" + QString::number( lit->value().toInt() );
2932 }
2933 }
2934 else
2935 {
2936 srsName = lit->value().toString();
2937 if ( srsName.startsWith( QLatin1String( "EPSG:" ), Qt::CaseInsensitive ) )
2938 return true;
2939 }
2940 }
2941
2943 if ( !srsName.isEmpty() )
2945 if ( crs.isValid() )
2946 {
2947 if ( mHonourAxisOrientation && crs.hasAxisInverted() )
2948 {
2949 axisInversion = !axisInversion;
2950 }
2951 }
2952
2953 return true;
2954}
2955
2957{
2958 // ST_GeometryFromText
2959 if ( node->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 )
2960 {
2961 QList<QgsSQLStatement::Node *> args = node->args()->list();
2962 if ( args.size() != 1 && args.size() != 2 )
2963 {
2964 mErrorMessage = QObject::tr( "Function %1 should have 1 or 2 arguments" ).arg( node->name() );
2965 return QDomElement();
2966 }
2967
2968 QgsSQLStatement::Node *firstFnArg = args[0];
2969 if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
2970 {
2971 mErrorMessage = QObject::tr( "%1: First argument must be string literal" ).arg( node->name() );
2972 return QDomElement();
2973 }
2974
2975 QString srsName;
2976 bool axisInversion;
2977 if ( ! processSRSName( node, args, args.size() == 2, srsName, axisInversion ) )
2978 {
2979 return QDomElement();
2980 }
2981
2982 const QString wkt = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
2983 const QgsGeometry geom = QgsGeometry::fromWkt( wkt );
2984 const QDomElement geomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, srsName, axisInversion,
2985 QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2986 mGeomId ++;
2987 if ( geomElem.isNull() )
2988 {
2989 mErrorMessage = QObject::tr( "%1: invalid WKT" ).arg( node->name() );
2990 return QDomElement();
2991 }
2992 mGMLUsed = true;
2993 return geomElem;
2994 }
2995
2996 // ST_MakeEnvelope
2997 if ( node->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 )
2998 {
2999 QList<QgsSQLStatement::Node *> args = node->args()->list();
3000 if ( args.size() != 4 && args.size() != 5 )
3001 {
3002 mErrorMessage = QObject::tr( "Function %1 should have 4 or 5 arguments" ).arg( node->name() );
3003 return QDomElement();
3004 }
3005
3006 QgsRectangle rect;
3007
3008 for ( int i = 0; i < 4; i++ )
3009 {
3010 QgsSQLStatement::Node *arg = args[i];
3011 if ( arg->nodeType() != QgsSQLStatement::ntLiteral )
3012 {
3013 mErrorMessage = QObject::tr( "%1: Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
3014 return QDomElement();
3015 }
3016 const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( arg );
3017 double val = 0.0;
3018 if ( lit->value().userType() == QMetaType::Type::Int )
3019 val = lit->value().toInt();
3020 else if ( lit->value().userType() == QMetaType::Type::LongLong )
3021 val = lit->value().toLongLong();
3022 else if ( lit->value().userType() == QMetaType::Type::Double )
3023 val = lit->value().toDouble();
3024 else
3025 {
3026 mErrorMessage = QObject::tr( "%1 Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
3027 return QDomElement();
3028 }
3029 if ( i == 0 )
3030 rect.setXMinimum( val );
3031 else if ( i == 1 )
3032 rect.setYMinimum( val );
3033 else if ( i == 2 )
3034 rect.setXMaximum( val );
3035 else
3036 rect.setYMaximum( val );
3037 }
3038
3039 QString srsName;
3040 bool axisInversion;
3041 if ( ! processSRSName( node, args, args.size() == 5, srsName, axisInversion ) )
3042 {
3043 return QDomElement();
3044 }
3045
3046 mGMLUsed = true;
3047
3048 return ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
3049 QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, srsName, axisInversion, 15 ) :
3050 QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, srsName, axisInversion, 15 );
3051 }
3052
3053 // ST_GeomFromGML
3054 if ( node->name().compare( QLatin1String( "ST_GeomFromGML" ), Qt::CaseInsensitive ) == 0 )
3055 {
3056 QList<QgsSQLStatement::Node *> args = node->args()->list();
3057 if ( args.size() != 1 )
3058 {
3059 mErrorMessage = QObject::tr( "Function %1 should have 1 argument" ).arg( node->name() );
3060 return QDomElement();
3061 }
3062
3063 QgsSQLStatement::Node *firstFnArg = args[0];
3064 if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
3065 {
3066 mErrorMessage = QObject::tr( "%1: Argument must be string literal" ).arg( node->name() );
3067 return QDomElement();
3068 }
3069
3070 QDomDocument geomDoc;
3071 const QString gml = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
3072 if ( !geomDoc.setContent( gml, true ) )
3073 {
3074 mErrorMessage = QObject::tr( "ST_GeomFromGML: unable to parse XML" );
3075 return QDomElement();
3076 }
3077
3078 const QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
3079 mGMLUsed = true;
3080 return geomNode.toElement();
3081 }
3082
3083 // Binary geometry operators
3084 QString ogcName( mapBinarySpatialToOgc( node->name() ) );
3085 if ( !ogcName.isEmpty() )
3086 {
3087 QList<QgsSQLStatement::Node *> args = node->args()->list();
3088 if ( args.size() != 2 )
3089 {
3090 mErrorMessage = QObject::tr( "Function %1 should have 2 arguments" ).arg( node->name() );
3091 return QDomElement();
3092 }
3093
3094 for ( int i = 0; i < 2; i ++ )
3095 {
3096 if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
3097 ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
3098 static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
3099 {
3100 mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
3101 break;
3102 }
3103 }
3104
3105 //if( ogcName == "Intersects" && mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
3106 // ogcName = "Intersect";
3107 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + ogcName );
3108 const auto constArgs = args;
3109 for ( QgsSQLStatement::Node *n : constArgs )
3110 {
3111 const QDomElement childElem = toOgcFilter( n );
3112 if ( !mErrorMessage.isEmpty() )
3113 {
3114 mCurrentSRSName.clear();
3115 return QDomElement();
3116 }
3117
3118 funcElem.appendChild( childElem );
3119 }
3120
3121 mCurrentSRSName.clear();
3122 return funcElem;
3123 }
3124
3125 ogcName = mapTernarySpatialToOgc( node->name() );
3126 if ( !ogcName.isEmpty() )
3127 {
3128 QList<QgsSQLStatement::Node *> args = node->args()->list();
3129 if ( args.size() != 3 )
3130 {
3131 mErrorMessage = QObject::tr( "Function %1 should have 3 arguments" ).arg( node->name() );
3132 return QDomElement();
3133 }
3134
3135 for ( int i = 0; i < 2; i ++ )
3136 {
3137 if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
3138 ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
3139 static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
3140 {
3141 mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
3142 break;
3143 }
3144 }
3145
3146 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + node->name().mid( 3 ) );
3147 for ( int i = 0; i < 2; i++ )
3148 {
3149 const QDomElement childElem = toOgcFilter( args[i] );
3150 if ( !mErrorMessage.isEmpty() )
3151 {
3152 mCurrentSRSName.clear();
3153 return QDomElement();
3154 }
3155
3156 funcElem.appendChild( childElem );
3157 }
3158 mCurrentSRSName.clear();
3159
3160 QgsSQLStatement::Node *distanceNode = args[2];
3161 if ( distanceNode->nodeType() != QgsSQLStatement::ntLiteral )
3162 {
3163 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() );
3164 return QDomElement();
3165 }
3166 const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( distanceNode );
3167 if ( QgsVariantUtils::isNull( lit->value() ) )
3168 {
3169 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() );
3170 return QDomElement();
3171 }
3172 QString distance;
3173 QString unit( QStringLiteral( "m" ) );
3174 switch ( lit->value().userType() )
3175 {
3176 case QMetaType::Type::Int:
3177 distance = QString::number( lit->value().toInt() );
3178 break;
3179 case QMetaType::Type::LongLong:
3180 distance = QString::number( lit->value().toLongLong() );
3181 break;
3182 case QMetaType::Type::Double:
3183 distance = qgsDoubleToString( lit->value().toDouble() );
3184 break;
3185 case QMetaType::Type::QString:
3186 {
3187 distance = lit->value().toString();
3188 for ( int i = 0; i < distance.size(); i++ )
3189 {
3190 if ( !( ( distance[i] >= '0' && distance[i] <= '9' ) || distance[i] == '-' || distance[i] == '.' || distance[i] == 'e' || distance[i] == 'E' ) )
3191 {
3192 unit = distance.mid( i ).trimmed();
3193 distance = distance.mid( 0, i );
3194 break;
3195 }
3196 }
3197 break;
3198 }
3199
3200 default:
3201 mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( static_cast<QMetaType::Type>( lit->value().userType() ) );
3202 return QDomElement();
3203 }
3204
3205 QDomElement distanceElem = mDoc.createElement( mFilterPrefix + ":Distance" );
3206 if ( mFilterVersion == QgsOgcUtils::FILTER_FES_2_0 )
3207 distanceElem.setAttribute( QStringLiteral( "uom" ), unit );
3208 else
3209 distanceElem.setAttribute( QStringLiteral( "unit" ), unit );
3210 distanceElem.appendChild( mDoc.createTextNode( distance ) );
3211 funcElem.appendChild( distanceElem );
3212 return funcElem;
3213 }
3214
3215 // Other function
3216 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
3217 funcElem.setAttribute( QStringLiteral( "name" ), node->name() );
3218 const auto constList = node->args()->list();
3219 for ( QgsSQLStatement::Node *n : constList )
3220 {
3221 const QDomElement childElem = toOgcFilter( n );
3222 if ( !mErrorMessage.isEmpty() )
3223 return QDomElement();
3224
3225 funcElem.appendChild( childElem );
3226 }
3227 return funcElem;
3228}
3229
3231 const QString &leftTable )
3232{
3233 QgsSQLStatement::Node *onExpr = node->onExpr();
3234 if ( onExpr )
3235 {
3236 return toOgcFilter( onExpr );
3237 }
3238
3239 QList<QDomElement> listElem;
3240 const auto constUsingColumns = node->usingColumns();
3241 for ( const QString &columnName : constUsingColumns )
3242 {
3243 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
3244 QDomElement propElem1 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3245 propElem1.appendChild( mDoc.createTextNode( leftTable + "/" + columnName ) );
3246 eqElem.appendChild( propElem1 );
3247 QDomElement propElem2 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3248 propElem2.appendChild( mDoc.createTextNode( node->tableDef()->name() + "/" + columnName ) );
3249 eqElem.appendChild( propElem2 );
3250 listElem.append( eqElem );
3251 }
3252
3253 if ( listElem.size() == 1 )
3254 {
3255 return listElem[0];
3256 }
3257 else if ( listElem.size() > 1 )
3258 {
3259 QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3260 const auto constListElem = listElem;
3261 for ( const QDomElement &elem : constListElem )
3262 {
3263 andElem.appendChild( elem );
3264 }
3265 return andElem;
3266 }
3267
3268 return QDomElement();
3269}
3270
3271void QgsOgcUtilsSQLStatementToFilter::visit( const QgsSQLStatement::NodeTableDef *node )
3272{
3273 if ( node->alias().isEmpty() )
3274 {
3275 mMapTableAliasToNames[ node->name()] = node->name();
3276 }
3277 else
3278 {
3279 mMapTableAliasToNames[ node->alias()] = node->name();
3280 }
3281}
3282
3284{
3285 QList<QDomElement> listElem;
3286
3287 if ( mFilterVersion != QgsOgcUtils::FILTER_FES_2_0 &&
3288 ( node->tables().size() != 1 || !node->joins().empty() ) )
3289 {
3290 mErrorMessage = QObject::tr( "Joins are only supported with WFS 2.0" );
3291 return QDomElement();
3292 }
3293
3294 // Register all table name aliases
3295 const auto constTables = node->tables();
3296 for ( QgsSQLStatement::NodeTableDef *table : constTables )
3297 {
3298 visit( table );
3299 }
3300 const auto constJoins = node->joins();
3301 for ( QgsSQLStatement::NodeJoin *join : constJoins )
3302 {
3303 visit( join->tableDef() );
3304 }
3305
3306 // Process JOIN conditions
3307 const QList< QgsSQLStatement::NodeTableDef *> nodeTables = node->tables();
3308 QString leftTable = nodeTables.at( nodeTables.length() - 1 )->name();
3309 for ( QgsSQLStatement::NodeJoin *join : constJoins )
3310 {
3311 const QDomElement joinElem = toOgcFilter( join, leftTable );
3312 if ( !mErrorMessage.isEmpty() )
3313 return QDomElement();
3314 listElem.append( joinElem );
3315 leftTable = join->tableDef()->name();
3316 }
3317
3318 // Process WHERE conditions
3319 if ( node->where() )
3320 {
3321 const QDomElement whereElem = toOgcFilter( node->where() );
3322 if ( !mErrorMessage.isEmpty() )
3323 return QDomElement();
3324 listElem.append( whereElem );
3325 }
3326
3327 // Concatenate all conditions
3328 if ( listElem.size() == 1 )
3329 {
3330 return listElem[0];
3331 }
3332 else if ( listElem.size() > 1 )
3333 {
3334 QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3335 const auto constListElem = listElem;
3336 for ( const QDomElement &elem : constListElem )
3337 {
3338 andElem.appendChild( elem );
3339 }
3340 return andElem;
3341 }
3342
3343 return QDomElement();
3344}
3345
3347 : mLayer( layer )
3348{
3349 mPropertyName = QStringLiteral( "PropertyName" );
3350 mPrefix = QStringLiteral( "ogc" );
3351
3352 if ( version == QgsOgcUtils::FILTER_FES_2_0 )
3353 {
3354 mPropertyName = QStringLiteral( "ValueReference" );
3355 mPrefix = QStringLiteral( "fes" );
3356 }
3357}
3358
3360{
3361 if ( element.isNull() )
3362 return nullptr;
3363
3364 // check for binary operators
3365 if ( isBinaryOperator( element.tagName() ) )
3366 {
3367 return nodeBinaryOperatorFromOgcFilter( element );
3368 }
3369
3370 // check for spatial operators
3371 if ( isSpatialOperator( element.tagName() ) )
3372 {
3373 return nodeSpatialOperatorFromOgcFilter( element );
3374 }
3375
3376 // check for other OGC operators, convert them to expressions
3377 if ( element.tagName() == QLatin1String( "Not" ) )
3378 {
3379 return nodeNotFromOgcFilter( element );
3380 }
3381 else if ( element.tagName() == QLatin1String( "PropertyIsNull" ) )
3382 {
3383 return nodePropertyIsNullFromOgcFilter( element );
3384 }
3385 else if ( element.tagName() == QLatin1String( "Literal" ) )
3386 {
3387 return nodeLiteralFromOgcFilter( element );
3388 }
3389 else if ( element.tagName() == QLatin1String( "Function" ) )
3390 {
3391 return nodeFunctionFromOgcFilter( element );
3392 }
3393 else if ( element.tagName() == mPropertyName )
3394 {
3395 return nodeColumnRefFromOgcFilter( element );
3396 }
3397 else if ( element.tagName() == QLatin1String( "PropertyIsBetween" ) )
3398 {
3399 return nodeIsBetweenFromOgcFilter( element );
3400 }
3401
3402 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() );
3403 return nullptr;
3404}
3405
3407{
3408 if ( element.isNull() )
3409 return nullptr;
3410
3411 int op = binaryOperatorFromTagName( element.tagName() );
3412 if ( op < 0 )
3413 {
3414 mErrorMessage = QObject::tr( "'%1' binary operator not supported." ).arg( element.tagName() );
3415 return nullptr;
3416 }
3417
3418 if ( op == QgsExpressionNodeBinaryOperator::boLike && element.hasAttribute( QStringLiteral( "matchCase" ) ) && element.attribute( QStringLiteral( "matchCase" ) ) == QLatin1String( "false" ) )
3419 {
3421 }
3422
3423 QDomElement operandElem = element.firstChildElement();
3424 std::unique_ptr<QgsExpressionNode> expr( nodeFromOgcFilter( operandElem ) );
3425
3426 if ( !expr )
3427 {
3428 mErrorMessage = QObject::tr( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
3429 return nullptr;
3430 }
3431
3432 const std::unique_ptr<QgsExpressionNode> leftOp( expr->clone() );
3433 for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
3434 {
3435 std::unique_ptr<QgsExpressionNode> opRight( nodeFromOgcFilter( operandElem ) );
3436 if ( !opRight )
3437 {
3438 mErrorMessage = QObject::tr( "invalid right operand for '%1' binary operator" ).arg( element.tagName() );
3439 return nullptr;
3440 }
3441
3443 {
3444 QString wildCard;
3445 if ( element.hasAttribute( QStringLiteral( "wildCard" ) ) )
3446 {
3447 wildCard = element.attribute( QStringLiteral( "wildCard" ) );
3448 }
3449 QString singleChar;
3450 if ( element.hasAttribute( QStringLiteral( "singleChar" ) ) )
3451 {
3452 singleChar = element.attribute( QStringLiteral( "singleChar" ) );
3453 }
3454 QString escape = QStringLiteral( "\\" );
3455 if ( element.hasAttribute( QStringLiteral( "escape" ) ) )
3456 {
3457 escape = element.attribute( QStringLiteral( "escape" ) );
3458 }
3459 if ( element.hasAttribute( QStringLiteral( "escapeChar" ) ) )
3460 {
3461 escape = element.attribute( QStringLiteral( "escapeChar" ) );
3462 }
3463 // replace
3464 QString oprValue = static_cast<const QgsExpressionNodeLiteral *>( opRight.get() )->value().toString();
3465 if ( !wildCard.isEmpty() && wildCard != QLatin1String( "%" ) )
3466 {
3467 oprValue.replace( '%', QLatin1String( "\\%" ) );
3468 if ( oprValue.startsWith( wildCard ) )
3469 {
3470 oprValue.replace( 0, 1, QStringLiteral( "%" ) );
3471 }
3472 const QRegularExpression rx( "[^" + QgsStringUtils::qRegExpEscape( escape ) + "](" + QgsStringUtils::qRegExpEscape( wildCard ) + ")" );
3473 QRegularExpressionMatch match = rx.match( oprValue );
3474 int pos;
3475 while ( match.hasMatch() )
3476 {
3477 pos = match.capturedStart();
3478 oprValue.replace( pos + 1, 1, QStringLiteral( "%" ) );
3479 pos += 1;
3480 match = rx.match( oprValue, pos );
3481 }
3482 oprValue.replace( escape + wildCard, wildCard );
3483 }
3484 if ( !singleChar.isEmpty() && singleChar != QLatin1String( "_" ) )
3485 {
3486 oprValue.replace( '_', QLatin1String( "\\_" ) );
3487 if ( oprValue.startsWith( singleChar ) )
3488 {
3489 oprValue.replace( 0, 1, QStringLiteral( "_" ) );
3490 }
3491 const QRegularExpression rx( "[^" + QgsStringUtils::qRegExpEscape( escape ) + "](" + QgsStringUtils::qRegExpEscape( singleChar ) + ")" );
3492 QRegularExpressionMatch match = rx.match( oprValue );
3493 int pos;
3494 while ( match.hasMatch() )
3495 {
3496 pos = match.capturedStart();
3497 oprValue.replace( pos + 1, 1, QStringLiteral( "_" ) );
3498 pos += 1;
3499 match = rx.match( oprValue, pos );
3500 }
3501 oprValue.replace( escape + singleChar, singleChar );
3502 }
3503 if ( !escape.isEmpty() && escape != QLatin1String( "\\" ) )
3504 {
3505 oprValue.replace( escape + escape, escape );
3506 }
3507 opRight.reset( new QgsExpressionNodeLiteral( oprValue ) );
3508 }
3509
3510 expr.reset( new QgsExpressionNodeBinaryOperator( static_cast< QgsExpressionNodeBinaryOperator::BinaryOperator >( op ), expr.release(), opRight.release() ) );
3511 }
3512
3513 if ( expr == leftOp )
3514 {
3515 mErrorMessage = QObject::tr( "only one operand for '%1' binary operator" ).arg( element.tagName() );
3516 return nullptr;
3517 }
3518
3519 return dynamic_cast< QgsExpressionNodeBinaryOperator * >( expr.release() );
3520}
3521
3522
3524{
3525 // we are exploiting the fact that our function names are the same as the XML tag names
3526 const int opIdx = QgsExpression::functionIndex( element.tagName().toLower() );
3527
3528 std::unique_ptr<QgsExpressionNode::NodeList> gml2Args( new QgsExpressionNode::NodeList() );
3529 QDomElement childElem = element.firstChildElement();
3530 QString gml2Str;
3531 while ( !childElem.isNull() && gml2Str.isEmpty() )
3532 {
3533 if ( childElem.tagName() != mPropertyName )
3534 {
3535 QTextStream gml2Stream( &gml2Str );
3536 childElem.save( gml2Stream, 0 );
3537 }
3538 childElem = childElem.nextSiblingElement();
3539 }
3540 if ( !gml2Str.isEmpty() )
3541 {
3542 gml2Args->append( new QgsExpressionNodeLiteral( QVariant( gml2Str.remove( '\n' ) ) ) );
3543 }
3544 else
3545 {
3546 mErrorMessage = QObject::tr( "No OGC Geometry found" );
3547 return nullptr;
3548 }
3549
3550 std::unique_ptr<QgsExpressionNode::NodeList> opArgs( new QgsExpressionNode::NodeList() );
3551 opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "$geometry" ) ), new QgsExpressionNode::NodeList() ) );
3552 opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "geomFromGML" ) ), gml2Args.release() ) );
3553
3554 return new QgsExpressionNodeFunction( opIdx, opArgs.release() );
3555}
3556
3558{
3559 if ( element.isNull() || element.tagName() != mPropertyName )
3560 {
3561 mErrorMessage = QObject::tr( "%1:PropertyName expected, got %2" ).arg( mPrefix, element.tagName() );
3562 return nullptr;
3563 }
3564
3565 return new QgsExpressionNodeColumnRef( element.firstChild().nodeValue() );
3566}
3567
3569{
3570 if ( element.isNull() || element.tagName() != QLatin1String( "Literal" ) )
3571 {
3572 mErrorMessage = QObject::tr( "%1:Literal expected, got %2" ).arg( mPrefix, element.tagName() );
3573 return nullptr;
3574 }
3575
3576 std::unique_ptr<QgsExpressionNode> root;
3577 if ( !element.hasChildNodes() )
3578 {
3579 root.reset( new QgsExpressionNodeLiteral( QVariant( "" ) ) );
3580 return root.release();
3581 }
3582
3583 // the literal content can have more children (e.g. CDATA section, text, ...)
3584 QDomNode childNode = element.firstChild();
3585 while ( !childNode.isNull() )
3586 {
3587 std::unique_ptr<QgsExpressionNode> operand;
3588
3589 if ( childNode.nodeType() == QDomNode::ElementNode )
3590 {
3591 // found a element node (e.g. PropertyName), convert it
3592 const QDomElement operandElem = childNode.toElement();
3593 operand.reset( nodeFromOgcFilter( operandElem ) );
3594 if ( !operand )
3595 {
3596 mErrorMessage = QObject::tr( "'%1' is an invalid or not supported content for %2:Literal" ).arg( operandElem.tagName(), mPrefix );
3597 return nullptr;
3598 }
3599 }
3600 else
3601 {
3602 // probably a text/CDATA node
3603 QVariant value = childNode.nodeValue();
3604
3605 bool converted = false;
3606
3607 // try to convert the node content to corresponding field type if possible
3608 if ( mLayer )
3609 {
3610 QDomElement propertyNameElement = element.previousSiblingElement( mPropertyName );
3611 if ( propertyNameElement.isNull() || propertyNameElement.tagName() != mPropertyName )
3612 {
3613 propertyNameElement = element.nextSiblingElement( mPropertyName );
3614 }
3615 if ( !propertyNameElement.isNull() || propertyNameElement.tagName() == mPropertyName )
3616 {
3617 const int fieldIndex = mLayer->fields().indexOf( propertyNameElement.firstChild().nodeValue() );
3618 if ( fieldIndex != -1 )
3619 {
3620 const QgsField field = mLayer->fields().field( propertyNameElement.firstChild().nodeValue() );
3621 field.convertCompatible( value );
3622 converted = true;
3623 }
3624 }
3625 }
3626 if ( !converted )
3627 {
3628 // try to convert the node content to number if possible,
3629 // otherwise let's use it as string
3630 bool ok;
3631 const double d = value.toDouble( &ok );
3632 if ( ok )
3633 value = d;
3634 }
3635
3636 operand.reset( new QgsExpressionNodeLiteral( value ) );
3637 if ( !operand )
3638 continue;
3639 }
3640
3641 // use the concat operator to merge the ogc:Literal children
3642 if ( !root )
3643 {
3644 root = std::move( operand );
3645 }
3646 else
3647 {
3648 root.reset( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boConcat, root.release(), operand.release() ) );
3649 }
3650
3651 childNode = childNode.nextSibling();
3652 }
3653
3654 if ( root )
3655 return root.release();
3656
3657 return nullptr;
3658}
3659
3661{
3662 if ( element.tagName() != QLatin1String( "Not" ) )
3663 return nullptr;
3664
3665 const QDomElement operandElem = element.firstChildElement();
3666 std::unique_ptr<QgsExpressionNode> operand( nodeFromOgcFilter( operandElem ) );
3667 if ( !operand )
3668 {
3669 mErrorMessage = QObject::tr( "invalid operand for '%1' unary operator" ).arg( element.tagName() );
3670 return nullptr;
3671 }
3672
3674}
3675
3677{
3678 // convert ogc:PropertyIsNull to IS operator with NULL right operand
3679 if ( element.tagName() != QLatin1String( "PropertyIsNull" ) )
3680 {
3681 return nullptr;
3682 }
3683
3684 const QDomElement operandElem = element.firstChildElement();
3685 std::unique_ptr<QgsExpressionNode> opLeft( nodeFromOgcFilter( operandElem ) );
3686 if ( !opLeft )
3687 return nullptr;
3688
3689 std::unique_ptr<QgsExpressionNode> opRight( new QgsExpressionNodeLiteral( QVariant() ) );
3690 return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boIs, opLeft.release(), opRight.release() );
3691}
3692
3694{
3695 if ( element.isNull() || element.tagName() != QLatin1String( "Function" ) )
3696 {
3697 mErrorMessage = QObject::tr( "%1:Function expected, got %2" ).arg( mPrefix, element.tagName() );
3698 return nullptr;
3699 }
3700
3701 for ( int i = 0; i < QgsExpression::Functions().size(); i++ )
3702 {
3703 const QgsExpressionFunction *funcDef = QgsExpression::Functions()[i];
3704
3705 if ( element.attribute( QStringLiteral( "name" ) ) != funcDef->name() )
3706 continue;
3707
3708 std::unique_ptr<QgsExpressionNode::NodeList> args( new QgsExpressionNode::NodeList() );
3709
3710 QDomElement operandElem = element.firstChildElement();
3711 while ( !operandElem.isNull() )
3712 {
3713 std::unique_ptr<QgsExpressionNode> op( nodeFromOgcFilter( operandElem ) );
3714 if ( !op )
3715 {
3716 return nullptr;
3717 }
3718 args->append( op.release() );
3719
3720 operandElem = operandElem.nextSiblingElement();
3721 }
3722
3723 return new QgsExpressionNodeFunction( i, args.release() );
3724 }
3725
3726 return nullptr;
3727}
3728
3730{
3731 // <ogc:PropertyIsBetween> encode a Range check
3732 std::unique_ptr<QgsExpressionNode> operand;
3733 std::unique_ptr<QgsExpressionNode> lowerBound;
3734 std::unique_ptr<QgsExpressionNode> upperBound;
3735
3736 QDomElement operandElem = element.firstChildElement();
3737 while ( !operandElem.isNull() )
3738 {
3739 if ( operandElem.tagName() == QLatin1String( "LowerBoundary" ) )
3740 {
3741 const QDomElement lowerBoundElem = operandElem.firstChildElement();
3742 lowerBound.reset( nodeFromOgcFilter( lowerBoundElem ) );
3743 }
3744 else if ( operandElem.tagName() == QLatin1String( "UpperBoundary" ) )
3745 {
3746 const QDomElement upperBoundElem = operandElem.firstChildElement();
3747 upperBound.reset( nodeFromOgcFilter( upperBoundElem ) );
3748 }
3749 else
3750 {
3751 // <ogc:expression>
3752 operand.reset( nodeFromOgcFilter( operandElem ) );
3753 }
3754
3755 if ( operand && lowerBound && upperBound )
3756 break;
3757
3758 operandElem = operandElem.nextSiblingElement();
3759 }
3760
3761 if ( !operand || !lowerBound || !upperBound )
3762 {
3763 mErrorMessage = QObject::tr( "missing some required sub-elements in %1:PropertyIsBetween" ).arg( mPrefix );
3764 return nullptr;
3765 }
3766
3767 std::unique_ptr<QgsExpressionNode> leOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boLE, operand->clone(), upperBound.release() ) );
3768 std::unique_ptr<QgsExpressionNode> geOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boGE, operand.release(), lowerBound.release() ) );
3769 return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boAnd, geOperator.release(), leOperator.release() );
3770}
3771
3773{
3774 return mErrorMessage;
3775}
3776
3777QgsOgcCrsUtils::CRSFlavor QgsOgcCrsUtils::parseCrsName( const QString &crsName, QString &authority, QString &code )
3778{
3779 const thread_local QRegularExpression re_url( QRegularExpression::anchoredPattern( QStringLiteral( "http://www\\.opengis\\.net/gml/srs/epsg\\.xml#(.+)" ) ), QRegularExpression::CaseInsensitiveOption );
3780 if ( const QRegularExpressionMatch match = re_url.match( crsName ); match.hasMatch() )
3781 {
3782 authority = QStringLiteral( "EPSG" );
3783 code = match.captured( 1 );
3785 }
3786
3787 const thread_local QRegularExpression re_ogc_urn( QRegularExpression::anchoredPattern( QStringLiteral( "urn:ogc:def:crs:([^:]+).+(?<=:)([^:]+)" ) ), QRegularExpression::CaseInsensitiveOption );
3788 if ( const QRegularExpressionMatch match = re_ogc_urn.match( crsName ); match.hasMatch() )
3789 {
3790 authority = match.captured( 1 );
3791 code = match.captured( 2 );
3792 return CRSFlavor::OGC_URN;
3793 }
3794
3795 const thread_local QRegularExpression re_x_ogc_urn( QRegularExpression::anchoredPattern( QStringLiteral( "urn:x-ogc:def:crs:([^:]+).+(?<=:)([^:]+)" ) ), QRegularExpression::CaseInsensitiveOption );
3796 if ( const QRegularExpressionMatch match = re_x_ogc_urn.match( crsName ); match.hasMatch() )
3797 {
3798 authority = match.captured( 1 );
3799 code = match.captured( 2 );
3800 return CRSFlavor::X_OGC_URN;
3801 }
3802
3803 const thread_local QRegularExpression re_http_uri( QRegularExpression::anchoredPattern( QStringLiteral( "http://www\\.opengis\\.net/def/crs/([^/]+).+/([^/]+)" ) ), QRegularExpression::CaseInsensitiveOption );
3804 if ( const QRegularExpressionMatch match = re_http_uri.match( crsName ); match.hasMatch() )
3805 {
3806 authority = match.captured( 1 );
3807 code = match.captured( 2 );
3809 }
3810
3811 const thread_local QRegularExpression re_auth_code( QRegularExpression::anchoredPattern( QStringLiteral( "([^:]+):(.+)" ) ), QRegularExpression::CaseInsensitiveOption );
3812 if ( const QRegularExpressionMatch match = re_auth_code.match( crsName ); match.hasMatch() )
3813 {
3814 authority = match.captured( 1 );
3815 code = match.captured( 2 );
3816 return CRSFlavor::AUTH_CODE;
3817 }
3818
3819 return CRSFlavor::UNKNOWN;
3820}
GeometryOperationResult
Success or failure of a geometry operation.
Definition qgis.h:1889
@ 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:472
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() const
Returns the x minimum value (left side of rectangle).
void setYMinimum(double y)
Set the minimum y value.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
void setXMinimum(double x)
Set the minimum x value.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
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:5834
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6108
QMap< QString, QString > QgsStringMap
Definition qgis.h:6455
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