QGIS API Documentation 3.99.0-Master (752b475928d)
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 QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, QString(), QString(), geometryName, srsName, honourAxisOrientation, invertAxisOrientation, fieldNameToXPathMap, namespacePrefixToUriMap );
2093 const QDomElement exprRootElem = utils.expressionNodeToOgcFilter( node, &exp, &context );
2094
2095 if ( errorMessage )
2096 {
2097 *errorMessage = utils.errorMessage();
2098 }
2099
2100 if ( !exprRootElem.isNull() )
2101 {
2102 if ( requiresFilterElement )
2103 {
2104 QDomElement filterElem = filterElement( doc, gmlVersion, filterVersion, utils.GMLNamespaceUsed() );
2105
2106 filterElem.appendChild( exprRootElem );
2107 return filterElem;
2108 }
2109 return exprRootElem;
2110 }
2111
2112 return QDomElement();
2113}
2114
2116 QDomDocument &doc,
2117 GMLVersion gmlVersion,
2118 FilterVersion filterVersion,
2119 const QList<LayerProperties> &layerProperties,
2120 bool honourAxisOrientation,
2121 bool invertAxisOrientation,
2122 const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename,
2123 QString *errorMessage,
2124 const QMap<QString, QString> &fieldNameToXPathMap,
2125 const QMap<QString, QString> &namespacePrefixToUriMap )
2126{
2127 if ( !statement.rootNode() )
2128 return QDomElement();
2129
2130 QgsOgcUtilsSQLStatementToFilter utils( doc, gmlVersion, filterVersion,
2131 layerProperties, honourAxisOrientation, invertAxisOrientation,
2132 mapUnprefixedTypenameToPrefixedTypename, fieldNameToXPathMap, namespacePrefixToUriMap );
2133 const QDomElement exprRootElem = utils.toOgcFilter( statement.rootNode() );
2134 if ( errorMessage )
2135 *errorMessage = utils.errorMessage();
2136 if ( exprRootElem.isNull() )
2137 return QDomElement();
2138
2139 QDomElement filterElem = filterElement( doc, gmlVersion, filterVersion, utils.GMLNamespaceUsed() );
2140
2141 QSet<QString> setNamespaceURI;
2142 for ( const LayerProperties &props : layerProperties )
2143 {
2144 if ( !props.mNamespacePrefix.isEmpty() && !props.mNamespaceURI.isEmpty() &&
2145 !setNamespaceURI.contains( props.mNamespaceURI ) )
2146 {
2147 setNamespaceURI.insert( props.mNamespaceURI );
2148 QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:" ) + props.mNamespacePrefix );
2149 attr.setValue( props.mNamespaceURI );
2150 filterElem.setAttributeNode( attr );
2151 }
2152 }
2153 filterElem.appendChild( exprRootElem );
2154 return filterElem;
2155}
2156
2157//
2158
2159
2161{
2162 switch ( node->nodeType() )
2163 {
2165 return expressionUnaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeUnaryOperator *>( node ), expression, context );
2167 return expressionBinaryOperatorToOgcFilter( static_cast<const QgsExpressionNodeBinaryOperator *>( node ), expression, context );
2169 return expressionInOperatorToOgcFilter( static_cast<const QgsExpressionNodeInOperator *>( node ), expression, context );
2171 return expressionFunctionToOgcFilter( static_cast<const QgsExpressionNodeFunction *>( node ), expression, context );
2173 return expressionLiteralToOgcFilter( static_cast<const QgsExpressionNodeLiteral *>( node ), expression, context );
2175 return expressionColumnRefToOgcFilter( static_cast<const QgsExpressionNodeColumnRef *>( node ), expression, context );
2176
2177 default:
2178 mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
2179 return QDomElement();
2180 }
2181}
2182
2183QDomElement QgsOgcUtilsExprToFilter::expressionUnaryOperatorToOgcFilter( const QgsExpressionNodeUnaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
2184{
2185 const QDomElement operandElem = expressionNodeToOgcFilter( node->operand(), expression, context );
2186 if ( !mErrorMessage.isEmpty() )
2187 return QDomElement();
2188
2189 QDomElement uoElem;
2190 switch ( node->op() )
2191 {
2193 uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2194 if ( node->operand()->nodeType() == QgsExpressionNode::ntLiteral )
2195 {
2196 // operand expression already created a Literal node:
2197 // take the literal value, prepend - and remove old literal node
2198 uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2199 mDoc.removeChild( operandElem );
2200 }
2201 else
2202 {
2203 mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2204 return QDomElement();
2205 }
2206 break;
2208 uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2209 uoElem.appendChild( operandElem );
2210 break;
2211
2212 default:
2213 mErrorMessage = QObject::tr( "Unary operator '%1' not implemented yet" ).arg( node->text() );
2214 return QDomElement();
2215 }
2216
2217 return uoElem;
2218}
2219
2220
2221QDomElement QgsOgcUtilsExprToFilter::expressionBinaryOperatorToOgcFilter( const QgsExpressionNodeBinaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
2222{
2223 const QDomElement leftElem = expressionNodeToOgcFilter( node->opLeft(), expression, context );
2224 if ( !mErrorMessage.isEmpty() )
2225 return QDomElement();
2226
2228
2229 // before right operator is parsed: to allow NULL handling
2231 {
2232 if ( node->opRight()->nodeType() == QgsExpressionNode::ntLiteral )
2233 {
2234 const QgsExpressionNodeLiteral *rightLit = static_cast<const QgsExpressionNodeLiteral *>( node->opRight() );
2235 if ( QgsVariantUtils::isNull( rightLit->value() ) )
2236 {
2237
2238 QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2239 elem.appendChild( leftElem );
2240
2242 {
2243 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2244 notElem.appendChild( elem );
2245 return notElem;
2246 }
2247
2248 return elem;
2249 }
2250
2251 // continue with equal / not equal operator once the null case is handled
2253 }
2254
2255 }
2256
2257 const QDomElement rightElem = expressionNodeToOgcFilter( node->opRight(), expression, context );
2258 if ( !mErrorMessage.isEmpty() )
2259 return QDomElement();
2260
2261
2262 const QString opText = binaryOperatorToTagName( op );
2263 if ( opText.isEmpty() )
2264 {
2265 // not implemented binary operators
2266 // TODO: regex, % (mod), ^ (pow) are not supported yet
2267 mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( node->text() );
2268 return QDomElement();
2269 }
2270
2271 QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2272
2274 {
2276 boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2277
2278 // setup wildCards to <ogc:PropertyIsLike>
2279 boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2280 boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2281 if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2282 boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2283 else
2284 boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2285 }
2286
2287 boElem.appendChild( leftElem );
2288 boElem.appendChild( rightElem );
2289 return boElem;
2290}
2291
2292
2293QDomElement QgsOgcUtilsExprToFilter::expressionLiteralToOgcFilter( const QgsExpressionNodeLiteral *node, QgsExpression *expression, const QgsExpressionContext *context )
2294{
2295 Q_UNUSED( expression )
2296 Q_UNUSED( context )
2297 QString value;
2298 switch ( node->value().userType() )
2299 {
2300 case QMetaType::Type::Int:
2301 value = QString::number( node->value().toInt() );
2302 break;
2303 case QMetaType::Type::Double:
2304 value = qgsDoubleToString( node->value().toDouble() );
2305 break;
2306 case QMetaType::Type::QString:
2307 value = node->value().toString();
2308 break;
2309 case QMetaType::Type::QDate:
2310 value = node->value().toDate().toString( Qt::ISODate );
2311 break;
2312 case QMetaType::Type::QDateTime:
2313 value = node->value().toDateTime().toString( Qt::ISODate );
2314 break;
2315
2316 default:
2317 mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( static_cast<QMetaType::Type>( node->value().userType() ) );
2318 return QDomElement();
2319 }
2320
2321 QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2322 litElem.appendChild( mDoc.createTextNode( value ) );
2323 return litElem;
2324}
2325
2326
2327QDomElement QgsOgcUtilsExprToFilter::expressionColumnRefToOgcFilter( const QgsExpressionNodeColumnRef *node, QgsExpression *expression, const QgsExpressionContext *context )
2328{
2329 Q_UNUSED( expression )
2330 Q_UNUSED( context )
2331 QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2332 if ( !mFieldNameToXPathMap.isEmpty() )
2333 {
2334 const auto iterFieldName = mFieldNameToXPathMap.constFind( node->name() );
2335 if ( iterFieldName != mFieldNameToXPathMap.constEnd() )
2336 {
2337 const QString xpath( *iterFieldName );
2338
2339 if ( !mNamespacePrefixToUriMap.isEmpty() )
2340 {
2341 const QStringList parts = xpath.split( '/' );
2342 QSet<QString> setNamespacePrefix;
2343 for ( const QString &part : std::as_const( parts ) )
2344 {
2345 const QStringList subparts = part.split( ':' );
2346 if ( subparts.size() == 2 && !setNamespacePrefix.contains( subparts[0] ) )
2347 {
2348 const auto iterNamespacePrefix = mNamespacePrefixToUriMap.constFind( subparts[0] );
2349 if ( iterNamespacePrefix != mNamespacePrefixToUriMap.constEnd() )
2350 {
2351 setNamespacePrefix.insert( subparts[0] );
2352 QDomAttr attr = mDoc.createAttribute( QStringLiteral( "xmlns:" ) + subparts[0] );
2353 attr.setValue( *iterNamespacePrefix );
2354 propElem.setAttributeNode( attr );
2355 }
2356 }
2357 }
2358 }
2359
2360 propElem.appendChild( mDoc.createTextNode( xpath ) );
2361
2362 return propElem;
2363 }
2364 }
2365 QString columnRef( node->name() );
2366 if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
2367 columnRef = mNamespacePrefix + QStringLiteral( ":" ) + columnRef;
2368 propElem.appendChild( mDoc.createTextNode( columnRef ) );
2369 return propElem;
2370}
2371
2372
2373
2374QDomElement QgsOgcUtilsExprToFilter::expressionInOperatorToOgcFilter( const QgsExpressionNodeInOperator *node, QgsExpression *expression, const QgsExpressionContext *context )
2375{
2376 if ( node->list()->list().size() == 1 )
2377 {
2378 const QDomElement leftNode = expressionNodeToOgcFilter( node->node(), expression, context );
2379 const QDomElement firstListNode = expressionNodeToOgcFilter( node->list()->list().first(), expression, context );
2380 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2381 eqElem.appendChild( leftNode );
2382 eqElem.appendChild( firstListNode );
2383 if ( node->isNotIn() )
2384 {
2385 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2386 notElem.appendChild( eqElem );
2387 return notElem;
2388 }
2389 return eqElem;
2390 }
2391
2392 QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2393 const QDomElement leftNode = expressionNodeToOgcFilter( node->node(), expression, context );
2394
2395 const auto constList = node->list()->list();
2396 for ( QgsExpressionNode *n : constList )
2397 {
2398 const QDomElement listNode = expressionNodeToOgcFilter( n, expression, context );
2399 if ( !mErrorMessage.isEmpty() )
2400 return QDomElement();
2401
2402 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2403 eqElem.appendChild( leftNode.cloneNode() );
2404 eqElem.appendChild( listNode );
2405
2406 orElem.appendChild( eqElem );
2407 }
2408
2409 if ( node->isNotIn() )
2410 {
2411 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2412 notElem.appendChild( orElem );
2413 return notElem;
2414 }
2415
2416 return orElem;
2417}
2418
2419Q_GLOBAL_STATIC_WITH_ARGS( QgsStringMap, BINARY_SPATIAL_OPS_MAP, (
2420{
2421 { QLatin1String( "disjoint" ), QLatin1String( "Disjoint" ) },
2422 { QLatin1String( "intersects" ), QLatin1String( "Intersects" )},
2423 { QLatin1String( "touches" ), QLatin1String( "Touches" ) },
2424 { QLatin1String( "crosses" ), QLatin1String( "Crosses" ) },
2425 { QLatin1String( "contains" ), QLatin1String( "Contains" ) },
2426 { QLatin1String( "overlaps" ), QLatin1String( "Overlaps" ) },
2427 { QLatin1String( "within" ), QLatin1String( "Within" ) }
2428} ) )
2429
2430static bool isBinarySpatialOperator( const QString &fnName )
2431{
2432 return BINARY_SPATIAL_OPS_MAP()->contains( fnName );
2433}
2434
2435static QString tagNameForSpatialOperator( const QString &fnName )
2436{
2437 return BINARY_SPATIAL_OPS_MAP()->value( fnName );
2438}
2439
2440static bool isGeometryColumn( const QgsExpressionNode *node )
2441{
2442 if ( node->nodeType() != QgsExpressionNode::ntFunction )
2443 return false;
2444
2445 const QgsExpressionNodeFunction *fn = static_cast<const QgsExpressionNodeFunction *>( node );
2447 return fd->name() == QLatin1String( "$geometry" ) || ( fd->name() == QLatin1String( "var" ) && fn->referencedVariables().contains( QLatin1String( "geometry" ) ) );
2448}
2449
2450static QgsGeometry geometryFromConstExpr( const QgsExpressionNode *node )
2451{
2452 // Right now we support only geomFromWKT(' ..... ')
2453 // Ideally we should support any constant sub-expression (not dependent on feature's geometry or attributes)
2454
2455 if ( node->nodeType() == QgsExpressionNode::ntFunction )
2456 {
2457 const QgsExpressionNodeFunction *fnNode = static_cast<const QgsExpressionNodeFunction *>( node );
2459 if ( fnDef->name() == QLatin1String( "geom_from_wkt" ) )
2460 {
2461 const QList<QgsExpressionNode *> &args = fnNode->args()->list();
2462 if ( args[0]->nodeType() == QgsExpressionNode::ntLiteral )
2463 {
2464 const QString wkt = static_cast<const QgsExpressionNodeLiteral *>( args[0] )->value().toString();
2465 return QgsGeometry::fromWkt( wkt );
2466 }
2467 }
2468 }
2469 return QgsGeometry();
2470}
2471
2472
2473QDomElement QgsOgcUtilsExprToFilter::expressionFunctionToOgcFilter( const QgsExpressionNodeFunction *node, QgsExpression *expression, const QgsExpressionContext *context )
2474{
2475 QgsExpressionFunction *fd = QgsExpression::Functions()[node->fnIndex()];
2476
2477 if ( fd->name() == QLatin1String( "intersects_bbox" ) )
2478 {
2479 QList<QgsExpressionNode *> argNodes = node->args()->list();
2480 Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2481
2482 const QgsGeometry geom = geometryFromConstExpr( argNodes[1] );
2483 if ( !geom.isNull() && isGeometryColumn( argNodes[0] ) )
2484 {
2485 QgsRectangle rect = geom.boundingBox();
2486
2487 mGMLUsed = true;
2488
2489 const QDomElement elemBox = ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
2490 QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, mSrsName, mInvertAxisOrientation ) :
2491 QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, mSrsName, mInvertAxisOrientation );
2492
2493 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":BBOX" );
2494
2495 if ( !mGeometryName.isEmpty() )
2496 {
2497 // Geometry column is optional for a BBOX filter.
2498 QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2499 QString columnRef( mGeometryName );
2500 if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
2501 columnRef = mNamespacePrefix + QStringLiteral( ":" ) + columnRef;
2502 geomProperty.appendChild( mDoc.createTextNode( columnRef ) );
2503
2504 funcElem.appendChild( geomProperty );
2505 }
2506 funcElem.appendChild( elemBox );
2507 return funcElem;
2508 }
2509 else
2510 {
2511 mErrorMessage = QObject::tr( "<BBOX> is currently supported only in form: bbox(@geometry, geomFromWKT('…'))" );
2512 return QDomElement();
2513 }
2514 }
2515
2516 if ( isBinarySpatialOperator( fd->name() ) )
2517 {
2518 QList<QgsExpressionNode *> argNodes = node->args()->list();
2519 Q_ASSERT( argNodes.count() == 2 ); // binary spatial ops must have two args
2520
2521 QgsExpressionNode *otherNode = nullptr;
2522 if ( isGeometryColumn( argNodes[0] ) )
2523 otherNode = argNodes[1];
2524 else if ( isGeometryColumn( argNodes[1] ) )
2525 otherNode = argNodes[0];
2526 else
2527 {
2528 mErrorMessage = QObject::tr( "Unable to translate spatial operator: at least one must refer to geometry." );
2529 return QDomElement();
2530 }
2531
2532 QDomElement otherGeomElem;
2533
2534 // the other node must be a geometry constructor
2535 if ( otherNode->nodeType() != QgsExpressionNode::ntFunction )
2536 {
2537 mErrorMessage = QObject::tr( "spatial operator: the other operator must be a geometry constructor function" );
2538 return QDomElement();
2539 }
2540
2541 const QgsExpressionNodeFunction *otherFn = static_cast<const QgsExpressionNodeFunction *>( otherNode );
2542 QgsExpressionFunction *otherFnDef = QgsExpression::Functions()[otherFn->fnIndex()];
2543 if ( otherFnDef->name() == QLatin1String( "geom_from_wkt" ) )
2544 {
2545 QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2546 if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2547 {
2548 mErrorMessage = QObject::tr( "geom_from_wkt: argument must be string literal" );
2549 return QDomElement();
2550 }
2551 const QString wkt = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2552 const QgsGeometry geom = QgsGeometry::fromWkt( wkt );
2553 otherGeomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation,
2554 QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2555 if ( otherGeomElem.isNull() )
2556 {
2557 mErrorMessage = QObject::tr( "geom_from_wkt: unable to generate GML from wkt geometry" );
2558 return QDomElement();
2559 }
2560 mGeomId ++;
2561 }
2562 else if ( otherFnDef->name() == QLatin1String( "geom_from_gml" ) )
2563 {
2564 QgsExpressionNode *firstFnArg = otherFn->args()->list()[0];
2565 if ( firstFnArg->nodeType() != QgsExpressionNode::ntLiteral )
2566 {
2567 mErrorMessage = QObject::tr( "geom_from_gml: argument must be string literal" );
2568 return QDomElement();
2569 }
2570
2571 QDomDocument geomDoc;
2572 const QString gml = static_cast<const QgsExpressionNodeLiteral *>( firstFnArg )->value().toString();
2573 // wrap the string into a root tag to have "gml" namespace
2574 const QString xml = QStringLiteral( "<tmp xmlns:gml=\"%1\">%2</tmp>" ).arg( GML_NAMESPACE, gml );
2575 if ( !geomDoc.setContent( xml, true ) )
2576 {
2577 mErrorMessage = QObject::tr( "geom_from_gml: unable to parse XML" );
2578 return QDomElement();
2579 }
2580
2581 const QDomNode geomNode = mDoc.importNode( geomDoc.documentElement().firstChildElement(), true );
2582 otherGeomElem = geomNode.toElement();
2583 }
2584 else if ( otherNode->hasCachedStaticValue() && otherNode->cachedStaticValue().userType() == qMetaTypeId< QgsGeometry>() )
2585 {
2586 QgsGeometry geom = otherNode->cachedStaticValue().value<QgsGeometry>();
2587 otherGeomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation,
2588 QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
2589 if ( otherGeomElem.isNull() )
2590 {
2591 mErrorMessage = QObject::tr( "geom from static value: unable to generate GML from static variable" );
2592 return QDomElement();
2593 }
2594 mGeomId ++;
2595 }
2596 else
2597 {
2598 mErrorMessage = QObject::tr( "spatial operator: unknown geometry constructor function" );
2599 return QDomElement();
2600 }
2601
2602 mGMLUsed = true;
2603
2604 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + tagNameForSpatialOperator( fd->name() ) );
2605 QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2606 QString columnRef( mGeometryName );
2607 if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
2608 columnRef = mNamespacePrefix + QStringLiteral( ":" ) + columnRef;
2609 geomProperty.appendChild( mDoc.createTextNode( columnRef ) );
2610 funcElem.appendChild( geomProperty );
2611 funcElem.appendChild( otherGeomElem );
2612 return funcElem;
2613 }
2614
2615 if ( fd->isStatic( node, expression, context ) )
2616 {
2617 const QVariant result = fd->run( node->args(), context, expression, node );
2618 const QgsExpressionNodeLiteral literal( result );
2619 return expressionLiteralToOgcFilter( &literal, expression, context );
2620 }
2621
2622 if ( fd->params() == 0 )
2623 {
2624 mErrorMessage = QObject::tr( "Special columns/constants are not supported." );
2625 return QDomElement();
2626 }
2627
2628 // this is somehow wrong - we are just hoping that the other side supports the same functions as we do...
2629 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
2630 funcElem.setAttribute( QStringLiteral( "name" ), fd->name() );
2631 const auto constList = node->args()->list();
2632 for ( QgsExpressionNode *n : constList )
2633 {
2634 const QDomElement childElem = expressionNodeToOgcFilter( n, expression, context );
2635 if ( !mErrorMessage.isEmpty() )
2636 return QDomElement();
2637
2638 funcElem.appendChild( childElem );
2639 }
2640
2641 return funcElem;
2642}
2643
2644//
2645
2647 QgsOgcUtils::GMLVersion gmlVersion,
2648 QgsOgcUtils::FilterVersion filterVersion,
2649 const QList<QgsOgcUtils::LayerProperties> &layerProperties,
2650 bool honourAxisOrientation,
2651 bool invertAxisOrientation,
2652 const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename,
2653 const QMap<QString, QString> &fieldNameToXPathMap,
2654 const QMap<QString, QString> &namespacePrefixToUriMap )
2655 : mDoc( doc )
2656 , mGMLVersion( gmlVersion )
2657 , mFilterVersion( filterVersion )
2658 , mLayerProperties( layerProperties )
2659 , mHonourAxisOrientation( honourAxisOrientation )
2660 , mInvertAxisOrientation( invertAxisOrientation )
2661 , mFilterPrefix( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "fes" : "ogc" )
2662 , mPropertyName( ( filterVersion == QgsOgcUtils::FILTER_FES_2_0 ) ? "ValueReference" : "PropertyName" )
2663 , mMapUnprefixedTypenameToPrefixedTypename( mapUnprefixedTypenameToPrefixedTypename )
2664 , mFieldNameToXPathMap( fieldNameToXPathMap )
2665 , mNamespacePrefixToUriMap( namespacePrefixToUriMap )
2666{
2667}
2668
2670{
2671 switch ( node->nodeType() )
2672 {
2674 return toOgcFilter( static_cast<const QgsSQLStatement::NodeUnaryOperator *>( node ) );
2676 return toOgcFilter( static_cast<const QgsSQLStatement::NodeBinaryOperator *>( node ) );
2678 return toOgcFilter( static_cast<const QgsSQLStatement::NodeInOperator *>( node ) );
2680 return toOgcFilter( static_cast<const QgsSQLStatement::NodeBetweenOperator *>( node ) );
2682 return toOgcFilter( static_cast<const QgsSQLStatement::NodeFunction *>( node ) );
2684 return toOgcFilter( static_cast<const QgsSQLStatement::NodeLiteral *>( node ) );
2686 return toOgcFilter( static_cast<const QgsSQLStatement::NodeColumnRef *>( node ) );
2688 return toOgcFilter( static_cast<const QgsSQLStatement::NodeSelect *>( node ) );
2689
2690 default:
2691 mErrorMessage = QObject::tr( "Node type not supported: %1" ).arg( node->nodeType() );
2692 return QDomElement();
2693 }
2694}
2695
2696
2698{
2699
2700 const QDomElement operandElem = toOgcFilter( node->operand() );
2701 if ( !mErrorMessage.isEmpty() )
2702 return QDomElement();
2703
2704 QDomElement uoElem;
2705 switch ( node->op() )
2706 {
2708 uoElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2709 if ( node->operand()->nodeType() == QgsSQLStatement::ntLiteral )
2710 {
2711 // operand expression already created a Literal node:
2712 // take the literal value, prepend - and remove old literal node
2713 uoElem.appendChild( mDoc.createTextNode( "-" + operandElem.text() ) );
2714 mDoc.removeChild( operandElem );
2715 }
2716 else
2717 {
2718 mErrorMessage = QObject::tr( "This use of unary operator not implemented yet" );
2719 return QDomElement();
2720 }
2721 break;
2723 uoElem = mDoc.createElement( mFilterPrefix + ":Not" );
2724 uoElem.appendChild( operandElem );
2725 break;
2726
2727 default:
2728 mErrorMessage = QObject::tr( "Unary operator %1 not implemented yet" ).arg( QgsSQLStatement::UNARY_OPERATOR_TEXT[node->op()] );
2729 return QDomElement();
2730 }
2731
2732 return uoElem;
2733}
2734
2735
2737{
2738 const QDomElement leftElem = toOgcFilter( node->opLeft() );
2739 if ( !mErrorMessage.isEmpty() )
2740 return QDomElement();
2741
2743
2744 // before right operator is parsed: to allow NULL handling
2746 {
2747 if ( node->opRight()->nodeType() == QgsSQLStatement::ntLiteral )
2748 {
2749 const QgsSQLStatement::NodeLiteral *rightLit = static_cast<const QgsSQLStatement::NodeLiteral *>( node->opRight() );
2750 if ( QgsVariantUtils::isNull( rightLit->value() ) )
2751 {
2752
2753 QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsNull" );
2754 elem.appendChild( leftElem );
2755
2756 if ( op == QgsSQLStatement::boIsNot )
2757 {
2758 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2759 notElem.appendChild( elem );
2760 return notElem;
2761 }
2762
2763 return elem;
2764 }
2765
2766 // continue with equal / not equal operator once the null case is handled
2768 }
2769
2770 }
2771
2772 const QDomElement rightElem = toOgcFilter( node->opRight() );
2773 if ( !mErrorMessage.isEmpty() )
2774 return QDomElement();
2775
2776
2777 QString opText;
2778 if ( op == QgsSQLStatement::boOr )
2779 opText = QStringLiteral( "Or" );
2780 else if ( op == QgsSQLStatement::boAnd )
2781 opText = QStringLiteral( "And" );
2782 else if ( op == QgsSQLStatement::boEQ )
2783 opText = QStringLiteral( "PropertyIsEqualTo" );
2784 else if ( op == QgsSQLStatement::boNE )
2785 opText = QStringLiteral( "PropertyIsNotEqualTo" );
2786 else if ( op == QgsSQLStatement::boLE )
2787 opText = QStringLiteral( "PropertyIsLessThanOrEqualTo" );
2788 else if ( op == QgsSQLStatement::boGE )
2789 opText = QStringLiteral( "PropertyIsGreaterThanOrEqualTo" );
2790 else if ( op == QgsSQLStatement::boLT )
2791 opText = QStringLiteral( "PropertyIsLessThan" );
2792 else if ( op == QgsSQLStatement::boGT )
2793 opText = QStringLiteral( "PropertyIsGreaterThan" );
2794 else if ( op == QgsSQLStatement::boLike )
2795 opText = QStringLiteral( "PropertyIsLike" );
2796 else if ( op == QgsSQLStatement::boILike )
2797 opText = QStringLiteral( "PropertyIsLike" );
2798
2799 if ( opText.isEmpty() )
2800 {
2801 // not implemented binary operators
2802 mErrorMessage = QObject::tr( "Binary operator %1 not implemented yet" ).arg( QgsSQLStatement::BINARY_OPERATOR_TEXT[op] );
2803 return QDomElement();
2804 }
2805
2806 QDomElement boElem = mDoc.createElement( mFilterPrefix + ":" + opText );
2807
2809 {
2810 if ( op == QgsSQLStatement::boILike )
2811 boElem.setAttribute( QStringLiteral( "matchCase" ), QStringLiteral( "false" ) );
2812
2813 // setup wildCards to <ogc:PropertyIsLike>
2814 boElem.setAttribute( QStringLiteral( "wildCard" ), QStringLiteral( "%" ) );
2815 boElem.setAttribute( QStringLiteral( "singleChar" ), QStringLiteral( "_" ) );
2816 if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2817 boElem.setAttribute( QStringLiteral( "escape" ), QStringLiteral( "\\" ) );
2818 else
2819 boElem.setAttribute( QStringLiteral( "escapeChar" ), QStringLiteral( "\\" ) );
2820 }
2821
2822 boElem.appendChild( leftElem );
2823 boElem.appendChild( rightElem );
2824 return boElem;
2825}
2826
2827
2829{
2830 QString value;
2831 switch ( node->value().userType() )
2832 {
2833 case QMetaType::Type::Int:
2834 value = QString::number( node->value().toInt() );
2835 break;
2836 case QMetaType::Type::LongLong:
2837 value = QString::number( node->value().toLongLong() );
2838 break;
2839 case QMetaType::Type::Double:
2840 value = qgsDoubleToString( node->value().toDouble() );
2841 break;
2842 case QMetaType::Type::QString:
2843 value = node->value().toString();
2844 break;
2845
2846 default:
2847 mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( static_cast<QMetaType::Type>( node->value().userType() ) );
2848 return QDomElement();
2849 }
2850
2851 QDomElement litElem = mDoc.createElement( mFilterPrefix + ":Literal" );
2852 litElem.appendChild( mDoc.createTextNode( value ) );
2853 return litElem;
2854}
2855
2856
2858{
2859 QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
2860 if ( node->tableName().isEmpty() || mLayerProperties.size() == 1 )
2861 {
2862 if ( !mFieldNameToXPathMap.isEmpty() )
2863 {
2864 const auto iterFieldName = mFieldNameToXPathMap.constFind( node->name() );
2865 if ( iterFieldName != mFieldNameToXPathMap.constEnd() )
2866 {
2867 const QString xpath( *iterFieldName );
2868
2869 if ( !mNamespacePrefixToUriMap.isEmpty() )
2870 {
2871 const QStringList parts = xpath.split( '/' );
2872 QSet<QString> setNamespacePrefix;
2873 for ( const QString &part : std::as_const( parts ) )
2874 {
2875 const QStringList subparts = part.split( ':' );
2876 if ( subparts.size() == 2 && !setNamespacePrefix.contains( subparts[0] ) )
2877 {
2878 const auto iterNamespacePrefix = mNamespacePrefixToUriMap.constFind( subparts[0] );
2879 if ( iterNamespacePrefix != mNamespacePrefixToUriMap.constEnd() )
2880 {
2881 setNamespacePrefix.insert( subparts[0] );
2882 QDomAttr attr = mDoc.createAttribute( QStringLiteral( "xmlns:" ) + subparts[0] );
2883 attr.setValue( *iterNamespacePrefix );
2884 propElem.setAttributeNode( attr );
2885 }
2886 }
2887 }
2888 }
2889
2890 propElem.appendChild( mDoc.createTextNode( xpath ) );
2891
2892 return propElem;
2893 }
2894 }
2895 if ( mLayerProperties.size() == 1 && !mLayerProperties[0].mNamespacePrefix.isEmpty() && !mLayerProperties[0].mNamespaceURI.isEmpty() )
2896 propElem.appendChild( mDoc.createTextNode(
2897 mLayerProperties[0].mNamespacePrefix + QStringLiteral( ":" ) + node->name() ) );
2898 else
2899 propElem.appendChild( mDoc.createTextNode( node->name() ) );
2900 }
2901 else
2902 {
2903 QString tableName( mMapTableAliasToNames[node->tableName()] );
2904 if ( mMapUnprefixedTypenameToPrefixedTypename.contains( tableName ) )
2905 tableName = mMapUnprefixedTypenameToPrefixedTypename[tableName];
2906 propElem.appendChild( mDoc.createTextNode( tableName + "/" + node->name() ) );
2907 }
2908 return propElem;
2909}
2910
2912{
2913 if ( node->list()->list().size() == 1 )
2914 {
2915 const QDomElement leftNode = toOgcFilter( node->node() );
2916 const QDomElement firstListNode = toOgcFilter( node->list()->list().first() );
2917 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2918 eqElem.appendChild( leftNode );
2919 eqElem.appendChild( firstListNode );
2920 if ( node->isNotIn() )
2921 {
2922 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2923 notElem.appendChild( eqElem );
2924 return notElem;
2925 }
2926 return eqElem;
2927 }
2928
2929 QDomElement orElem = mDoc.createElement( mFilterPrefix + ":Or" );
2930 const QDomElement leftNode = toOgcFilter( node->node() );
2931
2932 const auto constList = node->list()->list();
2933 for ( QgsSQLStatement::Node *n : constList )
2934 {
2935 const QDomElement listNode = toOgcFilter( n );
2936 if ( !mErrorMessage.isEmpty() )
2937 return QDomElement();
2938
2939 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
2940 eqElem.appendChild( leftNode.cloneNode() );
2941 eqElem.appendChild( listNode );
2942
2943 orElem.appendChild( eqElem );
2944 }
2945
2946 if ( node->isNotIn() )
2947 {
2948 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2949 notElem.appendChild( orElem );
2950 return notElem;
2951 }
2952
2953 return orElem;
2954}
2955
2957{
2958 QDomElement elem = mDoc.createElement( mFilterPrefix + ":PropertyIsBetween" );
2959 elem.appendChild( toOgcFilter( node->node() ) );
2960 QDomElement lowerBoundary = mDoc.createElement( mFilterPrefix + ":LowerBoundary" );
2961 lowerBoundary.appendChild( toOgcFilter( node->minVal() ) );
2962 elem.appendChild( lowerBoundary );
2963 QDomElement upperBoundary = mDoc.createElement( mFilterPrefix + ":UpperBoundary" );
2964 upperBoundary.appendChild( toOgcFilter( node->maxVal() ) );
2965 elem.appendChild( upperBoundary );
2966
2967 if ( node->isNotBetween() )
2968 {
2969 QDomElement notElem = mDoc.createElement( mFilterPrefix + ":Not" );
2970 notElem.appendChild( elem );
2971 return notElem;
2972 }
2973
2974 return elem;
2975}
2976
2977static QString mapBinarySpatialToOgc( const QString &name )
2978{
2979 QString nameCompare( name );
2980 if ( name.size() > 3 && QStringView {name} .mid( 0, 3 ).toString().compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2981 nameCompare = name.mid( 3 );
2982 QStringList spatialOps;
2983 spatialOps << QStringLiteral( "BBOX" ) << QStringLiteral( "Intersects" ) << QStringLiteral( "Contains" ) << QStringLiteral( "Crosses" ) << QStringLiteral( "Equals" )
2984 << QStringLiteral( "Disjoint" ) << QStringLiteral( "Overlaps" ) << QStringLiteral( "Touches" ) << QStringLiteral( "Within" );
2985 const auto constSpatialOps = spatialOps;
2986 for ( QString op : constSpatialOps )
2987 {
2988 if ( nameCompare.compare( op, Qt::CaseInsensitive ) == 0 )
2989 return op;
2990 }
2991 return QString();
2992}
2993
2994static QString mapTernarySpatialToOgc( const QString &name )
2995{
2996 QString nameCompare( name );
2997 if ( name.size() > 3 && QStringView {name} .mid( 0, 3 ).compare( QLatin1String( "ST_" ), Qt::CaseInsensitive ) == 0 )
2998 nameCompare = name.mid( 3 );
2999 if ( nameCompare.compare( QLatin1String( "DWithin" ), Qt::CaseInsensitive ) == 0 )
3000 return QStringLiteral( "DWithin" );
3001 if ( nameCompare.compare( QLatin1String( "Beyond" ), Qt::CaseInsensitive ) == 0 )
3002 return QStringLiteral( "Beyond" );
3003 return QString();
3004}
3005
3006QString QgsOgcUtilsSQLStatementToFilter::getGeometryColumnSRSName( const QgsSQLStatement::Node *node )
3007{
3008 if ( node->nodeType() != QgsSQLStatement::ntColumnRef )
3009 return QString();
3010
3011 const QgsSQLStatement::NodeColumnRef *col = static_cast<const QgsSQLStatement::NodeColumnRef *>( node );
3012 if ( !col->tableName().isEmpty() )
3013 {
3014 const auto constMLayerProperties = mLayerProperties;
3015 for ( const QgsOgcUtils::LayerProperties &prop : constMLayerProperties )
3016 {
3017 if ( prop.mName.compare( mMapTableAliasToNames[col->tableName()], Qt::CaseInsensitive ) == 0 &&
3018 prop.mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
3019 {
3020 return prop.mSRSName;
3021 }
3022 }
3023 }
3024 if ( !mLayerProperties.empty() &&
3025 mLayerProperties.at( 0 ).mGeometryAttribute.compare( col->name(), Qt::CaseInsensitive ) == 0 )
3026 {
3027 return mLayerProperties.at( 0 ).mSRSName;
3028 }
3029 return QString();
3030}
3031
3032bool QgsOgcUtilsSQLStatementToFilter::processSRSName( const QgsSQLStatement::NodeFunction *mainNode,
3033 QList<QgsSQLStatement::Node *> args,
3034 bool lastArgIsSRSName,
3035 QString &srsName,
3036 bool &axisInversion )
3037{
3038 srsName = mCurrentSRSName;
3039 axisInversion = mInvertAxisOrientation;
3040
3041 if ( lastArgIsSRSName )
3042 {
3043 QgsSQLStatement::Node *lastArg = args[ args.size() - 1 ];
3044 if ( lastArg->nodeType() != QgsSQLStatement::ntLiteral )
3045 {
3046 mErrorMessage = QObject::tr( "%1: Last argument must be string or integer literal" ).arg( mainNode->name() );
3047 return false;
3048 }
3049 const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( lastArg );
3050 if ( lit->value().userType() == QMetaType::Type::Int )
3051 {
3052 if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
3053 {
3054 srsName = "EPSG:" + QString::number( lit->value().toInt() );
3055 }
3056 else
3057 {
3058 srsName = "urn:ogc:def:crs:EPSG::" + QString::number( lit->value().toInt() );
3059 }
3060 }
3061 else
3062 {
3063 srsName = lit->value().toString();
3064 if ( srsName.startsWith( QLatin1String( "EPSG:" ), Qt::CaseInsensitive ) )
3065 return true;
3066 }
3067 }
3068
3069 QgsCoordinateReferenceSystem crs;
3070 if ( !srsName.isEmpty() )
3072 if ( crs.isValid() )
3073 {
3074 if ( mHonourAxisOrientation && crs.hasAxisInverted() )
3075 {
3076 axisInversion = !axisInversion;
3077 }
3078 }
3079
3080 return true;
3081}
3082
3084{
3085 // ST_GeometryFromText
3086 if ( node->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 )
3087 {
3088 QList<QgsSQLStatement::Node *> args = node->args()->list();
3089 if ( args.size() != 1 && args.size() != 2 )
3090 {
3091 mErrorMessage = QObject::tr( "Function %1 should have 1 or 2 arguments" ).arg( node->name() );
3092 return QDomElement();
3093 }
3094
3095 QgsSQLStatement::Node *firstFnArg = args[0];
3096 if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
3097 {
3098 mErrorMessage = QObject::tr( "%1: First argument must be string literal" ).arg( node->name() );
3099 return QDomElement();
3100 }
3101
3102 QString srsName;
3103 bool axisInversion;
3104 if ( ! processSRSName( node, args, args.size() == 2, srsName, axisInversion ) )
3105 {
3106 return QDomElement();
3107 }
3108
3109 const QString wkt = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
3110 const QgsGeometry geom = QgsGeometry::fromWkt( wkt );
3111 const QDomElement geomElem = QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, srsName, axisInversion,
3112 QStringLiteral( "qgis_id_geom_%1" ).arg( mGeomId ) );
3113 mGeomId ++;
3114 if ( geomElem.isNull() )
3115 {
3116 mErrorMessage = QObject::tr( "%1: invalid WKT" ).arg( node->name() );
3117 return QDomElement();
3118 }
3119 mGMLUsed = true;
3120 return geomElem;
3121 }
3122
3123 // ST_MakeEnvelope
3124 if ( node->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 )
3125 {
3126 QList<QgsSQLStatement::Node *> args = node->args()->list();
3127 if ( args.size() != 4 && args.size() != 5 )
3128 {
3129 mErrorMessage = QObject::tr( "Function %1 should have 4 or 5 arguments" ).arg( node->name() );
3130 return QDomElement();
3131 }
3132
3133 QgsRectangle rect;
3134
3135 for ( int i = 0; i < 4; i++ )
3136 {
3137 QgsSQLStatement::Node *arg = args[i];
3138 if ( arg->nodeType() != QgsSQLStatement::ntLiteral )
3139 {
3140 mErrorMessage = QObject::tr( "%1: Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
3141 return QDomElement();
3142 }
3143 const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( arg );
3144 double val = 0.0;
3145 if ( lit->value().userType() == QMetaType::Type::Int )
3146 val = lit->value().toInt();
3147 else if ( lit->value().userType() == QMetaType::Type::LongLong )
3148 val = lit->value().toLongLong();
3149 else if ( lit->value().userType() == QMetaType::Type::Double )
3150 val = lit->value().toDouble();
3151 else
3152 {
3153 mErrorMessage = QObject::tr( "%1 Argument %2 must be numeric literal" ).arg( node->name() ).arg( i + 1 );
3154 return QDomElement();
3155 }
3156 if ( i == 0 )
3157 rect.setXMinimum( val );
3158 else if ( i == 1 )
3159 rect.setYMinimum( val );
3160 else if ( i == 2 )
3161 rect.setXMaximum( val );
3162 else
3163 rect.setYMaximum( val );
3164 }
3165
3166 QString srsName;
3167 bool axisInversion;
3168 if ( ! processSRSName( node, args, args.size() == 5, srsName, axisInversion ) )
3169 {
3170 return QDomElement();
3171 }
3172
3173 mGMLUsed = true;
3174
3175 return ( mGMLVersion == QgsOgcUtils::GML_2_1_2 ) ?
3176 QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, srsName, axisInversion, 15 ) :
3177 QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, srsName, axisInversion, 15 );
3178 }
3179
3180 // ST_GeomFromGML
3181 if ( node->name().compare( QLatin1String( "ST_GeomFromGML" ), Qt::CaseInsensitive ) == 0 )
3182 {
3183 QList<QgsSQLStatement::Node *> args = node->args()->list();
3184 if ( args.size() != 1 )
3185 {
3186 mErrorMessage = QObject::tr( "Function %1 should have 1 argument" ).arg( node->name() );
3187 return QDomElement();
3188 }
3189
3190 QgsSQLStatement::Node *firstFnArg = args[0];
3191 if ( firstFnArg->nodeType() != QgsSQLStatement::ntLiteral )
3192 {
3193 mErrorMessage = QObject::tr( "%1: Argument must be string literal" ).arg( node->name() );
3194 return QDomElement();
3195 }
3196
3197 QDomDocument geomDoc;
3198 const QString gml = static_cast<const QgsSQLStatement::NodeLiteral *>( firstFnArg )->value().toString();
3199 // wrap the string into a root tag to have "gml" namespace
3200 const QString xml = QStringLiteral( "<tmp xmlns:gml=\"%1\">%2</tmp>" ).arg( GML_NAMESPACE, gml );
3201 if ( !geomDoc.setContent( xml, true ) )
3202 {
3203 mErrorMessage = QObject::tr( "ST_GeomFromGML: unable to parse XML" );
3204 return QDomElement();
3205 }
3206
3207 const QDomNode geomNode = mDoc.importNode( geomDoc.documentElement().firstChildElement(), true );
3208 mGMLUsed = true;
3209 return geomNode.toElement();
3210 }
3211
3212 // Binary geometry operators
3213 QString ogcName( mapBinarySpatialToOgc( node->name() ) );
3214 if ( !ogcName.isEmpty() )
3215 {
3216 QList<QgsSQLStatement::Node *> args = node->args()->list();
3217 if ( args.size() != 2 )
3218 {
3219 mErrorMessage = QObject::tr( "Function %1 should have 2 arguments" ).arg( node->name() );
3220 return QDomElement();
3221 }
3222
3223 for ( int i = 0; i < 2; i ++ )
3224 {
3225 if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
3226 ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
3227 static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
3228 {
3229 mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
3230 break;
3231 }
3232 }
3233
3234 //if( ogcName == "Intersects" && mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
3235 // ogcName = "Intersect";
3236 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + ogcName );
3237 const auto constArgs = args;
3238 for ( QgsSQLStatement::Node *n : constArgs )
3239 {
3240 const QDomElement childElem = toOgcFilter( n );
3241 if ( !mErrorMessage.isEmpty() )
3242 {
3243 mCurrentSRSName.clear();
3244 return QDomElement();
3245 }
3246
3247 funcElem.appendChild( childElem );
3248 }
3249
3250 mCurrentSRSName.clear();
3251 return funcElem;
3252 }
3253
3254 ogcName = mapTernarySpatialToOgc( node->name() );
3255 if ( !ogcName.isEmpty() )
3256 {
3257 QList<QgsSQLStatement::Node *> args = node->args()->list();
3258 if ( args.size() != 3 )
3259 {
3260 mErrorMessage = QObject::tr( "Function %1 should have 3 arguments" ).arg( node->name() );
3261 return QDomElement();
3262 }
3263
3264 for ( int i = 0; i < 2; i ++ )
3265 {
3266 if ( args[i]->nodeType() == QgsSQLStatement::ntFunction &&
3267 ( static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_GeometryFromText" ), Qt::CaseInsensitive ) == 0 ||
3268 static_cast<const QgsSQLStatement::NodeFunction *>( args[i] )->name().compare( QLatin1String( "ST_MakeEnvelope" ), Qt::CaseInsensitive ) == 0 ) )
3269 {
3270 mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
3271 break;
3272 }
3273 }
3274
3275 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + node->name().mid( 3 ) );
3276 for ( int i = 0; i < 2; i++ )
3277 {
3278 const QDomElement childElem = toOgcFilter( args[i] );
3279 if ( !mErrorMessage.isEmpty() )
3280 {
3281 mCurrentSRSName.clear();
3282 return QDomElement();
3283 }
3284
3285 funcElem.appendChild( childElem );
3286 }
3287 mCurrentSRSName.clear();
3288
3289 QgsSQLStatement::Node *distanceNode = args[2];
3290 if ( distanceNode->nodeType() != QgsSQLStatement::ntLiteral )
3291 {
3292 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() );
3293 return QDomElement();
3294 }
3295 const QgsSQLStatement::NodeLiteral *lit = static_cast<const QgsSQLStatement::NodeLiteral *>( distanceNode );
3296 if ( QgsVariantUtils::isNull( lit->value() ) )
3297 {
3298 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() );
3299 return QDomElement();
3300 }
3301 QString distance;
3302 QString unit( QStringLiteral( "m" ) );
3303 switch ( lit->value().userType() )
3304 {
3305 case QMetaType::Type::Int:
3306 distance = QString::number( lit->value().toInt() );
3307 break;
3308 case QMetaType::Type::LongLong:
3309 distance = QString::number( lit->value().toLongLong() );
3310 break;
3311 case QMetaType::Type::Double:
3312 distance = qgsDoubleToString( lit->value().toDouble() );
3313 break;
3314 case QMetaType::Type::QString:
3315 {
3316 distance = lit->value().toString();
3317 for ( int i = 0; i < distance.size(); i++ )
3318 {
3319 if ( !( ( distance[i] >= '0' && distance[i] <= '9' ) || distance[i] == '-' || distance[i] == '.' || distance[i] == 'e' || distance[i] == 'E' ) )
3320 {
3321 unit = distance.mid( i ).trimmed();
3322 distance = distance.mid( 0, i );
3323 break;
3324 }
3325 }
3326 break;
3327 }
3328
3329 default:
3330 mErrorMessage = QObject::tr( "Literal type not supported: %1" ).arg( static_cast<QMetaType::Type>( lit->value().userType() ) );
3331 return QDomElement();
3332 }
3333
3334 QDomElement distanceElem = mDoc.createElement( mFilterPrefix + ":Distance" );
3335 if ( mFilterVersion == QgsOgcUtils::FILTER_FES_2_0 )
3336 distanceElem.setAttribute( QStringLiteral( "uom" ), unit );
3337 else
3338 distanceElem.setAttribute( QStringLiteral( "unit" ), unit );
3339 distanceElem.appendChild( mDoc.createTextNode( distance ) );
3340 funcElem.appendChild( distanceElem );
3341 return funcElem;
3342 }
3343
3344 // Other function
3345 QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":Function" );
3346 funcElem.setAttribute( QStringLiteral( "name" ), node->name() );
3347 const auto constList = node->args()->list();
3348 for ( QgsSQLStatement::Node *n : constList )
3349 {
3350 const QDomElement childElem = toOgcFilter( n );
3351 if ( !mErrorMessage.isEmpty() )
3352 return QDomElement();
3353
3354 funcElem.appendChild( childElem );
3355 }
3356 return funcElem;
3357}
3358
3360 const QString &leftTable )
3361{
3362 QgsSQLStatement::Node *onExpr = node->onExpr();
3363 if ( onExpr )
3364 {
3365 return toOgcFilter( onExpr );
3366 }
3367
3368 QList<QDomElement> listElem;
3369 const auto constUsingColumns = node->usingColumns();
3370 for ( const QString &columnName : constUsingColumns )
3371 {
3372 QDomElement eqElem = mDoc.createElement( mFilterPrefix + ":PropertyIsEqualTo" );
3373 QDomElement propElem1 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3374 propElem1.appendChild( mDoc.createTextNode( leftTable + "/" + columnName ) );
3375 eqElem.appendChild( propElem1 );
3376 QDomElement propElem2 = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
3377 propElem2.appendChild( mDoc.createTextNode( node->tableDef()->name() + "/" + columnName ) );
3378 eqElem.appendChild( propElem2 );
3379 listElem.append( eqElem );
3380 }
3381
3382 if ( listElem.size() == 1 )
3383 {
3384 return listElem[0];
3385 }
3386 else if ( listElem.size() > 1 )
3387 {
3388 QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3389 const auto constListElem = listElem;
3390 for ( const QDomElement &elem : constListElem )
3391 {
3392 andElem.appendChild( elem );
3393 }
3394 return andElem;
3395 }
3396
3397 return QDomElement();
3398}
3399
3400void QgsOgcUtilsSQLStatementToFilter::visit( const QgsSQLStatement::NodeTableDef *node )
3401{
3402 if ( node->alias().isEmpty() )
3403 {
3404 mMapTableAliasToNames[ node->name()] = node->name();
3405 }
3406 else
3407 {
3408 mMapTableAliasToNames[ node->alias()] = node->name();
3409 }
3410}
3411
3413{
3414 QList<QDomElement> listElem;
3415
3416 if ( mFilterVersion != QgsOgcUtils::FILTER_FES_2_0 &&
3417 ( node->tables().size() != 1 || !node->joins().empty() ) )
3418 {
3419 mErrorMessage = QObject::tr( "Joins are only supported with WFS 2.0" );
3420 return QDomElement();
3421 }
3422
3423 // Register all table name aliases
3424 const auto constTables = node->tables();
3425 for ( QgsSQLStatement::NodeTableDef *table : constTables )
3426 {
3427 visit( table );
3428 }
3429 const auto constJoins = node->joins();
3430 for ( QgsSQLStatement::NodeJoin *join : constJoins )
3431 {
3432 visit( join->tableDef() );
3433 }
3434
3435 // Process JOIN conditions
3436 const QList< QgsSQLStatement::NodeTableDef *> nodeTables = node->tables();
3437 QString leftTable = nodeTables.at( nodeTables.length() - 1 )->name();
3438 for ( QgsSQLStatement::NodeJoin *join : constJoins )
3439 {
3440 const QDomElement joinElem = toOgcFilter( join, leftTable );
3441 if ( !mErrorMessage.isEmpty() )
3442 return QDomElement();
3443 listElem.append( joinElem );
3444 leftTable = join->tableDef()->name();
3445 }
3446
3447 // Process WHERE conditions
3448 if ( node->where() )
3449 {
3450 const QDomElement whereElem = toOgcFilter( node->where() );
3451 if ( !mErrorMessage.isEmpty() )
3452 return QDomElement();
3453 listElem.append( whereElem );
3454 }
3455
3456 // Concatenate all conditions
3457 if ( listElem.size() == 1 )
3458 {
3459 return listElem[0];
3460 }
3461 else if ( listElem.size() > 1 )
3462 {
3463 QDomElement andElem = mDoc.createElement( mFilterPrefix + ":And" );
3464 const auto constListElem = listElem;
3465 for ( const QDomElement &elem : constListElem )
3466 {
3467 andElem.appendChild( elem );
3468 }
3469 return andElem;
3470 }
3471
3472 return QDomElement();
3473}
3474
3476 : mLayer( layer )
3477{
3478 mPropertyName = QStringLiteral( "PropertyName" );
3479 mPrefix = QStringLiteral( "ogc" );
3480
3481 if ( version == QgsOgcUtils::FILTER_FES_2_0 )
3482 {
3483 mPropertyName = QStringLiteral( "ValueReference" );
3484 mPrefix = QStringLiteral( "fes" );
3485 }
3486}
3487
3489{
3490 if ( element.isNull() )
3491 return nullptr;
3492
3493 // check for binary operators
3494 if ( isBinaryOperator( element.tagName() ) )
3495 {
3496 return nodeBinaryOperatorFromOgcFilter( element );
3497 }
3498
3499 // check for spatial operators
3500 if ( isSpatialOperator( element.tagName() ) )
3501 {
3502 return nodeSpatialOperatorFromOgcFilter( element );
3503 }
3504
3505 // check for other OGC operators, convert them to expressions
3506 if ( element.tagName() == QLatin1String( "Not" ) )
3507 {
3508 return nodeNotFromOgcFilter( element );
3509 }
3510 else if ( element.tagName() == QLatin1String( "PropertyIsNull" ) )
3511 {
3512 return nodePropertyIsNullFromOgcFilter( element );
3513 }
3514 else if ( element.tagName() == QLatin1String( "Literal" ) )
3515 {
3516 return nodeLiteralFromOgcFilter( element );
3517 }
3518 else if ( element.tagName() == QLatin1String( "Function" ) )
3519 {
3520 return nodeFunctionFromOgcFilter( element );
3521 }
3522 else if ( element.tagName() == mPropertyName )
3523 {
3524 return nodeColumnRefFromOgcFilter( element );
3525 }
3526 else if ( element.tagName() == QLatin1String( "PropertyIsBetween" ) )
3527 {
3528 return nodeIsBetweenFromOgcFilter( element );
3529 }
3530
3531 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() );
3532 return nullptr;
3533}
3534
3536{
3537 if ( element.isNull() )
3538 return nullptr;
3539
3540 int op = binaryOperatorFromTagName( element.tagName() );
3541 if ( op < 0 )
3542 {
3543 mErrorMessage = QObject::tr( "'%1' binary operator not supported." ).arg( element.tagName() );
3544 return nullptr;
3545 }
3546
3547 if ( op == QgsExpressionNodeBinaryOperator::boLike && element.hasAttribute( QStringLiteral( "matchCase" ) ) && element.attribute( QStringLiteral( "matchCase" ) ) == QLatin1String( "false" ) )
3548 {
3550 }
3551
3552 QDomElement operandElem = element.firstChildElement();
3553 std::unique_ptr<QgsExpressionNode> expr( nodeFromOgcFilter( operandElem ) );
3554
3555 if ( !expr )
3556 {
3557 mErrorMessage = QObject::tr( "invalid left operand for '%1' binary operator" ).arg( element.tagName() );
3558 return nullptr;
3559 }
3560
3561 const std::unique_ptr<QgsExpressionNode> leftOp( expr->clone() );
3562 for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
3563 {
3564 std::unique_ptr<QgsExpressionNode> opRight( nodeFromOgcFilter( operandElem ) );
3565 if ( !opRight )
3566 {
3567 mErrorMessage = QObject::tr( "invalid right operand for '%1' binary operator" ).arg( element.tagName() );
3568 return nullptr;
3569 }
3570
3572 {
3573 QString wildCard;
3574 if ( element.hasAttribute( QStringLiteral( "wildCard" ) ) )
3575 {
3576 wildCard = element.attribute( QStringLiteral( "wildCard" ) );
3577 }
3578 QString singleChar;
3579 if ( element.hasAttribute( QStringLiteral( "singleChar" ) ) )
3580 {
3581 singleChar = element.attribute( QStringLiteral( "singleChar" ) );
3582 }
3583 QString escape = QStringLiteral( "\\" );
3584 if ( element.hasAttribute( QStringLiteral( "escape" ) ) )
3585 {
3586 escape = element.attribute( QStringLiteral( "escape" ) );
3587 }
3588 if ( element.hasAttribute( QStringLiteral( "escapeChar" ) ) )
3589 {
3590 escape = element.attribute( QStringLiteral( "escapeChar" ) );
3591 }
3592 // replace
3593 QString oprValue = static_cast<const QgsExpressionNodeLiteral *>( opRight.get() )->value().toString();
3594 if ( !wildCard.isEmpty() && wildCard != QLatin1String( "%" ) )
3595 {
3596 oprValue.replace( '%', QLatin1String( "\\%" ) );
3597 if ( oprValue.startsWith( wildCard ) )
3598 {
3599 oprValue.replace( 0, 1, QStringLiteral( "%" ) );
3600 }
3601 const QRegularExpression rx( "[^" + QgsStringUtils::qRegExpEscape( escape ) + "](" + QgsStringUtils::qRegExpEscape( wildCard ) + ")" );
3602 QRegularExpressionMatch match = rx.match( oprValue );
3603 int pos;
3604 while ( match.hasMatch() )
3605 {
3606 pos = match.capturedStart();
3607 oprValue.replace( pos + 1, 1, QStringLiteral( "%" ) );
3608 pos += 1;
3609 match = rx.match( oprValue, pos );
3610 }
3611 oprValue.replace( escape + wildCard, wildCard );
3612 }
3613 if ( !singleChar.isEmpty() && singleChar != QLatin1String( "_" ) )
3614 {
3615 oprValue.replace( '_', QLatin1String( "\\_" ) );
3616 if ( oprValue.startsWith( singleChar ) )
3617 {
3618 oprValue.replace( 0, 1, QStringLiteral( "_" ) );
3619 }
3620 const QRegularExpression rx( "[^" + QgsStringUtils::qRegExpEscape( escape ) + "](" + QgsStringUtils::qRegExpEscape( singleChar ) + ")" );
3621 QRegularExpressionMatch match = rx.match( oprValue );
3622 int pos;
3623 while ( match.hasMatch() )
3624 {
3625 pos = match.capturedStart();
3626 oprValue.replace( pos + 1, 1, QStringLiteral( "_" ) );
3627 pos += 1;
3628 match = rx.match( oprValue, pos );
3629 }
3630 oprValue.replace( escape + singleChar, singleChar );
3631 }
3632 if ( !escape.isEmpty() && escape != QLatin1String( "\\" ) )
3633 {
3634 oprValue.replace( escape + escape, escape );
3635 }
3636 opRight = std::make_unique<QgsExpressionNodeLiteral>( oprValue );
3637 }
3638
3639 expr = std::make_unique<QgsExpressionNodeBinaryOperator>( static_cast< QgsExpressionNodeBinaryOperator::BinaryOperator >( op ), expr.release(), opRight.release() );
3640 }
3641
3642 if ( expr == leftOp )
3643 {
3644 mErrorMessage = QObject::tr( "only one operand for '%1' binary operator" ).arg( element.tagName() );
3645 return nullptr;
3646 }
3647
3648 return dynamic_cast< QgsExpressionNodeBinaryOperator * >( expr.release() );
3649}
3650
3651
3653{
3654 // we are exploiting the fact that our function names are the same as the XML tag names
3655 const int opIdx = QgsExpression::functionIndex( element.tagName().toLower() );
3656
3657 auto gml2Args = std::make_unique<QgsExpressionNode::NodeList>();
3658 QDomElement childElem = element.firstChildElement();
3659 QString gml2Str;
3660 while ( !childElem.isNull() && gml2Str.isEmpty() )
3661 {
3662 if ( childElem.tagName() != mPropertyName )
3663 {
3664 QTextStream gml2Stream( &gml2Str );
3665 childElem.save( gml2Stream, 0 );
3666 }
3667 childElem = childElem.nextSiblingElement();
3668 }
3669 if ( !gml2Str.isEmpty() )
3670 {
3671 gml2Args->append( new QgsExpressionNodeLiteral( QVariant( gml2Str.remove( '\n' ) ) ) );
3672 }
3673 else
3674 {
3675 mErrorMessage = QObject::tr( "No OGC Geometry found" );
3676 return nullptr;
3677 }
3678
3679 auto opArgs = std::make_unique<QgsExpressionNode::NodeList>();
3680 opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "$geometry" ) ), new QgsExpressionNode::NodeList() ) );
3681 opArgs->append( new QgsExpressionNodeFunction( QgsExpression::functionIndex( QStringLiteral( "geomFromGML" ) ), gml2Args.release() ) );
3682
3683 return new QgsExpressionNodeFunction( opIdx, opArgs.release() );
3684}
3685
3687{
3688 if ( element.isNull() || element.tagName() != mPropertyName )
3689 {
3690 mErrorMessage = QObject::tr( "%1:PropertyName expected, got %2" ).arg( mPrefix, element.tagName() );
3691 return nullptr;
3692 }
3693
3694 return new QgsExpressionNodeColumnRef( element.firstChild().nodeValue() );
3695}
3696
3698{
3699 if ( element.isNull() || element.tagName() != QLatin1String( "Literal" ) )
3700 {
3701 mErrorMessage = QObject::tr( "%1:Literal expected, got %2" ).arg( mPrefix, element.tagName() );
3702 return nullptr;
3703 }
3704
3705 std::unique_ptr<QgsExpressionNode> root;
3706 if ( !element.hasChildNodes() )
3707 {
3708 root = std::make_unique<QgsExpressionNodeLiteral>( QVariant( "" ) );
3709 return root.release();
3710 }
3711
3712 // the literal content can have more children (e.g. CDATA section, text, ...)
3713 QDomNode childNode = element.firstChild();
3714 while ( !childNode.isNull() )
3715 {
3716 std::unique_ptr<QgsExpressionNode> operand;
3717
3718 if ( childNode.nodeType() == QDomNode::ElementNode )
3719 {
3720 // found a element node (e.g. PropertyName), convert it
3721 const QDomElement operandElem = childNode.toElement();
3722 operand.reset( nodeFromOgcFilter( operandElem ) );
3723 if ( !operand )
3724 {
3725 mErrorMessage = QObject::tr( "'%1' is an invalid or not supported content for %2:Literal" ).arg( operandElem.tagName(), mPrefix );
3726 return nullptr;
3727 }
3728 }
3729 else
3730 {
3731 // probably a text/CDATA node
3732 QVariant value = childNode.nodeValue();
3733
3734 bool converted = false;
3735
3736 // try to convert the node content to corresponding field type if possible
3737 if ( mLayer )
3738 {
3739 QDomElement propertyNameElement = element.previousSiblingElement( mPropertyName );
3740 if ( propertyNameElement.isNull() || propertyNameElement.tagName() != mPropertyName )
3741 {
3742 propertyNameElement = element.nextSiblingElement( mPropertyName );
3743 }
3744 if ( !propertyNameElement.isNull() || propertyNameElement.tagName() == mPropertyName )
3745 {
3746 const int fieldIndex = mLayer->fields().indexOf( propertyNameElement.firstChild().nodeValue() );
3747 if ( fieldIndex != -1 )
3748 {
3749 const QgsField field = mLayer->fields().field( propertyNameElement.firstChild().nodeValue() );
3750 field.convertCompatible( value );
3751 converted = true;
3752 }
3753 }
3754 }
3755 if ( !converted )
3756 {
3757 // try to convert the node content to number if possible,
3758 // otherwise let's use it as string
3759 bool ok;
3760 const double d = value.toDouble( &ok );
3761 if ( ok )
3762 value = d;
3763 }
3764
3765 operand = std::make_unique<QgsExpressionNodeLiteral>( value );
3766 }
3767
3768 // use the concat operator to merge the ogc:Literal children
3769 if ( !root )
3770 {
3771 root = std::move( operand );
3772 }
3773 else
3774 {
3775 root = std::make_unique<QgsExpressionNodeBinaryOperator>( QgsExpressionNodeBinaryOperator::boConcat, root.release(), operand.release() );
3776 }
3777
3778 childNode = childNode.nextSibling();
3779 }
3780
3781 if ( root )
3782 return root.release();
3783
3784 return nullptr;
3785}
3786
3788{
3789 if ( element.tagName() != QLatin1String( "Not" ) )
3790 return nullptr;
3791
3792 const QDomElement operandElem = element.firstChildElement();
3793 std::unique_ptr<QgsExpressionNode> operand( nodeFromOgcFilter( operandElem ) );
3794 if ( !operand )
3795 {
3796 mErrorMessage = QObject::tr( "invalid operand for '%1' unary operator" ).arg( element.tagName() );
3797 return nullptr;
3798 }
3799
3801}
3802
3804{
3805 // convert ogc:PropertyIsNull to IS operator with NULL right operand
3806 if ( element.tagName() != QLatin1String( "PropertyIsNull" ) )
3807 {
3808 return nullptr;
3809 }
3810
3811 const QDomElement operandElem = element.firstChildElement();
3812 std::unique_ptr<QgsExpressionNode> opLeft( nodeFromOgcFilter( operandElem ) );
3813 if ( !opLeft )
3814 return nullptr;
3815
3816 std::unique_ptr<QgsExpressionNode> opRight( new QgsExpressionNodeLiteral( QVariant() ) );
3817 return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boIs, opLeft.release(), opRight.release() );
3818}
3819
3821{
3822 if ( element.isNull() || element.tagName() != QLatin1String( "Function" ) )
3823 {
3824 mErrorMessage = QObject::tr( "%1:Function expected, got %2" ).arg( mPrefix, element.tagName() );
3825 return nullptr;
3826 }
3827
3828 for ( int i = 0; i < QgsExpression::Functions().size(); i++ )
3829 {
3830 const QgsExpressionFunction *funcDef = QgsExpression::Functions()[i];
3831
3832 if ( element.attribute( QStringLiteral( "name" ) ) != funcDef->name() )
3833 continue;
3834
3835 auto args = std::make_unique<QgsExpressionNode::NodeList>();
3836
3837 QDomElement operandElem = element.firstChildElement();
3838 while ( !operandElem.isNull() )
3839 {
3840 std::unique_ptr<QgsExpressionNode> op( nodeFromOgcFilter( operandElem ) );
3841 if ( !op )
3842 {
3843 return nullptr;
3844 }
3845 args->append( op.release() );
3846
3847 operandElem = operandElem.nextSiblingElement();
3848 }
3849
3850 return new QgsExpressionNodeFunction( i, args.release() );
3851 }
3852
3853 return nullptr;
3854}
3855
3857{
3858 // <ogc:PropertyIsBetween> encode a Range check
3859 std::unique_ptr<QgsExpressionNode> operand;
3860 std::unique_ptr<QgsExpressionNode> lowerBound;
3861 std::unique_ptr<QgsExpressionNode> upperBound;
3862
3863 QDomElement operandElem = element.firstChildElement();
3864 while ( !operandElem.isNull() )
3865 {
3866 if ( operandElem.tagName() == QLatin1String( "LowerBoundary" ) )
3867 {
3868 const QDomElement lowerBoundElem = operandElem.firstChildElement();
3869 lowerBound.reset( nodeFromOgcFilter( lowerBoundElem ) );
3870 }
3871 else if ( operandElem.tagName() == QLatin1String( "UpperBoundary" ) )
3872 {
3873 const QDomElement upperBoundElem = operandElem.firstChildElement();
3874 upperBound.reset( nodeFromOgcFilter( upperBoundElem ) );
3875 }
3876 else
3877 {
3878 // <ogc:expression>
3879 operand.reset( nodeFromOgcFilter( operandElem ) );
3880 }
3881
3882 if ( operand && lowerBound && upperBound )
3883 break;
3884
3885 operandElem = operandElem.nextSiblingElement();
3886 }
3887
3888 if ( !operand || !lowerBound || !upperBound )
3889 {
3890 mErrorMessage = QObject::tr( "missing some required sub-elements in %1:PropertyIsBetween" ).arg( mPrefix );
3891 return nullptr;
3892 }
3893
3894 std::unique_ptr<QgsExpressionNode> leOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boLE, operand->clone(), upperBound.release() ) );
3895 std::unique_ptr<QgsExpressionNode> geOperator( new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boGE, operand.release(), lowerBound.release() ) );
3896 return new QgsExpressionNodeBinaryOperator( QgsExpressionNodeBinaryOperator::boAnd, geOperator.release(), leOperator.release() );
3897}
3898
3900{
3901 return mErrorMessage;
3902}
3903
3904QgsOgcCrsUtils::CRSFlavor QgsOgcCrsUtils::parseCrsName( const QString &crsName, QString &authority, QString &code )
3905{
3906 const thread_local QRegularExpression re_url( QRegularExpression::anchoredPattern( QStringLiteral( "http://www\\.opengis\\.net/gml/srs/epsg\\.xml#(.+)" ) ), QRegularExpression::CaseInsensitiveOption );
3907 if ( const QRegularExpressionMatch match = re_url.match( crsName ); match.hasMatch() )
3908 {
3909 authority = QStringLiteral( "EPSG" );
3910 code = match.captured( 1 );
3912 }
3913
3914 const thread_local QRegularExpression re_ogc_urn( QRegularExpression::anchoredPattern( QStringLiteral( "urn:ogc:def:crs:([^:]+).+(?<=:)([^:]+)" ) ), QRegularExpression::CaseInsensitiveOption );
3915 if ( const QRegularExpressionMatch match = re_ogc_urn.match( crsName ); match.hasMatch() )
3916 {
3917 authority = match.captured( 1 );
3918 code = match.captured( 2 );
3919 return CRSFlavor::OGC_URN;
3920 }
3921
3922 const thread_local QRegularExpression re_x_ogc_urn( QRegularExpression::anchoredPattern( QStringLiteral( "urn:x-ogc:def:crs:([^:]+).+(?<=:)([^:]+)" ) ), QRegularExpression::CaseInsensitiveOption );
3923 if ( const QRegularExpressionMatch match = re_x_ogc_urn.match( crsName ); match.hasMatch() )
3924 {
3925 authority = match.captured( 1 );
3926 code = match.captured( 2 );
3927 return CRSFlavor::X_OGC_URN;
3928 }
3929
3930 const thread_local QRegularExpression re_http_uri( QRegularExpression::anchoredPattern( QStringLiteral( "http://www\\.opengis\\.net/def/crs/([^/]+).+/([^/]+)" ) ), QRegularExpression::CaseInsensitiveOption );
3931 if ( const QRegularExpressionMatch match = re_http_uri.match( crsName ); match.hasMatch() )
3932 {
3933 authority = match.captured( 1 );
3934 code = match.captured( 2 );
3936 }
3937
3938 const thread_local QRegularExpression re_auth_code( QRegularExpression::anchoredPattern( QStringLiteral( "([^:]+):(.+)" ) ), QRegularExpression::CaseInsensitiveOption );
3939 if ( const QRegularExpressionMatch match = re_auth_code.match( crsName ); match.hasMatch() )
3940 {
3941 authority = match.captured( 1 );
3942 code = match.captured( 2 );
3943 return CRSFlavor::AUTH_CODE;
3944 }
3945
3946 return CRSFlavor::UNKNOWN;
3947}
3948
3949QgsGeometry QgsOgcUtils::geometryFromGMLUsingGdal( const QDomElement &geometryElement )
3950{
3951 QString gml;
3952 QTextStream gmlStream( &gml );
3953 geometryElement.save( gmlStream, 0 );
3954 gdal::ogr_geometry_unique_ptr ogrGeom { OGR_G_CreateFromGML( gml.toUtf8().constData() ) };
3955 return QgsOgrUtils::ogrGeometryToQgsGeometry( ogrGeom.get() );
3956}
3957
3958QgsGeometry QgsOgcUtils::geometryFromGMLMultiCurve( const QDomElement &geometryElement )
3959{
3960 return geometryFromGMLUsingGdal( geometryElement );
3961}
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