QGIS API Documentation 3.31.0-Master (9938c594d0)
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 );
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 // QgsDebugMsgLevel( QStringLiteral( "x, y is %1,%2" ).arg( x, 'f' ).arg( y, 'f' ), 2 );
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 {
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 {
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 {
2220 const QDomElement leftNode = expressionNodeToOgcFilter( node->node(), expression, context );
2221 const QDomElement firstListNode = expressionNodeToOgcFilter( node->list()->list().first(), expression, context );
2222 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2223 eqElem.appendChild( leftNode );
2224 eqElem.appendChild( firstListNode );
2225 if ( node->isNotIn() )
2226 {
2227 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2228 notElem.appendChild( eqElem );
2229 return notElem;
2230 }
2231 return eqElem;
2232 }
2233
2234 QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2235 const QDomElement leftNode = expressionNodeToOgcFilter( node->node(), expression, context );
2236
2237 const auto constList = node->list()->list();
2238 for ( QgsExpressionNode *n : constList )
2239 {
2240 const QDomElement listNode = expressionNodeToOgcFilter( n, expression, context );
2241 if ( !mErrorMessage.isEmpty() )
2242 return QDomElement();
2243
2244 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2245 eqElem.appendChild( leftNode.cloneNode() );
2246 eqElem.appendChild( listNode );
2247
2248 orElem.appendChild( eqElem );
2249 }
2250
2251 if ( node->isNotIn() )
2252 {
2253 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2254 notElem.appendChild( orElem );
2255 return notElem;
2256 }
2257
2258 return orElem;
2259}
2260
2261Q_GLOBAL_STATIC_WITH_ARGS( QgsStringMap, BINARY_SPATIAL_OPS_MAP, (
2262{
2263 { QLatin1String( "disjoint" ), QLatin1String( "Disjoint" ) },
2264 { QLatin1String( "intersects" ), QLatin1String( "Intersects" )},
2265 { QLatin1String( "touches" ), QLatin1String( "Touches" ) },
2266 { QLatin1String( "crosses" ), QLatin1String( "Crosses" ) },
2267 { QLatin1String( "contains" ), QLatin1String( "Contains" ) },
2268 { QLatin1String( "overlaps" ), QLatin1String( "Overlaps" ) },
2269 { QLatin1String( "within" ), QLatin1String( "Within" ) }
2270} ) )
2271
2272static bool isBinarySpatialOperator( const QString &fnName )
2273{
2274 return BINARY_SPATIAL_OPS_MAP()->contains( fnName );
2275}
2276
2277static QString tagNameForSpatialOperator( const QString &fnName )
2278{
2279 return BINARY_SPATIAL_OPS_MAP()->value( fnName );
2280}
2281
2282static bool isGeometryColumn( const QgsExpressionNode *node )
2283{
2284 if ( node->nodeType() != QgsExpressionNode::ntFunction )
2285 return false;
2286
2287 const QgsExpressionNodeFunction *fn = static_cast<const QgsExpressionNodeFunction *>( node );
2289 return fd->name() == QLatin1String( "$geometry" );
2290}
2291
2292static QgsGeometry geometryFromConstExpr( const QgsExpressionNode *node )
2293{
2294 // Right now we support only geomFromWKT(' ..... ')
2295 // Ideally we should support any constant sub-expression (not dependent on feature's geometry or attributes)
2296
2297 if ( node->nodeType() == QgsExpressionNode::ntFunction )
2298 {
2299 const QgsExpressionNodeFunction *fnNode = static_cast<const QgsExpressionNodeFunction *>( node );
2301 if ( fnDef->name() == QLatin1String( "geom_from_wkt" ) )
2302 {
2303 const QList<QgsExpressionNode *> &args = fnNode->args()->list();
2304 if ( args[0]->nodeType() == QgsExpressionNode::ntLiteral )
2305 {
2306 const QString wkt = static_cast<const QgsExpressionNodeLiteral *>( args[0] )->value().toString();
2307 return QgsGeometry::fromWkt( wkt );
2308 }
2309 }
2310 }
2311 return QgsGeometry();
2312}
2313
2314
2315QDomElement QgsOgcUtilsExprToFilter::expressionFunctionToOgcFilter( const QgsExpressionNodeFunction *node, QgsExpression *expression, const QgsExpressionContext *context )
2316{
2318
2319 if ( fd->name() == QLatin1String( "intersects_bbox" ) )
2320 {
2321 QList<QgsExpressionNode *> argNodes = node->args()->list();
2322 Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2323
2324 const QgsGeometry geom = geometryFromConstExpr( argNodes[1] );
2325 if ( !geom.isNull() && isGeometryColumn( argNodes[0] ) )
2326 {
2327 QgsRectangle rect = geom.boundingBox();
2328
2329 mGMLUsed = true;
2330
2331 const QDomElement elemBox = ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
2332 QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, mSrsName, mInvertAxisOrientation ) :
2333 QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, mSrsName, mInvertAxisOrientation );
2334
2335 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":BBOX" );
2336
2337 if ( !mGeometryName.isEmpty() )
2338 {
2339 // Geometry column is optional for a BBOX filter.
2340 QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2341 QString columnRef( mGeometryName );
2342 if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
2343 columnRef = mNamespacePrefix + QStringLiteral( ":" ) + columnRef;
2344 geomProperty.appendChild( mDoc.createTextNode( columnRef ) );
2345
2346 funcElem.appendChild( geomProperty );
2347 }
2348 funcElem.appendChild( elemBox );
2349 return funcElem;
2350 }
2351 else
2352 {
2353 mErrorMessage = QObject::tr( "<BBOX> is currently supported only in form: bbox($geometry, geomFromWKT('…'))" );
2354 return QDomElement();
2355 }
2356 }
2357
2358 if ( isBinarySpatialOperator( fd->name() ) )
2359 {
2360 QList<QgsExpressionNode *> argNodes = node->args()->list();
2361 Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2362
2363 QgsExpressionNode *otherNode = nullptr;
2364 if ( isGeometryColumn( argNodes[0] ) )
2365 otherNode = argNodes[1];
2366 else if ( isGeometryColumn( argNodes[1] ) )
2367 otherNode = argNodes[0];
2368 else
2369 {
2370 mErrorMessage = QObject::tr( "Unable to translate spatial operator: at least one must refer to geometry." );
2371 return QDomElement();
2372 }
2373
2374 QDomElement otherGeomElem;
2375
2376 // the other node must be a geometry constructor
2377 if ( otherNode->nodeType() != QgsExpressionNode::ntFunction )
2378 {
2379 mErrorMessage = QObject::tr( "spatial operator: the other operator must be a geometry constructor function" );
2380 return QDomElement();
2381 }
2382
2383 const QgsExpressionNodeFunction *otherFn = static_cast<const QgsExpressionNodeFunction *>( otherNode );
2384 QgsExpressionFunction *otherFnDef = QgsExpression::Functions()[otherFn->fnIndex()];
2385 if ( otherFnDef->name() == QLatin1String( "geom_from_wkt" ) )
2386 {
2387 QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2388 if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2389 {
2390 mErrorMessage = QObject::tr( "geom_from_wkt: argument must be string literal" );
2391 return QDomElement();
2392 }
2393 const QString wkt = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2394 const QgsGeometry geom = QgsGeometry::fromWkt( wkt );
2395 otherGeomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation,
2396 QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2397 if ( otherGeomElem.isNull() )
2398 {
2399 mErrorMessage = QObject::tr( "geom_from_wkt: unable to generate GML from wkt geometry" );
2400 return QDomElement();
2401 }
2402 mGeomId ++;
2403 }
2404 else if ( otherFnDef->name() == QLatin1String( "geom_from_gml" ) )
2405 {
2406 QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2407 if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2408 {
2409 mErrorMessage = QObject::tr( "geom_from_gml: argument must be string literal" );
2410 return QDomElement();
2411 }
2412
2413 QDomDocument geomDoc;
2414 const QString gml = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2415 if ( !geomDoc.setContent( gml, true ) )
2416 {
2417 mErrorMessage = QObject::tr( "geom_from_gml: unable to parse XML" );
2418 return QDomElement();
2419 }
2420
2421 const QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
2422 otherGeomElem = geomNode.toElement();
2423 }
2424 else if ( otherNode->hasCachedStaticValue() && otherNode->cachedStaticValue().userType() == QMetaType::type( "QgsGeometry" ) )
2425 {
2426 QgsGeometry geom = otherNode->cachedStaticValue().value<QgsGeometry>();
2427 otherGeomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation,
2428 QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2429 if ( otherGeomElem.isNull() )
2430 {
2431 mErrorMessage = QObject::tr( "geom from static value: unable to generate GML from static variable" );
2432 return QDomElement();
2433 }
2434 mGeomId ++;
2435 }
2436 else
2437 {
2438 mErrorMessage = QObject::tr( "spatial operator: unknown geometry constructor function" );
2439 return QDomElement();
2440 }
2441
2442 mGMLUsed = true;
2443
2444 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + tagNameForSpatialOperator( fd->name() ) );
2445 QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2446 QString columnRef( mGeometryName );
2447 if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
2448 columnRef = mNamespacePrefix + QStringLiteral( ":" ) + columnRef;
2449 geomProperty.appendChild( mDoc.createTextNode( columnRef ) );
2450 funcElem.appendChild( geomProperty );
2451 funcElem.appendChild( otherGeomElem );
2452 return funcElem;
2453 }
2454
2455 if ( fd->isStatic( node, expression, context ) )
2456 {
2457 const QVariant result = fd->run( node->args(), context, expression, node );
2458 const QgsExpressionNodeLiteral literal( result );
2459 return expressionLiteralToOgcFilter( &literal, expression, context );
2460 }
2461
2462 if ( fd->params() == 0 )
2463 {
2464 mErrorMessage = QObject::tr( "Special columns/constants are not supported." );
2465 return QDomElement();
2466 }
2467
2468 // this is somehow wrong - we are just hoping that the other side supports the same functions as we do...
2469 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
2470 funcElem.setAttribute( QStringLiteral( "name" ), fd->name() );
2471 const auto constList = node->args()->list();
2472 for ( QgsExpressionNode *n : constList )
2473 {
2474 const QDomElement childElem = expressionNodeToOgcFilter( n, expression, context );
2475 if ( !mErrorMessage.isEmpty() )
2476 return QDomElement();
2477
2478 funcElem.appendChild( childElem );
2479 }
2480
2481 return funcElem;
2482}
2483
2484//
2485
2487 QgsOgcUtils::GMLVersion gmlVersion,
2488 QgsOgcUtils::FilterVersion filterVersion,
2489 const QList<QgsOgcUtils::LayerProperties> &layerProperties,
2490 bool honourAxisOrientation,
2491 bool invertAxisOrientation,
2492 const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename )
2493 : mDoc( doc )
2494 , mGMLUsed( false )
2495 , mGMLVersion( gmlVersion )
2496 , mFilterVersion( filterVersion )
2497 , mLayerProperties( layerProperties )
2498 , mHonourAxisOrientation( honourAxisOrientation )
2499 , mInvertAxisOrientation( invertAxisOrientation )
2500 , mFilterPrefix( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "fes" : "ogc" )
2501 , mPropertyName( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "ValueReference" : "PropertyName" )
2502 , mGeomId( 1 )
2503 , mMapUnprefixedTypenameToPrefixedTypename( mapUnprefixedTypenameToPrefixedTypename )
2504{
2505}
2506
2508{
2509 switch ( node->nodeType() )
2510 {
2512 return toOgcFilter( static_cast<const QgsSQLStatement::NodeUnaryOperator *>( node ) );
2514 return toOgcFilter( static_cast<const QgsSQLStatement::NodeBinaryOperator *>( node ) );
2516 return toOgcFilter( static_cast<const QgsSQLStatement::NodeInOperator *>( node ) );
2518 return toOgcFilter( static_cast<const QgsSQLStatement::NodeBetweenOperator *>( node ) );
2520 return toOgcFilter( static_cast<const QgsSQLStatement::NodeFunction *>( node ) );
2522 return toOgcFilter( static_cast<const QgsSQLStatement::NodeLiteral *>( node ) );
2524 return toOgcFilter( static_cast<const QgsSQLStatement::NodeColumnRef *>( node ) );
2526 return toOgcFilter( static_cast<const QgsSQLStatement::NodeSelect *>( node ) );
2527
2528 default:
2529 mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
2530 return QDomElement();
2531 }
2532}
2533
2534
2536{
2537
2538 const QDomElement operandElem = toOgcFilter( node->operand() );
2539 if ( !mErrorMessage.isEmpty() )
2540 return QDomElement();
2541
2542 QDomElement uoElem;
2543 switch ( node->op() )
2544 {
2546 uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2547 if ( node->operand()->nodeType() == QgsSQLStatement::ntLiteral )
2548 {
2549 // operand expression already created a Literal node:
2550 // take the literal value, prepend - and remove old literal node
2551 uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2552 mDoc.removeChild( operandElem );
2553 }
2554 else
2555 {
2556 mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2557 return QDomElement();
2558 }
2559 break;
2561 uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2562 uoElem.appendChild( operandElem );
2563 break;
2564
2565 default:
2566 mErrorMessage = QObject::tr( "Unary operator %1 not implemented yet" ).arg( QgsSQLStatement::UNARY_OPERATOR_TEXT[node->op()] );
2567 return QDomElement();
2568 }
2569
2570 return uoElem;
2571}
2572
2573
2575{
2576 const QDomElement leftElem = toOgcFilter( node->opLeft() );
2577 if ( !mErrorMessage.isEmpty() )
2578 return QDomElement();
2579
2581
2582 // before right operator is parsed: to allow NULL handling
2584 {
2585 if ( node->opRight()->nodeType() == QgsSQLStatement::ntLiteral )
2586 {
2587 const QgsSQLStatement::NodeLiteral *rightLit = static_cast<const QgsSQLStatement::NodeLiteral *>( node->opRight() );
2588 if ( QgsVariantUtils::isNull( rightLit->value() ) )
2589 {
2590
2591 QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2592 elem.appendChild( leftElem );
2593
2594 if ( op == QgsSQLStatement::boIsNot )
2595 {
2596 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2597 notElem.appendChild( elem );
2598 return notElem;
2599 }
2600
2601 return elem;
2602 }
2603
2604 // continue with equal / not equal operator once the null case is handled
2606 }
2607
2608 }
2609
2610 const QDomElement rightElem = toOgcFilter( node->opRight() );
2611 if ( !mErrorMessage.isEmpty() )
2612 return QDomElement();
2613
2614
2615 QString opText;
2616 if ( op == QgsSQLStatement::boOr )
2617 opText = QStringLiteral( "Or" );
2618 else if ( op == QgsSQLStatement::boAnd )
2619 opText = QStringLiteral( "And" );
2620 else if ( op == QgsSQLStatement::boEQ )
2621 opText = QStringLiteral( "PropertyIsEqualTo" );
2622 else if ( op == QgsSQLStatement::boNE )
2623 opText = QStringLiteral( "PropertyIsNotEqualTo" );
2624 else if ( op == QgsSQLStatement::boLE )
2625 opText = QStringLiteral( "PropertyIsLessThanOrEqualTo" );
2626 else if ( op == QgsSQLStatement::boGE )
2627 opText = QStringLiteral( "PropertyIsGreaterThanOrEqualTo" );
2628 else if ( op == QgsSQLStatement::boLT )
2629 opText = QStringLiteral( "PropertyIsLessThan" );
2630 else if ( op == QgsSQLStatement::boGT )
2631 opText = QStringLiteral( "PropertyIsGreaterThan" );
2632 else if ( op == QgsSQLStatement::boLike )
2633 opText = QStringLiteral( "PropertyIsLike" );
2634 else if ( op == QgsSQLStatement::boILike )
2635 opText = QStringLiteral( "PropertyIsLike" );
2636
2637 if ( opText.isEmpty() )
2638 {
2639 // not implemented binary operators
2640 mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( QgsSQLStatement::BINARY_OPERATOR_TEXT[op] );
2641 return QDomElement();
2642 }
2643
2644 QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2645
2647 {
2648 if ( op == QgsSQLStatement::boILike )
2649 boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2650
2651 // setup wildCards to <ogc:PropertyIsLike>
2652 boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2653 boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2654 if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2655 boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2656 else
2657 boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2658 }
2659
2660 boElem.appendChild( leftElem );
2661 boElem.appendChild( rightElem );
2662 return boElem;
2663}
2664
2665
2667{
2668 QString value;
2669 switch ( node->value().type() )
2670 {
2671 case QVariant::Int:
2672 value = QString::number( node->value().toInt() );
2673 break;
2674 case QVariant::LongLong:
2675 value = QString::number( node->value().toLongLong() );
2676 break;
2677 case QVariant::Double:
2678 value = qgsDoubleToString( node->value().toDouble() );
2679 break;
2680 case QVariant::String:
2681 value = node->value().toString();
2682 break;
2683
2684 default:
2685 mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( node->value().type() );
2686 return QDomElement();
2687 }
2688
2689 QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2690 litElem.appendChild( mDoc.createTextNode( value ) );
2691 return litElem;
2692}
2693
2694
2696{
2697 QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2698 if ( node->tableName().isEmpty() || mLayerProperties.size() == 1 )
2699 {
2700 if ( mLayerProperties.size() == 1 && !mLayerProperties[0].mNamespacePrefix.isEmpty() && !mLayerProperties[0].mNamespaceURI.isEmpty() )
2701 propElem.appendChild( mDoc.createTextNode(
2702 mLayerProperties[0].mNamespacePrefix + QStringLiteral( ":" ) + node->name() ) );
2703 else
2704 propElem.appendChild( mDoc.createTextNode( node->name() ) );
2705 }
2706 else
2707 {
2708 QString tableName( mMapTableAliasToNames[node->tableName()] );
2709 if ( mMapUnprefixedTypenameToPrefixedTypename.contains( tableName ) )
2710 tableName = mMapUnprefixedTypenameToPrefixedTypename[tableName];
2711 propElem.appendChild( mDoc.createTextNode( tableName + "/" + node->name() ) );
2712 }
2713 return propElem;
2714}
2715
2717{
2718 if ( node->list()->list().size() == 1 )
2719 {
2720 const QDomElement leftNode = toOgcFilter( node->node() );
2721 const QDomElement firstListNode = toOgcFilter( node->list()->list().first() );
2722 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2723 eqElem.appendChild( leftNode );
2724 eqElem.appendChild( firstListNode );
2725 if ( node->isNotIn() )
2726 {
2727 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2728 notElem.appendChild( eqElem );
2729 return notElem;
2730 }
2731 return eqElem;
2732 }
2733
2734 QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2735 const QDomElement leftNode = toOgcFilter( node->node() );
2736
2737 const auto constList = node->list()->list();
2738 for ( QgsSQLStatement::Node *n : constList )
2739 {
2740 const QDomElement listNode = toOgcFilter( n );
2741 if ( !mErrorMessage.isEmpty() )
2742 return QDomElement();
2743
2744 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2745 eqElem.appendChild( leftNode.cloneNode() );
2746 eqElem.appendChild( listNode );
2747
2748 orElem.appendChild( eqElem );
2749 }
2750
2751 if ( node->isNotIn() )
2752 {
2753 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2754 notElem.appendChild( orElem );
2755 return notElem;
2756 }
2757
2758 return orElem;
2759}
2760
2762{
2763 QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsBetween" );
2764 elem.appendChild( toOgcFilter( node->node() ) );
2765 QDomElement lowerBoundary = mDoc.createElement( mFilterPrefix + ":LowerBoundary" );
2766 lowerBoundary.appendChild( toOgcFilter( node->minVal() ) );
2767 elem.appendChild( lowerBoundary );
2768 QDomElement upperBoundary = mDoc.createElement( mFilterPrefix + ":UpperBoundary" );
2769 upperBoundary.appendChild( toOgcFilter( node->maxVal() ) );
2770 elem.appendChild( upperBoundary );
2771
2772 if ( node->isNotBetween() )
2773 {
2774 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2775 notElem.appendChild( elem );
2776 return notElem;
2777 }
2778
2779 return elem;
2780}
2781
2782static QString mapBinarySpatialToOgc( const QString &name )
2783{
2784 QString nameCompare( name );
2785#if QT_VERSION < QT_VERSION_CHECK(5, 15, 2)
2786 if ( name.size() > 3 && name.midRef( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2787 nameCompare = name.mid( 3 );
2788#else
2789 if ( name.size() > 3 && QStringView {name}.mid( 0, 3 ).toString().compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2790 nameCompare = name.mid( 3 );
2791#endif
2792 QStringList spatialOps;
2793 spatialOps << QStringLiteral( "BBOX" ) << QStringLiteral( "Intersects" ) << QStringLiteral( "Contains" ) << QStringLiteral( "Crosses" ) << QStringLiteral( "Equals" )
2794 << QStringLiteral( "Disjoint" ) << QStringLiteral( "Overlaps" ) << QStringLiteral( "Touches" ) << QStringLiteral( "Within" );
2795 const auto constSpatialOps = spatialOps;
2796 for ( QString op : constSpatialOps )
2797 {
2798 if ( nameCompare.compare( op, Qt::CaseInsensitive ) == 0 )
2799 return op;
2800 }
2801 return QString();
2802}
2803
2804static QString mapTernarySpatialToOgc( const QString &name )
2805{
2806 QString nameCompare( name );
2807#if QT_VERSION < QT_VERSION_CHECK(5, 15, 2)
2808 if ( name.size() > 3 && name.midRef( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2809 nameCompare = name.mid( 3 );
2810#else
2811 if ( name.size() > 3 && QStringView {name}.mid( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2812 nameCompare = name.mid( 3 );
2813#endif
2814 if ( nameCompare.compare( QLatin1String( "DWithin" ), Qt::CaseInsensitive ) == 0 )
2815 return QStringLiteral( "DWithin" );
2816 if ( nameCompare.compare( QLatin1String( "Beyond" ), Qt::CaseInsensitive ) == 0 )
2817 return QStringLiteral( "Beyond" );
2818 return QString();
2819}
2820
2821QString QgsOgcUtilsSQLStatementToFilter::getGeometryColumnSRSName( const QgsSQLStatement::Node *node )
2822{
2823 if ( node->nodeType() != QgsSQLStatement::ntColumnRef )
2824 return QString();
2825
2826 const QgsSQLStatement::NodeColumnRef *col = static_cast<const QgsSQLStatement::NodeColumnRef *>( node );
2827 if ( !col->tableName().isEmpty() )
2828 {
2829 const auto constMLayerProperties = mLayerProperties;
2830 for ( const QgsOgcUtils::LayerProperties &prop : constMLayerProperties )
2831 {
2832 if ( prop.mName.compare( mMapTableAliasToNames[col->tableName()], Qt::CaseInsensitive ) == 0 &&
2833 prop.mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
2834 {
2835 return prop.mSRSName;
2836 }
2837 }
2838 }
2839 if ( !mLayerProperties.empty() &&
2840 mLayerProperties.at( 0 ).mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
2841 {
2842 return mLayerProperties.at( 0 ).mSRSName;
2843 }
2844 return QString();
2845}
2846
2847bool QgsOgcUtilsSQLStatementToFilter::processSRSName( const QgsSQLStatement::NodeFunction *mainNode,
2848 QList<QgsSQLStatement::Node *> args,
2849 bool lastArgIsSRSName,
2850 QString &srsName,
2851 bool &axisInversion )
2852{
2853 srsName = mCurrentSRSName;
2854 axisInversion = mInvertAxisOrientation;
2855
2856 if ( lastArgIsSRSName )
2857 {
2858 QgsSQLStatement::Node *lastArg = args[ args.size() - 1 ];
2859 if ( lastArg->nodeType() != QgsSQLStatement::ntLiteral )
2860 {
2861 mErrorMessage = QObject::tr( "%1: Last argument must be string or integer literal" ).arg( mainNode->name() );
2862 return false;
2863 }
2864 const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( lastArg );
2865 if ( lit->value().type() == QVariant::Int )
2866 {
2867 if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2868 {
2869 srsName = "EPSG:" + QString::number( lit->value().toInt() );
2870 }
2871 else
2872 {
2873 srsName = "urn:ogc:def:crs:EPSG::" + QString::number( lit->value().toInt() );
2874 }
2875 }
2876 else
2877 {
2878 srsName = lit->value().toString();
2879 if ( srsName.startsWith( QLatin1String( "EPSG:" ), Qt::CaseInsensitive ) )
2880 return true;
2881 }
2882 }
2883
2885 if ( !srsName.isEmpty() )
2887 if ( crs.isValid() )
2888 {
2889 if ( mHonourAxisOrientation && crs.hasAxisInverted() )
2890 {
2891 axisInversion = !axisInversion;
2892 }
2893 }
2894
2895 return true;
2896}
2897
2899{
2900 // ST_GeometryFromText
2901 if ( node->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 )
2902 {
2903 QList<QgsSQLStatement::Node *> args = node->args()->list();
2904 if ( args.size() != 1 && args.size() != 2 )
2905 {
2906 mErrorMessage = QObject::tr( "Function %1 should have 1 or 2 arguments" ).arg( node->name() );
2907 return QDomElement();
2908 }
2909
2910 QgsSQLStatement::Node *firstFnArg = args[0];
2911 if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
2912 {
2913 mErrorMessage = QObject::tr( "%1: First argument must be string literal" ).arg( node->name() );
2914 return QDomElement();
2915 }
2916
2917 QString srsName;
2918 bool axisInversion;
2919 if ( ! processSRSName( node, args, args.size() == 2, srsName, axisInversion ) )
2920 {
2921 return QDomElement();
2922 }
2923
2924 const QString wkt = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
2925 const QgsGeometry geom = QgsGeometry::fromWkt( wkt );
2926 const QDomElement geomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, srsName, axisInversion,
2927 QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2928 mGeomId ++;
2929 if ( geomElem.isNull() )
2930 {
2931 mErrorMessage = QObject::tr( "%1: invalid WKT" ).arg( node->name() );
2932 return QDomElement();
2933 }
2934 mGMLUsed = true;
2935 return geomElem;
2936 }
2937
2938 // ST_MakeEnvelope
2939 if ( node->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 )
2940 {
2941 QList<QgsSQLStatement::Node *> args = node->args()->list();
2942 if ( args.size() != 4 && args.size() != 5 )
2943 {
2944 mErrorMessage = QObject::tr( "Function %1 should have 4 or 5 arguments" ).arg( node->name() );
2945 return QDomElement();
2946 }
2947
2948 QgsRectangle rect;
2949
2950 for ( int i = 0; i < 4; i++ )
2951 {
2952 QgsSQLStatement::Node *arg = args[i];
2953 if ( arg->nodeType() != QgsSQLStatement::ntLiteral )
2954 {
2955 mErrorMessage = QObject::tr( "%1: Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
2956 return QDomElement();
2957 }
2958 const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( arg );
2959 double val = 0.0;
2960 if ( lit->value().type() == QVariant::Int )
2961 val = lit->value().toInt();
2962 else if ( lit->value().type() == QVariant::LongLong )
2963 val = lit->value().toLongLong();
2964 else if ( lit->value().type() == QVariant::Double )
2965 val = lit->value().toDouble();
2966 else
2967 {
2968 mErrorMessage = QObject::tr( "%1 Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
2969 return QDomElement();
2970 }
2971 if ( i == 0 )
2972 rect.setXMinimum( val );
2973 else if ( i == 1 )
2974 rect.setYMinimum( val );
2975 else if ( i == 2 )
2976 rect.setXMaximum( val );
2977 else
2978 rect.setYMaximum( val );
2979 }
2980
2981 QString srsName;
2982 bool axisInversion;
2983 if ( ! processSRSName( node, args, args.size() == 5, srsName, axisInversion ) )
2984 {
2985 return QDomElement();
2986 }
2987
2988 mGMLUsed = true;
2989
2990 return ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
2991 QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, srsName, axisInversion, 15 ) :
2992 QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, srsName, axisInversion, 15 );
2993 }
2994
2995 // ST_GeomFromGML
2996 if ( node->name().compare( QLatin1String( "ST_GeomFromGML" ), Qt::CaseInsensitive ) == 0 )
2997 {
2998 QList<QgsSQLStatement::Node *> args = node->args()->list();
2999 if ( args.size() != 1 )
3000 {
3001 mErrorMessage = QObject::tr( "Function %1 should have 1 argument" ).arg( node->name() );
3002 return QDomElement();
3003 }
3004
3005 QgsSQLStatement::Node *firstFnArg = args[0];
3006 if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
3007 {
3008 mErrorMessage = QObject::tr( "%1: Argument must be string literal" ).arg( node->name() );
3009 return QDomElement();
3010 }
3011
3012 QDomDocument geomDoc;
3013 const QString gml = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
3014 if ( !geomDoc.setContent( gml, true ) )
3015 {
3016 mErrorMessage = QObject::tr( "ST_GeomFromGML: unable to parse XML" );
3017 return QDomElement();
3018 }
3019
3020 const QDomNode geomNode = mDoc.importNode( geomDoc.documentElement(), true );
3021 mGMLUsed = true;
3022 return geomNode.toElement();
3023 }
3024
3025 // Binary geometry operators
3026 QString ogcName( mapBinarySpatialToOgc( node->name() ) );
3027 if ( !ogcName.isEmpty() )
3028 {
3029 QList<QgsSQLStatement::Node *> args = node->args()->list();
3030 if ( args.size() != 2 )
3031 {
3032 mErrorMessage = QObject::tr( "Function %1 should have 2 arguments" ).arg( node->name() );
3033 return QDomElement();
3034 }
3035
3036 for ( int i = 0; i < 2; i ++ )
3037 {
3038 if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
3039 ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
3040 static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
3041 {
3042 mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
3043 break;
3044 }
3045 }
3046
3047 //if( ogcName == "Intersects" && mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
3048 // ogcName = "Intersect";
3049 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + ogcName );
3050 const auto constArgs = args;
3051 for ( QgsSQLStatement::Node *n : constArgs )
3052 {
3053 const QDomElement childElem = toOgcFilter( n );
3054 if ( !mErrorMessage.isEmpty() )
3055 {
3056 mCurrentSRSName.clear();
3057 return QDomElement();
3058 }
3059
3060 funcElem.appendChild( childElem );
3061 }
3062
3063 mCurrentSRSName.clear();
3064 return funcElem;
3065 }
3066
3067 ogcName = mapTernarySpatialToOgc( node->name() );
3068 if ( !ogcName.isEmpty() )
3069 {
3070 QList<QgsSQLStatement::Node *> args = node->args()->list();
3071 if ( args.size() != 3 )
3072 {
3073 mErrorMessage = QObject::tr( "Function %1 should have 3 arguments" ).arg( node->name() );
3074 return QDomElement();
3075 }
3076
3077 for ( int i = 0; i < 2; i ++ )
3078 {
3079 if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
3080 ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
3081 static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
3082 {
3083 mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
3084 break;
3085 }
3086 }
3087
3088 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + node->name().mid( 3 ) );
3089 for ( int i = 0; i < 2; i++ )
3090 {
3091 const QDomElement childElem = toOgcFilter( args[i] );
3092 if ( !mErrorMessage.isEmpty() )
3093 {
3094 mCurrentSRSName.clear();
3095 return QDomElement();
3096 }
3097
3098 funcElem.appendChild( childElem );
3099 }
3100 mCurrentSRSName.clear();
3101
3102 QgsSQLStatement::Node *distanceNode = args[2];
3103 if ( distanceNode->nodeType() != QgsSQLStatement::ntLiteral )
3104 {
3105 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() );
3106 return QDomElement();
3107 }
3108 const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( distanceNode );
3109 if ( QgsVariantUtils::isNull( lit->value() ) )
3110 {
3111 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() );
3112 return QDomElement();
3113 }
3114 QString distance;
3115 QString unit( QStringLiteral( "m" ) );
3116 switch ( lit->value().type() )
3117 {
3118 case QVariant::Int:
3119 distance = QString::number( lit->value().toInt() );
3120 break;
3121 case QVariant::LongLong:
3122 distance = QString::number( lit->value().toLongLong() );
3123 break;
3124 case QVariant::Double:
3125 distance = qgsDoubleToString( lit->value().toDouble() );
3126 break;
3127 case QVariant::String:
3128 {
3129 distance = lit->value().toString();
3130 for ( int i = 0; i < distance.size(); i++ )
3131 {
3132 if ( !( ( distance[i] >= '0' && distance[i] <= '9' ) || distance[i] == '-' || distance[i] == '.' || distance[i] == 'e' || distance[i] == 'E' ) )
3133 {
3134 unit = distance.mid( i ).trimmed();
3135 distance = distance.mid( 0, i );
3136 break;
3137 }
3138 }
3139 break;
3140 }
3141
3142 default:
3143 mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( lit->value().type() );
3144 return QDomElement();
3145 }
3146
3147 QDomElement distanceElem = mDoc.createElement( mFilterPrefix + ":Distance" );
3148 if ( mFilterVersion == QgsOgcUtils::FILTER_FES_2_0 )
3149 distanceElem.setAttribute( QStringLiteral( "uom" ), unit );
3150 else
3151 distanceElem.setAttribute( QStringLiteral( "unit" ), unit );
3152 distanceElem.appendChild( mDoc.createTextNode( distance ) );
3153 funcElem.appendChild( distanceElem );
3154 return funcElem;
3155 }
3156
3157 // Other function
3158 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
3159 funcElem.setAttribute( QStringLiteral( "name" ), node->name() );
3160 const auto constList = node->args()->list();
3161 for ( QgsSQLStatement::Node *n : constList )
3162 {
3163 const QDomElement childElem = toOgcFilter( n );
3164 if ( !mErrorMessage.isEmpty() )
3165 return QDomElement();
3166
3167 funcElem.appendChild( childElem );
3168 }
3169 return funcElem;
3170}
3171
3173 const QString &leftTable )
3174{
3175 QgsSQLStatement::Node *onExpr = node->onExpr();
3176 if ( onExpr )
3177 {
3178 return toOgcFilter( onExpr );
3179 }
3180
3181 QList<QDomElement> listElem;
3182 const auto constUsingColumns = node->usingColumns();
3183 for ( const QString &columnName : constUsingColumns )
3184 {
3185 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
3186 QDomElement propElem1 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3187 propElem1.appendChild( mDoc.createTextNode( leftTable + "/" + columnName ) );
3188 eqElem.appendChild( propElem1 );
3189 QDomElement propElem2 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3190 propElem2.appendChild( mDoc.createTextNode( node->tableDef()->name() + "/" + columnName ) );
3191 eqElem.appendChild( propElem2 );
3192 listElem.append( eqElem );
3193 }
3194
3195 if ( listElem.size() == 1 )
3196 {
3197 return listElem[0];
3198 }
3199 else if ( listElem.size() > 1 )
3200 {
3201 QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3202 const auto constListElem = listElem;
3203 for ( const QDomElement &elem : constListElem )
3204 {
3205 andElem.appendChild( elem );
3206 }
3207 return andElem;
3208 }
3209
3210 return QDomElement();
3211}
3212
3213void QgsOgcUtilsSQLStatementToFilter::visit( const QgsSQLStatement::NodeTableDef *node )
3214{
3215 if ( node->alias().isEmpty() )
3216 {
3217 mMapTableAliasToNames[ node->name()] = node->name();
3218 }
3219 else
3220 {
3221 mMapTableAliasToNames[ node->alias()] = node->name();
3222 }
3223}
3224
3226{
3227 QList<QDomElement> listElem;
3228
3229 if ( mFilterVersion != QgsOgcUtils::FILTER_FES_2_0 &&
3230 ( node->tables().size() != 1 || !node->joins().empty() ) )
3231 {
3232 mErrorMessage = QObject::tr( "Joins are only supported with WFS 2.0" );
3233 return QDomElement();
3234 }
3235
3236 // Register all table name aliases
3237 const auto constTables = node->tables();
3238 for ( QgsSQLStatement::NodeTableDef *table : constTables )
3239 {
3240 visit( table );
3241 }
3242 const auto constJoins = node->joins();
3243 for ( QgsSQLStatement::NodeJoin *join : constJoins )
3244 {
3245 visit( join->tableDef() );
3246 }
3247
3248 // Process JOIN conditions
3249 const QList< QgsSQLStatement::NodeTableDef *> nodeTables = node->tables();
3250 QString leftTable = nodeTables.at( nodeTables.length() - 1 )->name();
3251 for ( QgsSQLStatement::NodeJoin *join : constJoins )
3252 {
3253 const QDomElement joinElem = toOgcFilter( join, leftTable );
3254 if ( !mErrorMessage.isEmpty() )
3255 return QDomElement();
3256 listElem.append( joinElem );
3257 leftTable = join->tableDef()->name();
3258 }
3259
3260 // Process WHERE conditions
3261 if ( node->where() )
3262 {
3263 const QDomElement whereElem = toOgcFilter( node->where() );
3264 if ( !mErrorMessage.isEmpty() )
3265 return QDomElement();
3266 listElem.append( whereElem );
3267 }
3268
3269 // Concatenate all conditions
3270 if ( listElem.size() == 1 )
3271 {
3272 return listElem[0];
3273 }
3274 else if ( listElem.size() > 1 )
3275 {
3276 QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3277 const auto constListElem = listElem;
3278 for ( const QDomElement &elem : constListElem )
3279 {
3280 andElem.appendChild( elem );
3281 }
3282 return andElem;
3283 }
3284
3285 return QDomElement();
3286}
3287
3289 : mLayer( layer )
3290{
3291 mPropertyName = QStringLiteral( "PropertyName" );
3292 mPrefix = QStringLiteral( "ogc" );
3293
3294 if ( version == QgsOgcUtils::FILTER_FES_2_0 )
3295 {
3296 mPropertyName = QStringLiteral( "ValueReference" );
3297 mPrefix = QStringLiteral( "fes" );
3298 }
3299}
3300
3302{
3303 if ( element.isNull() )
3304 return nullptr;
3305
3306 // check for binary operators
3307 if ( isBinaryOperator( element.tagName() ) )
3308 {
3309 return nodeBinaryOperatorFromOgcFilter( element );
3310 }
3311
3312 // check for spatial operators
3313 if ( isSpatialOperator( element.tagName() ) )
3314 {
3315 return nodeSpatialOperatorFromOgcFilter( element );
3316 }
3317
3318 // check for other OGC operators, convert them to expressions
3319 if ( element.tagName() == QLatin1String( "Not" ) )
3320 {
3321 return nodeNotFromOgcFilter( element );
3322 }
3323 else if ( element.tagName() == QLatin1String( "PropertyIsNull" ) )
3324 {
3325 return nodePropertyIsNullFromOgcFilter( element );
3326 }
3327 else if ( element.tagName() == QLatin1String( "Literal" ) )
3328 {
3329 return nodeLiteralFromOgcFilter( element );
3330 }
3331 else if ( element.tagName() == QLatin1String( "Function" ) )
3332 {
3333 return nodeFunctionFromOgcFilter( element );
3334 }
3335 else if ( element.tagName() == mPropertyName )
3336 {
3337 return nodeColumnRefFromOgcFilter( element );
3338 }
3339 else if ( element.tagName() == QLatin1String( "PropertyIsBetween" ) )
3340 {
3341 return nodeIsBetweenFromOgcFilter( element );
3342 }
3343
3344 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() );
3345 return nullptr;
3346}
3347
3349{
3350 if ( element.isNull() )
3351 return nullptr;
3352
3353 int op = binaryOperatorFromTagName( element.tagName() );
3354 if ( op < 0 )
3355 {
3356 mErrorMessage = QObject::tr( "'%1' binary operator not supported." ).arg( element.tagName() );
3357 return nullptr;
3358 }
3359
3360 if ( op == QgsExpressionNodeBinaryOperator::boLike && element.hasAttribute( QStringLiteral( "matchCase" ) ) && element.attribute( QStringLiteral( "matchCase" ) ) == QLatin1String( "false" ) )
3361 {
3363 }
3364
3365 QDomElement operandElem = element.firstChildElement();
3366 std::unique_ptr<QgsExpressionNode> expr( nodeFromOgcFilter( operandElem ) );
3367
3368 if ( !expr )
3369 {
3370 mErrorMessage = QObject::tr( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
3371 return nullptr;
3372 }
3373
3374 const std::unique_ptr<QgsExpressionNode> leftOp( expr->clone() );
3375 for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
3376 {
3377 std::unique_ptr<QgsExpressionNode> opRight( nodeFromOgcFilter( operandElem ) );
3378 if ( !opRight )
3379 {
3380 mErrorMessage = QObject::tr( "invalid right operand for '%1' binary operator" ).arg( element.tagName() );
3381 return nullptr;
3382 }
3383
3385 {
3386 QString wildCard;
3387 if ( element.hasAttribute( QStringLiteral( "wildCard" ) ) )
3388 {
3389 wildCard = element.attribute( QStringLiteral( "wildCard" ) );
3390 }
3391 QString singleChar;
3392 if ( element.hasAttribute( QStringLiteral( "singleChar" ) ) )
3393 {
3394 singleChar = element.attribute( QStringLiteral( "singleChar" ) );
3395 }
3396 QString escape = QStringLiteral( "\\" );
3397 if ( element.hasAttribute( QStringLiteral( "escape" ) ) )
3398 {
3399 escape = element.attribute( QStringLiteral( "escape" ) );
3400 }
3401 if ( element.hasAttribute( QStringLiteral( "escapeChar" ) ) )
3402 {
3403 escape = element.attribute( QStringLiteral( "escapeChar" ) );
3404 }
3405 // replace
3406 QString oprValue = static_cast<const QgsExpressionNodeLiteral *>( opRight.get() )->value().toString();
3407 if ( !wildCard.isEmpty() && wildCard != QLatin1String( "%" ) )
3408 {
3409 oprValue.replace( '%', QLatin1String( "\\%" ) );
3410 if ( oprValue.startsWith( wildCard ) )
3411 {
3412 oprValue.replace( 0, 1, QStringLiteral( "%" ) );
3413 }
3414 const QRegularExpression rx( "[^" + QgsStringUtils::qRegExpEscape( escape ) + "](" + QgsStringUtils::qRegExpEscape( wildCard ) + ")" );
3415 QRegularExpressionMatch match = rx.match( oprValue );
3416 int pos;
3417 while ( match.hasMatch() )
3418 {
3419 pos = match.capturedStart();
3420 oprValue.replace( pos + 1, 1, QStringLiteral( "%" ) );
3421 pos += 1;
3422 match = rx.match( oprValue, pos );
3423 }
3424 oprValue.replace( escape + wildCard, wildCard );
3425 }
3426 if ( !singleChar.isEmpty() && singleChar != QLatin1String( "_" ) )
3427 {
3428 oprValue.replace( '_', QLatin1String( "\\_" ) );
3429 if ( oprValue.startsWith( singleChar ) )
3430 {
3431 oprValue.replace( 0, 1, QStringLiteral( "_" ) );
3432 }
3433 const QRegularExpression rx( "[^" + QgsStringUtils::qRegExpEscape( escape ) + "](" + QgsStringUtils::qRegExpEscape( singleChar ) + ")" );
3434 QRegularExpressionMatch match = rx.match( oprValue );
3435 int pos;
3436 while ( match.hasMatch() )
3437 {
3438 pos = match.capturedStart();
3439 oprValue.replace( pos + 1, 1, QStringLiteral( "_" ) );
3440 pos += 1;
3441 match = rx.match( oprValue, pos );
3442 }
3443 oprValue.replace( escape + singleChar, singleChar );
3444 }
3445 if ( !escape.isEmpty() && escape != QLatin1String( "\\" ) )
3446 {
3447 oprValue.replace( escape + escape, escape );
3448 }
3449 opRight.reset( new QgsExpressionNodeLiteral( oprValue ) );
3450 }
3451
3452 expr.reset( new QgsExpressionNodeBinaryOperator( static_cast< QgsExpressionNodeBinaryOperator::BinaryOperator >( op ), expr.release(), opRight.release() ) );
3453 }
3454
3455 if ( expr == leftOp )
3456 {
3457 mErrorMessage = QObject::tr( "only one operand for '%1' binary operator" ).arg( element.tagName() );
3458 return nullptr;
3459 }
3460
3461 return dynamic_cast< QgsExpressionNodeBinaryOperator * >( expr.release() );
3462}
3463
3464
3466{
3467 // we are exploiting the fact that our function names are the same as the XML tag names
3468 const int opIdx = QgsExpression::functionIndex( element.tagName().toLower() );
3469
3470 std::unique_ptr<QgsExpressionNode::NodeList> gml2Args( new QgsExpressionNode::NodeList() );
3471 QDomElement childElem = element.firstChildElement();
3472 QString gml2Str;
3473 while ( !childElem.isNull() && gml2Str.isEmpty() )
3474 {
3475 if ( childElem.tagName() != mPropertyName )
3476 {
3477 QTextStream gml2Stream( &gml2Str );
3478 childElem.save( gml2Stream, 0 );
3479 }
3480 childElem = childElem.nextSiblingElement();
3481 }
3482 if ( !gml2Str.isEmpty() )
3483 {
3484 gml2Args->append( new QgsExpressionNodeLiteral( QVariant( gml2Str.remove( '\n' ) ) ) );
3485 }
3486 else
3487 {
3488 mErrorMessage = QObject::tr( "No OGC Geometry found" );
3489 return nullptr;
3490 }
3491
3492 std::unique_ptr<QgsExpressionNode::NodeList> opArgs( new QgsExpressionNode::NodeList() );
3493 opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "$geometry" ) ), new QgsExpressionNode::NodeList() ) );
3494 opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "geomFromGML" ) ), gml2Args.release() ) );
3495
3496 return new QgsExpressionNodeFunction( opIdx, opArgs.release() );
3497}
3498
3500{
3501 if ( element.isNull() || element.tagName() != mPropertyName )
3502 {
3503 mErrorMessage = QObject::tr( "%1:PropertyName expected, got %2" ).arg( mPrefix, element.tagName() );
3504 return nullptr;
3505 }
3506
3507 return new QgsExpressionNodeColumnRef( element.firstChild().nodeValue() );
3508}
3509
3511{
3512 if ( element.isNull() || element.tagName() != QLatin1String( "Literal" ) )
3513 {
3514 mErrorMessage = QObject::tr( "%1:Literal expected, got %2" ).arg( mPrefix, element.tagName() );
3515 return nullptr;
3516 }
3517
3518 std::unique_ptr<QgsExpressionNode> root;
3519 if ( !element.hasChildNodes() )
3520 {
3521 root.reset( new QgsExpressionNodeLiteral( QVariant( "" ) ) );
3522 return root.release();
3523 }
3524
3525 // the literal content can have more children (e.g. CDATA section, text, ...)
3526 QDomNode childNode = element.firstChild();
3527 while ( !childNode.isNull() )
3528 {
3529 std::unique_ptr<QgsExpressionNode> operand;
3530
3531 if ( childNode.nodeType() == QDomNode::ElementNode )
3532 {
3533 // found a element node (e.g. PropertyName), convert it
3534 const QDomElement operandElem = childNode.toElement();
3535 operand.reset( nodeFromOgcFilter( operandElem ) );
3536 if ( !operand )
3537 {
3538 mErrorMessage = QObject::tr( "'%1' is an invalid or not supported content for %2:Literal" ).arg( operandElem.tagName(), mPrefix );
3539 return nullptr;
3540 }
3541 }
3542 else
3543 {
3544 // probably a text/CDATA node
3545 QVariant value = childNode.nodeValue();
3546
3547 bool converted = false;
3548
3549 // try to convert the node content to corresponding field type if possible
3550 if ( mLayer )
3551 {
3552 QDomElement propertyNameElement = element.previousSiblingElement( mPropertyName );
3553 if ( propertyNameElement.isNull() || propertyNameElement.tagName() != mPropertyName )
3554 {
3555 propertyNameElement = element.nextSiblingElement( mPropertyName );
3556 }
3557 if ( !propertyNameElement.isNull() || propertyNameElement.tagName() == mPropertyName )
3558 {
3559 const int fieldIndex = mLayer->fields().indexOf( propertyNameElement.firstChild().nodeValue() );
3560 if ( fieldIndex != -1 )
3561 {
3562 const QgsField field = mLayer->fields().field( propertyNameElement.firstChild().nodeValue() );
3563 field.convertCompatible( value );
3564 converted = true;
3565 }
3566 }
3567 }
3568 if ( !converted )
3569 {
3570 // try to convert the node content to number if possible,
3571 // otherwise let's use it as string
3572 bool ok;
3573 const double d = value.toDouble( &ok );
3574 if ( ok )
3575 value = d;
3576 }
3577
3578 operand.reset( new QgsExpressionNodeLiteral( value ) );
3579 if ( !operand )
3580 continue;
3581 }
3582
3583 // use the concat operator to merge the ogc:Literal children
3584 if ( !root )
3585 {
3586 root = std::move( operand );
3587 }
3588 else
3589 {
3590 root.reset( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boConcat, root.release(), operand.release() ) );
3591 }
3592
3593 childNode = childNode.nextSibling();
3594 }
3595
3596 if ( root )
3597 return root.release();
3598
3599 return nullptr;
3600}
3601
3603{
3604 if ( element.tagName() != QLatin1String( "Not" ) )
3605 return nullptr;
3606
3607 const QDomElement operandElem = element.firstChildElement();
3608 std::unique_ptr<QgsExpressionNode> operand( nodeFromOgcFilter( operandElem ) );
3609 if ( !operand )
3610 {
3611 mErrorMessage = QObject::tr( "invalid operand for '%1' unary operator" ).arg( element.tagName() );
3612 return nullptr;
3613 }
3614
3616}
3617
3619{
3620 // convert ogc:PropertyIsNull to IS operator with NULL right operand
3621 if ( element.tagName() != QLatin1String( "PropertyIsNull" ) )
3622 {
3623 return nullptr;
3624 }
3625
3626 const QDomElement operandElem = element.firstChildElement();
3627 std::unique_ptr<QgsExpressionNode> opLeft( nodeFromOgcFilter( operandElem ) );
3628 if ( !opLeft )
3629 return nullptr;
3630
3631 std::unique_ptr<QgsExpressionNode> opRight( new QgsExpressionNodeLiteral( QVariant() ) );
3632 return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boIs, opLeft.release(), opRight.release() );
3633}
3634
3636{
3637 if ( element.isNull() || element.tagName() != QLatin1String( "Function" ) )
3638 {
3639 mErrorMessage = QObject::tr( "%1:Function expected, got %2" ).arg( mPrefix, element.tagName() );
3640 return nullptr;
3641 }
3642
3643 for ( int i = 0; i < QgsExpression::Functions().size(); i++ )
3644 {
3645 const QgsExpressionFunction *funcDef = QgsExpression::Functions()[i];
3646
3647 if ( element.attribute( QStringLiteral( "name" ) ) != funcDef->name() )
3648 continue;
3649
3650 std::unique_ptr<QgsExpressionNode::NodeList> args( new QgsExpressionNode::NodeList() );
3651
3652 QDomElement operandElem = element.firstChildElement();
3653 while ( !operandElem.isNull() )
3654 {
3655 std::unique_ptr<QgsExpressionNode> op( nodeFromOgcFilter( operandElem ) );
3656 if ( !op )
3657 {
3658 return nullptr;
3659 }
3660 args->append( op.release() );
3661
3662 operandElem = operandElem.nextSiblingElement();
3663 }
3664
3665 return new QgsExpressionNodeFunction( i, args.release() );
3666 }
3667
3668 return nullptr;
3669}
3670
3672{
3673 // <ogc:PropertyIsBetween> encode a Range check
3674 std::unique_ptr<QgsExpressionNode> operand;
3675 std::unique_ptr<QgsExpressionNode> lowerBound;
3676 std::unique_ptr<QgsExpressionNode> upperBound;
3677
3678 QDomElement operandElem = element.firstChildElement();
3679 while ( !operandElem.isNull() )
3680 {
3681 if ( operandElem.tagName() == QLatin1String( "LowerBoundary" ) )
3682 {
3683 const QDomElement lowerBoundElem = operandElem.firstChildElement();
3684 lowerBound.reset( nodeFromOgcFilter( lowerBoundElem ) );
3685 }
3686 else if ( operandElem.tagName() == QLatin1String( "UpperBoundary" ) )
3687 {
3688 const QDomElement upperBoundElem = operandElem.firstChildElement();
3689 upperBound.reset( nodeFromOgcFilter( upperBoundElem ) );
3690 }
3691 else
3692 {
3693 // <ogc:expression>
3694 operand.reset( nodeFromOgcFilter( operandElem ) );
3695 }
3696
3697 if ( operand && lowerBound && upperBound )
3698 break;
3699
3700 operandElem = operandElem.nextSiblingElement();
3701 }
3702
3703 if ( !operand || !lowerBound || !upperBound )
3704 {
3705 mErrorMessage = QObject::tr( "missing some required sub-elements in %1:PropertyIsBetween" ).arg( mPrefix );
3706 return nullptr;
3707 }
3708
3709 std::unique_ptr<QgsExpressionNode> leOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boLE, operand->clone(), upperBound.release() ) );
3710 std::unique_ptr<QgsExpressionNode> geOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boGE, operand.release(), lowerBound.release() ) );
3711 return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boAnd, geOperator.release(), leOperator.release() );
3712}
3713
3715{
3716 return mErrorMessage;
3717}
3718
3719QgsOgcCrsUtils::CRSFlavor QgsOgcCrsUtils::parseCrsName( const QString &crsName, QString &authority, QString &code )
3720{
3721 const thread_local QRegularExpression re_url( QRegularExpression::anchoredPattern( QStringLiteral( "http://www\\.opengis\\.net/gml/srs/epsg\\.xml#(.+)" ) ), QRegularExpression::CaseInsensitiveOption );
3722 if ( const QRegularExpressionMatch match = re_url.match( crsName ); match.hasMatch() )
3723 {
3724 authority = QStringLiteral( "EPSG" );
3725 code = match.captured( 1 );
3727 }
3728
3729 const thread_local QRegularExpression re_ogc_urn( QRegularExpression::anchoredPattern( QStringLiteral( "urn:ogc:def:crs:([^:]+).+(?<=:)([^:]+)" ) ), QRegularExpression::CaseInsensitiveOption );
3730 if ( const QRegularExpressionMatch match = re_ogc_urn.match( crsName ); match.hasMatch() )
3731 {
3732 authority = match.captured( 1 );
3733 code = match.captured( 2 );
3734 return CRSFlavor::OGC_URN;
3735 }
3736
3737 const thread_local QRegularExpression re_x_ogc_urn( QRegularExpression::anchoredPattern( QStringLiteral( "urn:x-ogc:def:crs:([^:]+).+(?<=:)([^:]+)" ) ), QRegularExpression::CaseInsensitiveOption );
3738 if ( const QRegularExpressionMatch match = re_x_ogc_urn.match( crsName ); match.hasMatch() )
3739 {
3740 authority = match.captured( 1 );
3741 code = match.captured( 2 );
3742 return CRSFlavor::X_OGC_URN;
3743 }
3744
3745 const thread_local QRegularExpression re_http_uri( QRegularExpression::anchoredPattern( QStringLiteral( "http://www\\.opengis\\.net/def/crs/([^/]+).+/([^/]+)" ) ), QRegularExpression::CaseInsensitiveOption );
3746 if ( const QRegularExpressionMatch match = re_http_uri.match( crsName ); match.hasMatch() )
3747 {
3748 authority = match.captured( 1 );
3749 code = match.captured( 2 );
3751 }
3752
3753 const thread_local QRegularExpression re_auth_code( QRegularExpression::anchoredPattern( QStringLiteral( "([^:]+):(.+)" ) ), QRegularExpression::CaseInsensitiveOption );
3754 if ( const QRegularExpressionMatch match = re_auth_code.match( crsName ); match.hasMatch() )
3755 {
3756 authority = match.captured( 1 );
3757 code = match.captured( 2 );
3758 return CRSFlavor::AUTH_CODE;
3759 }
3760
3761 return CRSFlavor::UNKNOWN;
3762}
GeometryOperationResult
Success or failure of a geometry operation.
Definition: qgis.h:1392
@ Success
Operation succeeded.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:154
@ LineString25D
LineString25D.
@ LineString
LineString.
@ MultiPolygon25D
MultiPolygon25D.
@ MultiPoint
MultiPoint.
@ Polygon
Polygon.
@ MultiLineString25D
MultiLineString25D.
@ MultiPolygon
MultiPolygon.
@ MultiLineString
MultiLineString.
@ MultiPoint25D
MultiPoint25D.
@ Point25D
Point25D.
@ Polygon25D
Polygon25D.
virtual void swapXy()=0
Swaps the x and y coordinates from the geometry.
A const WKB pointer.
Definition: qgswkbptr.h:138
Qgis::WkbType readHeader() const
readHeader
Definition: qgswkbptr.cpp:55
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool createFromUserInput(const QString &definition)
Set up this CRS from various text formats.
bool hasAxisInverted() const
Returns whether 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:67
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:53
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
Definition: qgsfield.cpp:452
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
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
Qgis::WkbType wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
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:596
@ 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:391
bool GMLNamespaceUsed() const
Returns whether the gml: namespace is used.
Definition: qgsogcutils.h:408
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:411
Internal use by QgsOgcUtils.
Definition: qgsogcutils.h:443
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:529
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:544
QString errorMessage() const
Returns the error message.
Definition: qgsogcutils.h:547
The QgsOgcUtils class provides various utility functions for conversion between OGC (Open Geospatial ...
Definition: qgsogcutils.h:54
static QDomElement elseFilterExpression(QDomDocument &doc)
Creates an ElseFilter from doc.
static QgsRectangle rectangleFromGMLBox(const QDomNode &boxNode)
Read rectangle from GML2 Box.
static QDomElement expressionToOgcExpression(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=nullptr, bool requiresFilterElement=false)
Creates an OGC expression XML element from the exp expression with default values for the geometry na...
static QColor colorFromOgcFill(const QDomElement &fillElement)
Parse XML with OGC fill into QColor.
static QDomElement expressionToOgcFilter(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=nullptr)
Creates OGC filter XML element.
GMLVersion
GML version.
Definition: qgsogcutils.h:80
FilterVersion
OGC filter version.
Definition: qgsogcutils.h:181
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
#define FALLTHROUGH
Definition: qgis.h:4567
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:3895
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition: qgis.h:4160
QMap< QString, QString > QgsStringMap
Definition: qgis.h:4501
const QgsField & field
Definition: qgsfield.h:554
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:62
const QgsMapLayer * layer
Definition: qgsogcutils.h:72
QgsCoordinateTransformContext transformContext
Definition: qgsogcutils.h:73