1 /*************************************************************************** 2 qgsogcutils.h 3 --------------------- 4 begin : March 2013 5 copyright : (C) 2013 by Martin Dobias 6 email : wonder dot sk at gmail dot com 7 *************************************************************************** 8 * * 9 * This program is free software; you can redistribute it and/or modify * 10 * it under the terms of the GNU General Public License as published by * 11 * the Free Software Foundation; either version 2 of the License, or * 12 * (at your option) any later version. * 13 * * 14 ***************************************************************************/ 15 #ifndef QGSOGCUTILS_H 16 #define QGSOGCUTILS_H 17 18 class QColor; 19 class QDomNode; 20 class QDomElement; 21 class QDomDocument; 22 class QString; 23 24 #include "qgis_core.h" 25 #include "qgis_sip.h" 26 #include <list> 27 #include <QVector> 28 29 class QgsExpression; 30 class QgsGeometry; 31 class QgsPointXY; 32 class QgsRectangle; 33 class QgsVectorLayer; 34 35 #include "qgsgeometry.h" 36 #include "qgsexpression.h" 37 #include "qgsexpressionnode.h" 38 #include "qgsexpressionnodeimpl.h" 39 #include "qgssqlstatement.h" 40 #include "qgscoordinatetransformcontext.h" 41 42 /** 43 * \ingroup core 44 * \brief The QgsOgcUtils class provides various utility functions for conversion between 45 * OGC (Open Geospatial Consortium) standards and QGIS internal representations. 46 * 47 * Currently supported standards: 48 * 49 * - GML2 - Geography Markup Language (import, export) 50 */ 51 class CORE_EXPORT QgsOgcUtils 52 { 53 public: 54 55 /** 56 * The Context struct stores the current layer and coordinate transform context. 57 * \since QGIS 3.14 58 */ 59 struct Context 60 { 61 62 /** 63 * Constructs a Context from \a layer and \a transformContext 64 */ 65 Context( const QgsMapLayer *layer = nullptr, const QgsCoordinateTransformContext &transformContext = QgsCoordinateTransformContext() ) layerContext66 : layer( layer ) 67 , transformContext( transformContext ) 68 { 69 } 70 const QgsMapLayer *layer = nullptr; 71 QgsCoordinateTransformContext transformContext; 72 }; 73 74 /** 75 *GML version 76 */ 77 enum GMLVersion 78 { 79 GML_2_1_2, 80 GML_3_1_0, 81 GML_3_2_1, 82 }; 83 84 /** 85 * Static method that creates geometry from GML 86 * \param xmlString xml representation of the geometry. GML elements are expected to be 87 * in default namespace (\verbatim {<Point>...</Point> \endverbatim) or in 88 * "gml" namespace (\verbatim <gml:Point>...</gml:Point> \endverbatim) 89 * \param context QgsOgcUtils context 90 */ 91 static QgsGeometry geometryFromGML( const QString &xmlString, const QgsOgcUtils::Context &context = QgsOgcUtils::Context() ); 92 93 /** 94 * Static method that creates geometry from GML 95 */ 96 static QgsGeometry geometryFromGML( const QDomNode &geometryNode, const QgsOgcUtils::Context &context = QgsOgcUtils::Context() ); 97 98 //! Read rectangle from GML2 Box 99 static QgsRectangle rectangleFromGMLBox( const QDomNode &boxNode ); 100 101 //! Read rectangle from GML3 Envelope 102 static QgsRectangle rectangleFromGMLEnvelope( const QDomNode &envelopeNode ); 103 104 /** 105 * Exports the geometry to GML 106 * \returns QDomElement 107 * \since QGIS 2.16 108 */ 109 static QDomElement geometryToGML( const QgsGeometry &geometry, QDomDocument &doc, 110 QgsOgcUtils::GMLVersion gmlVersion, 111 const QString &srsName, 112 bool invertAxisOrientation, 113 const QString &gmlIdBase, 114 int precision = 17 ); 115 116 /** 117 * Exports the geometry to GML2 or GML3 118 * \returns QDomElement 119 */ 120 static QDomElement geometryToGML( const QgsGeometry &geometry, QDomDocument &doc, const QString &format, int precision = 17 ); 121 122 /** 123 * Exports the geometry to GML2 124 * \returns QDomElement 125 */ 126 static QDomElement geometryToGML( const QgsGeometry &geometry, QDomDocument &doc, int precision = 17 ); 127 128 /** 129 * Exports the rectangle to GML2 Box 130 * \returns QDomElement 131 */ 132 static QDomElement rectangleToGMLBox( QgsRectangle *box, QDomDocument &doc, int precision = 17 ); 133 134 /** 135 * Exports the rectangle to GML2 Box 136 * \returns QDomElement 137 * \since QGIS 2.16 138 */ 139 static QDomElement rectangleToGMLBox( QgsRectangle *box, QDomDocument &doc, 140 const QString &srsName, 141 bool invertAxisOrientation, 142 int precision = 17 ); 143 144 /** 145 * Exports the rectangle to GML3 Envelope 146 * \returns QDomElement 147 */ 148 static QDomElement rectangleToGMLEnvelope( QgsRectangle *env, QDomDocument &doc, int precision = 17 ); 149 150 /** 151 * Exports the rectangle to GML3 Envelope 152 * \returns QDomElement 153 * \since QGIS 2.16 154 */ 155 static QDomElement rectangleToGMLEnvelope( QgsRectangle *env, QDomDocument &doc, 156 const QString &srsName, 157 bool invertAxisOrientation, 158 int precision = 17 ); 159 160 161 //! Parse XML with OGC fill into QColor 162 static QColor colorFromOgcFill( const QDomElement &fillElement ); 163 164 //! Parse XML with OGC filter into QGIS expression 165 static QgsExpression *expressionFromOgcFilter( const QDomElement &element, QgsVectorLayer *layer = nullptr ) SIP_FACTORY; 166 167 /** 168 * Creates OGC filter XML element. Supports minimum standard filter 169 * according to the OGC filter specs (=,!=,<,>,<=,>=,AND,OR,NOT) 170 * \returns valid \verbatim <Filter> \endverbatim QDomElement on success, 171 * otherwise null QDomElement 172 */ 173 static QDomElement expressionToOgcFilter( const QgsExpression &exp, QDomDocument &doc, QString *errorMessage = nullptr ); 174 175 /** 176 * OGC filter version 177 */ 178 enum FilterVersion 179 { 180 FILTER_OGC_1_0, 181 FILTER_OGC_1_1, 182 FILTER_FES_2_0 183 }; 184 185 /** 186 * Returns an expression from a WFS filter embedded in a document. 187 * \param element The WFS Filter 188 * \param version The WFS version 189 * \param layer Layer to use to retrieve field values from literal filters 190 * \since QGIS 3.4 191 */ 192 static QgsExpression *expressionFromOgcFilter( const QDomElement &element, FilterVersion version, QgsVectorLayer *layer = nullptr ) SIP_FACTORY; 193 194 /** 195 * Creates OGC filter XML element. Supports minimum standard filter 196 * according to the OGC filter specs (=,!=,<,>,<=,>=,AND,OR,NOT) 197 * \returns valid \verbatim <Filter> \endverbatim QDomElement on success, 198 * otherwise null QDomElement 199 * \note not available in Python bindings 200 * \since QGIS 2.16 201 */ 202 static QDomElement expressionToOgcFilter( const QgsExpression &exp, 203 QDomDocument &doc, 204 QgsOgcUtils::GMLVersion gmlVersion, 205 FilterVersion filterVersion, 206 const QString &geometryName, 207 const QString &srsName, 208 bool honourAxisOrientation, 209 bool invertAxisOrientation, 210 QString *errorMessage = nullptr ) SIP_SKIP; 211 212 /** 213 * Creates an OGC expression XML element. 214 * \returns valid OGC expression QDomElement on success, 215 * otherwise null QDomElement 216 */ 217 static QDomElement expressionToOgcExpression( const QgsExpression &exp, QDomDocument &doc, QString *errorMessage = nullptr ); 218 219 /** 220 * Creates an OGC expression XML element. 221 * \returns valid OGC expression QDomElement on success, 222 * otherwise null QDomElement 223 */ 224 static QDomElement expressionToOgcExpression( const QgsExpression &exp, 225 QDomDocument &doc, 226 QgsOgcUtils::GMLVersion gmlVersion, 227 FilterVersion filterVersion, 228 const QString &geometryName, 229 const QString &srsName, 230 bool honourAxisOrientation, 231 bool invertAxisOrientation, 232 QString *errorMessage = nullptr ); 233 234 #ifndef SIP_RUN 235 236 /** 237 * \ingroup core 238 * \brief Layer properties. Used by SQLStatementToOgcFilter(). 239 * \note not available in Python bindings 240 * \since QGIS 2.16 241 */ 242 class LayerProperties 243 { 244 public: 245 //! Constructor 246 LayerProperties() = default; 247 248 //! Layer name 249 QString mName; 250 //! Geometry attribute name 251 QString mGeometryAttribute; 252 //! SRS name 253 QString mSRSName; 254 //! Namespace prefix 255 QString mNamespacePrefix; 256 //! Namespace URI 257 QString mNamespaceURI; 258 }; 259 #endif 260 261 /** 262 * Creates OGC filter XML element from the WHERE and JOIN clauses of a SQL 263 * statement. Supports minimum standard filter 264 * according to the OGC filter specs (=,!=,<,>,<=,>=,AND,OR,NOT,LIKE,BETWEEN,IN) 265 * Supports layer joins. 266 * Supports ST_GeometryFromText(wkt[, srid/srsname]), 267 * ST_MakeEnvelope(xmin,ymin,xmax,ymax[, srid/srsname]) 268 * ST_GeomFromGML(serialized_gml_string) 269 * BBOX() 270 * ST_Intersects(), ST_Contains(), ST_Crosses(), ST_Equals(), 271 * ST_Disjoint(), ST_Overlaps(), ST_Touches(), ST_Within() 272 * ST_DWithin(), ST_Beyond() 273 * custom functions 274 * \returns valid \verbatim <Filter> \endverbatim QDomElement on success, 275 * otherwise null QDomElement 276 * \note not available in Python bindings 277 * \since QGIS 2.16 278 */ 279 static QDomElement SQLStatementToOgcFilter( const QgsSQLStatement &statement, 280 QDomDocument &doc, 281 QgsOgcUtils::GMLVersion gmlVersion, 282 FilterVersion filterVersion, 283 const QList<LayerProperties> &layerProperties, 284 bool honourAxisOrientation, 285 bool invertAxisOrientation, 286 const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename, 287 QString *errorMessage = nullptr ) SIP_SKIP; 288 289 private: 290 291 //! Static method that creates geometry from GML Point 292 static QgsGeometry geometryFromGMLPoint( const QDomElement &geometryElement ); 293 //! Static method that creates geometry from GML LineString 294 static QgsGeometry geometryFromGMLLineString( const QDomElement &geometryElement ); 295 //! Static method that creates geometry from GML Polygon 296 static QgsGeometry geometryFromGMLPolygon( const QDomElement &geometryElement ); 297 //! Static method that creates geometry from GML MultiPoint 298 static QgsGeometry geometryFromGMLMultiPoint( const QDomElement &geometryElement ); 299 //! Static method that creates geometry from GML MultiLineString 300 static QgsGeometry geometryFromGMLMultiLineString( const QDomElement &geometryElement ); 301 //! Static method that creates geometry from GML MultiPolygon 302 static QgsGeometry geometryFromGMLMultiPolygon( const QDomElement &geometryElement ); 303 304 /** 305 * Reads the \verbatim <gml:coordinates> \endverbatim element and extracts the coordinates as points 306 * \param coords list where the found coordinates are appended 307 * \param elem the \verbatim <gml:coordinates> \endverbatim element 308 * \returns boolean FALSE on success 309 */ 310 static bool readGMLCoordinates( QgsPolylineXY &coords, const QDomElement &elem ); 311 312 /** 313 * Reads the \verbatim <gml:pos> \endverbatim or \verbatim <gml:posList> \endverbatim 314 * and extracts the coordinates as points 315 * \param coords list where the found coordinates are appended 316 * \param elem the \verbatim <gml:pos> \endverbatim or 317 * \verbatim <gml:posList> \endverbatim element 318 * \returns boolean FALSE on success 319 */ 320 static bool readGMLPositions( QgsPolylineXY &coords, const QDomElement &elem ); 321 322 323 /** 324 * Create a GML coordinates element from a point list. 325 * \param points list of data points 326 * \param doc the GML document 327 * \returns QDomElement 328 */ 329 static QDomElement createGMLCoordinates( const QgsPolylineXY &points, QDomDocument &doc ); 330 331 /** 332 * Create a GML pos or posList element from a point list. 333 * \param points list of data points 334 * \param doc the GML document 335 * \returns QDomElement 336 */ 337 static QDomElement createGMLPositions( const QgsPolylineXY &points, QDomDocument &doc ); 338 339 //! handle a generic sub-expression 340 static QgsExpressionNode *nodeFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer = nullptr ); 341 //! handle a generic binary operator 342 static QgsExpressionNodeBinaryOperator *nodeBinaryOperatorFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer = nullptr ); 343 //! handles various spatial operation tags (\verbatim <Intersects> \endverbatim, \verbatim <Touches> \endverbatim etc.) 344 static QgsExpressionNodeFunction *nodeSpatialOperatorFromOgcFilter( QDomElement &element, QString &errorMessage ); 345 //! handle \verbatim <Not> \endverbatim tag 346 static QgsExpressionNodeUnaryOperator *nodeNotFromOgcFilter( QDomElement &element, QString &errorMessage ); 347 //! handles \verbatim <Function> \endverbatim tag 348 static QgsExpressionNodeFunction *nodeFunctionFromOgcFilter( QDomElement &element, QString &errorMessage ); 349 //! handles \verbatim <Literal> \endverbatim tag 350 static QgsExpressionNode *nodeLiteralFromOgcFilter( QDomElement &element, QString &errorMessage, QgsVectorLayer *layer = nullptr ); 351 //! handles \verbatim <PropertyName> \endverbatim tag 352 static QgsExpressionNodeColumnRef *nodeColumnRefFromOgcFilter( QDomElement &element, QString &errorMessage ); 353 //! handles \verbatim <PropertyIsBetween> \endverbatim tag 354 static QgsExpressionNode *nodeIsBetweenFromOgcFilter( QDomElement &element, QString &errorMessage ); 355 //! handles \verbatim <PropertyIsNull> \endverbatim tag 356 static QgsExpressionNodeBinaryOperator *nodePropertyIsNullFromOgcFilter( QDomElement &element, QString &errorMessage ); 357 }; 358 359 #ifndef SIP_RUN 360 361 /** 362 * \ingroup core 363 * \brief Internal use by QgsOgcUtils 364 * \note not available in Python bindings 365 */ 366 class QgsOgcUtilsExprToFilter 367 { 368 public: 369 //! Constructor 370 QgsOgcUtilsExprToFilter( QDomDocument &doc, 371 QgsOgcUtils::GMLVersion gmlVersion, 372 QgsOgcUtils::FilterVersion filterVersion, 373 const QString &geometryName, 374 const QString &srsName, 375 bool honourAxisOrientation, 376 bool invertAxisOrientation ); 377 378 //! Convert an expression to a OGC filter 379 QDomElement expressionNodeToOgcFilter( const QgsExpressionNode *node, QgsExpression *expression, const QgsExpressionContext *context ); 380 381 //! Returns whether the gml: namespace is used GMLNamespaceUsed()382 bool GMLNamespaceUsed() const { return mGMLUsed; } 383 384 //! Returns the error message. errorMessage()385 QString errorMessage() const { return mErrorMessage; } 386 387 private: 388 QDomDocument &mDoc; 389 bool mGMLUsed; 390 QgsOgcUtils::GMLVersion mGMLVersion; 391 QgsOgcUtils::FilterVersion mFilterVersion; 392 const QString &mGeometryName; 393 const QString &mSrsName; 394 bool mInvertAxisOrientation; 395 QString mErrorMessage; 396 QString mFilterPrefix; 397 QString mPropertyName; 398 int mGeomId; 399 400 QDomElement expressionUnaryOperatorToOgcFilter( const QgsExpressionNodeUnaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context ); 401 QDomElement expressionBinaryOperatorToOgcFilter( const QgsExpressionNodeBinaryOperator *node, QgsExpression *expression, const QgsExpressionContext *context ); 402 QDomElement expressionLiteralToOgcFilter( const QgsExpressionNodeLiteral *node, QgsExpression *expression, const QgsExpressionContext *context ); 403 QDomElement expressionColumnRefToOgcFilter( const QgsExpressionNodeColumnRef *node, QgsExpression *expression, const QgsExpressionContext *context ); 404 QDomElement expressionInOperatorToOgcFilter( const QgsExpressionNodeInOperator *node, QgsExpression *expression, const QgsExpressionContext *context ); 405 QDomElement expressionFunctionToOgcFilter( const QgsExpressionNodeFunction *node, QgsExpression *expression, const QgsExpressionContext *context ); 406 }; 407 408 /** 409 * \ingroup core 410 * \brief Internal use by QgsOgcUtils 411 * \note not available in Python bindings 412 * \since QGIS 3.4 413 */ 414 class QgsOgcUtilsExpressionFromFilter 415 { 416 public: 417 418 /** 419 * Constructor for QgsOgcUtilsExpressionFromFilter. 420 * \param version WFS Version 421 * \param layer Layer to use to retrieve field values from literal filters 422 */ 423 QgsOgcUtilsExpressionFromFilter( QgsOgcUtils::FilterVersion version = QgsOgcUtils::FILTER_OGC_1_0, 424 const QgsVectorLayer *layer = nullptr ); 425 426 /** 427 * Returns an expression node from a WFS filter embedded in a document 428 * element. NULLPTR is returned when an error happened. 429 * \param element The WFS filter 430 */ 431 QgsExpressionNode *nodeFromOgcFilter( const QDomElement &element ); 432 433 /** 434 * Returns the underlying error message, or an empty string in case of no 435 * error. 436 */ 437 QString errorMessage() const; 438 439 /** 440 * Returns an expression node from a WFS filter embedded in a document with 441 * binary operators. 442 * 443 */ 444 QgsExpressionNodeBinaryOperator *nodeBinaryOperatorFromOgcFilter( const QDomElement &element ); 445 446 /** 447 * Returns an expression node from a WFS filter embedded in a document with 448 * spatial operators. 449 */ 450 QgsExpressionNodeFunction *nodeSpatialOperatorFromOgcFilter( const QDomElement &element ); 451 452 /** 453 * Returns an expression node from a WFS filter embedded in a document with 454 * column references. 455 */ 456 QgsExpressionNodeColumnRef *nodeColumnRefFromOgcFilter( const QDomElement &element ); 457 458 /** 459 * Returns an expression node from a WFS filter embedded in a document with 460 * literal tag. 461 */ 462 QgsExpressionNode *nodeLiteralFromOgcFilter( const QDomElement &element ); 463 464 /** 465 * Returns an expression node from a WFS filter embedded in a document with 466 * Not operator. 467 */ 468 QgsExpressionNodeUnaryOperator *nodeNotFromOgcFilter( const QDomElement &element ); 469 470 /** 471 * Returns an expression node from a WFS filter embedded in a document with 472 * IsNull operator. 473 */ 474 QgsExpressionNodeBinaryOperator *nodePropertyIsNullFromOgcFilter( const QDomElement &element ); 475 476 /** 477 * Returns an expression node from a WFS filter embedded in a document with 478 * functions. 479 */ 480 QgsExpressionNodeFunction *nodeFunctionFromOgcFilter( const QDomElement &element ); 481 482 /** 483 * Returns an expression node from a WFS filter embedded in a document with 484 * boudnaries operator. 485 */ 486 QgsExpressionNode *nodeIsBetweenFromOgcFilter( const QDomElement &element ); 487 488 private: 489 const QgsVectorLayer *mLayer = nullptr; 490 QString mErrorMessage; 491 QString mPropertyName; 492 QString mPrefix; 493 }; 494 495 /** 496 * \ingroup core 497 * \brief Internal use by QgsOgcUtils 498 * \note not available in Python bindings 499 */ 500 class QgsOgcUtilsSQLStatementToFilter 501 { 502 public: 503 //! Constructor 504 QgsOgcUtilsSQLStatementToFilter( QDomDocument &doc, 505 QgsOgcUtils::GMLVersion gmlVersion, 506 QgsOgcUtils::FilterVersion filterVersion, 507 const QList<QgsOgcUtils::LayerProperties> &layerProperties, 508 bool honourAxisOrientation, 509 bool invertAxisOrientation, 510 const QMap< QString, QString> &mapUnprefixedTypenameToPrefixedTypename ); 511 512 //! Convert a SQL statement to a OGC filter 513 QDomElement toOgcFilter( const QgsSQLStatement::Node *node ); 514 515 //! Returns whether the gml: namespace is used GMLNamespaceUsed()516 bool GMLNamespaceUsed() const { return mGMLUsed; } 517 518 //! Returns the error message. errorMessage()519 QString errorMessage() const { return mErrorMessage; } 520 521 private: 522 QDomDocument &mDoc; 523 bool mGMLUsed; 524 QgsOgcUtils::GMLVersion mGMLVersion; 525 QgsOgcUtils::FilterVersion mFilterVersion; 526 const QList<QgsOgcUtils::LayerProperties> &mLayerProperties; 527 bool mHonourAxisOrientation; 528 bool mInvertAxisOrientation; 529 QString mErrorMessage; 530 QString mFilterPrefix; 531 QString mPropertyName; 532 int mGeomId; 533 QString mCurrentSRSName; 534 QMap<QString, QString> mMapTableAliasToNames; 535 const QMap< QString, QString> &mMapUnprefixedTypenameToPrefixedTypename; 536 537 QDomElement toOgcFilter( const QgsSQLStatement::NodeUnaryOperator *node ); 538 QDomElement toOgcFilter( const QgsSQLStatement::NodeBinaryOperator *node ); 539 QDomElement toOgcFilter( const QgsSQLStatement::NodeLiteral *node ); 540 QDomElement toOgcFilter( const QgsSQLStatement::NodeColumnRef *node ); 541 QDomElement toOgcFilter( const QgsSQLStatement::NodeInOperator *node ); 542 QDomElement toOgcFilter( const QgsSQLStatement::NodeBetweenOperator *node ); 543 QDomElement toOgcFilter( const QgsSQLStatement::NodeFunction *node ); 544 QDomElement toOgcFilter( const QgsSQLStatement::NodeJoin *node, const QString &leftTable ); 545 QDomElement toOgcFilter( const QgsSQLStatement::NodeSelect *node ); 546 547 void visit( const QgsSQLStatement::NodeTableDef *node ); 548 QString getGeometryColumnSRSName( const QgsSQLStatement::Node *node ); 549 bool processSRSName( const QgsSQLStatement::NodeFunction *mainNode, 550 QList<QgsSQLStatement::Node *> args, 551 bool lastArgIsSRSName, 552 QString &srsName, 553 bool &axisInversion ); 554 }; 555 #endif // #ifndef SIP_RUN 556 557 #endif // QGSOGCUTILS_H 558