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