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