QGIS API Documentation 4.3.0-Master (35968c35f7e)
Loading...
Searching...
No Matches
qgsarcgisrestutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsarcgisrestutils.cpp
3 ----------------------
4 begin : Nov 25, 2015
5 copyright : (C) 2015 by Sandro Mani
6 email : manisandro@gmail.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
16#include "qgsarcgisrestutils.h"
17
19#include "qgscircularstring.h"
26#include "qgscolorrampimpl.h"
27#include "qgscurve.h"
28#include "qgsfields.h"
29#include "qgsgeometryengine.h"
31#include "qgslinestring.h"
32#include "qgslinesymbollayer.h"
33#include "qgslogger.h"
34#include "qgsmulticurve.h"
35#include "qgsmultilinestring.h"
36#include "qgsmultipoint.h"
37#include "qgsmultipolygon.h"
38#include "qgsmultisurface.h"
39#include "qgspallabeling.h"
40#include "qgspolygon.h"
42#include "qgsrectangle.h"
43#include "qgsrenderer.h"
46#include "qgssymbol.h"
48#include "qgssymbollayer.h"
49#include "qgsvariantutils.h"
51
52#include <QRegularExpression>
53#include <QString>
54#include <QUrl>
55
56#include "moc_qgsarcgisrestutils.cpp"
57
58using namespace Qt::StringLiterals;
59
60QMetaType::Type QgsArcGisRestUtils::convertFieldType( const QString &esriFieldType )
61{
62 if ( esriFieldType == "esriFieldTypeInteger"_L1 )
63 return QMetaType::Type::LongLong;
64 if ( esriFieldType == "esriFieldTypeSmallInteger"_L1 )
65 return QMetaType::Type::Int;
66 if ( esriFieldType == "esriFieldTypeDouble"_L1 )
67 return QMetaType::Type::Double;
68 if ( esriFieldType == "esriFieldTypeSingle"_L1 )
69 return QMetaType::Type::Double;
70 if ( esriFieldType == "esriFieldTypeString"_L1 )
71 return QMetaType::Type::QString;
72 if ( esriFieldType == "esriFieldTypeDate"_L1 )
73 return QMetaType::Type::QDateTime;
74 if ( esriFieldType == "esriFieldTypeGeometry"_L1 )
75 return QMetaType::Type::UnknownType; // Geometry column should not appear as field
76 if ( esriFieldType == "esriFieldTypeOID"_L1 )
77 return QMetaType::Type::LongLong;
78 if ( esriFieldType == "esriFieldTypeBlob"_L1 )
79 return QMetaType::Type::QByteArray;
80 if ( esriFieldType == "esriFieldTypeGlobalID"_L1 )
81 return QMetaType::Type::QString;
82 if ( esriFieldType == "esriFieldTypeRaster"_L1 )
83 return QMetaType::Type::QByteArray;
84 if ( esriFieldType == "esriFieldTypeGUID"_L1 )
85 return QMetaType::Type::QString;
86 if ( esriFieldType == "esriFieldTypeXML"_L1 )
87 return QMetaType::Type::QString;
88 return QMetaType::Type::UnknownType;
89}
90
92{
93 // http://resources.arcgis.com/en/help/arcobjects-cpp/componenthelp/index.html#//000w0000001p000000
94 if ( esriGeometryType == "esriGeometryNull"_L1 )
96 else if ( esriGeometryType == "esriGeometryPoint"_L1 )
98 else if ( esriGeometryType == "esriGeometryMultipoint"_L1 )
100 else if ( esriGeometryType == "esriGeometryPolyline"_L1 )
102 else if ( esriGeometryType == "esriGeometryPolygon"_L1 )
104 else if ( esriGeometryType == "esriGeometryEnvelope"_L1 )
106 // Unsupported (either by qgis, or format unspecified by the specification)
107 // esriGeometryCircularArc
108 // esriGeometryEllipticArc
109 // esriGeometryBezier3Curve
110 // esriGeometryPath
111 // esriGeometryRing
112 // esriGeometryLine
113 // esriGeometryAny
114 // esriGeometryMultiPatch
115 // esriGeometryTriangleStrip
116 // esriGeometryTriangleFan
117 // esriGeometryRay
118 // esriGeometrySphere
119 // esriGeometryTriangles
120 // esriGeometryBag
122}
123
124std::unique_ptr< QgsPoint > QgsArcGisRestUtils::convertPoint( const QVariantList &coordList, Qgis::WkbType pointType )
125{
126 const int nCoords = static_cast< int >( coordList.size() );
127 if ( nCoords < 2 )
128 return nullptr;
129 bool xok = false, yok = false;
130 const double x = coordList[0].toDouble( &xok );
131 const double y = coordList[1].toDouble( &yok );
132 if ( !xok || !yok )
133 return nullptr;
134 const bool hasZ = QgsWkbTypes::hasZ( pointType );
135 const double z = hasZ && nCoords >= 3 ? coordList[2].toDouble() : std::numeric_limits< double >::quiet_NaN();
136
137 // if point has just M but not Z, then the point dimension list will only have X, Y, M, otherwise it will have X, Y, Z, M
138 const double m = QgsWkbTypes::hasM( pointType ) && ( ( hasZ && nCoords >= 4 ) || ( !hasZ && nCoords >= 3 ) ) ? coordList[hasZ ? 3 : 2].toDouble() : std::numeric_limits< double >::quiet_NaN();
139 return std::make_unique< QgsPoint >( pointType, x, y, z, m );
140}
141
142std::unique_ptr< QgsCircularString > QgsArcGisRestUtils::convertCircularString( const QVariantMap &curveData, Qgis::WkbType pointType, const QgsPoint &startPoint )
143{
144 const QVariantList coordsList = curveData[u"c"_s].toList();
145 if ( coordsList.isEmpty() )
146 return nullptr;
147 const int coordsListSize = static_cast< int >( coordsList.size() );
148
149 QVector<QgsPoint> points;
150 points.reserve( coordsListSize + 1 );
151 points.append( startPoint );
152
153 for ( int i = 0; i < coordsListSize - 1; )
154 {
155 // first point is end point, second is point on curve
156 // i.e. the opposite to what QGIS requires!
157 std::unique_ptr< QgsPoint > endPoint( convertPoint( coordsList.at( i ).toList(), pointType ) );
158 if ( !endPoint )
159 return nullptr;
160 i++;
161 std::unique_ptr< QgsPoint > interiorPoint( convertPoint( coordsList.at( i ).toList(), pointType ) );
162 if ( !interiorPoint )
163 return nullptr;
164 i++;
165 points << *interiorPoint;
166 points << *endPoint;
167 }
168 auto curve = std::make_unique< QgsCircularString>();
169 curve->setPoints( points );
170 return curve;
171}
172
173std::unique_ptr< QgsCurve > QgsArcGisRestUtils::convertCompoundCurve( const QVariantList &curvesList, Qgis::WkbType pointType )
174{
175 // [[6,3],[5,3],{"b":[[3,2],[6,1],[2,4]]},[1,2],{"c": [[3,3],[1,4]]}]
176 auto compoundCurve = std::make_unique< QgsCompoundCurve >();
177
178 QVector< double > lineX;
179 QVector< double > lineY;
180 QVector< double > lineZ;
181 QVector< double > lineM;
182 const int maxCurveListSize = static_cast< int >( curvesList.size() );
183 lineX.resize( maxCurveListSize );
184 lineY.resize( maxCurveListSize );
185
186 const bool hasZ = QgsWkbTypes::hasZ( pointType );
187 if ( hasZ )
188 lineZ.resize( maxCurveListSize );
189 const bool hasM = QgsWkbTypes::hasM( pointType );
190 if ( hasM )
191 lineM.resize( maxCurveListSize );
192
193 double *outLineX = lineX.data();
194 double *outLineY = lineY.data();
195 double *outLineZ = lineZ.data();
196 double *outLineM = lineM.data();
197 int actualLineSize = 0;
198
199 bool xok = false;
200 bool yok = false;
201
202 int curveListIndex = 0;
203 for ( const QVariant &curveData : curvesList )
204 {
205 if ( curveData.userType() == QMetaType::Type::QVariantList )
206 {
207 const QVariantList coordList = curveData.toList();
208 const int nCoords = static_cast< int >( coordList.size() );
209 if ( nCoords < 2 )
210 return nullptr;
211
212 const double x = coordList[0].toDouble( &xok );
213 const double y = coordList[1].toDouble( &yok );
214 if ( !xok || !yok )
215 return nullptr;
216
217 actualLineSize++;
218 *outLineX++ = x;
219 *outLineY++ = y;
220 if ( hasZ )
221 {
222 *outLineZ++ = nCoords >= 3 ? coordList[2].toDouble() : std::numeric_limits< double >::quiet_NaN();
223 }
224
225 if ( hasM )
226 {
227 // if point has just M but not Z, then the point dimension list will only have X, Y, M, otherwise it will have X, Y, Z, M
228 *outLineM++ = ( ( hasZ && nCoords >= 4 ) || ( !hasZ && nCoords >= 3 ) ) ? coordList[hasZ ? 3 : 2].toDouble() : std::numeric_limits< double >::quiet_NaN();
229 }
230 }
231 else if ( curveData.userType() == QMetaType::Type::QVariantMap )
232 {
233 // The last point of the linestring is the start point of this circular string
234 QgsPoint lastLineStringPoint;
235 if ( actualLineSize > 0 )
236 {
237 lastLineStringPoint
238 = QgsPoint( lineX.at( actualLineSize - 1 ), lineY.at( actualLineSize - 1 ), hasZ ? lineZ.at( actualLineSize - 1 ) : std::numeric_limits< double >::quiet_NaN(), hasM ? lineM.at( actualLineSize - 1 ) : std::numeric_limits< double >::quiet_NaN() );
239 }
240 std::unique_ptr< QgsCircularString > circularString( convertCircularString( curveData.toMap(), pointType, lastLineStringPoint ) );
241 if ( !circularString )
242 {
243 return nullptr;
244 }
245
246 if ( actualLineSize > 0 )
247 {
248 lineX.resize( actualLineSize );
249 lineY.resize( actualLineSize );
250 if ( hasZ )
251 lineZ.resize( actualLineSize );
252 if ( hasM )
253 lineM.resize( actualLineSize );
254
255 compoundCurve->addCurve( new QgsLineString( lineX, lineY, lineZ, lineM ) );
256 lineX.resize( maxCurveListSize - curveListIndex );
257 lineY.resize( maxCurveListSize - curveListIndex );
258 if ( hasZ )
259 lineZ.resize( maxCurveListSize - curveListIndex );
260 if ( hasM )
261 lineM.resize( maxCurveListSize - curveListIndex );
262 outLineX = lineX.data();
263 outLineY = lineY.data();
264 outLineZ = lineZ.data();
265 outLineM = lineM.data();
266 }
267
268 // If the previous curve had less than two points, remove it
269 if ( compoundCurve->curveAt( compoundCurve->nCurves() - 1 )->nCoordinates() < 2 )
270 compoundCurve->removeCurve( compoundCurve->nCurves() - 1 );
271
272 const QgsPoint endPointCircularString = circularString->endPoint();
273 compoundCurve->addCurve( circularString.release() );
274
275 // Prepare a new line string
276 actualLineSize = 1;
277 *outLineX++ = endPointCircularString.x();
278 *outLineY++ = endPointCircularString.y();
279 if ( hasZ )
280 *outLineZ++ = endPointCircularString.z();
281 if ( hasM )
282 *outLineM++ = endPointCircularString.m();
283 }
284 curveListIndex++;
285 }
286
287 if ( actualLineSize == 1 && compoundCurve->nCurves() > 0 )
288 {
289 const QgsCurve *finalCurve = compoundCurve->curveAt( compoundCurve->nCurves() - 1 );
290 const QgsPoint finalCurveEndPoint = finalCurve->endPoint();
291 if ( qgsDoubleNear( finalCurveEndPoint.x(), lineX.at( 0 ) )
292 && qgsDoubleNear( finalCurveEndPoint.y(), lineY.at( 0 ) )
293 && ( !hasZ || qgsDoubleNear( finalCurveEndPoint.z(), lineZ.at( 0 ) ) )
294 && ( !hasM || qgsDoubleNear( finalCurveEndPoint.m(), lineM.at( 0 ) ) ) )
295 {
296 actualLineSize = 0; // redundant final curve containing a duplicate vertex
297 }
298 }
299
300 if ( actualLineSize > 0 )
301 {
302 lineX.resize( actualLineSize );
303 lineY.resize( actualLineSize );
304 if ( hasZ )
305 lineZ.resize( actualLineSize );
306 if ( hasM )
307 lineM.resize( actualLineSize );
308 compoundCurve->addCurve( new QgsLineString( lineX, lineY, lineZ, lineM ) );
309 }
310
311 return compoundCurve;
312}
313
314std::unique_ptr<QgsLineString> QgsArcGisRestUtils::convertLineString( const QVariantList &curvesList, Qgis::WkbType pointType )
315{
316 auto linestring = std::make_unique< QgsLineString >();
317
318 QVector< double > lineX;
319 QVector< double > lineY;
320 QVector< double > lineZ;
321 QVector< double > lineM;
322 const int maxCurveListSize = static_cast< int >( curvesList.size() );
323 lineX.resize( maxCurveListSize );
324 lineY.resize( maxCurveListSize );
325
326 const bool hasZ = QgsWkbTypes::hasZ( pointType );
327 if ( hasZ )
328 lineZ.resize( maxCurveListSize );
329 const bool hasM = QgsWkbTypes::hasM( pointType );
330 if ( hasM )
331 lineM.resize( maxCurveListSize );
332
333 double *outLineX = lineX.data();
334 double *outLineY = lineY.data();
335 double *outLineZ = lineZ.data();
336 double *outLineM = lineM.data();
337
338 bool xok = false;
339 bool yok = false;
340
341 int actualLineSize = 0;
342 for ( const QVariant &curveData : curvesList )
343 {
344 if ( curveData.userType() == QMetaType::Type::QVariantList )
345 {
346 const QVariantList coordList = curveData.toList();
347 const int nCoords = static_cast< int >( coordList.size() );
348 if ( nCoords < 2 )
349 return nullptr;
350
351 const double x = coordList[0].toDouble( &xok );
352 const double y = coordList[1].toDouble( &yok );
353 if ( !xok || !yok )
354 return nullptr;
355
356 actualLineSize++;
357 *outLineX++ = x;
358 *outLineY++ = y;
359 if ( hasZ )
360 {
361 *outLineZ++ = nCoords >= 3 ? coordList[2].toDouble() : std::numeric_limits< double >::quiet_NaN();
362 }
363
364 if ( hasM )
365 {
366 // if point has just M but not Z, then the point dimension list will only have X, Y, M, otherwise it will have X, Y, Z, M
367 *outLineM++ = ( ( hasZ && nCoords >= 4 ) || ( !hasZ && nCoords >= 3 ) ) ? coordList[hasZ ? 3 : 2].toDouble() : std::numeric_limits< double >::quiet_NaN();
368 }
369 }
370 else
371 {
372 QgsDebugError( u"Found unexpected value when parsing ESRI json line string. Expected list, got %1"_s.arg( curveData.metaType().name() ) );
373 return nullptr;
374 }
375 }
376
377 if ( actualLineSize == 0 )
378 return nullptr;
379
380 lineX.resize( actualLineSize );
381 lineY.resize( actualLineSize );
382 if ( hasZ )
383 lineZ.resize( actualLineSize );
384 if ( hasM )
385 lineM.resize( actualLineSize );
386 return std::make_unique< QgsLineString>( lineX, lineY, lineZ, lineM );
387}
388
389std::unique_ptr< QgsPoint > QgsArcGisRestUtils::convertGeometryPoint( const QVariantMap &geometryData, Qgis::WkbType pointType )
390{
391 // {"x" : <x>, "y" : <y>, "z" : <z>, "m" : <m>}
392 bool xok = false, yok = false;
393 double x = geometryData[u"x"_s].toDouble( &xok );
394 double y = geometryData[u"y"_s].toDouble( &yok );
395 if ( !xok || !yok )
396 return nullptr;
397 double z = geometryData[u"z"_s].toDouble();
398 double m = geometryData[u"m"_s].toDouble();
399 return std::make_unique< QgsPoint >( pointType, x, y, z, m );
400}
401
402std::unique_ptr< QgsMultiPoint > QgsArcGisRestUtils::convertMultiPoint( const QVariantMap &geometryData, Qgis::WkbType pointType )
403{
404 // {"points" : [[ <x1>, <y1>, <z1>, <m1> ] , [ <x2>, <y2>, <z2>, <m2> ], ... ]}
405 const QVariantList coordsList = geometryData[u"points"_s].toList();
406
407 auto multiPoint = std::make_unique< QgsMultiPoint >();
408 multiPoint->reserve( static_cast< int >( coordsList.size() ) );
409 for ( const QVariant &coordData : coordsList )
410 {
411 const QVariantList coordList = coordData.toList();
412 std::unique_ptr< QgsPoint > p = convertPoint( coordList, pointType );
413 if ( !p )
414 {
415 continue;
416 }
417 multiPoint->addGeometry( p.release() );
418 }
419
420 // second chance -- sometimes layers are reported as multipoint but features have single
421 // point geometries. Silently handle this and upgrade to multipoint.
422 std::unique_ptr< QgsPoint > p = convertGeometryPoint( geometryData, pointType );
423 if ( p )
424 multiPoint->addGeometry( p.release() );
425
426 if ( multiPoint->numGeometries() == 0 )
427 {
428 // didn't find any points, so reset geometry to null
429 multiPoint.reset();
430 }
431 return multiPoint;
432}
433
434std::unique_ptr< QgsMultiCurve > QgsArcGisRestUtils::convertGeometryPolyline( const QVariantMap &geometryData, Qgis::WkbType pointType, bool allowCurves )
435{
436 // {"curvePaths": [[[0,0], {"c": [[3,3],[1,4]]} ]]}
437 QVariantList pathsList;
438 if ( geometryData[u"paths"_s].isValid() )
439 pathsList = geometryData[u"paths"_s].toList();
440 else if ( geometryData[u"curvePaths"_s].isValid() )
441 pathsList = geometryData[u"curvePaths"_s].toList();
442 if ( pathsList.isEmpty() )
443 return nullptr;
444 std::unique_ptr< QgsMultiCurve > multiCurve = allowCurves ? std::make_unique< QgsMultiCurve >() : std::make_unique< QgsMultiLineString >();
445 multiCurve->reserve( static_cast< int >( pathsList.size() ) );
446 for ( const QVariant &pathData : std::as_const( pathsList ) )
447 {
448 std::unique_ptr< QgsCurve > curve = allowCurves ? convertCompoundCurve( pathData.toList(), pointType ) : convertLineString( pathData.toList(), pointType );
449 if ( !curve )
450 {
451 return nullptr;
452 }
453 multiCurve->addGeometry( curve.release() );
454 }
455 return multiCurve;
456}
457
458std::unique_ptr< QgsMultiSurface > QgsArcGisRestUtils::convertGeometryPolygon( const QVariantMap &geometryData, Qgis::WkbType pointType, bool allowCurves )
459{
460 // {"curveRings": [[[0,0], {"c": [[3,3],[1,4]]} ]]}
461 QVariantList ringsList;
462 if ( geometryData[u"rings"_s].isValid() )
463 ringsList = geometryData[u"rings"_s].toList();
464 else if ( geometryData[u"ringPaths"_s].isValid() )
465 ringsList = geometryData[u"ringPaths"_s].toList();
466 if ( ringsList.isEmpty() )
467 return nullptr;
468
469 QList< QgsCurve * > curves;
470 for ( int i = 0, n = static_cast< int >( ringsList.size() ); i < n; ++i )
471 {
472 std::unique_ptr< QgsCurve > curve = allowCurves ? convertCompoundCurve( ringsList[i].toList(), pointType ) : convertLineString( ringsList[i].toList(), pointType );
473 if ( !curve )
474 {
475 continue;
476 }
477 curves.append( curve.release() );
478 }
479 if ( curves.count() == 0 )
480 return nullptr;
481
482 std::unique_ptr< QgsMultiSurface > result = allowCurves ? std::make_unique< QgsMultiSurface >() : std::make_unique< QgsMultiPolygon >();
483 if ( curves.count() == 1 )
484 {
485 // shortcut for exterior ring only
486 std::unique_ptr< QgsCurvePolygon > newPolygon = allowCurves ? std::make_unique< QgsCurvePolygon >() : std::make_unique< QgsPolygon >();
487 newPolygon->setExteriorRing( curves.takeAt( 0 ) );
488 result->addGeometry( newPolygon.release() );
489 return result;
490 }
491
492 std::sort( curves.begin(), curves.end(), []( const QgsCurve *a, const QgsCurve *b ) -> bool {
493 double a_area = 0.0;
494 double b_area = 0.0;
495 a->sumUpArea( a_area );
496 b->sumUpArea( b_area );
497 return std::abs( a_area ) > std::abs( b_area );
498 } );
499 result->reserve( static_cast< int >( curves.size() ) );
500 while ( !curves.isEmpty() )
501 {
502 QgsCurve *exterior = curves.takeFirst();
503 QgsCurvePolygon *newPolygon = allowCurves ? new QgsCurvePolygon() : new QgsPolygon();
504 newPolygon->setExteriorRing( exterior );
505 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( newPolygon ) );
506 engine->prepareGeometry();
507
508 QMutableListIterator< QgsCurve * > it( curves );
509 while ( it.hasNext() )
510 {
511 QgsCurve *curve = it.next();
512 QgsRectangle boundingBox = newPolygon->boundingBox();
513 if ( boundingBox.intersects( curve->boundingBox() ) )
514 {
515 QgsPoint point = curve->startPoint();
516 if ( engine->contains( &point ) )
517 {
518 newPolygon->addInteriorRing( curve );
519 it.remove();
520 engine.reset( QgsGeometry::createGeometryEngine( newPolygon ) );
521 engine->prepareGeometry();
522 }
523 }
524 }
525 result->addGeometry( newPolygon );
526 }
527 if ( result->numGeometries() == 0 )
528 return nullptr;
529
530 return result;
531}
532
533std::unique_ptr< QgsPolygon > QgsArcGisRestUtils::convertEnvelope( const QVariantMap &geometryData )
534{
535 // {"xmin" : -109.55, "ymin" : 25.76, "xmax" : -86.39, "ymax" : 49.94}
536 bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false;
537 double xmin = geometryData[u"xmin"_s].toDouble( &xminOk );
538 double ymin = geometryData[u"ymin"_s].toDouble( &yminOk );
539 double xmax = geometryData[u"xmax"_s].toDouble( &xmaxOk );
540 double ymax = geometryData[u"ymax"_s].toDouble( &ymaxOk );
541 if ( !xminOk || !yminOk || !xmaxOk || !ymaxOk )
542 return nullptr;
543 auto ext = std::make_unique< QgsLineString>();
544 ext->addVertex( QgsPoint( xmin, ymin ) );
545 ext->addVertex( QgsPoint( xmax, ymin ) );
546 ext->addVertex( QgsPoint( xmax, ymax ) );
547 ext->addVertex( QgsPoint( xmin, ymax ) );
548 ext->addVertex( QgsPoint( xmin, ymin ) );
549 auto poly = std::make_unique< QgsPolygon >();
550 poly->setExteriorRing( ext.release() );
551 return poly;
552}
553
554std::unique_ptr< QgsAbstractGeometry > QgsArcGisRestUtils::convertGeometry(
555 const QVariantMap &geometryData, const QString &esriGeometryType, bool readM, bool readZ, bool allowCurves, QgsCoordinateReferenceSystem *crs
556)
557{
558 Qgis::WkbType pointType = QgsWkbTypes::zmType( Qgis::WkbType::Point, readZ, readM );
559 if ( crs )
560 {
561 *crs = convertSpatialReference( geometryData[u"spatialReference"_s].toMap() );
562 }
563
564 // http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#/Geometry_Objects/02r3000000n1000000/
565 if ( esriGeometryType == "esriGeometryNull"_L1 )
566 return nullptr;
567 else if ( esriGeometryType == "esriGeometryPoint"_L1 )
568 return convertGeometryPoint( geometryData, pointType );
569 else if ( esriGeometryType == "esriGeometryMultipoint"_L1 )
570 return convertMultiPoint( geometryData, pointType );
571 else if ( esriGeometryType == "esriGeometryPolyline"_L1 )
572 return convertGeometryPolyline( geometryData, pointType, allowCurves );
573 else if ( esriGeometryType == "esriGeometryPolygon"_L1 )
574 return convertGeometryPolygon( geometryData, pointType, allowCurves );
575 else if ( esriGeometryType == "esriGeometryEnvelope"_L1 )
576 return convertEnvelope( geometryData );
577 // Unsupported (either by qgis, or format unspecified by the specification)
578 // esriGeometryCircularArc
579 // esriGeometryEllipticArc
580 // esriGeometryBezier3Curve
581 // esriGeometryPath
582 // esriGeometryRing
583 // esriGeometryLine
584 // esriGeometryAny
585 // esriGeometryMultiPatch
586 // esriGeometryTriangleStrip
587 // esriGeometryTriangleFan
588 // esriGeometryRay
589 // esriGeometrySphere
590 // esriGeometryTriangles
591 // esriGeometryBag
592 return nullptr;
593}
594
596{
598
599 QString spatialReference = spatialReferenceMap[u"latestWkid"_s].toString();
600 if ( spatialReference.isEmpty() )
601 spatialReference = spatialReferenceMap[u"wkid"_s].toString();
602
603 // prefer using authority/id wherever we can
604 if ( !spatialReference.isEmpty() )
605 {
606 crs.createFromString( u"EPSG:%1"_s.arg( spatialReference ) );
607 if ( !crs.isValid() )
608 {
609 // Try as an ESRI auth
610 crs.createFromString( u"ESRI:%1"_s.arg( spatialReference ) );
611 }
612 }
613 else if ( !spatialReferenceMap[u"wkt"_s].toString().isEmpty() )
614 {
615 // otherwise fallback to WKT
616 crs.createFromWkt( spatialReferenceMap[u"wkt"_s].toString() );
617 }
618
619 if ( !crs.isValid() )
620 {
621 // If no spatial reference, just use WGS84
622 // TODO -- this needs further investigation! Most ESRI server services default to 3857, so that would likely be
623 // a safer fallback to use...
624 crs.createFromString( u"EPSG:4326"_s );
625 }
626 return crs;
627}
628
629std::unique_ptr< QgsSymbol > QgsArcGisRestUtils::convertSymbol( const QVariantMap &symbolData, QgsSymbolConverterContext &context )
630{
631 return QgsSymbolConverterEsriRest().createSymbol( symbolData, context );
632}
633
634std::unique_ptr< QgsSymbol > QgsArcGisRestUtils::convertSymbol( const QVariantMap &symbolData )
635{
636 QgsReadWriteContext rwContext;
637 QgsSymbolConverterContext context( rwContext );
638 return convertSymbol( symbolData, context );
639}
640
641std::unique_ptr<QgsAbstractVectorLayerLabeling > QgsArcGisRestUtils::convertLabeling( const QVariantList &labelingData )
642{
643 if ( labelingData.empty() )
644 return nullptr;
645
646 QgsRuleBasedLabeling::Rule *root = new QgsRuleBasedLabeling::Rule( new QgsPalLayerSettings(), 0, 0, QString(), QString(), false );
647 root->setActive( true );
648
649 int i = 1;
650 for ( const QVariant &lbl : labelingData )
651 {
652 const QVariantMap labeling = lbl.toMap();
653
655 QgsTextFormat format;
656
657 const QString placement = labeling.value( u"labelPlacement"_s ).toString();
658 if ( placement == "esriServerPointLabelPlacementAboveCenter"_L1 )
659 {
662 }
663 else if ( placement == "esriServerPointLabelPlacementBelowCenter"_L1 )
664 {
667 }
668 else if ( placement == "esriServerPointLabelPlacementCenterCenter"_L1 )
669 {
672 }
673 else if ( placement == "esriServerPointLabelPlacementAboveLeft"_L1 )
674 {
677 }
678 else if ( placement == "esriServerPointLabelPlacementBelowLeft"_L1 )
679 {
682 }
683 else if ( placement == "esriServerPointLabelPlacementCenterLeft"_L1 )
684 {
687 }
688 else if ( placement == "esriServerPointLabelPlacementAboveRight"_L1 )
689 {
692 }
693 else if ( placement == "esriServerPointLabelPlacementBelowRight"_L1 )
694 {
697 }
698 else if ( placement == "esriServerPointLabelPlacementCenterRight"_L1 )
699 {
702 }
703 else if ( placement == "esriServerLinePlacementAboveAfter"_L1 || placement == "esriServerLinePlacementAboveStart"_L1 || placement == "esriServerLinePlacementAboveAlong"_L1 )
704 {
707 }
708 else if ( placement == "esriServerLinePlacementBelowAfter"_L1 || placement == "esriServerLinePlacementBelowStart"_L1 || placement == "esriServerLinePlacementBelowAlong"_L1 )
709 {
712 }
713 else if ( placement == "esriServerLinePlacementCenterAfter"_L1 || placement == "esriServerLinePlacementCenterStart"_L1 || placement == "esriServerLinePlacementCenterAlong"_L1 )
714 {
717 }
718 else if ( placement == "esriServerPolygonPlacementAlwaysHorizontal"_L1 )
719 {
721 }
722
723 const bool repeatLabel = labeling.value( u"repeatLabel"_s ).toBool();
724 if ( repeatLabel && settings->placement == Qgis::LabelPlacement::Line )
725 {
726 const double repeatLabelDistance = labeling.value( u"repeatLabelDistance"_s ).toDouble();
727 if ( repeatLabelDistance > 0.0 )
728 {
729 settings->repeatDistance = repeatLabelDistance;
731 }
732 }
733
734 const double minScale = labeling.value( u"minScale"_s ).toDouble();
735 const double maxScale = labeling.value( u"maxScale"_s ).toDouble();
736
737 QVariantMap symbol = labeling.value( u"symbol"_s ).toMap();
738 format.setColor( convertColor( symbol.value( u"color"_s ) ) );
739 const double haloSize = symbol.value( u"haloSize"_s ).toDouble();
740 if ( !qgsDoubleNear( haloSize, 0.0 ) )
741 {
743 buffer.setEnabled( true );
744 buffer.setSize( haloSize );
746 buffer.setColor( convertColor( symbol.value( u"haloColor"_s ) ) );
747 format.setBuffer( buffer );
748 }
749
750 const QString fontFamily = symbol.value( u"font"_s ).toMap().value( u"family"_s ).toString();
751 const QString fontStyle = symbol.value( u"font"_s ).toMap().value( u"style"_s ).toString();
752 const QString fontWeight = symbol.value( u"font"_s ).toMap().value( u"weight"_s ).toString();
753 const int fontSize = symbol.value( u"font"_s ).toMap().value( u"size"_s ).toInt();
754 QFont font( fontFamily, fontSize );
755 font.setStyleName( fontStyle );
756 font.setWeight( fontWeight == "bold"_L1 ? QFont::Bold : QFont::Normal );
757
758 format.setFont( font );
759 format.setSize( fontSize );
761
762 settings->setFormat( format );
763
764 QString where = labeling.value( u"where"_s ).toString();
765 QgsExpression exp( where );
766 // If the where clause isn't parsed as valid, don't use its
767 if ( !exp.isValid() )
768 where.clear();
769
770 settings->fieldName = convertLabelingExpression( labeling.value( u"labelExpression"_s ).toString() );
771 settings->isExpression = true;
772
773 QgsRuleBasedLabeling::Rule *child = new QgsRuleBasedLabeling::Rule( settings, maxScale, minScale, where, QObject::tr( "ASF label %1" ).arg( i++ ), false );
774 child->setActive( true );
775 root->appendChild( child );
776 }
777
778 return std::make_unique< QgsRuleBasedLabeling >( root );
779}
780
781void QgsArcGisRestUtils::applyVisualVariables( const QVariantMap &rendererData, QgsSymbol *symbol, QgsSymbolConverterContext &context )
782{
783 if ( !symbol )
784 return;
785
786 const QVariantList visualVariablesData = rendererData.value( u"visualVariables"_s ).toList();
787 for ( const QVariant &visualVariable : visualVariablesData )
788 {
789 const QVariantMap visualVariableData = visualVariable.toMap();
790 const QString variableType = visualVariableData.value( u"type"_s ).toString();
791
792 if ( variableType == "rotationInfo"_L1 )
793 {
794 const QString field = visualVariableData.value( u"field"_s ).toString();
795 if ( field.isEmpty() )
796 {
797 // Check if it was a valueExpression that we don't support yet
798 if ( !visualVariableData.value( u"valueExpression"_s ).toString().isEmpty() )
799 context.pushWarning( QObject::tr( "ESRI rotationInfo valueExpression is not yet supported" ) );
800 continue;
801 }
802
803 const QString rotationType = visualVariableData.value( u"rotationType"_s ).toString();
804
805 QgsProperty angleProperty;
806 if ( rotationType == "arithmetic"_L1 )
807 {
808 // ArcGIS arithmetic: 0° = East, counter-clockwise
809 // QGIS: 0° = North, clockwise
810 // Conversion: QGIS_angle = 90 - ArcGIS_angle
811 angleProperty = QgsProperty::fromExpression( u"90 - %1"_s.arg( QgsExpression::quotedColumnRef( field ) ) );
812 }
813 else if ( rotationType == "geographic"_L1 )
814 {
815 // ArcGIS geographic: 0° = North, clockwise (same as QGIS)
816 angleProperty = QgsProperty::fromField( field );
817 }
818 else
819 {
820 context.pushWarning( QObject::tr( "ESRI rotationInfo rotationType '%1' is not supported" ).arg( rotationType ) );
821 continue;
822 }
823
824 // Apply rotation to all symbol layers
825 for ( int layer = 0; layer < symbol->symbolLayerCount(); ++layer )
826 {
827 symbol->symbolLayer( layer )->setDataDefinedProperty( QgsSymbolLayer::Property::Angle, angleProperty );
828 }
829 }
830 else
831 {
832 context.pushWarning( QObject::tr( "ESRI visualVariable type '%1' is not currently supported" ).arg( variableType ) );
833 }
834 }
835}
836
837std::unique_ptr< QgsFeatureRenderer > QgsArcGisRestUtils::convertRenderer( const QVariantMap &rendererData, QgsSymbolConverterContext &context )
838{
839 const QString type = rendererData.value( u"type"_s ).toString();
840
841 if ( type == "simple"_L1 )
842 {
843 const QVariantMap symbolProps = rendererData.value( u"symbol"_s ).toMap();
844 std::unique_ptr< QgsSymbol > symbol( convertSymbol( symbolProps, context ) );
845 if ( symbol )
846 {
847 // Apply visual variables (e.g., rotation) to the symbol
848 applyVisualVariables( rendererData, symbol.get(), context );
849 return std::make_unique< QgsSingleSymbolRenderer >( symbol.release() );
850 }
851 else
852 return nullptr;
853 }
854 else if ( type == "uniqueValue"_L1 )
855 {
856 const QString field1 = rendererData.value( u"field1"_s ).toString();
857 const QString field2 = rendererData.value( u"field2"_s ).toString();
858 const QString field3 = rendererData.value( u"field3"_s ).toString();
859 QString attribute;
860 if ( !field2.isEmpty() || !field3.isEmpty() )
861 {
862 const QString delimiter = rendererData.value( u"fieldDelimiter"_s ).toString();
863 if ( !field3.isEmpty() )
864 {
865 attribute = u"concat(\"%1\",'%2',\"%3\",'%4',\"%5\")"_s.arg( field1, delimiter, field2, delimiter, field3 );
866 }
867 else
868 {
869 attribute = u"concat(\"%1\",'%2',\"%3\")"_s.arg( field1, delimiter, field2 );
870 }
871 }
872 else
873 {
874 attribute = field1;
875 }
876
877 const QVariantList categories = rendererData.value( u"uniqueValueInfos"_s ).toList();
878 QgsCategoryList categoryList;
879 for ( const QVariant &category : categories )
880 {
881 const QVariantMap categoryData = category.toMap();
882 const QString valueString = categoryData.value( u"value"_s ).toString();
883 // Esri uses the literal string "<Null>" to represent a NULL field value in unique value renderers.
884 const QVariant value = valueString == "<Null>"_L1 ? QVariant() : QVariant( valueString );
885 const QString label = categoryData.value( u"label"_s ).toString();
886 std::unique_ptr< QgsSymbol > symbol( QgsArcGisRestUtils::convertSymbol( categoryData.value( u"symbol"_s ).toMap(), context ) );
887 if ( symbol )
888 {
889 // Apply visual variables (e.g., rotation) to the symbol
890 applyVisualVariables( rendererData, symbol.get(), context );
891
892 categoryList.append( QgsRendererCategory( value, symbol.release(), label ) );
893 }
894 }
895
896 std::unique_ptr< QgsSymbol > defaultSymbol( convertSymbol( rendererData.value( u"defaultSymbol"_s ).toMap(), context ) );
897 if ( defaultSymbol )
898 {
899 // Apply visual variables (e.g., rotation) to the symbol
900 applyVisualVariables( rendererData, defaultSymbol.get(), context );
901 categoryList.append( QgsRendererCategory( QVariant(), defaultSymbol.release(), rendererData.value( u"defaultLabel"_s ).toString() ) );
902 }
903
904 if ( categoryList.empty() )
905 return nullptr;
906
907 auto renderer = std::make_unique< QgsCategorizedSymbolRenderer >( attribute, categoryList );
908 return renderer;
909 }
910 else if ( type == "classBreaks"_L1 )
911 {
912 const QString attrName = rendererData.value( u"field"_s ).toString();
913
914 const QVariantList classBreakInfos = rendererData.value( u"classBreakInfos"_s ).toList();
915 const QVariantMap authoringInfo = rendererData.value( u"authoringInfo"_s ).toMap();
916 QVariantMap symbolData;
917
918 QString esriMode = authoringInfo.value( u"classificationMethod"_s ).toString();
919 if ( esriMode.isEmpty() )
920 {
921 esriMode = rendererData.value( u"classificationMethod"_s ).toString();
922 }
923
924 if ( !classBreakInfos.isEmpty() )
925 {
926 symbolData = classBreakInfos.at( 0 ).toMap().value( u"symbol"_s ).toMap();
927 }
928 std::unique_ptr< QgsSymbol > symbol( QgsArcGisRestUtils::convertSymbol( symbolData, context ) );
929 if ( !symbol )
930 return nullptr;
931
932 const QVariantList visualVariablesData = rendererData.value( u"visualVariables"_s ).toList();
933
934 for ( const QVariant &visualVariable : visualVariablesData )
935 {
936 const QVariantMap visualVariableData = visualVariable.toMap();
937 const QString variableType = visualVariableData.value( u"type"_s ).toString();
938 if ( variableType == "sizeInfo"_L1 )
939 {
940 continue;
941 }
942 else if ( variableType == "colorInfo"_L1 )
943 {
944 const QVariantList stops = visualVariableData.value( u"stops"_s ).toList();
945 if ( stops.size() < 2 )
946 continue;
947
948 // layer has continuous coloring, so convert to a symbol using color ramp assistant
949 bool ok = false;
950 const double minValue = stops.front().toMap().value( u"value"_s ).toDouble( &ok );
951 if ( !ok )
952 continue;
953 const QColor minColor = convertColor( stops.front().toMap().value( u"color"_s ) );
954
955 const double maxValue = stops.back().toMap().value( u"value"_s ).toDouble( &ok );
956 if ( !ok )
957 continue;
958 const QColor maxColor = convertColor( stops.back().toMap().value( u"color"_s ) );
959
960 QgsGradientStopsList gradientStops;
961 for ( int i = 1; i < stops.size() - 1; ++i )
962 {
963 const QVariantMap stopData = stops.at( i ).toMap();
964 const double breakpoint = stopData.value( u"value"_s ).toDouble();
965 const double scaledBreakpoint = ( breakpoint - minValue ) / ( maxValue - minValue );
966 const QColor fillColor = convertColor( stopData.value( u"color"_s ) );
967
968 gradientStops.append( QgsGradientStop( scaledBreakpoint, fillColor ) );
969 }
970
971 auto colorRamp = std::make_unique< QgsGradientColorRamp >( minColor, maxColor, false, gradientStops );
972
973 QgsProperty colorProperty = QgsProperty::fromField( attrName );
974 colorProperty.setTransformer( new QgsColorRampTransformer( minValue, maxValue, colorRamp.release() ) );
975 for ( int layer = 0; layer < symbol->symbolLayerCount(); ++layer )
976 {
977 symbol->symbolLayer( layer )->setDataDefinedProperty( QgsSymbolLayer::Property::FillColor, colorProperty );
978 }
979
980 return std::make_unique< QgsSingleSymbolRenderer >( symbol.release() );
981 }
982 else if ( variableType == "rotationInfo"_L1 )
983 {
984 // Rotation will be handled below after the renderer is created
985 continue;
986 }
987 else
988 {
989 context.pushWarning( QObject::tr( "ESRI visualVariable type '%1' is not currently supported" ).arg( variableType ) );
990 }
991 }
992
993 double lastValue = rendererData.value( u"minValue"_s ).toDouble();
994
995 auto graduatedRenderer = std::make_unique< QgsGraduatedSymbolRenderer >( attrName );
996
997 graduatedRenderer->setSourceSymbol( symbol.release() );
998
999 if ( esriMode == "esriClassifyDefinedInterval"_L1 )
1000 {
1002 graduatedRenderer->setClassificationMethod( method );
1003 }
1004 else if ( esriMode == "esriClassifyEqualInterval"_L1 )
1005 {
1007 graduatedRenderer->setClassificationMethod( method );
1008 }
1009 else if ( esriMode == "esriClassifyGeometricalInterval"_L1 )
1010 {
1012 graduatedRenderer->setClassificationMethod( method );
1013 }
1014 else if ( esriMode == "esriClassifyManual"_L1 )
1015 {
1017 graduatedRenderer->setClassificationMethod( method );
1018 }
1019 else if ( esriMode == "esriClassifyNaturalBreaks"_L1 )
1020 {
1022 graduatedRenderer->setClassificationMethod( method );
1023 }
1024 else if ( esriMode == "esriClassifyQuantile"_L1 )
1025 {
1027 graduatedRenderer->setClassificationMethod( method );
1028 }
1029 else if ( esriMode == "esriClassifyStandardDeviation"_L1 )
1030 {
1032 graduatedRenderer->setClassificationMethod( method );
1033 }
1034 else if ( !esriMode.isEmpty() )
1035 {
1036 context.pushWarning( QObject::tr( "ESRI classification mode '%1' is not currently supported" ).arg( esriMode ) );
1037 }
1038
1039 for ( const QVariant &classBreakInfo : classBreakInfos )
1040 {
1041 const QVariantMap symbolData = classBreakInfo.toMap().value( u"symbol"_s ).toMap();
1042 std::unique_ptr< QgsSymbol > symbol( QgsArcGisRestUtils::convertSymbol( symbolData, context ) );
1043 double classMaxValue = classBreakInfo.toMap().value( u"classMaxValue"_s ).toDouble();
1044 const QString label = classBreakInfo.toMap().value( u"label"_s ).toString();
1045
1046 // Apply visual variables (e.g., rotation) to the symbol
1047 applyVisualVariables( rendererData, symbol.get(), context );
1048
1049 QgsRendererRange range;
1050
1051 range.setLowerValue( lastValue );
1052 range.setUpperValue( classMaxValue );
1053 range.setLabel( label );
1054 range.setSymbol( symbol.release() );
1055
1056 lastValue = classMaxValue;
1057 graduatedRenderer->addClass( range );
1058 }
1059
1060 return graduatedRenderer;
1061 }
1062 else if ( type == "heatmap"_L1 )
1063 {
1064 // currently unsupported
1065 return nullptr;
1066 }
1067 else if ( type == "vectorField"_L1 )
1068 {
1069 // currently unsupported
1070 return nullptr;
1071 }
1072 return nullptr;
1073}
1074
1075std::unique_ptr< QgsFeatureRenderer > QgsArcGisRestUtils::convertRenderer( const QVariantMap &rendererData )
1076{
1077 QgsReadWriteContext rwContext;
1078 QgsSymbolConverterContext context( rwContext );
1079 return convertRenderer( rendererData, context );
1080}
1081
1083{
1084 QString expression = string;
1085
1086 // Replace a few ArcGIS token to QGIS equivalents
1087 const thread_local QRegularExpression rx1 = QRegularExpression( u"(?=([^\"\\\\]*(\\\\.|\"([^\"\\\\]*\\\\.)*[^\"\\\\]*\"))*[^\"]*$)(\\s|^)CONCAT(\\s|$)"_s );
1088 expression = expression.replace( rx1, u"\\4||\\5"_s );
1089
1090 const thread_local QRegularExpression rx2 = QRegularExpression( u"(?=([^\"\\\\]*(\\\\.|\"([^\"\\\\]*\\\\.)*[^\"\\\\]*\"))*[^\"]*$)(\\s|^)NEWLINE(\\s|$)"_s );
1091 expression = expression.replace( rx2, u"\\4'\\n'\\5"_s );
1092
1093 // ArcGIS's double quotes are single quotes in QGIS
1094 const thread_local QRegularExpression rx3 = QRegularExpression( u"\"(.*?(?<!\\\\))\""_s );
1095 expression = expression.replace( rx3, u"'\\1'"_s );
1096 const thread_local QRegularExpression rx4 = QRegularExpression( u"\\\\\""_s );
1097 expression = expression.replace( rx4, u"\""_s );
1098
1099 // ArcGIS's square brakets are double quotes in QGIS
1100 const thread_local QRegularExpression rx5 = QRegularExpression( u"\\[([^]]*)\\]"_s );
1101 expression = expression.replace( rx5, u"\"\\1\""_s );
1102
1103 return expression;
1104}
1105
1106QColor QgsArcGisRestUtils::convertColor( const QVariant &colorData )
1107{
1108 return QgsSymbolConverterEsriRest::convertColor( colorData );
1109}
1110
1111Qt::PenStyle QgsArcGisRestUtils::convertLineStyle( const QString &style )
1112{
1114}
1115
1116Qt::BrushStyle QgsArcGisRestUtils::convertFillStyle( const QString &style )
1117{
1119}
1120
1121QDateTime QgsArcGisRestUtils::convertDateTime( const QVariant &value )
1122{
1123 if ( QgsVariantUtils::isNull( value ) )
1124 return QDateTime();
1125 bool ok = false;
1126 QDateTime dt = QDateTime::fromMSecsSinceEpoch( value.toLongLong( &ok ) );
1127 if ( !ok )
1128 {
1129 QgsDebugError( u"Invalid value %1 for datetime"_s.arg( value.toString() ) );
1130 return QDateTime();
1131 }
1132 else
1133 return dt;
1134}
1135
1137{
1138 if ( QgsVariantUtils::isNull( value ) )
1139 return QgsRectangle();
1140
1141 const QVariantMap coords = value.toMap();
1142 if ( coords.isEmpty() )
1143 return QgsRectangle();
1144
1145 bool ok;
1146
1147 const double xmin = coords.value( u"xmin"_s ).toDouble( &ok );
1148 if ( !ok )
1149 return QgsRectangle();
1150
1151 const double ymin = coords.value( u"ymin"_s ).toDouble( &ok );
1152 if ( !ok )
1153 return QgsRectangle();
1154
1155 const double xmax = coords.value( u"xmax"_s ).toDouble( &ok );
1156 if ( !ok )
1157 return QgsRectangle();
1158
1159 const double ymax = coords.value( u"ymax"_s ).toDouble( &ok );
1160 if ( !ok )
1161 return QgsRectangle();
1162
1163 return QgsRectangle( xmin, ymin, xmax, ymax );
1164}
1165
1166
1168{
1169 QVariantMap res;
1170 if ( geometry.isNull() )
1171 return QVariantMap();
1172
1173 const QgsAbstractGeometry *geom = geometry.constGet()->simplifiedTypeRef();
1174 switch ( QgsWkbTypes::flatType( geom->wkbType() ) )
1175 {
1178 return QVariantMap();
1179
1181 res = pointToJson( qgsgeometry_cast< const QgsPoint * >( geom ) );
1182 break;
1183
1185 res = lineStringToJson( qgsgeometry_cast< const QgsLineString * >( geom ) );
1186 break;
1187
1190 res = curveToJson( qgsgeometry_cast< const QgsCurve * >( geom ) );
1191 break;
1192
1194 res = polygonToJson( qgsgeometry_cast< const QgsPolygon * >( geom ) );
1195 break;
1196
1198 res = multiPointToJson( qgsgeometry_cast< const QgsMultiPoint * >( geom ) );
1199 break;
1200
1202 res = multiLineStringToJson( qgsgeometry_cast< const QgsMultiLineString * >( geom ) );
1203 break;
1204
1206 res = multiCurveToJson( qgsgeometry_cast< const QgsMultiCurve * >( geom ) );
1207 break;
1208
1210 res = multiPolygonToJson( qgsgeometry_cast< const QgsMultiPolygon * >( geom ) );
1211 break;
1212
1214 res = curvePolygonToJson( qgsgeometry_cast< const QgsCurvePolygon * >( geom ) );
1215 break;
1216
1218 res = multiSurfaceToJson( qgsgeometry_cast< const QgsMultiSurface * >( geom ) );
1219 break;
1220
1222 return QVariantMap(); // not supported by REST API
1223
1225 return QVariantMap(); //not yet supported, but could be
1226
1227 default:
1228 return QVariantMap(); //unreachable
1229 }
1230
1231 if ( crs.isValid() )
1232 {
1233 // add spatialReference information
1234 res.insert( u"spatialReference"_s, crsToJson( crs ) );
1235 }
1236
1237 return res;
1238}
1239
1240QVariantMap QgsArcGisRestUtils::pointToJson( const QgsPoint *point )
1241{
1242 QVariantMap data;
1243 if ( point->isEmpty() )
1244 data[u"x"_s] = u"NaN"_s;
1245 else
1246 {
1247 data[u"x"_s] = point->x();
1248 data[u"y"_s] = point->y();
1249
1250 if ( point->is3D() )
1251 data[u"z"_s] = !std::isnan( point->z() ) ? QVariant( point->z() ) : QVariant( u"NaN"_s );
1252
1253 if ( point->isMeasure() )
1254 data[u"m"_s] = !std::isnan( point->m() ) ? QVariant( point->m() ) : QVariant( u"NaN"_s );
1255 }
1256 return data;
1257}
1258
1259QVariantMap QgsArcGisRestUtils::multiPointToJson( const QgsMultiPoint *multiPoint )
1260{
1261 QVariantMap data;
1262 const bool hasZ = multiPoint->is3D();
1263 const bool hasM = multiPoint->isMeasure();
1264 data[u"hasM"_s] = hasM;
1265 data[u"hasZ"_s] = hasZ;
1266
1267 QVariantList pointsList;
1268 const int size = multiPoint->numGeometries();
1269 pointsList.reserve( size );
1270
1271 QVariantList pointList;
1272 for ( int i = 0; i < size; ++i )
1273 {
1274 const QgsPoint *point = multiPoint->pointN( i );
1275
1276 pointList.clear();
1277 pointList.append( point->x() );
1278 pointList.append( point->y() );
1279 if ( hasZ )
1280 pointList.append( point->z() );
1281 if ( hasM && !std::isnan( point->m() ) )
1282 pointList.append( point->m() );
1283
1284 pointsList.push_back( pointList );
1285 }
1286
1287 data[u"points"_s] = pointsList;
1288 return data;
1289}
1290
1291QVariantList QgsArcGisRestUtils::lineStringToJsonPath( const QgsLineString *line )
1292{
1293 const bool hasZ = line->is3D();
1294 const bool hasM = line->isMeasure();
1295
1296 QVariantList pointsList;
1297 const int size = line->numPoints();
1298 pointsList.reserve( size );
1299
1300 QVariantList pointList;
1301 const double *xData = line->xData();
1302 const double *yData = line->yData();
1303 const double *zData = hasZ ? line->zData() : nullptr;
1304 const double *mData = hasM ? line->mData() : nullptr;
1305
1306 for ( int i = 0; i < size; ++i )
1307 {
1308 pointList.clear();
1309 pointList.append( *xData++ );
1310 pointList.append( *yData++ );
1311
1312 if ( hasZ )
1313 pointList.append( *zData++ );
1314
1315 if ( hasM && !std::isnan( *mData ) )
1316 pointList.append( *mData );
1317 if ( hasM )
1318 mData++;
1319
1320 pointsList.push_back( pointList );
1321 }
1322 return pointsList;
1323}
1324
1325QVariantList QgsArcGisRestUtils::curveToJsonCurve( const QgsCurve *curve, bool includeStart )
1326{
1327 const bool hasZ = curve->is3D();
1328 const bool hasM = curve->isMeasure();
1329
1330 auto pointToList = [hasZ, hasM]( const QgsPoint &point ) -> QVariantList {
1331 QVariantList pointList;
1332
1333 pointList.append( point.x() );
1334 pointList.append( point.y() );
1335
1336 if ( hasZ )
1337 pointList.append( point.z() );
1338
1339 if ( hasM && !std::isnan( point.m() ) )
1340 pointList.append( point.m() );
1341
1342 return pointList;
1343 };
1344
1345 QVariantList res;
1346 switch ( QgsWkbTypes::flatType( curve->wkbType() ) )
1347 {
1349 {
1350 QVariantList part = lineStringToJsonPath( qgsgeometry_cast< const QgsLineString *>( curve ) );
1351 if ( !part.isEmpty() && !includeStart )
1352 part.removeAt( 0 );
1353 res = part;
1354 break;
1355 }
1356
1358 {
1359 const QgsCircularString *circularString = qgsgeometry_cast<const QgsCircularString * >( curve );
1360 if ( includeStart && !circularString->isEmpty() )
1361 {
1362 res.push_back( pointToList( circularString->startPoint() ) );
1363 }
1364
1365 const int size = circularString->numPoints();
1366 for ( int i = 1; i + 1 < size; i += 2 )
1367 {
1368 // end point comes BEFORE interior point!
1369 QVariantMap curvePart;
1370 QVariantList curveList;
1371 curveList.push_back( pointToList( circularString->pointN( i + 1 ) ) );
1372
1373 curveList.push_back( pointToList( circularString->pointN( i ) ) );
1374
1375 curvePart.insert( u"c"_s, curveList );
1376 res.push_back( curvePart );
1377 }
1378 break;
1379 }
1380
1382 {
1383 const QgsCompoundCurve *compoundCurve = qgsgeometry_cast<const QgsCompoundCurve * >( curve );
1384
1385 const int size = compoundCurve->nCurves();
1386 for ( int i = 0; i < size; ++i )
1387 {
1388 const QgsCurve *subCurve = compoundCurve->curveAt( i );
1389 res.append( curveToJsonCurve( subCurve, i == 0 ) );
1390 }
1391 break;
1392 }
1393
1394 default:
1395 break;
1396 }
1397 return res;
1398}
1399
1400QVariantMap QgsArcGisRestUtils::lineStringToJson( const QgsLineString *line )
1401{
1402 QVariantMap data;
1403 const bool hasZ = line->is3D();
1404 const bool hasM = line->isMeasure();
1405 data[u"hasM"_s] = hasM;
1406 data[u"hasZ"_s] = hasZ;
1407
1408 const QVariantList pointsList = lineStringToJsonPath( line );
1409
1410 QVariantList pointsData = QVariantList();
1411 pointsData.push_back( pointsList );
1412 data[u"paths"_s] = pointsData;
1413
1414 return data;
1415}
1416
1417QVariantMap QgsArcGisRestUtils::curveToJson( const QgsCurve *curve )
1418{
1419 QVariantMap data;
1420 const bool hasZ = curve->is3D();
1421 const bool hasM = curve->isMeasure();
1422 data[u"hasM"_s] = hasM;
1423 data[u"hasZ"_s] = hasZ;
1424
1425 const QVariantList curveList = curveToJsonCurve( curve, true );
1426
1427 QVariantList curveData = QVariantList();
1428 curveData.push_back( curveList );
1429 data[u"curvePaths"_s] = curveData;
1430
1431 return data;
1432}
1433
1434QVariantMap QgsArcGisRestUtils::multiLineStringToJson( const QgsMultiLineString *multiLine )
1435{
1436 QVariantMap data;
1437 const bool hasZ = multiLine->is3D();
1438 const bool hasM = multiLine->isMeasure();
1439 data[u"hasM"_s] = hasM;
1440 data[u"hasZ"_s] = hasZ;
1441
1442 const int size = multiLine->numGeometries();
1443 QVariantList paths;
1444 paths.reserve( size );
1445 for ( int i = 0; i < size; ++i )
1446 {
1447 const QgsLineString *line = multiLine->lineStringN( i );
1448 paths.push_back( lineStringToJsonPath( line ) );
1449 }
1450
1451 data[u"paths"_s] = paths;
1452 return data;
1453}
1454
1455QVariantMap QgsArcGisRestUtils::multiCurveToJson( const QgsMultiCurve *multiCurve )
1456{
1457 QVariantMap data;
1458 const bool hasZ = multiCurve->is3D();
1459 const bool hasM = multiCurve->isMeasure();
1460 data[u"hasM"_s] = hasM;
1461 data[u"hasZ"_s] = hasZ;
1462
1463 const int size = multiCurve->numGeometries();
1464 QVariantList paths;
1465 paths.reserve( size );
1466 for ( int i = 0; i < size; ++i )
1467 {
1468 const QgsCurve *curve = multiCurve->curveN( i );
1469 paths.push_back( curveToJsonCurve( curve, true ) );
1470 }
1471
1472 data[u"curvePaths"_s] = paths;
1473 return data;
1474}
1475
1476QVariantList QgsArcGisRestUtils::polygonToJsonRings( const QgsPolygon *polygon )
1477{
1478 QVariantList rings;
1479 const int numInteriorRings = polygon->numInteriorRings();
1480 rings.reserve( numInteriorRings + 1 );
1481
1482 if ( const QgsLineString *exterior = qgsgeometry_cast< const QgsLineString * >( polygon->exteriorRing() ) )
1483 {
1484 // exterior ring MUST be clockwise
1485 switch ( exterior->orientation() )
1486 {
1488 rings.push_back( lineStringToJsonPath( exterior ) );
1489 break;
1490
1492 {
1493 std::unique_ptr< QgsLineString > reversed( exterior->reversed() );
1494 rings.push_back( lineStringToJsonPath( reversed.get() ) );
1495 break;
1496 }
1498 break;
1499 }
1500 }
1501
1502 for ( int i = 0; i < numInteriorRings; ++i )
1503 {
1504 const QgsLineString *ring = qgsgeometry_cast< const QgsLineString * >( polygon->interiorRing( i ) );
1505 // holes MUST be counter-clockwise
1506 switch ( ring->orientation() )
1507 {
1509 rings.push_back( lineStringToJsonPath( ring ) );
1510 break;
1511
1513 {
1514 std::unique_ptr< QgsLineString > reversed( ring->reversed() );
1515 rings.push_back( lineStringToJsonPath( reversed.get() ) );
1516 break;
1517 }
1519 break;
1520 }
1521 }
1522 return rings;
1523}
1524
1525QVariantList QgsArcGisRestUtils::curvePolygonToJsonRings( const QgsCurvePolygon *polygon )
1526{
1527 QVariantList rings;
1528 const int numInteriorRings = polygon->numInteriorRings();
1529 rings.reserve( numInteriorRings + 1 );
1530
1531 if ( const QgsCurve *exterior = qgsgeometry_cast< const QgsCurve * >( polygon->exteriorRing() ) )
1532 {
1533 // exterior ring MUST be clockwise
1534 switch ( exterior->orientation() )
1535 {
1537 rings.push_back( curveToJsonCurve( exterior, true ) );
1538 break;
1539
1541 {
1542 std::unique_ptr< QgsCurve > reversed( exterior->reversed() );
1543 rings.push_back( curveToJsonCurve( reversed.get(), true ) );
1544 break;
1545 }
1547 break;
1548 }
1549 }
1550
1551 for ( int i = 0; i < numInteriorRings; ++i )
1552 {
1553 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( polygon->interiorRing( i ) );
1554 // holes MUST be counter-clockwise
1555 switch ( ring->orientation() )
1556 {
1558 rings.push_back( curveToJsonCurve( ring, true ) );
1559 break;
1560
1562 {
1563 std::unique_ptr< QgsCurve > reversed( ring->reversed() );
1564 rings.push_back( curveToJsonCurve( reversed.get(), true ) );
1565 break;
1566 }
1568 break;
1569 }
1570 }
1571 return rings;
1572}
1573
1574QVariantMap QgsArcGisRestUtils::polygonToJson( const QgsPolygon *polygon )
1575{
1576 QVariantMap data;
1577 const bool hasZ = polygon->is3D();
1578 const bool hasM = polygon->isMeasure();
1579 data[u"hasM"_s] = hasM;
1580 data[u"hasZ"_s] = hasZ;
1581 data[u"rings"_s] = polygonToJsonRings( polygon );
1582 return data;
1583}
1584
1585QVariantMap QgsArcGisRestUtils::curvePolygonToJson( const QgsCurvePolygon *polygon )
1586{
1587 QVariantMap data;
1588 const bool hasZ = polygon->is3D();
1589 const bool hasM = polygon->isMeasure();
1590 data[u"hasM"_s] = hasM;
1591 data[u"hasZ"_s] = hasZ;
1592 data[u"curveRings"_s] = curvePolygonToJsonRings( polygon );
1593 return data;
1594}
1595
1596QVariantMap QgsArcGisRestUtils::multiPolygonToJson( const QgsMultiPolygon *multiPolygon )
1597{
1598 QVariantMap data;
1599 const bool hasZ = multiPolygon->is3D();
1600 const bool hasM = multiPolygon->isMeasure();
1601 data[u"hasM"_s] = hasM;
1602 data[u"hasZ"_s] = hasZ;
1603
1604 const int size = multiPolygon->numGeometries();
1605 QVariantList rings;
1606 for ( int i = 0; i < size; ++i )
1607 {
1608 const QgsPolygon *polygon = multiPolygon->polygonN( i );
1609 rings.append( polygonToJsonRings( polygon ) );
1610 }
1611
1612 data[u"rings"_s] = rings;
1613 return data;
1614}
1615
1616QVariantMap QgsArcGisRestUtils::multiSurfaceToJson( const QgsMultiSurface *multiSurface )
1617{
1618 QVariantMap data;
1619 const bool hasZ = multiSurface->is3D();
1620 const bool hasM = multiSurface->isMeasure();
1621 data[u"hasM"_s] = hasM;
1622 data[u"hasZ"_s] = hasZ;
1623
1624 const int size = multiSurface->numGeometries();
1625 QVariantList rings;
1626 for ( int i = 0; i < size; ++i )
1627 {
1628 const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( multiSurface->geometryN( i ) );
1629 if ( !polygon )
1630 continue;
1631
1632 rings.append( curvePolygonToJsonRings( polygon ) );
1633 }
1634
1635 data[u"curveRings"_s] = rings;
1636 return data;
1637}
1638
1640{
1641 QVariantMap res;
1642 if ( !crs.isValid() )
1643 return res;
1644
1645 const QString authid = crs.authid();
1646 if ( !authid.isEmpty() )
1647 {
1648 const thread_local QRegularExpression rxAuthid( u"(\\w+):(\\d+)"_s );
1649 const QRegularExpressionMatch match = rxAuthid.match( authid );
1650 if ( match.hasMatch() && ( ( match.captured( 1 ).compare( "EPSG"_L1, Qt::CaseInsensitive ) == 0 ) || ( match.captured( 1 ).compare( "ESRI"_L1, Qt::CaseInsensitive ) == 0 ) ) )
1651 {
1652 const QString wkid = match.captured( 2 );
1653 res.insert( u"wkid"_s, wkid );
1654 return res;
1655 }
1656 }
1657
1658 // docs don't mention the WKT version support, so let's hope for 2.0...
1659 res.insert( u"wkt"_s, crs.toWkt( Qgis::CrsWktVariant::Wkt2_2019Simplified ) );
1660
1661 return res;
1662}
1663
1665{
1666 QVariantMap res;
1667 if ( ( flags & FeatureToJsonFlag::IncludeGeometry ) && feature.hasGeometry() )
1668 {
1669 res.insert( u"geometry"_s, geometryToJson( feature.geometry(), context, crs ) );
1670 }
1671
1672 QVariantMap attributes;
1673 const QgsFields fields = feature.fields();
1674 for ( const QgsField &field : fields )
1675 {
1676 QVariant value = feature.attribute( field.name() );
1677 if ( value.userType() == qMetaTypeId< QgsUnsetAttributeValue >() )
1678 {
1679 if ( flags.testFlag( FeatureToJsonFlag::SkipUnsetAttributes ) )
1680 continue;
1681 else
1682 value = QVariant(); // reset to null, we can't store 'QgsUnsetAttributeValue' as json
1683 }
1684
1685 if ( ( flags & FeatureToJsonFlag::IncludeNonObjectIdAttributes ) || field.name() == context.objectIdFieldName() )
1686 attributes.insert( field.name(), variantToAttributeValue( value, field.type(), context ) );
1687 }
1688 if ( !attributes.isEmpty() )
1689 {
1690 res.insert( u"attributes"_s, attributes );
1691 }
1692 return res;
1693}
1694
1695QVariant QgsArcGisRestUtils::variantToAttributeValue( const QVariant &variant, QMetaType::Type expectedType, const QgsArcGisRestContext &context )
1696{
1697 if ( QgsVariantUtils::isNull( variant ) )
1698 return QVariant();
1699
1700 switch ( expectedType )
1701 {
1702 case QMetaType::Type::QString:
1703 {
1704 const QString escaped = variant.toString().replace( '\\', "\\\\"_L1 ).replace( '"', "\\\""_L1 );
1705 return QString( QUrl::toPercentEncoding( escaped, "'" ) );
1706 }
1707
1708 case QMetaType::Type::QDateTime:
1709 case QMetaType::Type::QDate:
1710 {
1711 switch ( variant.userType() )
1712 {
1713 case QMetaType::Type::QDateTime:
1714 return variant.toDateTime().toMSecsSinceEpoch();
1715
1716 case QMetaType::Type::QDate:
1717 // for date values, assume start of day -- the REST api requires datetime values only, not plain dates
1718 if ( context.timeZone().isValid() )
1719 return QDateTime( variant.toDate(), QTime( 0, 0, 0 ), context.timeZone() ).toMSecsSinceEpoch();
1720 else
1721 return QDateTime( variant.toDate(), QTime( 0, 0, 0 ) ).toMSecsSinceEpoch();
1722
1723 default:
1724 return QVariant();
1725 }
1726 }
1727
1728 default:
1729 return variant;
1730 }
1731}
1732
1734{
1735 QVariantMap res;
1736 res.insert( u"name"_s, field.name() );
1737
1738 QString fieldType;
1739 switch ( field.type() )
1740 {
1741 case QMetaType::Type::LongLong:
1742 fieldType = u"esriFieldTypeInteger"_s;
1743 break;
1744
1745 case QMetaType::Type::Int:
1746 fieldType = u"esriFieldTypeSmallInteger"_s;
1747 break;
1748
1749 case QMetaType::Type::Double:
1750 fieldType = u"esriFieldTypeDouble"_s;
1751 break;
1752
1753 case QMetaType::Type::QString:
1754 fieldType = u"esriFieldTypeString"_s;
1755 break;
1756
1757 case QMetaType::Type::QDateTime:
1758 case QMetaType::Type::QDate:
1759 fieldType = u"esriFieldTypeDate"_s;
1760 break;
1761
1762 case QMetaType::Type::QByteArray:
1763 fieldType = u"esriFieldTypeBlob"_s;
1764 break;
1765
1766 default:
1767 // fallback to string
1768 fieldType = u"esriFieldTypeString"_s;
1769 break;
1770 }
1771 res.insert( u"type"_s, fieldType );
1772
1773 if ( !field.alias().isEmpty() )
1774 res.insert( u"alias"_s, field.alias() );
1775
1776 // nullable
1778 res.insert( u"nullable"_s, !notNullable );
1779
1780 // editable
1781 res.insert( u"editable"_s, true );
1782
1783 return res;
1784}
1785
1787{
1788 if ( type.compare( "FeatureServer"_L1, Qt::CaseInsensitive ) == 0 )
1790 else if ( type.compare( "MapServer"_L1, Qt::CaseInsensitive ) == 0 )
1792 else if ( type.compare( "ImageServer"_L1, Qt::CaseInsensitive ) == 0 )
1794 else if ( type.compare( "GlobeServer"_L1, Qt::CaseInsensitive ) == 0 )
1796 else if ( type.compare( "GPServer"_L1, Qt::CaseInsensitive ) == 0 )
1798 else if ( type.compare( "GeocodeServer"_L1, Qt::CaseInsensitive ) == 0 )
1800 else if ( type.compare( "SceneServer"_L1, Qt::CaseInsensitive ) == 0 )
1802
1804}
1805
1807{
1808 const QStringList parts = capabilities.split( ',' );
1809
1811 res.setFlag( Qgis::ArcGisRestServiceCapability::Query, parts.contains( "query"_L1, Qt::CaseInsensitive ) );
1812 res.setFlag( Qgis::ArcGisRestServiceCapability::Map, parts.contains( "map"_L1, Qt::CaseInsensitive ) );
1813 res.setFlag( Qgis::ArcGisRestServiceCapability::Update, parts.contains( "update"_L1, Qt::CaseInsensitive ) );
1814 res.setFlag( Qgis::ArcGisRestServiceCapability::Delete, parts.contains( "delete"_L1, Qt::CaseInsensitive ) );
1815 res.setFlag( Qgis::ArcGisRestServiceCapability::Create, parts.contains( "create"_L1, Qt::CaseInsensitive ) );
1816 res.setFlag( Qgis::ArcGisRestServiceCapability::Image, parts.contains( "image"_L1, Qt::CaseInsensitive ) );
1817 res.setFlag( Qgis::ArcGisRestServiceCapability::TilesOnly, parts.contains( "tilesonly"_L1, Qt::CaseInsensitive ) );
1818
1819 return res;
1820}
1821
1823{
1824 if ( pixelType.compare( "U8"_L1, Qt::CaseInsensitive ) == 0
1825 || pixelType.compare( "U4"_L1, Qt::CaseInsensitive ) == 0
1826 || pixelType.compare( "U2"_L1, Qt::CaseInsensitive ) == 0
1827 || pixelType.compare( "U1"_L1, Qt::CaseInsensitive ) == 0 )
1828 {
1829 return Qgis::DataType::Byte;
1830 }
1831 else if ( pixelType.compare( "S8"_L1, Qt::CaseInsensitive ) == 0 )
1832 {
1833 return Qgis::DataType::Int8;
1834 }
1835 else if ( pixelType.compare( "U16"_L1, Qt::CaseInsensitive ) == 0 )
1836 {
1838 }
1839 else if ( pixelType.compare( "S16"_L1, Qt::CaseInsensitive ) == 0 )
1840 {
1841 return Qgis::DataType::Int16;
1842 }
1843 else if ( pixelType.compare( "U32"_L1, Qt::CaseInsensitive ) == 0 )
1844 {
1846 }
1847 else if ( pixelType.compare( "S32"_L1, Qt::CaseInsensitive ) == 0 )
1848 {
1849 return Qgis::DataType::Int32;
1850 }
1851 else if ( pixelType.compare( "F32"_L1, Qt::CaseInsensitive ) == 0 )
1852 {
1854 }
1855 else if ( pixelType.compare( "F64"_L1, Qt::CaseInsensitive ) == 0 )
1856 {
1858 }
1859 else if ( pixelType.compare( "C64"_L1, Qt::CaseInsensitive ) == 0 )
1860 {
1861 // C64 = 32-bit real + 32-bit imaginary
1863 }
1864 else if ( pixelType.compare( "C128"_L1, Qt::CaseInsensitive ) == 0 )
1865 {
1866 // C128 = 64-bit real + 64-bit imaginary
1868 }
1869 else
1870 {
1871 QgsDebugError( u"Unknown pixelType: %1"_s.arg( pixelType ) );
1872 }
1873
1875}
1876
1878{
1879 if ( pixelType.compare( "U8"_L1, Qt::CaseInsensitive ) == 0
1880 || pixelType.compare( "U4"_L1, Qt::CaseInsensitive ) == 0
1881 || pixelType.compare( "U2"_L1, Qt::CaseInsensitive ) == 0
1882 || pixelType.compare( "U1"_L1, Qt::CaseInsensitive ) == 0
1883 || pixelType.compare( "S8"_L1, Qt::CaseInsensitive ) == 0
1884 || pixelType.compare( "U16"_L1, Qt::CaseInsensitive ) == 0
1885 || pixelType.compare( "S16"_L1, Qt::CaseInsensitive ) == 0 )
1886 {
1887 return PixelTypeLimitUsefulness { true, true };
1888 }
1889 else if ( pixelType.compare( "U32"_L1, Qt::CaseInsensitive ) == 0 )
1890 {
1891 return PixelTypeLimitUsefulness { true, false };
1892 }
1893 else if ( pixelType.compare( "S32"_L1, Qt::CaseInsensitive ) == 0
1894 || pixelType.compare( "F32"_L1, Qt::CaseInsensitive ) == 0
1895 || pixelType.compare( "F64"_L1, Qt::CaseInsensitive ) == 0
1896 || pixelType.compare( "C64"_L1, Qt::CaseInsensitive ) == 0
1897 || pixelType.compare( "C128"_L1, Qt::CaseInsensitive ) == 0 )
1898 {
1899 return PixelTypeLimitUsefulness { false, false };
1900 }
1901 else
1902 {
1903 QgsDebugError( u"Unknown pixelType: %1"_s.arg( pixelType ) );
1904 }
1905
1906 return PixelTypeLimitUsefulness { false, false };
1907}
1908
1909std::optional<std::pair<double, double> > QgsArcGisRestUtils::rangeForPixelType( const QString &pixelType )
1910{
1911 if ( pixelType.compare( "U8"_L1, Qt::CaseInsensitive ) == 0 )
1912 {
1913 return std::make_pair( 0.0, 255.0 );
1914 }
1915 else if ( pixelType.compare( "U4"_L1, Qt::CaseInsensitive ) == 0 )
1916 {
1917 return std::make_pair( 0.0, 15.0 );
1918 }
1919 else if ( pixelType.compare( "U2"_L1, Qt::CaseInsensitive ) == 0 )
1920 {
1921 return std::make_pair( 0.0, 3.0 );
1922 }
1923 else if ( pixelType.compare( "U1"_L1, Qt::CaseInsensitive ) == 0 )
1924 {
1925 return std::make_pair( 0.0, 1.0 );
1926 }
1927 else if ( pixelType.compare( "S8"_L1, Qt::CaseInsensitive ) == 0 )
1928 {
1929 return std::make_pair( -128.0, 127.0 );
1930 }
1931 else if ( pixelType.compare( "U16"_L1, Qt::CaseInsensitive ) == 0 )
1932 {
1933 return std::make_pair( 0.0, 65535.0 );
1934 }
1935 else if ( pixelType.compare( "S16"_L1, Qt::CaseInsensitive ) == 0 )
1936 {
1937 return std::make_pair( -32768.0, 32767.0 );
1938 }
1939 else if ( pixelType.compare( "U32"_L1, Qt::CaseInsensitive ) == 0 )
1940 {
1941 return std::make_pair( 0.0, static_cast<double>( std::numeric_limits<uint32_t>::max() ) );
1942 }
1943 else if ( pixelType.compare( "S32"_L1, Qt::CaseInsensitive ) == 0 )
1944 {
1945 return std::make_pair( static_cast<double>( std::numeric_limits<int32_t>::lowest() ), static_cast<double>( std::numeric_limits<int32_t>::max() ) );
1946 }
1947 else if ( pixelType.compare( "F32"_L1, Qt::CaseInsensitive ) == 0 )
1948 {
1949 return std::make_pair( static_cast<double>( std::numeric_limits<float>::lowest() ), static_cast<double>( std::numeric_limits<float>::max() ) );
1950 }
1951 else if ( pixelType.compare( "F64"_L1, Qt::CaseInsensitive ) == 0 )
1952 {
1953 return std::make_pair( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max() );
1954 }
1955 else if ( pixelType.compare( "C64"_L1, Qt::CaseInsensitive ) == 0 )
1956 {
1957 // C64 = 32-bit real + 32-bit imaginary
1958 return std::make_pair( static_cast<double>( std::numeric_limits<float>::lowest() ), static_cast<double>( std::numeric_limits<float>::max() ) );
1959 }
1960 else if ( pixelType.compare( "C128"_L1, Qt::CaseInsensitive ) == 0 )
1961 {
1962 // C128 = 64-bit real + 64-bit imaginary
1963 return std::make_pair( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max() );
1964 }
1965 else
1966 {
1967 QgsDebugError( u"Unknown pixelType: %1"_s.arg( pixelType ) );
1968 }
1969
1970 return std::nullopt;
1971}
1972
1974{
1975 if ( bandName.isEmpty() )
1976 {
1978 }
1979
1980 if ( bandName.compare( "Red"_L1, Qt::CaseInsensitive ) == 0 )
1982 else if ( bandName.compare( "Green"_L1, Qt::CaseInsensitive ) == 0 )
1984 else if ( bandName.compare( "Blue"_L1, Qt::CaseInsensitive ) == 0 )
1986 else if ( bandName.compare( "Alpha"_L1, Qt::CaseInsensitive ) == 0 )
1988 else if ( bandName.compare( "NIR"_L1, Qt::CaseInsensitive ) == 0
1989 || bandName.compare( "NearInfrared"_L1, Qt::CaseInsensitive ) == 0
1990 || bandName.compare( "NearIR"_L1, Qt::CaseInsensitive ) == 0
1991 || bandName.compare( "NarrowNIR"_L1, Qt::CaseInsensitive ) == 0 )
1993 else if ( bandName.startsWith( "SWIR"_L1, Qt::CaseInsensitive ) )
1995 else if ( bandName.startsWith( "VRE"_L1, Qt::CaseInsensitive ) || bandName.compare( "RedEdge"_L1, Qt::CaseInsensitive ) == 0 )
1997 else if ( bandName.startsWith( "Coastal"_L1, Qt::CaseInsensitive ) )
1999 else if ( bandName.startsWith( "Pan"_L1, Qt::CaseInsensitive ) )
2001 else if ( bandName.startsWith( "Thermal"_L1, Qt::CaseInsensitive ) || bandName.startsWith( "TIR"_L1, Qt::CaseInsensitive ) )
2003 else if ( bandName.compare( "Gray"_L1, Qt::CaseInsensitive ) == 0 || bandName.compare( "Grey"_L1, Qt::CaseInsensitive ) == 0 )
2005 else if ( bandName.compare( "Cyan"_L1, Qt::CaseInsensitive ) == 0 )
2007 else if ( bandName.compare( "Magenta"_L1, Qt::CaseInsensitive ) == 0 )
2009 else if ( bandName.compare( "Yellow"_L1, Qt::CaseInsensitive ) == 0 )
2011 else if ( bandName.compare( "Black"_L1, Qt::CaseInsensitive ) == 0 )
2013 else if ( bandName.compare( "Hue"_L1, Qt::CaseInsensitive ) == 0 )
2015 else if ( bandName.compare( "Saturation"_L1, Qt::CaseInsensitive ) == 0 )
2017 else if ( bandName.compare( "Lightness"_L1, Qt::CaseInsensitive ) == 0 )
2019
2020 // we don't log failures here -- a lot of bands will have non-interpretable names,
2021 // eg "Band 1"
2023}
2024
2026{
2027 ok = false;
2028 switch ( type )
2029 {
2031 return 0;
2032
2034 ok = true;
2035 return std::numeric_limits<quint8>::max();
2036
2038 ok = true;
2039 return std::numeric_limits<qint8>::lowest();
2040
2042 ok = true;
2043 return std::numeric_limits<quint16>::max();
2044
2046 ok = true;
2047 return std::numeric_limits<qint16>::lowest();
2048
2050 ok = true;
2051 return std::numeric_limits<quint32>::max();
2052
2054 ok = true;
2055 return std::numeric_limits<qint32>::lowest();
2056
2058 ok = true;
2059 return std::numeric_limits<float>::quiet_NaN();
2060
2062 ok = true;
2063 return std::numeric_limits<double>::quiet_NaN();
2064
2071 return 0;
2072 }
2074}
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1418
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
Definition qgis.h:1419
@ OnLine
Labels can be placed directly over a line feature.
Definition qgis.h:1416
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1417
@ NoOrientation
Unknown orientation or sentinel value.
Definition qgis.h:3673
@ CounterClockwise
Counter-clockwise direction.
Definition qgis.h:3672
@ Clockwise
Clockwise direction.
Definition qgis.h:3671
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
Definition qgis.h:1309
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
Definition qgis.h:1310
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature or along a line feature....
Definition qgis.h:1312
ArcGisRestServiceType
Available ArcGIS REST service types.
Definition qgis.h:4843
@ GeocodeServer
GeocodeServer.
Definition qgis.h:4849
@ SceneServer
SceneServer.
Definition qgis.h:4851
@ Unknown
Other unknown/unsupported type.
Definition qgis.h:4850
@ GlobeServer
GlobeServer.
Definition qgis.h:4847
@ ImageServer
ImageServer.
Definition qgis.h:4846
@ FeatureServer
FeatureServer.
Definition qgis.h:4844
@ AboveRight
Above right.
Definition qgis.h:1398
@ BelowLeft
Below left.
Definition qgis.h:1402
@ Above
Above center.
Definition qgis.h:1397
@ BelowRight
Below right.
Definition qgis.h:1404
@ Right
Right middle.
Definition qgis.h:1401
@ AboveLeft
Above left.
Definition qgis.h:1396
@ Below
Below center.
Definition qgis.h:1403
@ Over
Center middle.
Definition qgis.h:1400
DataType
Raster data types.
Definition qgis.h:413
@ CInt32
Complex Int32.
Definition qgis.h:424
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:421
@ CFloat64
Complex Float64.
Definition qgis.h:426
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:418
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:428
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30).
Definition qgis.h:416
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:417
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:415
@ UnknownDataType
Unknown or unspecified type.
Definition qgis.h:414
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition qgis.h:427
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:420
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:422
@ CFloat32
Complex Float32.
Definition qgis.h:425
@ CInt16
Complex Int16.
Definition qgis.h:423
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:419
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5763
QFlags< ArcGisRestServiceCapability > ArcGisRestServiceCapabilities
Available ArcGIS REST service capabilities.
Definition qgis.h:4880
@ Update
Update features.
Definition qgis.h:4867
@ Create
Create features.
Definition qgis.h:4869
@ TilesOnly
Service supports tiled image requests only.
Definition qgis.h:4871
@ Image
Image capabilities.
Definition qgis.h:4870
@ Delete
Delete features.
Definition qgis.h:4868
RasterColorInterpretation
Raster color interpretation.
Definition qgis.h:5267
@ NIRBand
Near-InfraRed (NIR) band [0.75 - 1.40 um].
Definition qgis.h:5293
@ GreenBand
Green band of RGBA image, or green spectral band [0.51 - 0.60 um].
Definition qgis.h:5272
@ SaturationBand
Saturation band of HLS image.
Definition qgis.h:5276
@ MagentaBand
Magenta band of CMYK image.
Definition qgis.h:5279
@ BlackBand
Black band of CMLY image.
Definition qgis.h:5281
@ AlphaBand
Alpha (0=transparent, 255=opaque).
Definition qgis.h:5274
@ SWIRBand
Short-Wavelength InfraRed (SWIR) band [1.40 - 3.00 um].
Definition qgis.h:5294
@ BlueBand
Blue band of RGBA image, or blue spectral band [0.45 - 0.53 um].
Definition qgis.h:5273
@ RedEdgeBand
Red-edge band [0.69 - 0.79 um].
Definition qgis.h:5292
@ YellowBand
Yellow band of CMYK image, or yellow spectral band [0.58 - 0.62 um].
Definition qgis.h:5280
@ CyanBand
Cyan band of CMYK image.
Definition qgis.h:5278
@ LightnessBand
Lightness band of HLS image.
Definition qgis.h:5277
@ CoastalBand
Coastal band [0.40 - 0.45 um].
Definition qgis.h:5291
@ HueBand
Hue band of HLS image.
Definition qgis.h:5275
@ TIRBand
Thermal InfraRed (TIR) band (MWIR or LWIR) [3 - 15 um].
Definition qgis.h:5297
@ RedBand
Red band of RGBA image, or red spectral band [0.62 - 0.69 um].
Definition qgis.h:5271
@ PanBand
Panchromatic band [0.40 - 1.00 um].
Definition qgis.h:5290
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:314
@ CompoundCurve
CompoundCurve.
Definition qgis.h:325
@ Point
Point.
Definition qgis.h:316
@ LineString
LineString.
Definition qgis.h:317
@ MultiPoint
MultiPoint.
Definition qgis.h:320
@ Polygon
Polygon.
Definition qgis.h:318
@ MultiPolygon
MultiPolygon.
Definition qgis.h:322
@ Triangle
Triangle.
Definition qgis.h:319
@ NoGeometry
No geometry.
Definition qgis.h:332
@ MultiLineString
MultiLineString.
Definition qgis.h:321
@ Unknown
Unknown.
Definition qgis.h:315
@ CircularString
CircularString.
Definition qgis.h:324
@ GeometryCollection
GeometryCollection.
Definition qgis.h:323
@ MultiCurve
MultiCurve.
Definition qgis.h:327
@ CurvePolygon
CurvePolygon.
Definition qgis.h:326
@ MultiSurface
MultiSurface.
Definition qgis.h:328
@ Wkt2_2019Simplified
WKT2_2019 with the simplification rule of WKT2_SIMPLIFIED.
Definition qgis.h:2630
Abstract base class for all geometries.
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
Contains the context of a ArcGIS REST service operation.
QTimeZone timeZone() const
Returns the time zone for datetime values.
QString objectIdFieldName() const
Returns the name of the objectId field.
static std::unique_ptr< QgsAbstractGeometry > convertGeometry(const QVariantMap &geometry, const QString &esriGeometryType, bool hasM, bool hasZ, bool allowCurves=true, QgsCoordinateReferenceSystem *crs=nullptr)
Converts an ESRI REST geometry JSON definition to a QgsAbstractGeometry.
static QVariantMap fieldDefinitionToJson(const QgsField &field)
Converts a field's definition to an ArcGIS REST JSON representation.
static QgsCoordinateReferenceSystem convertSpatialReference(const QVariantMap &spatialReferenceMap)
Converts a spatial reference JSON definition to a QgsCoordinateReferenceSystem value.
static QDateTime convertDateTime(const QVariant &value)
Converts a date time value to a QDateTime.
static Qgis::WkbType convertGeometryType(const QString &type)
Converts an ESRI REST geometry type to a WKB type.
static QString convertLabelingExpression(const QString &string)
Converts an ESRI labeling expression to a QGIS expression string.
static QVariantMap geometryToJson(const QgsGeometry &geometry, const QgsArcGisRestContext &context, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem())
Converts a geometry to an ArcGIS REST JSON representation.
static double defaultNoDataForDataType(Qgis::DataType type, bool &ok)
Returns a sensible no-data value to use for the specified data type.
static PixelTypeLimitUsefulness pixelTypeLimitUsefulness(const QString &pixelType)
Returns whether the theoretical minimum and maximum values for a given ESRI pixelType are practically...
static Qgis::ArcGisRestServiceType serviceTypeFromString(const QString &type)
Converts a string value to a REST service type.
static QVariant variantToAttributeValue(const QVariant &variant, QMetaType::Type expectedType, const QgsArcGisRestContext &context)
Converts a variant to a REST attribute value.
static QVariantMap featureToJson(const QgsFeature &feature, const QgsArcGisRestContext &context, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem(), QgsArcGisRestUtils::FeatureToJsonFlags flags=QgsArcGisRestUtils::FeatureToJsonFlags(static_cast< int >(QgsArcGisRestUtils::FeatureToJsonFlag::IncludeGeometry)|static_cast< int >(QgsArcGisRestUtils::FeatureToJsonFlag::IncludeNonObjectIdAttributes)))
Converts a feature to an ArcGIS REST JSON representation.
static Qt::PenStyle convertLineStyle(const QString &style)
Converts an ESRI line style to a Qt pen style.
@ IncludeGeometry
Whether to include the geometry definition.
@ IncludeNonObjectIdAttributes
Whether to include any non-objectId attributes.
static Qgis::ArcGisRestServiceCapabilities serviceCapabilitiesFromString(const QString &capabilities)
Parses a capabilities string to known values.
static std::optional< std::pair< double, double > > rangeForPixelType(const QString &pixelType)
Returns the valid data range given an ESRI pixelType string.
static QMetaType::Type convertFieldType(const QString &type)
Converts an ESRI REST field type to a QVariant type.
static Qt::BrushStyle convertFillStyle(const QString &style)
Converts an ESRI fill style to a Qt brush style.
static std::unique_ptr< QgsFeatureRenderer > convertRenderer(const QVariantMap &rendererData, QgsSymbolConverterContext &context)
Converts renderer JSON data to an equivalent QgsFeatureRenderer.
static Qgis::RasterColorInterpretation colorInterpretationFromBandName(const QString &bandName)
Attempts to match arbitrary band name strings to a QGIS raster color interpretation.
static QVariantMap crsToJson(const QgsCoordinateReferenceSystem &crs)
Converts a crs to an ArcGIS REST JSON representation.
static std::unique_ptr< QgsAbstractVectorLayerLabeling > convertLabeling(const QVariantList &data)
Converts labeling JSON data to an equivalent QGIS vector labeling.
QFlags< FeatureToJsonFlag > FeatureToJsonFlags
Flags which control the behavior of converting features to JSON.
static Qgis::DataType dataTypeFromString(const QString &pixelType)
Returns the raster data type corresponding to an ESRI pixelType string.
static std::unique_ptr< QgsSymbol > convertSymbol(const QVariantMap &definition, QgsSymbolConverterContext &context)
Converts a symbol JSON definition to a QgsSymbol.
static QColor convertColor(const QVariant &data)
Converts ESRI JSON color data to a QColor object.
static QgsRectangle convertRectangle(const QVariant &value)
Converts a rectangle value to a QgsRectangle.
A dummy implementation class method which does not compute any breaks.
A classification method which uses equal width intervals.
Implementation of a fixed interval classification.
A classification method for natural breaks, based on Jenks method.
A classification method which creates classes based on quantiles.
A classification method which classifies based on standard deviation of values.
QgsPropertyTransformer subclass for transforming a numeric value into a color from a color ramp.
int nCurves() const
Returns the number of curves in the geometry.
const QgsCurve * curveAt(int i) const
Returns the curve at the specified index.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool createFromWkt(const QString &wkt)
Sets this CRS using a WKT definition.
bool createFromString(const QString &definition)
Set up this CRS from a string definition.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
Qgis::AngularDirection orientation() const
Returns the curve's orientation, e.g.
Definition qgscurve.cpp:291
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
Handles parsing and evaluation of expressions (formerly called "search strings").
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes).
bool isValid() const
Checks if this expression is valid.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFields fields
Definition qgsfeature.h:65
QgsGeometry geometry
Definition qgsfeature.h:66
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
@ ConstraintNotNull
Field may not be null.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QMetaType::Type type
Definition qgsfield.h:63
QString name
Definition qgsfield.h:65
QString alias
Definition qgsfield.h:66
QgsFieldConstraints constraints
Definition qgsfield.h:68
Container of fields for a vector layer.
Definition qgsfields.h:45
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Represents a color stop within a QgsGradientColorRamp color ramp.
void setPlacementFlags(Qgis::LabelLinePlacementFlags flags)
Returns the line placement flags, which dictate how line labels can be placed above or below the line...
void setQuadrant(Qgis::LabelQuadrantPosition quadrant)
Sets the quadrant in which to offset labels from the point.
Line string geometry type, with support for z-dimension and m-values.
QgsLineString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
Multi curve geometry collection.
QgsCurve * curveN(int index)
Returns the curve with the specified index.
Multi line string geometry collection.
QgsLineString * lineStringN(int index)
Returns the line string with the specified index.
Multi point geometry collection.
QgsPoint * pointN(int index)
Returns the point with the specified index.
Multi polygon geometry collection.
QgsPolygon * polygonN(int index)
Returns the polygon with the specified index.
Multi surface geometry collection.
Contains settings for how a map layer will be labeled.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
Qgis::LabelPlacement placement
Label placement mode.
double repeatDistance
Distance for repeating labels for a single feature.
bool isExpression
true if this label is made from a expression string, e.g., FieldName || 'mm'
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
Qgis::RenderUnit repeatDistanceUnit
Units for repeating labels for a single feature.
QString fieldName
Name of field (or an expression) to use for label text.
const QgsLabelPointSettings & pointSettings() const
Returns the label point settings, which contain settings related to how the label engine places and f...
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
void clear() override
Clears the geometry, ie reset it to a null geometry.
Definition qgspoint.cpp:395
double z
Definition qgspoint.h:58
double x
Definition qgspoint.h:56
bool isEmpty() const override
Returns true if the geometry is empty.
Definition qgspoint.cpp:775
double m
Definition qgspoint.h:59
double y
Definition qgspoint.h:57
Polygon geometry type.
Definition qgspolygon.h:37
A store for object properties.
void setTransformer(QgsPropertyTransformer *transformer)
Sets an optional transformer to use for manipulating the calculated values for the property.
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
A container for the context for various read/write operations on objects.
A rectangle specified with double values.
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
Represents a value range for a QgsGraduatedSymbolRenderer.
void setUpperValue(double upperValue)
Sets the upper bound of the range.
void setSymbol(QgsSymbol *s)
Sets the symbol used for the range.
void setLabel(const QString &label)
Sets the label used for the range.
void setLowerValue(double lowerValue)
Sets the lower bound of the range.
A child rule for QgsRuleBasedLabeling.
void setActive(bool state)
Sets if this rule is active.
void appendChild(QgsRuleBasedLabeling::Rule *rule)
add child rule, take ownership, sets this as parent
int numPoints() const override
Returns the number of points in the curve.
bool isEmpty() const override
Returns true if the geometry is empty.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the simple curve does not have m values.
const double * yData() const
Returns a const pointer to the y vertex data.
QgsPoint startPoint() const override
Returns the starting point of the curve.
QgsPoint pointN(int i) const
Returns the specified point from inside the simple curve.
const double * xData() const
Returns a const pointer to the x vertex data.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the simple curve does not have z values.
Represents the context in which a QgsSymbolConverter conversion occurs.
void pushWarning(const QString &warning)
Pushes a warning message generated during the conversion.
A symbol converter for converting ESRI REST JSON symbols.
static Qt::PenStyle convertLineStyle(const QString &style)
Converts an ESRI line style to a Qt pen style.
std::unique_ptr< QgsSymbol > createSymbol(const QVariant &variant, QgsSymbolConverterContext &context) const override
Creates a new QgsSymbol from a QVariant representation.
static Qt::BrushStyle convertFillStyle(const QString &style)
Converts an ESRI fill style to a Qt brush style.
static QColor convertColor(const QVariant &data)
Converts ESRI JSON color data to a QColor object.
virtual void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the layer.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:227
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition qgssymbol.h:357
Container for settings relating to a text buffer.
void setColor(const QColor &color)
Sets the color for the buffer.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units used for the buffer size.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
void setSize(double size)
Sets the size of the buffer.
Container for all settings relating to text rendering.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setSize(double size)
Sets the size for rendered text.
void setFont(const QFont &font)
Sets the font used for rendering text.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the size of rendered text.
void setBuffer(const QgsTextBufferSettings &bufferSettings)
Sets the text's buffer settings.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static Qgis::WkbType zmType(Qgis::WkbType type, bool hasZ, bool hasM)
Returns the modified input geometry type according to hasZ / hasM.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
#define BUILTIN_UNREACHABLE
Definition qgis.h:8397
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7725
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QList< QgsRendererCategory > QgsCategoryList
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
#define QgsDebugError(str)
Definition qgslogger.h:71
Struct representing whether the theoretical limits of a pixel type are useful for describing actual d...