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