39#include <QRegularExpression>
44using namespace Qt::StringLiterals;
47#include <netinet/in.h>
53#define GML_NAMESPACE u"http://www.opengis.net/gml"_s
54#define GML32_NAMESPACE u"http://www.opengis.net/gml/3.2"_s
55#define OGC_NAMESPACE u"http://www.opengis.net/ogc"_s
56#define FES_NAMESPACE u"http://www.opengis.net/fes/2.0"_s
57#define SE_NAMESPACE u"http://www.opengis.net/se"_s
63 const QString &namespacePrefix,
64 const QString &namespaceURI,
65 const QString &geometryName,
66 const QString &srsName,
67 bool honourAxisOrientation,
68 bool invertAxisOrientation,
69 const QMap<QString, QString> &fieldNameToXPathMap,
70 const QMap<QString, QString> &namespacePrefixToUriMap
73 , mGMLVersion( gmlVersion )
74 , mFilterVersion( filterVersion )
75 , mNamespacePrefix( namespacePrefix )
76 , mNamespaceURI( namespaceURI )
77 , mGeometryName( geometryName )
79 , mInvertAxisOrientation( invertAxisOrientation )
80 , mFieldNameToXPathMap( fieldNameToXPathMap )
81 , mNamespacePrefixToUriMap( namespacePrefixToUriMap )
82 , mFilterPrefix( ( filterVersion ==
QgsOgcUtils::FILTER_FES_2_0 ) ?
"fes" :
"ogc" )
83 , mPropertyName( ( filterVersion ==
QgsOgcUtils::FILTER_FES_2_0 ) ?
"ValueReference" :
"PropertyName" )
86 if ( !mSrsName.isEmpty() )
90 if ( honourAxisOrientation && crs.hasAxisInverted() )
92 mInvertAxisOrientation = !mInvertAxisOrientation;
99 QDomElement geometryTypeElement = geometryNode.toElement();
100 QString geomType = geometryTypeElement.tagName();
103 if ( !( geomType ==
"Point"_L1
104 || geomType ==
"LineString"_L1
105 || geomType ==
"Polygon"_L1
106 || geomType ==
"MultiPoint"_L1
107 || geomType ==
"MultiLineString"_L1
108 || geomType ==
"MultiPolygon"_L1
109 || geomType ==
"Box"_L1
110 || geomType ==
"Envelope"_L1
111 || geomType ==
"MultiCurve"_L1 ) )
113 const QDomNode geometryChild = geometryNode.firstChild();
114 if ( geometryChild.isNull() )
118 geometryTypeElement = geometryChild.toElement();
119 geomType = geometryTypeElement.tagName();
122 if ( !( geomType ==
"Point"_L1
123 || geomType ==
"LineString"_L1
124 || geomType ==
"Polygon"_L1
125 || geomType ==
"MultiPoint"_L1
126 || geomType ==
"MultiLineString"_L1
127 || geomType ==
"MultiPolygon"_L1
128 || geomType ==
"Box"_L1
129 || geomType ==
"Envelope"_L1
130 || geomType ==
"MultiCurve"_L1 ) )
133 if ( geomType ==
"Point"_L1 )
135 geometry = geometryFromGMLPoint( geometryTypeElement );
137 else if ( geomType ==
"LineString"_L1 )
139 geometry = geometryFromGMLLineString( geometryTypeElement );
141 else if ( geomType ==
"Polygon"_L1 )
143 geometry = geometryFromGMLPolygon( geometryTypeElement );
145 else if ( geomType ==
"MultiPoint"_L1 )
147 geometry = geometryFromGMLMultiPoint( geometryTypeElement );
149 else if ( geomType ==
"MultiLineString"_L1 )
151 geometry = geometryFromGMLMultiLineString( geometryTypeElement );
153 else if ( geomType ==
"MultiCurve"_L1 )
155 geometry = geometryFromGMLMultiCurve( geometryTypeElement );
157 else if ( geomType ==
"MultiPolygon"_L1 )
159 geometry = geometryFromGMLMultiPolygon( geometryTypeElement );
161 else if ( geomType ==
"Box"_L1 )
165 else if ( geomType ==
"Envelope"_L1 )
178 if ( geometryTypeElement.hasAttribute( u
"srsName"_s ) )
180 QString srsName { geometryTypeElement.attribute( u
"srsName"_s ) };
183 const bool ignoreAxisOrientation { srsName.startsWith(
"http://www.opengis.net/gml/srs/"_L1 ) || srsName.startsWith(
"EPSG:"_L1 ) };
187 if ( srsName.startsWith(
"http://www.opengis.net/gml/srs/"_L1 ) )
189 const auto parts { srsName.split( QRegularExpression( QStringLiteral( R
"raw(/|#|\.)raw" ) ) ) };
190 if ( parts.length() == 10 )
192 srsName = u
"http://www.opengis.net/def/crs/%1/0/%2"_s.arg( parts[7].toUpper(), parts[9] );
226 const QString xml = u
"<tmp xmlns=\"%1\" xmlns:gml=\"%1\">%2</tmp>"_s.arg(
GML_NAMESPACE, xmlString );
228 if ( !doc.setContent( xml,
true ) )
231 return geometryFromGML( doc.documentElement().firstChildElement(), context );
235QgsGeometry QgsOgcUtils::geometryFromGMLPoint(
const QDomElement &geometryElement )
239 const QDomNodeList coordList = geometryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"coordinates"_s );
240 if ( !coordList.isEmpty() )
242 const QDomElement coordElement = coordList.at( 0 ).toElement();
243 if ( readGMLCoordinates( pointCoordinate, coordElement ) != 0 )
250 const QDomNodeList posList = geometryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"pos"_s );
251 if ( posList.size() < 1 )
253 return QgsGeometry();
255 const QDomElement posElement = posList.at( 0 ).toElement();
256 if ( readGMLPositions( pointCoordinate, posElement ) != 0 )
258 return QgsGeometry();
262 if ( pointCoordinate.empty() )
264 return QgsGeometry();
267 const bool hasZ { !std::isnan( pointCoordinate.first().z() ) };
268 QgsPolyline::const_iterator point_it = pointCoordinate.constBegin();
269 const char e =
static_cast<char>( htonl( 1 ) != 1 );
270 const double x = point_it->x();
271 const double y = point_it->y();
272 const int size = 1 +
static_cast<int>(
sizeof( int ) ) + ( hasZ ? 3 : 2 ) *
static_cast<int>(
sizeof( double ) );
275 unsigned char *wkb =
new unsigned char[size];
278 memcpy( &( wkb )[wkbPosition], &e, 1 );
280 memcpy( &( wkb )[wkbPosition], &type,
sizeof(
int ) );
281 wkbPosition +=
sizeof( int );
282 memcpy( &( wkb )[wkbPosition], &x,
sizeof(
double ) );
283 wkbPosition +=
sizeof( double );
284 memcpy( &( wkb )[wkbPosition], &y,
sizeof(
double ) );
288 wkbPosition +=
sizeof( double );
289 double z = point_it->z();
290 memcpy( &( wkb )[wkbPosition], &z,
sizeof(
double ) );
298QgsGeometry QgsOgcUtils::geometryFromGMLLineString(
const QDomElement &geometryElement )
302 const QDomNodeList coordList = geometryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"coordinates"_s );
303 if ( !coordList.isEmpty() )
305 const QDomElement coordElement = coordList.at( 0 ).toElement();
306 if ( readGMLCoordinates( lineCoordinates, coordElement ) != 0 )
308 return QgsGeometry();
313 const QDomNodeList posList = geometryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"posList"_s );
314 if ( posList.size() < 1 )
316 return QgsGeometry();
318 const QDomElement posElement = posList.at( 0 ).toElement();
319 if ( readGMLPositions( lineCoordinates, posElement ) != 0 )
321 return QgsGeometry();
325 const bool hasZ { !std::isnan( lineCoordinates.first().z() ) };
327 char e =
static_cast<char>( htonl( 1 ) != 1 );
328 const int size = 1 + 2 *
static_cast<int>(
sizeof( int ) + lineCoordinates.size() ) * ( hasZ ? 3 : 2 ) *
static_cast<int>(
sizeof(
double ) );
331 unsigned char *wkb =
new unsigned char[size];
335 int nPoints = lineCoordinates.size();
338 memcpy( &( wkb )[wkbPosition], &e, 1 );
340 memcpy( &( wkb )[wkbPosition], &type,
sizeof(
int ) );
341 wkbPosition +=
sizeof( int );
342 memcpy( &( wkb )[wkbPosition], &nPoints,
sizeof(
int ) );
343 wkbPosition +=
sizeof( int );
345 QgsPolyline::const_iterator iter;
346 for ( iter = lineCoordinates.constBegin(); iter != lineCoordinates.constEnd(); ++iter )
350 memcpy( &( wkb )[wkbPosition], &x,
sizeof(
double ) );
351 wkbPosition +=
sizeof( double );
352 memcpy( &( wkb )[wkbPosition], &y,
sizeof(
double ) );
353 wkbPosition +=
sizeof( double );
357 double z = iter->z();
358 memcpy( &( wkb )[wkbPosition], &z,
sizeof(
double ) );
359 wkbPosition +=
sizeof( double );
368QgsGeometry QgsOgcUtils::geometryFromGMLPolygon(
const QDomElement &geometryElement )
375 const QDomNodeList outerBoundaryList = geometryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"outerBoundaryIs"_s );
376 if ( !outerBoundaryList.isEmpty() )
378 QDomElement coordinatesElement = outerBoundaryList.at( 0 ).firstChild().firstChild().toElement();
379 if ( coordinatesElement.isNull() )
381 return QgsGeometry();
383 if ( readGMLCoordinates( exteriorPointList, coordinatesElement ) != 0 )
385 return QgsGeometry();
387 ringCoordinates.push_back( exteriorPointList );
390 const QDomNodeList innerBoundaryList = geometryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"innerBoundaryIs"_s );
391 for (
int i = 0; i < innerBoundaryList.size(); ++i )
394 coordinatesElement = innerBoundaryList.at( i ).firstChild().firstChild().toElement();
395 if ( coordinatesElement.isNull() )
397 return QgsGeometry();
399 if ( readGMLCoordinates( interiorPointList, coordinatesElement ) != 0 )
401 return QgsGeometry();
403 ringCoordinates.push_back( interiorPointList );
409 const QDomNodeList exteriorList = geometryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"exterior"_s );
410 if ( exteriorList.size() < 1 )
412 return QgsGeometry();
414 const QDomElement posElement = exteriorList.at( 0 ).firstChild().firstChild().toElement();
415 if ( posElement.isNull() )
417 return QgsGeometry();
419 if ( readGMLPositions( exteriorPointList, posElement ) != 0 )
421 return QgsGeometry();
423 ringCoordinates.push_back( exteriorPointList );
426 const QDomNodeList interiorList = geometryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"interior"_s );
427 for (
int i = 0; i < interiorList.size(); ++i )
430 const QDomElement posElement = interiorList.at( i ).firstChild().firstChild().toElement();
431 if ( posElement.isNull() )
433 return QgsGeometry();
436 if ( readGMLPositions( interiorPointList, posElement ) )
438 return QgsGeometry();
440 ringCoordinates.push_back( interiorPointList );
445 int nrings = ringCoordinates.size();
447 return QgsGeometry();
450 for ( QgsMultiPolyline::const_iterator it = ringCoordinates.constBegin(); it != ringCoordinates.constEnd(); ++it )
452 npoints += it->size();
455 const bool hasZ { !std::isnan( ringCoordinates.first().first().z() ) };
457 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 ) );
460 unsigned char *wkb =
new unsigned char[size];
463 char e =
static_cast<char>( htonl( 1 ) != 1 );
465 int nPointsInRing = 0;
469 memcpy( &( wkb )[wkbPosition], &e, 1 );
471 memcpy( &( wkb )[wkbPosition], &type,
sizeof(
int ) );
472 wkbPosition +=
sizeof( int );
473 memcpy( &( wkb )[wkbPosition], &nrings,
sizeof(
int ) );
474 wkbPosition +=
sizeof( int );
475 for ( QgsMultiPolyline::const_iterator it = ringCoordinates.constBegin(); it != ringCoordinates.constEnd(); ++it )
477 nPointsInRing = it->size();
478 memcpy( &( wkb )[wkbPosition], &nPointsInRing,
sizeof(
int ) );
479 wkbPosition +=
sizeof( int );
481 QgsPolyline::const_iterator iter;
482 for ( iter = it->begin(); iter != it->end(); ++iter )
487 memcpy( &( wkb )[wkbPosition], &x,
sizeof(
double ) );
488 wkbPosition +=
sizeof( double );
489 memcpy( &( wkb )[wkbPosition], &y,
sizeof(
double ) );
490 wkbPosition +=
sizeof( double );
495 memcpy( &( wkb )[wkbPosition], &z,
sizeof(
double ) );
496 wkbPosition +=
sizeof( double );
506QgsGeometry QgsOgcUtils::geometryFromGMLMultiPoint(
const QDomElement &geometryElement )
510 const QDomNodeList pointMemberList = geometryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"pointMember"_s );
511 if ( pointMemberList.size() < 1 )
513 return QgsGeometry();
515 QDomNodeList pointNodeList;
517 QDomNodeList coordinatesList;
518 QDomNodeList posList;
519 for (
int i = 0; i < pointMemberList.size(); ++i )
522 pointNodeList = pointMemberList.at( i ).toElement().elementsByTagNameNS(
GML_NAMESPACE, u
"Point"_s );
523 if ( pointNodeList.size() < 1 )
528 coordinatesList = pointNodeList.at( 0 ).toElement().elementsByTagNameNS(
GML_NAMESPACE, u
"coordinates"_s );
529 if ( !coordinatesList.isEmpty() )
531 currentPoint.clear();
532 if ( readGMLCoordinates( currentPoint, coordinatesList.at( 0 ).toElement() ) != 0 )
536 if ( currentPoint.empty() )
540 pointList.push_back( ( *currentPoint.begin() ) );
546 posList = pointNodeList.at( 0 ).toElement().elementsByTagNameNS(
GML_NAMESPACE, u
"pos"_s );
547 if ( posList.size() < 1 )
551 currentPoint.clear();
552 if ( readGMLPositions( currentPoint, posList.at( 0 ).toElement() ) != 0 )
556 if ( currentPoint.empty() )
560 pointList.push_back( ( *currentPoint.begin() ) );
564 int nPoints = pointList.size();
566 return QgsGeometry();
568 const bool hasZ { !std::isnan( pointList.first().z() ) };
571 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 ) ) );
574 unsigned char *wkb =
new unsigned char[size];
577 char e =
static_cast<char>( htonl( 1 ) != 1 );
580 memcpy( &( wkb )[wkbPosition], &e, 1 );
582 memcpy( &( wkb )[wkbPosition], &type,
sizeof(
int ) );
583 wkbPosition +=
sizeof( int );
584 memcpy( &( wkb )[wkbPosition], &nPoints,
sizeof(
int ) );
585 wkbPosition +=
sizeof( int );
587 for ( QgsPolyline::const_iterator it = pointList.constBegin(); it != pointList.constEnd(); ++it )
589 memcpy( &( wkb )[wkbPosition], &e, 1 );
591 memcpy( &( wkb )[wkbPosition], &pointType,
sizeof(
int ) );
592 wkbPosition +=
sizeof( int );
594 memcpy( &( wkb )[wkbPosition], &x,
sizeof(
double ) );
595 wkbPosition +=
sizeof( double );
597 memcpy( &( wkb )[wkbPosition], &y,
sizeof(
double ) );
598 wkbPosition +=
sizeof( double );
603 memcpy( &( wkb )[wkbPosition], &z,
sizeof(
double ) );
604 wkbPosition +=
sizeof( double );
613QgsGeometry QgsOgcUtils::geometryFromGMLMultiLineString(
const QDomElement &geometryElement )
625 QList< QgsPolyline > lineCoordinates;
626 QDomElement currentLineStringElement;
627 QDomNodeList currentCoordList;
628 QDomNodeList currentPosList;
630 const QDomNodeList lineStringMemberList = geometryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"lineStringMember"_s );
631 if ( !lineStringMemberList.isEmpty() )
633 for (
int i = 0; i < lineStringMemberList.size(); ++i )
635 const QDomNodeList lineStringNodeList = lineStringMemberList.at( i ).toElement().elementsByTagNameNS(
GML_NAMESPACE, u
"LineString"_s );
636 if ( lineStringNodeList.size() < 1 )
638 return QgsGeometry();
640 currentLineStringElement = lineStringNodeList.at( 0 ).toElement();
641 currentCoordList = currentLineStringElement.elementsByTagNameNS(
GML_NAMESPACE, u
"coordinates"_s );
642 if ( !currentCoordList.isEmpty() )
645 if ( readGMLCoordinates( currentPointList, currentCoordList.at( 0 ).toElement() ) != 0 )
647 return QgsGeometry();
649 lineCoordinates.push_back( currentPointList );
653 currentPosList = currentLineStringElement.elementsByTagNameNS(
GML_NAMESPACE, u
"posList"_s );
654 if ( currentPosList.size() < 1 )
656 return QgsGeometry();
659 if ( readGMLPositions( currentPointList, currentPosList.at( 0 ).toElement() ) != 0 )
661 return QgsGeometry();
663 lineCoordinates.push_back( currentPointList );
669 const QDomNodeList lineStringList = geometryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"LineString"_s );
670 if ( !lineStringList.isEmpty() )
672 for (
int i = 0; i < lineStringList.size(); ++i )
674 currentLineStringElement = lineStringList.at( i ).toElement();
675 currentCoordList = currentLineStringElement.elementsByTagNameNS(
GML_NAMESPACE, u
"coordinates"_s );
676 if ( !currentCoordList.isEmpty() )
679 if ( readGMLCoordinates( currentPointList, currentCoordList.at( 0 ).toElement() ) != 0 )
681 return QgsGeometry();
683 lineCoordinates.push_back( currentPointList );
684 return QgsGeometry();
688 currentPosList = currentLineStringElement.elementsByTagNameNS(
GML_NAMESPACE, u
"posList"_s );
689 if ( currentPosList.size() < 1 )
691 return QgsGeometry();
694 if ( readGMLPositions( currentPointList, currentPosList.at( 0 ).toElement() ) != 0 )
696 return QgsGeometry();
698 lineCoordinates.push_back( currentPointList );
704 return QgsGeometry();
708 int nLines = lineCoordinates.size();
710 return QgsGeometry();
712 const bool hasZ { !std::isnan( lineCoordinates.first().first().z() ) };
713 const int coordSize { hasZ ? 3 : 2 };
716 int size =
static_cast<int>( lineCoordinates.size() + 1 ) * ( 1 + 2 *
sizeof( int ) );
717 for ( QList< QgsPolyline >::const_iterator it = lineCoordinates.constBegin(); it != lineCoordinates.constEnd(); ++it )
719 size += it->size() * coordSize *
sizeof( double );
723 unsigned char *wkb =
new unsigned char[size];
726 char e =
static_cast<char>( htonl( 1 ) != 1 );
730 memcpy( &( wkb )[wkbPosition], &e, 1 );
732 memcpy( &( wkb )[wkbPosition], &type,
sizeof(
int ) );
733 wkbPosition +=
sizeof( int );
734 memcpy( &( wkb )[wkbPosition], &nLines,
sizeof(
int ) );
735 wkbPosition +=
sizeof( int );
737 for ( QList< QgsPolyline >::const_iterator it = lineCoordinates.constBegin(); it != lineCoordinates.constEnd(); ++it )
739 memcpy( &( wkb )[wkbPosition], &e, 1 );
741 memcpy( &( wkb )[wkbPosition], &lineType,
sizeof(
int ) );
742 wkbPosition +=
sizeof( int );
743 nPoints = it->size();
744 memcpy( &( wkb )[wkbPosition], &nPoints,
sizeof(
int ) );
745 wkbPosition +=
sizeof( int );
746 for ( QgsPolyline::const_iterator iter = it->begin(); iter != it->end(); ++iter )
751 memcpy( &( wkb )[wkbPosition], &x,
sizeof(
double ) );
752 wkbPosition +=
sizeof( double );
753 memcpy( &( wkb )[wkbPosition], &y,
sizeof(
double ) );
754 wkbPosition +=
sizeof( double );
759 memcpy( &( wkb )[wkbPosition], &z,
sizeof(
double ) );
760 wkbPosition +=
sizeof( double );
770QgsGeometry QgsOgcUtils::geometryFromGMLMultiPolygon(
const QDomElement &geometryElement )
773 QVector<QgsMultiPolyline> multiPolygonPoints;
774 QDomElement currentPolygonMemberElement;
775 QDomNodeList polygonList;
776 QDomElement currentPolygonElement;
778 QDomNodeList outerBoundaryList;
779 QDomElement currentOuterBoundaryElement;
780 QDomElement currentInnerBoundaryElement;
782 QDomNodeList exteriorList;
783 QDomElement currentExteriorElement;
784 QDomElement currentInteriorElement;
786 QDomNodeList linearRingNodeList;
787 QDomElement currentLinearRingElement;
789 QDomNodeList currentCoordinateList;
790 QDomNodeList currentPosList;
792 const QDomNodeList polygonMemberList = geometryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"polygonMember"_s );
794 for (
int i = 0; i < polygonMemberList.size(); ++i )
796 currentPolygonList.resize( 0 );
797 currentPolygonMemberElement = polygonMemberList.at( i ).toElement();
798 polygonList = currentPolygonMemberElement.elementsByTagNameNS(
GML_NAMESPACE, u
"Polygon"_s );
799 if ( polygonList.size() < 1 )
803 currentPolygonElement = polygonList.at( 0 ).toElement();
806 outerBoundaryList = currentPolygonElement.elementsByTagNameNS(
GML_NAMESPACE, u
"outerBoundaryIs"_s );
807 if ( !outerBoundaryList.isEmpty() )
809 currentOuterBoundaryElement = outerBoundaryList.at( 0 ).toElement();
812 linearRingNodeList = currentOuterBoundaryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"LinearRing"_s );
813 if ( linearRingNodeList.size() < 1 )
817 currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
818 currentCoordinateList = currentLinearRingElement.elementsByTagNameNS(
GML_NAMESPACE, u
"coordinates"_s );
819 if ( currentCoordinateList.size() < 1 )
823 if ( readGMLCoordinates( ringCoordinates, currentCoordinateList.at( 0 ).toElement() ) != 0 )
827 currentPolygonList.push_back( ringCoordinates );
830 const QDomNodeList innerBoundaryList = currentPolygonElement.elementsByTagNameNS(
GML_NAMESPACE, u
"innerBoundaryIs"_s );
831 for (
int j = 0; j < innerBoundaryList.size(); ++j )
834 currentInnerBoundaryElement = innerBoundaryList.at( j ).toElement();
835 linearRingNodeList = currentInnerBoundaryElement.elementsByTagNameNS(
GML_NAMESPACE, u
"LinearRing"_s );
836 if ( linearRingNodeList.size() < 1 )
840 currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
841 currentCoordinateList = currentLinearRingElement.elementsByTagNameNS(
GML_NAMESPACE, u
"coordinates"_s );
842 if ( currentCoordinateList.size() < 1 )
846 if ( readGMLCoordinates( ringCoordinates, currentCoordinateList.at( 0 ).toElement() ) != 0 )
850 currentPolygonList.push_back( ringCoordinates );
856 exteriorList = currentPolygonElement.elementsByTagNameNS(
GML_NAMESPACE, u
"exterior"_s );
857 if ( exteriorList.size() < 1 )
862 currentExteriorElement = exteriorList.at( 0 ).toElement();
865 linearRingNodeList = currentExteriorElement.elementsByTagNameNS(
GML_NAMESPACE, u
"LinearRing"_s );
866 if ( linearRingNodeList.size() < 1 )
870 currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
871 currentPosList = currentLinearRingElement.elementsByTagNameNS(
GML_NAMESPACE, u
"posList"_s );
872 if ( currentPosList.size() < 1 )
876 if ( readGMLPositions( ringPositions, currentPosList.at( 0 ).toElement() ) != 0 )
880 currentPolygonList.push_back( ringPositions );
883 const QDomNodeList interiorList = currentPolygonElement.elementsByTagNameNS(
GML_NAMESPACE, u
"interior"_s );
884 for (
int j = 0; j < interiorList.size(); ++j )
887 currentInteriorElement = interiorList.at( j ).toElement();
888 linearRingNodeList = currentInteriorElement.elementsByTagNameNS(
GML_NAMESPACE, u
"LinearRing"_s );
889 if ( linearRingNodeList.size() < 1 )
893 currentLinearRingElement = linearRingNodeList.at( 0 ).toElement();
894 currentPosList = currentLinearRingElement.elementsByTagNameNS(
GML_NAMESPACE, u
"posList"_s );
895 if ( currentPosList.size() < 1 )
899 if ( readGMLPositions( ringPositions, currentPosList.at( 0 ).toElement() ) != 0 )
903 currentPolygonList.push_back( ringPositions );
906 multiPolygonPoints.push_back( currentPolygonList );
909 int nPolygons = multiPolygonPoints.size();
911 return QgsGeometry();
913 const bool hasZ { !std::isnan( multiPolygonPoints.first().first().first().z() ) };
915 int size = 1 + 2 *
sizeof( int );
918 for (
auto it = multiPolygonPoints.constBegin(); it != multiPolygonPoints.constEnd(); ++it )
920 size += 1 + 2 *
sizeof( int );
921 for (
auto iter = it->begin(); iter != it->end(); ++iter )
923 size +=
static_cast<int>(
sizeof( int ) ) + ( hasZ ? 3 : 2 ) *
static_cast<int>( iter->size() *
sizeof(
double ) );
928 unsigned char *wkb =
new unsigned char[size];
930 char e =
static_cast<char>( htonl( 1 ) != 1 );
937 memcpy( &( wkb )[wkbPosition], &e, 1 );
939 memcpy( &( wkb )[wkbPosition], &type,
sizeof(
int ) );
940 wkbPosition +=
sizeof( int );
941 memcpy( &( wkb )[wkbPosition], &nPolygons,
sizeof(
int ) );
942 wkbPosition +=
sizeof( int );
946 for (
auto it = multiPolygonPoints.constBegin(); it != multiPolygonPoints.constEnd(); ++it )
948 memcpy( &( wkb )[wkbPosition], &e, 1 );
950 memcpy( &( wkb )[wkbPosition], &type,
sizeof(
int ) );
951 wkbPosition +=
sizeof( int );
953 memcpy( &( wkb )[wkbPosition], &nRings,
sizeof(
int ) );
954 wkbPosition +=
sizeof( int );
955 for (
auto iter = it->begin(); iter != it->end(); ++iter )
957 nPointsInRing = iter->size();
958 memcpy( &( wkb )[wkbPosition], &nPointsInRing,
sizeof(
int ) );
959 wkbPosition +=
sizeof( int );
960 for (
auto iterator = iter->begin(); iterator != iter->end(); ++iterator )
964 memcpy( &( wkb )[wkbPosition], &x,
sizeof(
double ) );
965 wkbPosition +=
sizeof( double );
966 memcpy( &( wkb )[wkbPosition], &y,
sizeof(
double ) );
967 wkbPosition +=
sizeof( double );
970 double z = iterator->z();
971 memcpy( &( wkb )[wkbPosition], &z,
sizeof(
double ) );
972 wkbPosition +=
sizeof( double );
983QDomElement QgsOgcUtils::filterElement( QDomDocument &doc, GMLVersion gmlVersion, FilterVersion filterVersion,
bool GMLUsed )
989 QDomAttr attr = doc.createAttribute( u
"xmlns:gml"_s );
994 filterElem.setAttributeNode( attr );
1000bool QgsOgcUtils::readGMLCoordinates(
QgsPolyline &coords,
const QDomElement &elem )
1002 QString coordSeparator = u
","_s;
1003 QString tupleSeparator = u
" "_s;
1008 if ( elem.hasAttribute( u
"cs"_s ) )
1010 coordSeparator = elem.attribute( u
"cs"_s );
1012 if ( elem.hasAttribute( u
"ts"_s ) )
1014 tupleSeparator = elem.attribute( u
"ts"_s );
1017 const QStringList tupels = elem.text().split( tupleSeparator, Qt::SkipEmptyParts );
1018 QStringList tuple_coords;
1020 bool conversionSuccess;
1022 QStringList::const_iterator it;
1023 for ( it = tupels.constBegin(); it != tupels.constEnd(); ++it )
1025 tuple_coords = ( *it ).split( coordSeparator, Qt::SkipEmptyParts );
1026 if ( tuple_coords.size() < 2 )
1030 x = tuple_coords.at( 0 ).toDouble( &conversionSuccess );
1031 if ( !conversionSuccess )
1035 y = tuple_coords.at( 1 ).toDouble( &conversionSuccess );
1036 if ( !conversionSuccess )
1040 if ( tuple_coords.size() > 2 )
1042 z = tuple_coords.at( 2 ).toDouble( &conversionSuccess );
1043 if ( !conversionSuccess )
1050 z = std::numeric_limits<double>::quiet_NaN();
1052 coords.append( QgsPoint( x, y, z ) );
1061 const QDomElement boxElem = boxNode.toElement();
1062 if ( boxElem.tagName() !=
"Box"_L1 )
1065 const QDomElement bElem = boxElem.firstChild().toElement();
1066 QString coordSeparator = u
","_s;
1067 QString tupleSeparator = u
" "_s;
1068 if ( bElem.hasAttribute( u
"cs"_s ) )
1070 coordSeparator = bElem.attribute( u
"cs"_s );
1072 if ( bElem.hasAttribute( u
"ts"_s ) )
1074 tupleSeparator = bElem.attribute( u
"ts"_s );
1077 const QString bString = bElem.text();
1078 bool ok1, ok2, ok3, ok4;
1079 const double xmin = bString.section( tupleSeparator, 0, 0 ).section( coordSeparator, 0, 0 ).toDouble( &ok1 );
1080 const double ymin = bString.section( tupleSeparator, 0, 0 ).section( coordSeparator, 1, 1 ).toDouble( &ok2 );
1081 const double xmax = bString.section( tupleSeparator, 1, 1 ).section( coordSeparator, 0, 0 ).toDouble( &ok3 );
1082 const double ymax = bString.section( tupleSeparator, 1, 1 ).section( coordSeparator, 1, 1 ).toDouble( &ok4 );
1084 if ( ok1 && ok2 && ok3 && ok4 )
1093bool QgsOgcUtils::readGMLPositions(
QgsPolyline &coords,
const QDomElement &elem )
1097 const QStringList pos = elem.text().split(
' ', Qt::SkipEmptyParts );
1099 bool conversionSuccess;
1100 const int posSize = pos.size();
1102 int srsDimension = 2;
1103 if ( elem.hasAttribute( u
"srsDimension"_s ) )
1105 srsDimension = elem.attribute( u
"srsDimension"_s ).toInt( &conversionSuccess );
1106 if ( !conversionSuccess )
1111 else if ( elem.hasAttribute( u
"dimension"_s ) )
1113 srsDimension = elem.attribute( u
"dimension"_s ).toInt( &conversionSuccess );
1114 if ( !conversionSuccess )
1120 for (
int i = 0; i < posSize / srsDimension; i++ )
1122 x = pos.at( i * srsDimension ).toDouble( &conversionSuccess );
1123 if ( !conversionSuccess )
1127 y = pos.at( i * srsDimension + 1 ).toDouble( &conversionSuccess );
1128 if ( !conversionSuccess )
1132 if ( srsDimension > 2 )
1134 z = pos.at( i * srsDimension + 2 ).toDouble( &conversionSuccess );
1135 if ( !conversionSuccess )
1142 z = std::numeric_limits<double>::quiet_NaN();
1144 coords.append( QgsPoint( x, y, z ) );
1154 const QDomElement envelopeElem = envelopeNode.toElement();
1155 if ( envelopeElem.tagName() !=
"Envelope"_L1 )
1158 const QDomNodeList lowerCornerList = envelopeElem.elementsByTagNameNS(
GML_NAMESPACE, u
"lowerCorner"_s );
1159 if ( lowerCornerList.size() < 1 )
1162 const QDomNodeList upperCornerList = envelopeElem.elementsByTagNameNS(
GML_NAMESPACE, u
"upperCorner"_s );
1163 if ( upperCornerList.size() < 1 )
1166 bool conversionSuccess;
1167 int srsDimension = 2;
1169 QDomElement elem = lowerCornerList.at( 0 ).toElement();
1170 if ( elem.hasAttribute( u
"srsDimension"_s ) )
1172 srsDimension = elem.attribute( u
"srsDimension"_s ).toInt( &conversionSuccess );
1173 if ( !conversionSuccess )
1178 else if ( elem.hasAttribute( u
"dimension"_s ) )
1180 srsDimension = elem.attribute( u
"dimension"_s ).toInt( &conversionSuccess );
1181 if ( !conversionSuccess )
1186 QString bString = elem.text();
1188 const double xmin = bString.section(
' ', 0, 0 ).toDouble( &conversionSuccess );
1189 if ( !conversionSuccess )
1191 const double ymin = bString.section(
' ', 1, 1 ).toDouble( &conversionSuccess );
1192 if ( !conversionSuccess )
1195 elem = upperCornerList.at( 0 ).toElement();
1196 if ( elem.hasAttribute( u
"srsDimension"_s ) )
1198 srsDimension = elem.attribute( u
"srsDimension"_s ).toInt( &conversionSuccess );
1199 if ( !conversionSuccess )
1204 else if ( elem.hasAttribute( u
"dimension"_s ) )
1206 srsDimension = elem.attribute( u
"dimension"_s ).toInt( &conversionSuccess );
1207 if ( !conversionSuccess )
1213 Q_UNUSED( srsDimension )
1215 bString = elem.text();
1216 const double xmax = bString.section(
' ', 0, 0 ).toDouble( &conversionSuccess );
1217 if ( !conversionSuccess )
1219 const double ymax = bString.section(
' ', 1, 1 ).toDouble( &conversionSuccess );
1220 if ( !conversionSuccess )
1238 return QDomElement();
1241 QDomElement boxElem = doc.createElement( u
"gml:Box"_s );
1242 if ( !srsName.isEmpty() )
1244 boxElem.setAttribute( u
"srsName"_s, srsName );
1246 QDomElement coordElem = doc.createElement( u
"gml:coordinates"_s );
1247 coordElem.setAttribute( u
"cs"_s, u
","_s );
1248 coordElem.setAttribute( u
"ts"_s, u
" "_s );
1250 QString coordString;
1259 const QDomText coordText = doc.createTextNode( coordString );
1260 coordElem.appendChild( coordText );
1261 boxElem.appendChild( coordElem );
1275 return QDomElement();
1278 QDomElement envElem = doc.createElement( u
"gml:Envelope"_s );
1279 if ( !srsName.isEmpty() )
1281 envElem.setAttribute( u
"srsName"_s, srsName );
1285 QDomElement lowerCornerElem = doc.createElement( u
"gml:lowerCorner"_s );
1289 const QDomText lowerCornerText = doc.createTextNode( posList );
1290 lowerCornerElem.appendChild( lowerCornerText );
1291 envElem.appendChild( lowerCornerElem );
1293 QDomElement upperCornerElem = doc.createElement( u
"gml:upperCorner"_s );
1297 const QDomText upperCornerText = doc.createTextNode( posList );
1298 upperCornerElem.appendChild( upperCornerText );
1299 envElem.appendChild( upperCornerElem );
1312 return QDomElement();
1315 QString cs = u
","_s;
1317 const QString ts = u
" "_s;
1319 QDomElement baseCoordElem;
1321 bool hasZValue =
false;
1323 const QByteArray wkb( geometry.
asWkb() );
1333 return QDomElement();
1346 baseCoordElem = doc.createElement( u
"gml:pos"_s );
1349 baseCoordElem = doc.createElement( u
"gml:posList"_s );
1356 baseCoordElem = doc.createElement( u
"gml:coordinates"_s );
1357 baseCoordElem.setAttribute( u
"cs"_s, cs );
1358 baseCoordElem.setAttribute( u
"ts"_s, ts );
1372 QDomElement pointElem = doc.createElement( u
"gml:Point"_s );
1373 if ( gmlVersion ==
GML_3_2_1 && !gmlIdBase.isEmpty() )
1374 pointElem.setAttribute( u
"gml:id"_s, gmlIdBase );
1375 if ( !srsName.isEmpty() )
1376 pointElem.setAttribute( u
"srsName"_s, srsName );
1377 QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1381 if ( invertAxisOrientation )
1395 const QDomText coordText = doc.createTextNode( coordString );
1397 coordElem.appendChild( coordText );
1399 coordElem.setAttribute( u
"srsDimension"_s, hasZValue ? u
"3"_s : u
"2"_s );
1400 pointElem.appendChild( coordElem );
1410 QDomElement multiPointElem = doc.createElement( u
"gml:MultiPoint"_s );
1411 if ( gmlVersion ==
GML_3_2_1 && !gmlIdBase.isEmpty() )
1412 multiPointElem.setAttribute( u
"gml:id"_s, gmlIdBase );
1413 if ( !srsName.isEmpty() )
1414 multiPointElem.setAttribute( u
"srsName"_s, srsName );
1419 for (
int idx = 0; idx < nPoints; ++idx )
1421 QDomElement pointMemberElem = doc.createElement( u
"gml:pointMember"_s );
1422 QDomElement pointElem = doc.createElement( u
"gml:Point"_s );
1423 if ( gmlVersion ==
GML_3_2_1 && !gmlIdBase.isEmpty() )
1424 pointElem.setAttribute( u
"gml:id"_s, gmlIdBase + u
".%1"_s.arg( idx + 1 ) );
1425 QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1431 if ( invertAxisOrientation )
1445 const QDomText coordText = doc.createTextNode( coordString );
1447 coordElem.appendChild( coordText );
1449 coordElem.setAttribute( u
"srsDimension"_s, hasZValue ? u
"3"_s : u
"2"_s );
1450 pointElem.appendChild( coordElem );
1453 pointMemberElem.appendChild( pointElem );
1454 multiPointElem.appendChild( pointMemberElem );
1456 return multiPointElem;
1465 QDomElement lineStringElem = doc.createElement( u
"gml:LineString"_s );
1466 if ( gmlVersion ==
GML_3_2_1 && !gmlIdBase.isEmpty() )
1467 lineStringElem.setAttribute( u
"gml:id"_s, gmlIdBase );
1468 if ( !srsName.isEmpty() )
1469 lineStringElem.setAttribute( u
"srsName"_s, srsName );
1475 QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1476 QString coordString;
1477 for (
int idx = 0; idx < nPoints; ++idx )
1486 if ( invertAxisOrientation )
1499 const QDomText coordText = doc.createTextNode( coordString );
1500 coordElem.appendChild( coordText );
1502 coordElem.setAttribute( u
"srsDimension"_s, hasZValue ? u
"3"_s : u
"2"_s );
1503 lineStringElem.appendChild( coordElem );
1504 return lineStringElem;
1513 QDomElement multiLineStringElem = doc.createElement( u
"gml:MultiLineString"_s );
1514 if ( gmlVersion ==
GML_3_2_1 && !gmlIdBase.isEmpty() )
1515 multiLineStringElem.setAttribute( u
"gml:id"_s, gmlIdBase );
1516 if ( !srsName.isEmpty() )
1517 multiLineStringElem.setAttribute( u
"srsName"_s, srsName );
1522 for (
int jdx = 0; jdx < nLines; jdx++ )
1524 QDomElement lineStringMemberElem = doc.createElement( u
"gml:lineStringMember"_s );
1525 QDomElement lineStringElem = doc.createElement( u
"gml:LineString"_s );
1526 if ( gmlVersion ==
GML_3_2_1 && !gmlIdBase.isEmpty() )
1527 lineStringElem.setAttribute( u
"gml:id"_s, gmlIdBase + u
".%1"_s.arg( jdx + 1 ) );
1534 QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1535 QString coordString;
1536 for (
int idx = 0; idx < nPoints; idx++ )
1545 if ( invertAxisOrientation )
1559 const QDomText coordText = doc.createTextNode( coordString );
1560 coordElem.appendChild( coordText );
1562 coordElem.setAttribute( u
"srsDimension"_s, hasZValue ? u
"3"_s : u
"2"_s );
1563 lineStringElem.appendChild( coordElem );
1564 lineStringMemberElem.appendChild( lineStringElem );
1565 multiLineStringElem.appendChild( lineStringMemberElem );
1567 return multiLineStringElem;
1576 QDomElement polygonElem = doc.createElement( u
"gml:Polygon"_s );
1577 if ( gmlVersion ==
GML_3_2_1 && !gmlIdBase.isEmpty() )
1578 polygonElem.setAttribute( u
"gml:id"_s, gmlIdBase );
1579 if ( !srsName.isEmpty() )
1580 polygonElem.setAttribute( u
"srsName"_s, srsName );
1586 if ( numRings == 0 )
1587 return QDomElement();
1589 for (
int idx = 0; idx < numRings; idx++ )
1591 QString boundaryName = ( gmlVersion ==
GML_2_1_2 ) ?
"gml:outerBoundaryIs" :
"gml:exterior";
1594 boundaryName = ( gmlVersion ==
GML_2_1_2 ) ?
"gml:innerBoundaryIs" :
"gml:interior";
1596 QDomElement boundaryElem = doc.createElement( boundaryName );
1597 QDomElement ringElem = doc.createElement( u
"gml:LinearRing"_s );
1602 QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1603 QString coordString;
1604 for (
int jdx = 0; jdx < nPoints; jdx++ )
1613 if ( invertAxisOrientation )
1627 const QDomText coordText = doc.createTextNode( coordString );
1628 coordElem.appendChild( coordText );
1630 coordElem.setAttribute( u
"srsDimension"_s, hasZValue ? u
"3"_s : u
"2"_s );
1631 ringElem.appendChild( coordElem );
1632 boundaryElem.appendChild( ringElem );
1633 polygonElem.appendChild( boundaryElem );
1645 QDomElement multiPolygonElem = doc.createElement( u
"gml:MultiPolygon"_s );
1646 if ( gmlVersion ==
GML_3_2_1 && !gmlIdBase.isEmpty() )
1647 multiPolygonElem.setAttribute( u
"gml:id"_s, gmlIdBase );
1648 if ( !srsName.isEmpty() )
1649 multiPolygonElem.setAttribute( u
"srsName"_s, srsName );
1652 wkbPtr >> numPolygons;
1654 for (
int kdx = 0; kdx < numPolygons; kdx++ )
1656 QDomElement polygonMemberElem = doc.createElement( u
"gml:polygonMember"_s );
1657 QDomElement polygonElem = doc.createElement( u
"gml:Polygon"_s );
1658 if ( gmlVersion ==
GML_3_2_1 && !gmlIdBase.isEmpty() )
1659 polygonElem.setAttribute( u
"gml:id"_s, gmlIdBase + u
".%1"_s.arg( kdx + 1 ) );
1666 for (
int idx = 0; idx < numRings; idx++ )
1668 QString boundaryName = ( gmlVersion ==
GML_2_1_2 ) ?
"gml:outerBoundaryIs" :
"gml:exterior";
1671 boundaryName = ( gmlVersion ==
GML_2_1_2 ) ?
"gml:innerBoundaryIs" :
"gml:interior";
1673 QDomElement boundaryElem = doc.createElement( boundaryName );
1674 QDomElement ringElem = doc.createElement( u
"gml:LinearRing"_s );
1679 QDomElement coordElem = baseCoordElem.cloneNode().toElement();
1680 QString coordString;
1681 for (
int jdx = 0; jdx < nPoints; jdx++ )
1690 if ( invertAxisOrientation )
1704 const QDomText coordText = doc.createTextNode( coordString );
1705 coordElem.appendChild( coordText );
1707 coordElem.setAttribute( u
"srsDimension"_s, hasZValue ? u
"3"_s : u
"2"_s );
1708 ringElem.appendChild( coordElem );
1709 boundaryElem.appendChild( ringElem );
1710 polygonElem.appendChild( boundaryElem );
1711 polygonMemberElem.appendChild( polygonElem );
1712 multiPolygonElem.appendChild( polygonMemberElem );
1715 return multiPolygonElem;
1718 return QDomElement();
1724 return QDomElement();
1730 return geometryToGML( geometry, doc, u
"GML2"_s, precision );
1733QDomElement QgsOgcUtils::createGMLCoordinates(
const QgsPolylineXY &points, QDomDocument &doc )
1735 QDomElement coordElem = doc.createElement( u
"gml:coordinates"_s );
1736 coordElem.setAttribute( u
"cs"_s, u
","_s );
1737 coordElem.setAttribute( u
"ts"_s, u
" "_s );
1739 QString coordString;
1740 QVector<QgsPointXY>::const_iterator pointIt = points.constBegin();
1741 for ( ; pointIt != points.constEnd(); ++pointIt )
1743 if ( pointIt != points.constBegin() )
1752 const QDomText coordText = doc.createTextNode( coordString );
1753 coordElem.appendChild( coordText );
1757QDomElement QgsOgcUtils::createGMLPositions(
const QgsPolylineXY &points, QDomDocument &doc )
1759 QDomElement posElem = doc.createElement( u
"gml:pos"_s );
1760 if ( points.size() > 1 )
1761 posElem = doc.createElement( u
"gml:posList"_s );
1762 posElem.setAttribute( u
"srsDimension"_s, u
"2"_s );
1764 QString coordString;
1765 QVector<QgsPointXY>::const_iterator pointIt = points.constBegin();
1766 for ( ; pointIt != points.constEnd(); ++pointIt )
1768 if ( pointIt != points.constBegin() )
1777 const QDomText coordText = doc.createTextNode( coordString );
1778 posElem.appendChild( coordText );
1786 if ( fillElement.isNull() || !fillElement.hasChildNodes() )
1794 QDomElement cssElem = fillElement.firstChildElement( u
"CssParameter"_s );
1795 while ( !cssElem.isNull() )
1797 cssName = cssElem.attribute( u
"name"_s, u
"not_found"_s );
1798 if ( cssName !=
"not_found"_L1 )
1800 elemText = cssElem.text();
1801 if ( cssName ==
"fill"_L1 )
1803 color.setNamedColor( elemText );
1805 else if ( cssName ==
"fill-opacity"_L1 )
1808 const double opacity = elemText.toDouble( &ok );
1811 color.setAlphaF( opacity );
1816 cssElem = cssElem.nextSiblingElement( u
"CssParameter"_s );
1830 if ( element.isNull() || !element.hasChildNodes() )
1837 if ( element.firstChild().nodeType() == QDomNode::TextNode )
1847 QDomElement childElem = element.firstChildElement();
1848 while ( !childElem.isNull() )
1860 if ( !expr->d->mRootNode )
1862 expr->d->mRootNode.reset( node );
1869 childElem = childElem.nextSiblingElement();
1873 expr->d->mExp = expr->
dump();
1881 BINARY_OPERATORS_TAG_NAMES_MAP,
1902static int binaryOperatorFromTagName(
const QString &tagName )
1904 return BINARY_OPERATORS_TAG_NAMES_MAP()->value( tagName, -1 );
1911 return u
"PropertyIsLike"_s;
1913 return BINARY_OPERATORS_TAG_NAMES_MAP()->key( op, QString() );
1916static bool isBinaryOperator(
const QString &tagName )
1918 return binaryOperatorFromTagName( tagName ) >= 0;
1922static bool isSpatialOperator(
const QString &tagName )
1924 static QStringList spatialOps;
1925 if ( spatialOps.isEmpty() )
1927 spatialOps << u
"BBOX"_s << u
"Intersects"_s << u
"Contains"_s << u
"Crosses"_s << u
"Equals"_s << u
"Disjoint"_s << u
"Overlaps"_s << u
"Touches"_s << u
"Within"_s;
1930 return spatialOps.contains( tagName );
1936 QgsExpressionNode *node = utils.nodeFromOgcFilter( element );
1937 errorMessage = utils.errorMessage();
1944 QgsExpressionNodeBinaryOperator *node = utils.nodeBinaryOperatorFromOgcFilter( element );
1945 errorMessage = utils.errorMessage();
1952 QgsExpressionNodeFunction *node = utils.nodeSpatialOperatorFromOgcFilter( element );
1953 errorMessage = utils.errorMessage();
1960 QgsExpressionNodeUnaryOperator *node = utils.nodeNotFromOgcFilter( element );
1961 errorMessage = utils.errorMessage();
1968 QgsExpressionNodeFunction *node = utils.nodeFunctionFromOgcFilter( element );
1969 errorMessage = utils.errorMessage();
1976 QgsExpressionNode *node = utils.nodeLiteralFromOgcFilter( element );
1977 errorMessage = utils.errorMessage();
1984 QgsExpressionNodeColumnRef *node = utils.nodeColumnRefFromOgcFilter( element );
1985 errorMessage = utils.errorMessage();
1989QgsExpressionNode *QgsOgcUtils::nodeIsBetweenFromOgcFilter( QDomElement &element, QString &errorMessage )
1992 QgsExpressionNode *node = utils.nodeIsBetweenFromOgcFilter( element );
1993 errorMessage = utils.errorMessage();
2000 QgsExpressionNodeBinaryOperator *node = utils.nodePropertyIsNullFromOgcFilter( element );
2001 errorMessage = utils.errorMessage();
2011 return expressionToOgcFilter( exp, doc,
GML_2_1_2,
FILTER_OGC_1_0, QString(), QString(), u
"geometry"_s, QString(),
false,
false, errorMessage );
2021 return doc.createElementNS(
SE_NAMESPACE, u
"se:ElseFilter"_s );
2030 const QString &namespacePrefix,
2031 const QString &namespaceURI,
2032 const QString &geometryName,
2033 const QString &srsName,
2034 bool honourAxisOrientation,
2035 bool invertAxisOrientation,
2036 QString *errorMessage,
2037 const QMap<QString, QString> &fieldNameToXPathMap,
2038 const QMap<QString, QString> &namespacePrefixToUriMap
2042 return QDomElement();
2049 utils( doc, gmlVersion, filterVersion, namespacePrefix, namespaceURI, geometryName, srsName, honourAxisOrientation, invertAxisOrientation, fieldNameToXPathMap, namespacePrefixToUriMap );
2053 if ( exprRootElem.isNull() )
2054 return QDomElement();
2056 QDomElement filterElem = filterElement( doc, gmlVersion, filterVersion, utils.
GMLNamespaceUsed() );
2058 if ( !namespacePrefix.isEmpty() && !namespaceURI.isEmpty() )
2060 QDomAttr attr = doc.createAttribute( u
"xmlns:"_s + namespacePrefix );
2061 attr.setValue( namespaceURI );
2062 filterElem.setAttributeNode( attr );
2065 filterElem.appendChild( exprRootElem );
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
2091 return QDomElement();
2093 QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, QString(), QString(), geometryName, srsName, honourAxisOrientation, invertAxisOrientation, fieldNameToXPathMap, namespacePrefixToUriMap );
2101 if ( !exprRootElem.isNull() )
2103 if ( requiresFilterElement )
2105 QDomElement filterElem = filterElement( doc, gmlVersion, filterVersion, utils.
GMLNamespaceUsed() );
2107 filterElem.appendChild( exprRootElem );
2110 return exprRootElem;
2113 return QDomElement();
2121 const QList<LayerProperties> &layerProperties,
2122 bool honourAxisOrientation,
2123 bool invertAxisOrientation,
2124 const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename,
2125 QString *errorMessage,
2126 const QMap<QString, QString> &fieldNameToXPathMap,
2127 const QMap<QString, QString> &namespacePrefixToUriMap
2131 return QDomElement();
2134 utils( doc, gmlVersion, filterVersion, layerProperties, honourAxisOrientation, invertAxisOrientation, mapUnprefixedTypenameToPrefixedTypename, fieldNameToXPathMap, namespacePrefixToUriMap );
2138 if ( exprRootElem.isNull() )
2139 return QDomElement();
2141 QDomElement filterElem = filterElement( doc, gmlVersion, filterVersion, utils.
GMLNamespaceUsed() );
2143 QSet<QString> setNamespaceURI;
2146 if ( !props.mNamespacePrefix.isEmpty() && !props.mNamespaceURI.isEmpty() && !setNamespaceURI.contains( props.mNamespaceURI ) )
2148 setNamespaceURI.insert( props.mNamespaceURI );
2149 QDomAttr attr = doc.createAttribute( u
"xmlns:"_s + props.mNamespacePrefix );
2150 attr.setValue( props.mNamespaceURI );
2151 filterElem.setAttributeNode( attr );
2154 filterElem.appendChild( exprRootElem );
2162 if ( gmlGeomType ==
"Point"_L1 )
2164 if ( gmlGeomType ==
"LineString"_L1 || gmlGeomType ==
"Curve"_L1 )
2166 if ( gmlGeomType ==
"Polygon"_L1 || gmlGeomType ==
"Surface"_L1 )
2168 if ( gmlGeomType ==
"MultiPoint"_L1 )
2170 if ( gmlGeomType ==
"MultiLineString"_L1 || gmlGeomType ==
"MultiCurve"_L1 )
2172 if ( gmlGeomType ==
"MultiPolygon"_L1 || gmlGeomType ==
"MultiSurface"_L1 )
2198 mErrorMessage = QObject::tr(
"Node type not supported: %1" ).arg( node->
nodeType() );
2199 return QDomElement();
2206 if ( !mErrorMessage.isEmpty() )
2207 return QDomElement();
2210 switch ( node->
op() )
2213 uoElem = mDoc.createElement( mFilterPrefix +
":Literal" );
2218 uoElem.appendChild( mDoc.createTextNode(
"-" + operandElem.text() ) );
2219 mDoc.removeChild( operandElem );
2223 mErrorMessage = QObject::tr(
"This use of unary operator not implemented yet" );
2224 return QDomElement();
2228 uoElem = mDoc.createElement( mFilterPrefix +
":Not" );
2229 uoElem.appendChild( operandElem );
2233 mErrorMessage = QObject::tr(
"Unary operator '%1' not implemented yet" ).arg( node->
text() );
2234 return QDomElement();
2248 QDomElement funcElem = mDoc.createElement( mFilterPrefix + u
":Function"_s );
2249 funcElem.setAttribute( u
"name"_s, u
"Concatenate"_s );
2251 std::function<bool(
const QgsExpressionNode * )> appendParts = [&](
const QgsExpressionNode *n ) ->
bool {
2254 const auto *binNode =
static_cast<const QgsExpressionNodeBinaryOperator *
>( n );
2257 return appendParts( binNode->opLeft() ) && appendParts( binNode->opRight() );
2264 if ( subElem.isNull() )
2268 funcElem.appendChild( subElem );
2272 if ( !appendParts( node ) )
2274 return QDomElement();
2280 if ( !mErrorMessage.isEmpty() )
2281 return QDomElement();
2288 const QgsExpressionNodeLiteral *rightLit =
static_cast<const QgsExpressionNodeLiteral *
>( node->
opRight() );
2291 QDomElement elem = mDoc.createElement( mFilterPrefix +
":PropertyIsNull" );
2292 elem.appendChild( leftElem );
2296 QDomElement notElem = mDoc.createElement( mFilterPrefix +
":Not" );
2297 notElem.appendChild( elem );
2310 if ( !mErrorMessage.isEmpty() )
2311 return QDomElement();
2314 const QString opText = binaryOperatorToTagName( op );
2315 if ( opText.isEmpty() )
2319 mErrorMessage = QObject::tr(
"Binary operator %1 not implemented yet" ).arg( node->
text() );
2320 return QDomElement();
2323 QDomElement boElem = mDoc.createElement( mFilterPrefix +
":" + opText );
2328 boElem.setAttribute( u
"matchCase"_s, u
"false"_s );
2331 boElem.setAttribute( u
"wildCard"_s, u
"%"_s );
2332 boElem.setAttribute( u
"singleChar"_s, u
"_"_s );
2334 boElem.setAttribute( u
"escape"_s, u
"\\"_s );
2336 boElem.setAttribute( u
"escapeChar"_s, u
"\\"_s );
2339 boElem.appendChild( leftElem );
2340 boElem.appendChild( rightElem );
2347 Q_UNUSED( expression )
2350 switch ( node->
value().userType() )
2352 case QMetaType::Type::Int:
2353 value = QString::number( node->
value().toInt() );
2355 case QMetaType::Type::Double:
2358 case QMetaType::Type::QString:
2359 value = node->
value().toString();
2361 case QMetaType::Type::QDate:
2362 value = node->
value().toDate().toString( Qt::ISODate );
2364 case QMetaType::Type::QDateTime:
2365 value = node->
value().toDateTime().toString( Qt::ISODate );
2369 mErrorMessage = QObject::tr(
"Literal type not supported: %1" ).arg(
static_cast<QMetaType::Type
>( node->
value().userType() ) );
2370 return QDomElement();
2373 QDomElement litElem = mDoc.createElement( mFilterPrefix +
":Literal" );
2374 litElem.appendChild( mDoc.createTextNode( value ) );
2381 Q_UNUSED( expression )
2383 QDomElement propElem = mDoc.createElement( mFilterPrefix +
":" + mPropertyName );
2384 if ( !mFieldNameToXPathMap.isEmpty() )
2386 const auto iterFieldName = mFieldNameToXPathMap.constFind( node->
name() );
2387 if ( iterFieldName != mFieldNameToXPathMap.constEnd() )
2389 const QString xpath( *iterFieldName );
2391 if ( !mNamespacePrefixToUriMap.isEmpty() )
2393 const QStringList parts = xpath.split(
'/' );
2394 QSet<QString> setNamespacePrefix;
2395 for (
const QString &part : std::as_const( parts ) )
2397 const QStringList subparts = part.split(
':' );
2398 if ( subparts.size() == 2 && !setNamespacePrefix.contains( subparts[0] ) )
2400 const auto iterNamespacePrefix = mNamespacePrefixToUriMap.constFind( subparts[0] );
2401 if ( iterNamespacePrefix != mNamespacePrefixToUriMap.constEnd() )
2403 setNamespacePrefix.insert( subparts[0] );
2404 QDomAttr attr = mDoc.createAttribute( u
"xmlns:"_s + subparts[0] );
2405 attr.setValue( *iterNamespacePrefix );
2406 propElem.setAttributeNode( attr );
2412 propElem.appendChild( mDoc.createTextNode( xpath ) );
2417 QString columnRef( node->
name() );
2418 if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
2419 columnRef = mNamespacePrefix + u
":"_s + columnRef;
2420 propElem.appendChild( mDoc.createTextNode( columnRef ) );
2427 if ( node->
list()->
list().size() == 1 )
2431 QDomElement eqElem = mDoc.createElement( mFilterPrefix +
":PropertyIsEqualTo" );
2432 eqElem.appendChild( leftNode );
2433 eqElem.appendChild( firstListNode );
2436 QDomElement notElem = mDoc.createElement( mFilterPrefix +
":Not" );
2437 notElem.appendChild( eqElem );
2443 QDomElement orElem = mDoc.createElement( mFilterPrefix +
":Or" );
2446 const auto constList = node->
list()->
list();
2447 for ( QgsExpressionNode *n : constList )
2450 if ( !mErrorMessage.isEmpty() )
2451 return QDomElement();
2453 QDomElement eqElem = mDoc.createElement( mFilterPrefix +
":PropertyIsEqualTo" );
2454 eqElem.appendChild( leftNode.cloneNode() );
2455 eqElem.appendChild( listNode );
2457 orElem.appendChild( eqElem );
2462 QDomElement notElem = mDoc.createElement( mFilterPrefix +
":Not" );
2463 notElem.appendChild( orElem );
2472 BINARY_SPATIAL_OPS_MAP,
2474 { {
"disjoint"_L1,
"Disjoint"_L1 },
2475 {
"intersects"_L1,
"Intersects"_L1 },
2476 {
"touches"_L1,
"Touches"_L1 },
2477 {
"crosses"_L1,
"Crosses"_L1 },
2478 {
"contains"_L1,
"Contains"_L1 },
2479 {
"overlaps"_L1,
"Overlaps"_L1 },
2480 {
"within"_L1,
"Within"_L1 } }
2484static bool isBinarySpatialOperator(
const QString &fnName )
2486 return BINARY_SPATIAL_OPS_MAP()->contains( fnName );
2489static QString tagNameForSpatialOperator(
const QString &fnName )
2491 return BINARY_SPATIAL_OPS_MAP()->value( fnName );
2513 if ( fnDef->
name() ==
"geom_from_wkt"_L1 )
2515 const QList<QgsExpressionNode *> &args = fnNode->
args()->
list();
2531 if ( fd->
name() ==
"intersects_bbox"_L1 )
2533 QList<QgsExpressionNode *> argNodes = node->
args()->
list();
2534 Q_ASSERT( argNodes.count() == 2 );
2536 const QgsGeometry geom = geometryFromConstExpr( argNodes[1] );
2537 if ( !geom.
isNull() && isGeometryColumn( argNodes[0] ) )
2546 QDomElement funcElem = mDoc.createElement( mFilterPrefix +
":BBOX" );
2548 if ( !mGeometryName.isEmpty() )
2551 QDomElement geomProperty = mDoc.createElement( mFilterPrefix +
":" + mPropertyName );
2552 QString columnRef( mGeometryName );
2553 if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
2554 columnRef = mNamespacePrefix + u
":"_s + columnRef;
2555 geomProperty.appendChild( mDoc.createTextNode( columnRef ) );
2557 funcElem.appendChild( geomProperty );
2559 funcElem.appendChild( elemBox );
2564 mErrorMessage = QObject::tr(
"<BBOX> is currently supported only in form: bbox(@geometry, geomFromWKT('…'))" );
2565 return QDomElement();
2569 if ( isBinarySpatialOperator( fd->
name() ) )
2571 QList<QgsExpressionNode *> argNodes = node->
args()->
list();
2572 Q_ASSERT( argNodes.count() == 2 );
2574 QgsExpressionNode *otherNode =
nullptr;
2575 if ( isGeometryColumn( argNodes[0] ) )
2576 otherNode = argNodes[1];
2577 else if ( isGeometryColumn( argNodes[1] ) )
2578 otherNode = argNodes[0];
2581 mErrorMessage = QObject::tr(
"Unable to translate spatial operator: at least one must refer to geometry." );
2582 return QDomElement();
2585 QDomElement otherGeomElem;
2590 mErrorMessage = QObject::tr(
"spatial operator: the other operator must be a geometry constructor function" );
2591 return QDomElement();
2594 const QgsExpressionNodeFunction *otherFn =
static_cast<const QgsExpressionNodeFunction *
>( otherNode );
2596 if ( otherFnDef->
name() ==
"geom_from_wkt"_L1 )
2598 QgsExpressionNode *firstFnArg = otherFn->
args()->
list()[0];
2601 mErrorMessage = QObject::tr(
"geom_from_wkt: argument must be string literal" );
2602 return QDomElement();
2604 const QString wkt =
static_cast<const QgsExpressionNodeLiteral *
>( firstFnArg )->value().toString();
2606 otherGeomElem =
QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation, u
"qgis_id_geom_%1"_s.arg( mGeomId ) );
2607 if ( otherGeomElem.isNull() )
2609 mErrorMessage = QObject::tr(
"geom_from_wkt: unable to generate GML from wkt geometry" );
2610 return QDomElement();
2614 else if ( otherFnDef->
name() ==
"geom_from_gml"_L1 )
2616 QgsExpressionNode *firstFnArg = otherFn->
args()->
list()[0];
2619 mErrorMessage = QObject::tr(
"geom_from_gml: argument must be string literal" );
2620 return QDomElement();
2623 QDomDocument geomDoc;
2624 const QString gml =
static_cast<const QgsExpressionNodeLiteral *
>( firstFnArg )->value().toString();
2626 const QString xml = u
"<tmp xmlns:gml=\"%1\">%2</tmp>"_s.arg(
GML_NAMESPACE, gml );
2627 if ( !geomDoc.setContent( xml,
true ) )
2629 mErrorMessage = QObject::tr(
"geom_from_gml: unable to parse XML" );
2630 return QDomElement();
2633 const QDomNode geomNode = mDoc.importNode( geomDoc.documentElement().firstChildElement(),
true );
2634 otherGeomElem = geomNode.toElement();
2639 otherGeomElem =
QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, mSrsName, mInvertAxisOrientation, u
"qgis_id_geom_%1"_s.arg( mGeomId ) );
2640 if ( otherGeomElem.
isNull() )
2642 mErrorMessage = QObject::tr(
"geom from static value: unable to generate GML from static variable" );
2643 return QDomElement();
2649 mErrorMessage = QObject::tr(
"spatial operator: unknown geometry constructor function" );
2650 return QDomElement();
2655 QDomElement funcElem = mDoc.createElement( mFilterPrefix +
":" + tagNameForSpatialOperator( fd->
name() ) );
2656 QDomElement geomProperty = mDoc.createElement( mFilterPrefix +
":" + mPropertyName );
2657 QString columnRef( mGeometryName );
2658 if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
2659 columnRef = mNamespacePrefix + u
":"_s + columnRef;
2660 geomProperty.appendChild( mDoc.createTextNode( columnRef ) );
2661 funcElem.appendChild( geomProperty );
2662 funcElem.appendChild( otherGeomElem );
2666 if ( fd->
isStatic( node, expression, context ) )
2668 const QVariant result = fd->
run( node->
args(), context, expression, node );
2669 const QgsExpressionNodeLiteral literal( result );
2670 return expressionLiteralToOgcFilter( &literal, expression, context );
2675 mErrorMessage = QObject::tr(
"Special columns/constants are not supported." );
2676 return QDomElement();
2680 QDomElement funcElem = mDoc.createElement( mFilterPrefix +
":Function" );
2681 funcElem.setAttribute( u
"name"_s, fd->
name() );
2682 const auto constList = node->
args()->
list();
2683 for ( QgsExpressionNode *n : constList )
2686 if ( !mErrorMessage.isEmpty() )
2687 return QDomElement();
2689 funcElem.appendChild( childElem );
2701 const QList<QgsOgcUtils::LayerProperties> &layerProperties,
2702 bool honourAxisOrientation,
2703 bool invertAxisOrientation,
2704 const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename,
2705 const QMap<QString, QString> &fieldNameToXPathMap,
2706 const QMap<QString, QString> &namespacePrefixToUriMap
2709 , mGMLVersion( gmlVersion )
2710 , mFilterVersion( filterVersion )
2711 , mLayerProperties( layerProperties )
2712 , mHonourAxisOrientation( honourAxisOrientation )
2713 , mInvertAxisOrientation( invertAxisOrientation )
2714 , mFilterPrefix( ( filterVersion ==
QgsOgcUtils::FILTER_FES_2_0 ) ?
"fes" :
"ogc" )
2715 , mPropertyName( ( filterVersion ==
QgsOgcUtils::FILTER_FES_2_0 ) ?
"ValueReference" :
"PropertyName" )
2716 , mMapUnprefixedTypenameToPrefixedTypename( mapUnprefixedTypenameToPrefixedTypename )
2717 , mFieldNameToXPathMap( fieldNameToXPathMap )
2718 , mNamespacePrefixToUriMap( namespacePrefixToUriMap )
2743 mErrorMessage = QObject::tr(
"Node type not supported: %1" ).arg( node->
nodeType() );
2744 return QDomElement();
2752 if ( !mErrorMessage.isEmpty() )
2753 return QDomElement();
2756 switch ( node->
op() )
2759 uoElem = mDoc.createElement( mFilterPrefix +
":Literal" );
2764 uoElem.appendChild( mDoc.createTextNode(
"-" + operandElem.text() ) );
2765 mDoc.removeChild( operandElem );
2769 mErrorMessage = QObject::tr(
"This use of unary operator not implemented yet" );
2770 return QDomElement();
2774 uoElem = mDoc.createElement( mFilterPrefix +
":Not" );
2775 uoElem.appendChild( operandElem );
2780 return QDomElement();
2790 if ( !mErrorMessage.isEmpty() )
2791 return QDomElement();
2800 const QgsSQLStatement::NodeLiteral *rightLit =
static_cast<const QgsSQLStatement::NodeLiteral *
>( node->
opRight() );
2803 QDomElement elem = mDoc.createElement( mFilterPrefix +
":PropertyIsNull" );
2804 elem.appendChild( leftElem );
2808 QDomElement notElem = mDoc.createElement( mFilterPrefix +
":Not" );
2809 notElem.appendChild( elem );
2822 if ( !mErrorMessage.isEmpty() )
2823 return QDomElement();
2832 opText = u
"PropertyIsEqualTo"_s;
2834 opText = u
"PropertyIsNotEqualTo"_s;
2836 opText = u
"PropertyIsLessThanOrEqualTo"_s;
2838 opText = u
"PropertyIsGreaterThanOrEqualTo"_s;
2840 opText = u
"PropertyIsLessThan"_s;
2842 opText = u
"PropertyIsGreaterThan"_s;
2844 opText = u
"PropertyIsLike"_s;
2846 opText = u
"PropertyIsLike"_s;
2848 if ( opText.isEmpty() )
2852 return QDomElement();
2855 QDomElement boElem = mDoc.createElement( mFilterPrefix +
":" + opText );
2860 boElem.setAttribute( u
"matchCase"_s, u
"false"_s );
2863 boElem.setAttribute( u
"wildCard"_s, u
"%"_s );
2864 boElem.setAttribute( u
"singleChar"_s, u
"_"_s );
2866 boElem.setAttribute( u
"escape"_s, u
"\\"_s );
2868 boElem.setAttribute( u
"escapeChar"_s, u
"\\"_s );
2871 boElem.appendChild( leftElem );
2872 boElem.appendChild( rightElem );
2880 switch ( node->
value().userType() )
2882 case QMetaType::Type::Int:
2883 value = QString::number( node->
value().toInt() );
2885 case QMetaType::Type::LongLong:
2886 value = QString::number( node->
value().toLongLong() );
2888 case QMetaType::Type::Double:
2891 case QMetaType::Type::QString:
2892 value = node->
value().toString();
2896 mErrorMessage = QObject::tr(
"Literal type not supported: %1" ).arg(
static_cast<QMetaType::Type
>( node->
value().userType() ) );
2897 return QDomElement();
2900 QDomElement litElem = mDoc.createElement( mFilterPrefix +
":Literal" );
2901 litElem.appendChild( mDoc.createTextNode( value ) );
2908 QDomElement propElem = mDoc.createElement( mFilterPrefix +
":" + mPropertyName );
2909 if ( node->
tableName().isEmpty() || mLayerProperties.size() == 1 )
2911 if ( !mFieldNameToXPathMap.isEmpty() )
2913 const auto iterFieldName = mFieldNameToXPathMap.constFind( node->
name() );
2914 if ( iterFieldName != mFieldNameToXPathMap.constEnd() )
2916 const QString xpath( *iterFieldName );
2918 if ( !mNamespacePrefixToUriMap.isEmpty() )
2920 const QStringList parts = xpath.split(
'/' );
2921 QSet<QString> setNamespacePrefix;
2922 for (
const QString &part : std::as_const( parts ) )
2924 const QStringList subparts = part.split(
':' );
2925 if ( subparts.size() == 2 && !setNamespacePrefix.contains( subparts[0] ) )
2927 const auto iterNamespacePrefix = mNamespacePrefixToUriMap.constFind( subparts[0] );
2928 if ( iterNamespacePrefix != mNamespacePrefixToUriMap.constEnd() )
2930 setNamespacePrefix.insert( subparts[0] );
2931 QDomAttr attr = mDoc.createAttribute( u
"xmlns:"_s + subparts[0] );
2932 attr.setValue( *iterNamespacePrefix );
2933 propElem.setAttributeNode( attr );
2939 propElem.appendChild( mDoc.createTextNode( xpath ) );
2944 if ( mLayerProperties.size() == 1 && !mLayerProperties[0].mNamespacePrefix.isEmpty() && !mLayerProperties[0].mNamespaceURI.isEmpty() )
2945 propElem.appendChild( mDoc.createTextNode( mLayerProperties[0].mNamespacePrefix + u
":"_s + node->
name() ) );
2947 propElem.appendChild( mDoc.createTextNode( node->
name() ) );
2951 QString tableName( mMapTableAliasToNames[node->
tableName()] );
2952 if ( mMapUnprefixedTypenameToPrefixedTypename.contains( tableName ) )
2953 tableName = mMapUnprefixedTypenameToPrefixedTypename[tableName];
2954 propElem.appendChild( mDoc.createTextNode( tableName +
"/" + node->
name() ) );
2961 if ( node->
list()->
list().size() == 1 )
2965 QDomElement eqElem = mDoc.createElement( mFilterPrefix +
":PropertyIsEqualTo" );
2966 eqElem.appendChild( leftNode );
2967 eqElem.appendChild( firstListNode );
2970 QDomElement notElem = mDoc.createElement( mFilterPrefix +
":Not" );
2971 notElem.appendChild( eqElem );
2977 QDomElement orElem = mDoc.createElement( mFilterPrefix +
":Or" );
2980 const auto constList = node->
list()->
list();
2981 for ( QgsSQLStatement::Node *n : constList )
2984 if ( !mErrorMessage.isEmpty() )
2985 return QDomElement();
2987 QDomElement eqElem = mDoc.createElement( mFilterPrefix +
":PropertyIsEqualTo" );
2988 eqElem.appendChild( leftNode.cloneNode() );
2989 eqElem.appendChild( listNode );
2991 orElem.appendChild( eqElem );
2996 QDomElement notElem = mDoc.createElement( mFilterPrefix +
":Not" );
2997 notElem.appendChild( orElem );
3006 QDomElement elem = mDoc.createElement( mFilterPrefix +
":PropertyIsBetween" );
3008 QDomElement lowerBoundary = mDoc.createElement( mFilterPrefix +
":LowerBoundary" );
3010 elem.appendChild( lowerBoundary );
3011 QDomElement upperBoundary = mDoc.createElement( mFilterPrefix +
":UpperBoundary" );
3013 elem.appendChild( upperBoundary );
3017 QDomElement notElem = mDoc.createElement( mFilterPrefix +
":Not" );
3018 notElem.appendChild( elem );
3025static QString mapBinarySpatialToOgc(
const QString &name )
3027 QString nameCompare( name );
3028 if ( name.size() > 3 && QStringView { name }.mid( 0, 3 ).toString().compare(
"ST_"_L1, Qt::CaseInsensitive ) == 0 )
3029 nameCompare = name.mid( 3 );
3030 QStringList spatialOps;
3031 spatialOps << u
"BBOX"_s << u
"Intersects"_s << u
"Contains"_s << u
"Crosses"_s << u
"Equals"_s << u
"Disjoint"_s << u
"Overlaps"_s << u
"Touches"_s << u
"Within"_s;
3032 const auto constSpatialOps = spatialOps;
3033 for ( QString op : constSpatialOps )
3035 if ( nameCompare.compare( op, Qt::CaseInsensitive ) == 0 )
3041static QString mapTernarySpatialToOgc(
const QString &name )
3043 QString nameCompare( name );
3044 if ( name.size() > 3 && QStringView { name }.mid( 0, 3 ).compare(
"ST_"_L1, Qt::CaseInsensitive ) == 0 )
3045 nameCompare = name.mid( 3 );
3046 if ( nameCompare.compare(
"DWithin"_L1, Qt::CaseInsensitive ) == 0 )
3047 return u
"DWithin"_s;
3048 if ( nameCompare.compare(
"Beyond"_L1, Qt::CaseInsensitive ) == 0 )
3053QString QgsOgcUtilsSQLStatementToFilter::getGeometryColumnSRSName(
const QgsSQLStatement::Node *node )
3058 const QgsSQLStatement::NodeColumnRef *col =
static_cast<const QgsSQLStatement::NodeColumnRef *
>( node );
3061 const auto constMLayerProperties = mLayerProperties;
3062 for (
const QgsOgcUtils::LayerProperties &prop : constMLayerProperties )
3064 if ( prop.mName.compare( mMapTableAliasToNames[col->
tableName()], Qt::CaseInsensitive ) == 0 && prop.mGeometryAttribute.compare( col->
name(), Qt::CaseInsensitive ) == 0 )
3066 return prop.mSRSName;
3070 if ( !mLayerProperties.empty() && mLayerProperties.at( 0 ).mGeometryAttribute.compare( col->
name(), Qt::CaseInsensitive ) == 0 )
3072 return mLayerProperties.at( 0 ).mSRSName;
3077bool QgsOgcUtilsSQLStatementToFilter::processSRSName(
const QgsSQLStatement::NodeFunction *mainNode, QList<QgsSQLStatement::Node *> args,
bool lastArgIsSRSName, QString &srsName,
bool &axisInversion )
3079 srsName = mCurrentSRSName;
3080 axisInversion = mInvertAxisOrientation;
3082 if ( lastArgIsSRSName )
3084 QgsSQLStatement::Node *lastArg = args[args.size() - 1];
3087 mErrorMessage = QObject::tr(
"%1: Last argument must be string or integer literal" ).arg( mainNode->
name() );
3090 const QgsSQLStatement::NodeLiteral *lit =
static_cast<const QgsSQLStatement::NodeLiteral *
>( lastArg );
3091 if ( lit->
value().userType() == QMetaType::Type::Int )
3095 srsName =
"EPSG:" + QString::number( lit->
value().toInt() );
3099 srsName =
"urn:ogc:def:crs:EPSG::" + QString::number( lit->
value().toInt() );
3104 srsName = lit->
value().toString();
3105 if ( srsName.startsWith(
"EPSG:"_L1, Qt::CaseInsensitive ) )
3110 QgsCoordinateReferenceSystem crs;
3111 if ( !srsName.isEmpty() )
3117 axisInversion = !axisInversion;
3127 if ( node->
name().compare(
"ST_GeometryFromText"_L1, Qt::CaseInsensitive ) == 0 )
3129 QList<QgsSQLStatement::Node *> args = node->
args()->
list();
3130 if ( args.size() != 1 && args.size() != 2 )
3132 mErrorMessage = QObject::tr(
"Function %1 should have 1 or 2 arguments" ).arg( node->
name() );
3133 return QDomElement();
3136 QgsSQLStatement::Node *firstFnArg = args[0];
3139 mErrorMessage = QObject::tr(
"%1: First argument must be string literal" ).arg( node->
name() );
3140 return QDomElement();
3145 if ( !processSRSName( node, args, args.size() == 2, srsName, axisInversion ) )
3147 return QDomElement();
3150 const QString wkt =
static_cast<const QgsSQLStatement::NodeLiteral *
>( firstFnArg )->value().toString();
3152 const QDomElement geomElem =
QgsOgcUtils::geometryToGML( geom, mDoc, mGMLVersion, srsName, axisInversion, u
"qgis_id_geom_%1"_s.arg( mGeomId ) );
3154 if ( geomElem.isNull() )
3156 mErrorMessage = QObject::tr(
"%1: invalid WKT" ).arg( node->
name() );
3157 return QDomElement();
3164 if ( node->
name().compare(
"ST_MakeEnvelope"_L1, Qt::CaseInsensitive ) == 0 )
3166 QList<QgsSQLStatement::Node *> args = node->
args()->
list();
3167 if ( args.size() != 4 && args.size() != 5 )
3169 mErrorMessage = QObject::tr(
"Function %1 should have 4 or 5 arguments" ).arg( node->
name() );
3170 return QDomElement();
3175 for (
int i = 0; i < 4; i++ )
3177 QgsSQLStatement::Node *arg = args[i];
3180 mErrorMessage = QObject::tr(
"%1: Argument %2 must be numeric literal" ).arg( node->
name() ).arg( i + 1 );
3181 return QDomElement();
3183 const QgsSQLStatement::NodeLiteral *lit =
static_cast<const QgsSQLStatement::NodeLiteral *
>( arg );
3185 if ( lit->
value().userType() == QMetaType::Type::Int )
3186 val = lit->
value().toInt();
3187 else if ( lit->
value().userType() == QMetaType::Type::LongLong )
3188 val = lit->
value().toLongLong();
3189 else if ( lit->
value().userType() == QMetaType::Type::Double )
3190 val = lit->
value().toDouble();
3193 mErrorMessage = QObject::tr(
"%1 Argument %2 must be numeric literal" ).arg( node->
name() ).arg( i + 1 );
3194 return QDomElement();
3208 if ( !processSRSName( node, args, args.size() == 5, srsName, axisInversion ) )
3210 return QDomElement();
3216 : QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, srsName, axisInversion, 15 );
3220 if ( node->
name().compare(
"ST_GeomFromGML"_L1, Qt::CaseInsensitive ) == 0 )
3222 QList<QgsSQLStatement::Node *> args = node->
args()->
list();
3223 if ( args.size() != 1 )
3225 mErrorMessage = QObject::tr(
"Function %1 should have 1 argument" ).arg( node->
name() );
3226 return QDomElement();
3229 QgsSQLStatement::Node *firstFnArg = args[0];
3232 mErrorMessage = QObject::tr(
"%1: Argument must be string literal" ).arg( node->
name() );
3233 return QDomElement();
3236 QDomDocument geomDoc;
3237 const QString gml =
static_cast<const QgsSQLStatement::NodeLiteral *
>( firstFnArg )->value().toString();
3239 const QString xml = u
"<tmp xmlns:gml=\"%1\">%2</tmp>"_s.arg(
GML_NAMESPACE, gml );
3240 if ( !geomDoc.setContent( xml,
true ) )
3242 mErrorMessage = QObject::tr(
"ST_GeomFromGML: unable to parse XML" );
3243 return QDomElement();
3246 const QDomNode geomNode = mDoc.importNode( geomDoc.documentElement().firstChildElement(),
true );
3248 return geomNode.toElement();
3252 QString ogcName( mapBinarySpatialToOgc( node->
name() ) );
3253 if ( !ogcName.isEmpty() )
3255 QList<QgsSQLStatement::Node *> args = node->
args()->
list();
3256 if ( args.size() != 2 )
3258 mErrorMessage = QObject::tr(
"Function %1 should have 2 arguments" ).arg( node->
name() );
3259 return QDomElement();
3262 for (
int i = 0; i < 2; i++ )
3265 (
static_cast<const QgsSQLStatement::NodeFunction *
>( args[i] )->name().compare(
"ST_GeometryFromText"_L1, Qt::CaseInsensitive ) == 0 ||
3266 static_cast<const QgsSQLStatement::NodeFunction *
>( args[i] )->name().compare(
"ST_MakeEnvelope"_L1, Qt::CaseInsensitive ) == 0 ) )
3268 mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
3275 QDomElement funcElem = mDoc.createElement( mFilterPrefix +
":" + ogcName );
3276 const auto constArgs = args;
3277 for ( QgsSQLStatement::Node *n : constArgs )
3280 if ( !mErrorMessage.isEmpty() )
3282 mCurrentSRSName.clear();
3283 return QDomElement();
3286 funcElem.appendChild( childElem );
3289 mCurrentSRSName.clear();
3293 ogcName = mapTernarySpatialToOgc( node->
name() );
3294 if ( !ogcName.isEmpty() )
3296 QList<QgsSQLStatement::Node *> args = node->
args()->
list();
3297 if ( args.size() != 3 )
3299 mErrorMessage = QObject::tr(
"Function %1 should have 3 arguments" ).arg( node->
name() );
3300 return QDomElement();
3303 for (
int i = 0; i < 2; i++ )
3306 (
static_cast<const QgsSQLStatement::NodeFunction *
>( args[i] )->name().compare(
"ST_GeometryFromText"_L1, Qt::CaseInsensitive ) == 0 ||
3307 static_cast<const QgsSQLStatement::NodeFunction *
>( args[i] )->name().compare(
"ST_MakeEnvelope"_L1, Qt::CaseInsensitive ) == 0 ) )
3309 mCurrentSRSName = getGeometryColumnSRSName( args[1 - i] );
3314 QDomElement funcElem = mDoc.createElement( mFilterPrefix +
":" + node->
name().mid( 3 ) );
3315 for (
int i = 0; i < 2; i++ )
3317 const QDomElement childElem =
toOgcFilter( args[i] );
3318 if ( !mErrorMessage.isEmpty() )
3320 mCurrentSRSName.clear();
3321 return QDomElement();
3324 funcElem.appendChild( childElem );
3326 mCurrentSRSName.clear();
3328 QgsSQLStatement::Node *distanceNode = args[2];
3331 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() );
3332 return QDomElement();
3334 const QgsSQLStatement::NodeLiteral *lit =
static_cast<const QgsSQLStatement::NodeLiteral *
>( distanceNode );
3337 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() );
3338 return QDomElement();
3341 QString unit( u
"m"_s );
3342 switch ( lit->
value().userType() )
3344 case QMetaType::Type::Int:
3345 distance = QString::number( lit->
value().toInt() );
3347 case QMetaType::Type::LongLong:
3348 distance = QString::number( lit->
value().toLongLong() );
3350 case QMetaType::Type::Double:
3353 case QMetaType::Type::QString:
3355 distance = lit->
value().toString();
3356 for (
int i = 0; i < distance.size(); i++ )
3358 if ( !( ( distance[i] >=
'0' && distance[i] <=
'9' ) || distance[i] ==
'-' || distance[i] ==
'.' || distance[i] ==
'e' || distance[i] ==
'E' ) )
3360 unit = distance.mid( i ).trimmed();
3361 distance = distance.mid( 0, i );
3369 mErrorMessage = QObject::tr(
"Literal type not supported: %1" ).arg(
static_cast<QMetaType::Type
>( lit->
value().userType() ) );
3370 return QDomElement();
3373 QDomElement distanceElem = mDoc.createElement( mFilterPrefix +
":Distance" );
3375 distanceElem.setAttribute( u
"uom"_s, unit );
3377 distanceElem.setAttribute( u
"unit"_s, unit );
3378 distanceElem.appendChild( mDoc.createTextNode( distance ) );
3379 funcElem.appendChild( distanceElem );
3384 QDomElement funcElem = mDoc.createElement( mFilterPrefix +
":Function" );
3385 funcElem.setAttribute( u
"name"_s, node->
name() );
3386 const auto constList = node->
args()->
list();
3387 for ( QgsSQLStatement::Node *n : constList )
3390 if ( !mErrorMessage.isEmpty() )
3391 return QDomElement();
3393 funcElem.appendChild( childElem );
3400 QgsSQLStatement::Node *onExpr = node->
onExpr();
3406 QList<QDomElement> listElem;
3408 for (
const QString &columnName : constUsingColumns )
3410 QDomElement eqElem = mDoc.createElement( mFilterPrefix +
":PropertyIsEqualTo" );
3411 QDomElement propElem1 = mDoc.createElement( mFilterPrefix +
":" + mPropertyName );
3412 propElem1.appendChild( mDoc.createTextNode( leftTable +
"/" + columnName ) );
3413 eqElem.appendChild( propElem1 );
3414 QDomElement propElem2 = mDoc.createElement( mFilterPrefix +
":" + mPropertyName );
3415 propElem2.appendChild( mDoc.createTextNode( node->
tableDef()->
name() +
"/" + columnName ) );
3416 eqElem.appendChild( propElem2 );
3417 listElem.append( eqElem );
3420 if ( listElem.size() == 1 )
3424 else if ( listElem.size() > 1 )
3426 QDomElement andElem = mDoc.createElement( mFilterPrefix +
":And" );
3427 const auto constListElem = listElem;
3428 for (
const QDomElement &elem : constListElem )
3430 andElem.appendChild( elem );
3435 return QDomElement();
3440 if ( node->
alias().isEmpty() )
3442 mMapTableAliasToNames[node->
name()] = node->
name();
3446 mMapTableAliasToNames[node->
alias()] = node->
name();
3452 QList<QDomElement> listElem;
3456 mErrorMessage = QObject::tr(
"Joins are only supported with WFS 2.0" );
3457 return QDomElement();
3461 const auto constTables = node->
tables();
3462 for ( QgsSQLStatement::NodeTableDef *table : constTables )
3466 const auto constJoins = node->
joins();
3467 for ( QgsSQLStatement::NodeJoin *join : constJoins )
3469 visit( join->tableDef() );
3473 const QList< QgsSQLStatement::NodeTableDef *> nodeTables = node->
tables();
3474 QString leftTable = nodeTables.at( nodeTables.length() - 1 )->name();
3475 for ( QgsSQLStatement::NodeJoin *join : constJoins )
3477 const QDomElement joinElem =
toOgcFilter( join, leftTable );
3478 if ( !mErrorMessage.isEmpty() )
3479 return QDomElement();
3480 listElem.append( joinElem );
3481 leftTable = join->tableDef()->name();
3485 if ( node->
where() )
3488 if ( !mErrorMessage.isEmpty() )
3489 return QDomElement();
3490 listElem.append( whereElem );
3494 if ( listElem.size() == 1 )
3498 else if ( listElem.size() > 1 )
3500 QDomElement andElem = mDoc.createElement( mFilterPrefix +
":And" );
3501 const auto constListElem = listElem;
3502 for (
const QDomElement &elem : constListElem )
3504 andElem.appendChild( elem );
3509 return QDomElement();
3515 mPropertyName = u
"PropertyName"_s;
3520 mPropertyName = u
"ValueReference"_s;
3527 if ( element.isNull() )
3531 if ( isBinaryOperator( element.tagName() ) )
3537 if ( isSpatialOperator( element.tagName() ) )
3543 if ( element.tagName() ==
"Not"_L1 )
3547 else if ( element.tagName() ==
"PropertyIsNull"_L1 )
3551 else if ( element.tagName() ==
"Literal"_L1 )
3555 else if ( element.tagName() ==
"Function"_L1 )
3559 else if ( element.tagName() == mPropertyName )
3563 else if ( element.tagName() ==
"PropertyIsBetween"_L1 )
3568 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() );
3574 if ( element.isNull() )
3577 int op = binaryOperatorFromTagName( element.tagName() );
3580 mErrorMessage = QObject::tr(
"'%1' binary operator not supported." ).arg( element.tagName() );
3589 QDomElement operandElem = element.firstChildElement();
3594 mErrorMessage = QObject::tr(
"invalid left operand for '%1' binary operator" ).arg( element.tagName() );
3598 const std::unique_ptr<QgsExpressionNode> leftOp( expr->clone() );
3599 for ( operandElem = operandElem.nextSiblingElement(); !operandElem.isNull(); operandElem = operandElem.nextSiblingElement() )
3604 mErrorMessage = QObject::tr(
"invalid right operand for '%1' binary operator" ).arg( element.tagName() );
3611 if ( element.hasAttribute( u
"wildCard"_s ) )
3613 wildCard = element.attribute( u
"wildCard"_s );
3616 if ( element.hasAttribute( u
"singleChar"_s ) )
3618 singleChar = element.attribute( u
"singleChar"_s );
3620 QString escape = u
"\\"_s;
3621 if ( element.hasAttribute( u
"escape"_s ) )
3623 escape = element.attribute( u
"escape"_s );
3625 if ( element.hasAttribute( u
"escapeChar"_s ) )
3627 escape = element.attribute( u
"escapeChar"_s );
3631 if ( !wildCard.isEmpty() && wildCard !=
"%"_L1 )
3633 oprValue.replace(
'%',
"\\%"_L1 );
3634 if ( oprValue.startsWith( wildCard ) )
3636 oprValue.replace( 0, 1, u
"%"_s );
3639 QRegularExpressionMatch match = rx.match( oprValue );
3641 while ( match.hasMatch() )
3643 pos = match.capturedStart();
3644 oprValue.replace( pos + 1, 1, u
"%"_s );
3646 match = rx.match( oprValue, pos );
3648 oprValue.replace( escape + wildCard, wildCard );
3650 if ( !singleChar.isEmpty() && singleChar !=
"_"_L1 )
3652 oprValue.replace(
'_',
"\\_"_L1 );
3653 if ( oprValue.startsWith( singleChar ) )
3655 oprValue.replace( 0, 1, u
"_"_s );
3658 QRegularExpressionMatch match = rx.match( oprValue );
3660 while ( match.hasMatch() )
3662 pos = match.capturedStart();
3663 oprValue.replace( pos + 1, 1, u
"_"_s );
3665 match = rx.match( oprValue, pos );
3667 oprValue.replace( escape + singleChar, singleChar );
3669 if ( !escape.isEmpty() && escape !=
"\\"_L1 )
3671 oprValue.replace( escape + escape, escape );
3673 opRight = std::make_unique<QgsExpressionNodeLiteral>( oprValue );
3679 if ( expr == leftOp )
3681 mErrorMessage = QObject::tr(
"only one operand for '%1' binary operator" ).arg( element.tagName() );
3694 auto gml2Args = std::make_unique<QgsExpressionNode::NodeList>();
3695 QDomElement childElem = element.firstChildElement();
3697 while ( !childElem.isNull() && gml2Str.isEmpty() )
3699 if ( childElem.tagName() != mPropertyName )
3701 QTextStream gml2Stream( &gml2Str );
3702 childElem.save( gml2Stream, 0 );
3704 childElem = childElem.nextSiblingElement();
3706 if ( !gml2Str.isEmpty() )
3712 mErrorMessage = QObject::tr(
"No OGC Geometry found" );
3716 auto opArgs = std::make_unique<QgsExpressionNode::NodeList>();
3725 if ( element.isNull() || element.tagName() != mPropertyName )
3727 mErrorMessage = QObject::tr(
"%1:PropertyName expected, got %2" ).arg( mPrefix, element.tagName() );
3736 if ( element.isNull() || element.tagName() !=
"Literal"_L1 )
3738 mErrorMessage = QObject::tr(
"%1:Literal expected, got %2" ).arg( mPrefix, element.tagName() );
3742 std::unique_ptr<QgsExpressionNode> root;
3743 if ( !element.hasChildNodes() )
3745 root = std::make_unique<QgsExpressionNodeLiteral>( QVariant(
"" ) );
3746 return root.release();
3750 QDomNode childNode = element.firstChild();
3751 while ( !childNode.isNull() )
3753 std::unique_ptr<QgsExpressionNode> operand;
3755 if ( childNode.nodeType() == QDomNode::ElementNode )
3758 const QDomElement operandElem = childNode.toElement();
3762 mErrorMessage = QObject::tr(
"'%1' is an invalid or not supported content for %2:Literal" ).arg( operandElem.tagName(), mPrefix );
3769 QVariant value = childNode.nodeValue();
3771 bool converted =
false;
3776 QDomElement propertyNameElement = element.previousSiblingElement( mPropertyName );
3777 if ( propertyNameElement.isNull() || propertyNameElement.tagName() != mPropertyName )
3779 propertyNameElement = element.nextSiblingElement( mPropertyName );
3781 if ( !propertyNameElement.isNull() || propertyNameElement.tagName() == mPropertyName )
3783 const int fieldIndex = mLayer->fields().indexOf( propertyNameElement.firstChild().nodeValue() );
3784 if ( fieldIndex != -1 )
3786 const QgsField field = mLayer->fields().field( propertyNameElement.firstChild().nodeValue() );
3797 const double d = value.toDouble( &ok );
3802 operand = std::make_unique<QgsExpressionNodeLiteral>( value );
3808 root = std::move( operand );
3815 childNode = childNode.nextSibling();
3819 return root.release();
3826 if ( element.tagName() !=
"Not"_L1 )
3829 const QDomElement operandElem = element.firstChildElement();
3833 mErrorMessage = QObject::tr(
"invalid operand for '%1' unary operator" ).arg( element.tagName() );
3843 if ( element.tagName() !=
"PropertyIsNull"_L1 )
3848 const QDomElement operandElem = element.firstChildElement();
3859 if ( element.isNull() || element.tagName() !=
"Function"_L1 )
3861 mErrorMessage = QObject::tr(
"%1:Function expected, got %2" ).arg( mPrefix, element.tagName() );
3869 if ( element.attribute( u
"name"_s ) != funcDef->
name() )
3872 auto args = std::make_unique<QgsExpressionNode::NodeList>();
3874 QDomElement operandElem = element.firstChildElement();
3875 while ( !operandElem.isNull() )
3882 args->append( op.release() );
3884 operandElem = operandElem.nextSiblingElement();
3896 std::unique_ptr<QgsExpressionNode> operand;
3897 std::unique_ptr<QgsExpressionNode> lowerBound;
3898 std::unique_ptr<QgsExpressionNode> upperBound;
3900 QDomElement operandElem = element.firstChildElement();
3901 while ( !operandElem.isNull() )
3903 if ( operandElem.tagName() ==
"LowerBoundary"_L1 )
3905 const QDomElement lowerBoundElem = operandElem.firstChildElement();
3908 else if ( operandElem.tagName() ==
"UpperBoundary"_L1 )
3910 const QDomElement upperBoundElem = operandElem.firstChildElement();
3919 if ( operand && lowerBound && upperBound )
3922 operandElem = operandElem.nextSiblingElement();
3925 if ( !operand || !lowerBound || !upperBound )
3927 mErrorMessage = QObject::tr(
"missing some required sub-elements in %1:PropertyIsBetween" ).arg( mPrefix );
3938 return mErrorMessage;
3943 const thread_local QRegularExpression re_url( QRegularExpression::anchoredPattern( u
"http://www\\.opengis\\.net/gml/srs/epsg\\.xml#(.+)"_s ), QRegularExpression::CaseInsensitiveOption );
3944 if (
const QRegularExpressionMatch match = re_url.match( crsName ); match.hasMatch() )
3946 authority = u
"EPSG"_s;
3947 code = match.captured( 1 );
3952 const thread_local QRegularExpression re_ogc_urn_without_version( QRegularExpression::anchoredPattern( u
"urn:ogc:def:crs:([^:]+):([^:]+)"_s ), QRegularExpression::CaseInsensitiveOption );
3953 if (
const QRegularExpressionMatch match = re_ogc_urn_without_version.match( crsName ); match.hasMatch() )
3955 authority = match.captured( 1 );
3956 code = match.captured( 2 );
3961 const thread_local QRegularExpression re_ogc_urn( QRegularExpression::anchoredPattern( u
"urn:ogc:def:crs:([^:]+):([^:]*):([^:]+)"_s ), QRegularExpression::CaseInsensitiveOption );
3962 if (
const QRegularExpressionMatch match = re_ogc_urn.match( crsName ); match.hasMatch() )
3964 authority = match.captured( 1 );
3965 const QString version = match.captured( 2 );
3966 code = match.captured( 3 );
3967 if ( authority.compare( u
"IAU"_s, Qt::CaseInsensitive ) == 0 && !version.isEmpty() )
3968 authority = u
"%1_%2"_s.arg( authority, version );
3972 const thread_local QRegularExpression re_x_ogc_urn( QRegularExpression::anchoredPattern( u
"urn:x-ogc:def:crs:([^:]+).+(?<=:)([^:]+)"_s ), QRegularExpression::CaseInsensitiveOption );
3973 if (
const QRegularExpressionMatch match = re_x_ogc_urn.match( crsName ); match.hasMatch() )
3975 authority = match.captured( 1 );
3976 code = match.captured( 2 );
3980 const thread_local QRegularExpression re_http_uri( QRegularExpression::anchoredPattern( u
"http://www\\.opengis\\.net/def/crs/([^/]+).+/([^/]+)"_s ), QRegularExpression::CaseInsensitiveOption );
3981 if (
const QRegularExpressionMatch match = re_http_uri.match( crsName ); match.hasMatch() )
3983 authority = match.captured( 1 );
3984 code = match.captured( 2 );
3988 const thread_local QRegularExpression re_auth_code( QRegularExpression::anchoredPattern( u
"([^:]+):(.+)"_s ), QRegularExpression::CaseInsensitiveOption );
3989 if (
const QRegularExpressionMatch match = re_auth_code.match( crsName ); match.hasMatch() )
3991 authority = match.captured( 1 );
3992 code = match.captured( 2 );
3999QgsGeometry QgsOgcUtils::geometryFromGMLUsingGdal(
const QDomElement &geometryElement )
4002 QTextStream gmlStream( &gml );
4003 geometryElement.save( gmlStream, 0 );
4008QgsGeometry QgsOgcUtils::geometryFromGMLMultiCurve(
const QDomElement &geometryElement )
4010 return geometryFromGMLUsingGdal( geometryElement );
GeometryOperationResult
Success or failure of a geometry operation.
@ Success
Operation succeeded.
WkbType
The WKB type describes the number of dimensions a geometry has.
@ LineString25D
LineString25D.
@ MultiPointZ
MultiPointZ.
@ MultiPolygon25D
MultiPolygon25D.
@ MultiLineString25D
MultiLineString25D.
@ MultiPolygon
MultiPolygon.
@ MultiLineString
MultiLineString.
@ MultiPoint25D
MultiPoint25D.
@ MultiLineStringZ
MultiLineStringZ.
@ MultiPolygonZ
MultiPolygonZ.
@ LineStringZ
LineStringZ.
virtual void swapXy()=0
Swaps the x and y coordinates from the geometry.
Qgis::WkbType readHeader() const
readHeader
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...
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.
BinaryOperator
list of binary operators
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.
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
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
@ 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 ...
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.
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 Qgis::WkbType geomTypeFromPropertyType(const QString &gmlGeomType)
Returns the Qgis::WkbType corresponding to a GML geometry type.
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.
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.
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.
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.
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.
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
QMap< QString, QString > QgsStringMap
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
QVector< QgsPolyline > QgsMultiPolyline
Multi polyline represented as a vector of polylines.
QgsPointSequence QgsPolyline
Polyline as represented as a vector of points.
#define QgsDebugMsgLevel(str, level)
Q_GLOBAL_STATIC_WITH_ARGS(IntMap, BINARY_OPERATORS_TAG_NAMES_MAP,({ { "Or"_L1, QgsExpressionNodeBinaryOperator::boOr }, { "And"_L1, QgsExpressionNodeBinaryOperator::boAnd }, { "PropertyIsEqualTo"_L1, QgsExpressionNodeBinaryOperator::boEQ }, { "PropertyIsNotEqualTo"_L1, QgsExpressionNodeBinaryOperator::boNE }, { "PropertyIsLessThanOrEqualTo"_L1, QgsExpressionNodeBinaryOperator::boLE }, { "PropertyIsGreaterThanOrEqualTo"_L1, QgsExpressionNodeBinaryOperator::boGE }, { "PropertyIsLessThan"_L1, QgsExpressionNodeBinaryOperator::boLT }, { "PropertyIsGreaterThan"_L1, QgsExpressionNodeBinaryOperator::boGT }, { "PropertyIsLike"_L1, QgsExpressionNodeBinaryOperator::boLike }, { "Add"_L1, QgsExpressionNodeBinaryOperator::boPlus }, { "Sub"_L1, QgsExpressionNodeBinaryOperator::boMinus }, { "Mul"_L1, QgsExpressionNodeBinaryOperator::boMul }, { "Div"_L1, QgsExpressionNodeBinaryOperator::boDiv }, })) static int binaryOperatorFromTagName(const QString &tagName)
QMap< QString, int > IntMap
The Context struct stores the current layer and coordinate transform context.
const QgsMapLayer * layer
QgsCoordinateTransformContext transformContext