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