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