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