QGIS API Documentation 4.3.0-Master (0de80482b60)
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 double minScale = labeling.value( u"minScale"_s ).toDouble();
724 const double maxScale = labeling.value( u"maxScale"_s ).toDouble();
725
726 QVariantMap symbol = labeling.value( u"symbol"_s ).toMap();
727 format.setColor( convertColor( symbol.value( u"color"_s ) ) );
728 const double haloSize = symbol.value( u"haloSize"_s ).toDouble();
729 if ( !qgsDoubleNear( haloSize, 0.0 ) )
730 {
732 buffer.setEnabled( true );
733 buffer.setSize( haloSize );
735 buffer.setColor( convertColor( symbol.value( u"haloColor"_s ) ) );
736 format.setBuffer( buffer );
737 }
738
739 const QString fontFamily = symbol.value( u"font"_s ).toMap().value( u"family"_s ).toString();
740 const QString fontStyle = symbol.value( u"font"_s ).toMap().value( u"style"_s ).toString();
741 const QString fontWeight = symbol.value( u"font"_s ).toMap().value( u"weight"_s ).toString();
742 const int fontSize = symbol.value( u"font"_s ).toMap().value( u"size"_s ).toInt();
743 QFont font( fontFamily, fontSize );
744 font.setStyleName( fontStyle );
745 font.setWeight( fontWeight == "bold"_L1 ? QFont::Bold : QFont::Normal );
746
747 format.setFont( font );
748 format.setSize( fontSize );
750
751 settings->setFormat( format );
752
753 QString where = labeling.value( u"where"_s ).toString();
754 QgsExpression exp( where );
755 // If the where clause isn't parsed as valid, don't use its
756 if ( !exp.isValid() )
757 where.clear();
758
759 settings->fieldName = convertLabelingExpression( labeling.value( u"labelExpression"_s ).toString() );
760 settings->isExpression = true;
761
762 QgsRuleBasedLabeling::Rule *child = new QgsRuleBasedLabeling::Rule( settings, maxScale, minScale, where, QObject::tr( "ASF label %1" ).arg( i++ ), false );
763 child->setActive( true );
764 root->appendChild( child );
765 }
766
767 return std::make_unique< QgsRuleBasedLabeling >( root );
768}
769
770void QgsArcGisRestUtils::applyVisualVariables( const QVariantMap &rendererData, QgsSymbol *symbol, QgsSymbolConverterContext &context )
771{
772 if ( !symbol )
773 return;
774
775 const QVariantList visualVariablesData = rendererData.value( u"visualVariables"_s ).toList();
776 for ( const QVariant &visualVariable : visualVariablesData )
777 {
778 const QVariantMap visualVariableData = visualVariable.toMap();
779 const QString variableType = visualVariableData.value( u"type"_s ).toString();
780
781 if ( variableType == "rotationInfo"_L1 )
782 {
783 const QString field = visualVariableData.value( u"field"_s ).toString();
784 if ( field.isEmpty() )
785 {
786 // Check if it was a valueExpression that we don't support yet
787 if ( !visualVariableData.value( u"valueExpression"_s ).toString().isEmpty() )
788 context.pushWarning( QObject::tr( "ESRI rotationInfo valueExpression is not yet supported" ) );
789 continue;
790 }
791
792 const QString rotationType = visualVariableData.value( u"rotationType"_s ).toString();
793
794 QgsProperty angleProperty;
795 if ( rotationType == "arithmetic"_L1 )
796 {
797 // ArcGIS arithmetic: 0° = East, counter-clockwise
798 // QGIS: 0° = North, clockwise
799 // Conversion: QGIS_angle = 90 - ArcGIS_angle
800 angleProperty = QgsProperty::fromExpression( u"90 - %1"_s.arg( QgsExpression::quotedColumnRef( field ) ) );
801 }
802 else if ( rotationType == "geographic"_L1 )
803 {
804 // ArcGIS geographic: 0° = North, clockwise (same as QGIS)
805 angleProperty = QgsProperty::fromField( field );
806 }
807 else
808 {
809 context.pushWarning( QObject::tr( "ESRI rotationInfo rotationType '%1' is not supported" ).arg( rotationType ) );
810 continue;
811 }
812
813 // Apply rotation to all symbol layers
814 for ( int layer = 0; layer < symbol->symbolLayerCount(); ++layer )
815 {
816 symbol->symbolLayer( layer )->setDataDefinedProperty( QgsSymbolLayer::Property::Angle, angleProperty );
817 }
818 }
819 else
820 {
821 context.pushWarning( QObject::tr( "ESRI visualVariable type '%1' is not currently supported" ).arg( variableType ) );
822 }
823 }
824}
825
826std::unique_ptr< QgsFeatureRenderer > QgsArcGisRestUtils::convertRenderer( const QVariantMap &rendererData, QgsSymbolConverterContext &context )
827{
828 const QString type = rendererData.value( u"type"_s ).toString();
829
830 if ( type == "simple"_L1 )
831 {
832 const QVariantMap symbolProps = rendererData.value( u"symbol"_s ).toMap();
833 std::unique_ptr< QgsSymbol > symbol( convertSymbol( symbolProps, context ) );
834 if ( symbol )
835 {
836 // Apply visual variables (e.g., rotation) to the symbol
837 applyVisualVariables( rendererData, symbol.get(), context );
838 return std::make_unique< QgsSingleSymbolRenderer >( symbol.release() );
839 }
840 else
841 return nullptr;
842 }
843 else if ( type == "uniqueValue"_L1 )
844 {
845 const QString field1 = rendererData.value( u"field1"_s ).toString();
846 const QString field2 = rendererData.value( u"field2"_s ).toString();
847 const QString field3 = rendererData.value( u"field3"_s ).toString();
848 QString attribute;
849 if ( !field2.isEmpty() || !field3.isEmpty() )
850 {
851 const QString delimiter = rendererData.value( u"fieldDelimiter"_s ).toString();
852 if ( !field3.isEmpty() )
853 {
854 attribute = u"concat(\"%1\",'%2',\"%3\",'%4',\"%5\")"_s.arg( field1, delimiter, field2, delimiter, field3 );
855 }
856 else
857 {
858 attribute = u"concat(\"%1\",'%2',\"%3\")"_s.arg( field1, delimiter, field2 );
859 }
860 }
861 else
862 {
863 attribute = field1;
864 }
865
866 const QVariantList categories = rendererData.value( u"uniqueValueInfos"_s ).toList();
867 QgsCategoryList categoryList;
868 for ( const QVariant &category : categories )
869 {
870 const QVariantMap categoryData = category.toMap();
871 const QString valueString = categoryData.value( u"value"_s ).toString();
872 // Esri uses the literal string "<Null>" to represent a NULL field value in unique value renderers.
873 const QVariant value = valueString == "<Null>"_L1 ? QVariant() : QVariant( valueString );
874 const QString label = categoryData.value( u"label"_s ).toString();
875 std::unique_ptr< QgsSymbol > symbol( QgsArcGisRestUtils::convertSymbol( categoryData.value( u"symbol"_s ).toMap(), context ) );
876 if ( symbol )
877 {
878 // Apply visual variables (e.g., rotation) to the symbol
879 applyVisualVariables( rendererData, symbol.get(), context );
880
881 categoryList.append( QgsRendererCategory( value, symbol.release(), label ) );
882 }
883 }
884
885 std::unique_ptr< QgsSymbol > defaultSymbol( convertSymbol( rendererData.value( u"defaultSymbol"_s ).toMap(), context ) );
886 if ( defaultSymbol )
887 {
888 // Apply visual variables (e.g., rotation) to the symbol
889 applyVisualVariables( rendererData, defaultSymbol.get(), context );
890 categoryList.append( QgsRendererCategory( QVariant(), defaultSymbol.release(), rendererData.value( u"defaultLabel"_s ).toString() ) );
891 }
892
893 if ( categoryList.empty() )
894 return nullptr;
895
896 auto renderer = std::make_unique< QgsCategorizedSymbolRenderer >( attribute, categoryList );
897 return renderer;
898 }
899 else if ( type == "classBreaks"_L1 )
900 {
901 const QString attrName = rendererData.value( u"field"_s ).toString();
902
903 const QVariantList classBreakInfos = rendererData.value( u"classBreakInfos"_s ).toList();
904 const QVariantMap authoringInfo = rendererData.value( u"authoringInfo"_s ).toMap();
905 QVariantMap symbolData;
906
907 QString esriMode = authoringInfo.value( u"classificationMethod"_s ).toString();
908 if ( esriMode.isEmpty() )
909 {
910 esriMode = rendererData.value( u"classificationMethod"_s ).toString();
911 }
912
913 if ( !classBreakInfos.isEmpty() )
914 {
915 symbolData = classBreakInfos.at( 0 ).toMap().value( u"symbol"_s ).toMap();
916 }
917 std::unique_ptr< QgsSymbol > symbol( QgsArcGisRestUtils::convertSymbol( symbolData, context ) );
918 if ( !symbol )
919 return nullptr;
920
921 const QVariantList visualVariablesData = rendererData.value( u"visualVariables"_s ).toList();
922
923 for ( const QVariant &visualVariable : visualVariablesData )
924 {
925 const QVariantMap visualVariableData = visualVariable.toMap();
926 const QString variableType = visualVariableData.value( u"type"_s ).toString();
927 if ( variableType == "sizeInfo"_L1 )
928 {
929 continue;
930 }
931 else if ( variableType == "colorInfo"_L1 )
932 {
933 const QVariantList stops = visualVariableData.value( u"stops"_s ).toList();
934 if ( stops.size() < 2 )
935 continue;
936
937 // layer has continuous coloring, so convert to a symbol using color ramp assistant
938 bool ok = false;
939 const double minValue = stops.front().toMap().value( u"value"_s ).toDouble( &ok );
940 if ( !ok )
941 continue;
942 const QColor minColor = convertColor( stops.front().toMap().value( u"color"_s ) );
943
944 const double maxValue = stops.back().toMap().value( u"value"_s ).toDouble( &ok );
945 if ( !ok )
946 continue;
947 const QColor maxColor = convertColor( stops.back().toMap().value( u"color"_s ) );
948
949 QgsGradientStopsList gradientStops;
950 for ( int i = 1; i < stops.size() - 1; ++i )
951 {
952 const QVariantMap stopData = stops.at( i ).toMap();
953 const double breakpoint = stopData.value( u"value"_s ).toDouble();
954 const double scaledBreakpoint = ( breakpoint - minValue ) / ( maxValue - minValue );
955 const QColor fillColor = convertColor( stopData.value( u"color"_s ) );
956
957 gradientStops.append( QgsGradientStop( scaledBreakpoint, fillColor ) );
958 }
959
960 auto colorRamp = std::make_unique< QgsGradientColorRamp >( minColor, maxColor, false, gradientStops );
961
962 QgsProperty colorProperty = QgsProperty::fromField( attrName );
963 colorProperty.setTransformer( new QgsColorRampTransformer( minValue, maxValue, colorRamp.release() ) );
964 for ( int layer = 0; layer < symbol->symbolLayerCount(); ++layer )
965 {
966 symbol->symbolLayer( layer )->setDataDefinedProperty( QgsSymbolLayer::Property::FillColor, colorProperty );
967 }
968
969 return std::make_unique< QgsSingleSymbolRenderer >( symbol.release() );
970 }
971 else if ( variableType == "rotationInfo"_L1 )
972 {
973 // Rotation will be handled below after the renderer is created
974 continue;
975 }
976 else
977 {
978 context.pushWarning( QObject::tr( "ESRI visualVariable type '%1' is not currently supported" ).arg( variableType ) );
979 }
980 }
981
982 double lastValue = rendererData.value( u"minValue"_s ).toDouble();
983
984 auto graduatedRenderer = std::make_unique< QgsGraduatedSymbolRenderer >( attrName );
985
986 graduatedRenderer->setSourceSymbol( symbol.release() );
987
988 if ( esriMode == "esriClassifyDefinedInterval"_L1 )
989 {
991 graduatedRenderer->setClassificationMethod( method );
992 }
993 else if ( esriMode == "esriClassifyEqualInterval"_L1 )
994 {
996 graduatedRenderer->setClassificationMethod( method );
997 }
998 else if ( esriMode == "esriClassifyGeometricalInterval"_L1 )
999 {
1001 graduatedRenderer->setClassificationMethod( method );
1002 }
1003 else if ( esriMode == "esriClassifyManual"_L1 )
1004 {
1006 graduatedRenderer->setClassificationMethod( method );
1007 }
1008 else if ( esriMode == "esriClassifyNaturalBreaks"_L1 )
1009 {
1011 graduatedRenderer->setClassificationMethod( method );
1012 }
1013 else if ( esriMode == "esriClassifyQuantile"_L1 )
1014 {
1016 graduatedRenderer->setClassificationMethod( method );
1017 }
1018 else if ( esriMode == "esriClassifyStandardDeviation"_L1 )
1019 {
1021 graduatedRenderer->setClassificationMethod( method );
1022 }
1023 else if ( !esriMode.isEmpty() )
1024 {
1025 context.pushWarning( QObject::tr( "ESRI classification mode '%1' is not currently supported" ).arg( esriMode ) );
1026 }
1027
1028 for ( const QVariant &classBreakInfo : classBreakInfos )
1029 {
1030 const QVariantMap symbolData = classBreakInfo.toMap().value( u"symbol"_s ).toMap();
1031 std::unique_ptr< QgsSymbol > symbol( QgsArcGisRestUtils::convertSymbol( symbolData, context ) );
1032 double classMaxValue = classBreakInfo.toMap().value( u"classMaxValue"_s ).toDouble();
1033 const QString label = classBreakInfo.toMap().value( u"label"_s ).toString();
1034
1035 // Apply visual variables (e.g., rotation) to the symbol
1036 applyVisualVariables( rendererData, symbol.get(), context );
1037
1038 QgsRendererRange range;
1039
1040 range.setLowerValue( lastValue );
1041 range.setUpperValue( classMaxValue );
1042 range.setLabel( label );
1043 range.setSymbol( symbol.release() );
1044
1045 lastValue = classMaxValue;
1046 graduatedRenderer->addClass( range );
1047 }
1048
1049 return graduatedRenderer;
1050 }
1051 else if ( type == "heatmap"_L1 )
1052 {
1053 // currently unsupported
1054 return nullptr;
1055 }
1056 else if ( type == "vectorField"_L1 )
1057 {
1058 // currently unsupported
1059 return nullptr;
1060 }
1061 return nullptr;
1062}
1063
1064std::unique_ptr< QgsFeatureRenderer > QgsArcGisRestUtils::convertRenderer( const QVariantMap &rendererData )
1065{
1066 QgsReadWriteContext rwContext;
1067 QgsSymbolConverterContext context( rwContext );
1068 return convertRenderer( rendererData, context );
1069}
1070
1072{
1073 QString expression = string;
1074
1075 // Replace a few ArcGIS token to QGIS equivalents
1076 const thread_local QRegularExpression rx1 = QRegularExpression( u"(?=([^\"\\\\]*(\\\\.|\"([^\"\\\\]*\\\\.)*[^\"\\\\]*\"))*[^\"]*$)(\\s|^)CONCAT(\\s|$)"_s );
1077 expression = expression.replace( rx1, u"\\4||\\5"_s );
1078
1079 const thread_local QRegularExpression rx2 = QRegularExpression( u"(?=([^\"\\\\]*(\\\\.|\"([^\"\\\\]*\\\\.)*[^\"\\\\]*\"))*[^\"]*$)(\\s|^)NEWLINE(\\s|$)"_s );
1080 expression = expression.replace( rx2, u"\\4'\\n'\\5"_s );
1081
1082 // ArcGIS's double quotes are single quotes in QGIS
1083 const thread_local QRegularExpression rx3 = QRegularExpression( u"\"(.*?(?<!\\\\))\""_s );
1084 expression = expression.replace( rx3, u"'\\1'"_s );
1085 const thread_local QRegularExpression rx4 = QRegularExpression( u"\\\\\""_s );
1086 expression = expression.replace( rx4, u"\""_s );
1087
1088 // ArcGIS's square brakets are double quotes in QGIS
1089 const thread_local QRegularExpression rx5 = QRegularExpression( u"\\[([^]]*)\\]"_s );
1090 expression = expression.replace( rx5, u"\"\\1\""_s );
1091
1092 return expression;
1093}
1094
1095QColor QgsArcGisRestUtils::convertColor( const QVariant &colorData )
1096{
1097 return QgsSymbolConverterEsriRest::convertColor( colorData );
1098}
1099
1100Qt::PenStyle QgsArcGisRestUtils::convertLineStyle( const QString &style )
1101{
1103}
1104
1105Qt::BrushStyle QgsArcGisRestUtils::convertFillStyle( const QString &style )
1106{
1108}
1109
1110QDateTime QgsArcGisRestUtils::convertDateTime( const QVariant &value )
1111{
1112 if ( QgsVariantUtils::isNull( value ) )
1113 return QDateTime();
1114 bool ok = false;
1115 QDateTime dt = QDateTime::fromMSecsSinceEpoch( value.toLongLong( &ok ) );
1116 if ( !ok )
1117 {
1118 QgsDebugError( u"Invalid value %1 for datetime"_s.arg( value.toString() ) );
1119 return QDateTime();
1120 }
1121 else
1122 return dt;
1123}
1124
1126{
1127 if ( QgsVariantUtils::isNull( value ) )
1128 return QgsRectangle();
1129
1130 const QVariantMap coords = value.toMap();
1131 if ( coords.isEmpty() )
1132 return QgsRectangle();
1133
1134 bool ok;
1135
1136 const double xmin = coords.value( u"xmin"_s ).toDouble( &ok );
1137 if ( !ok )
1138 return QgsRectangle();
1139
1140 const double ymin = coords.value( u"ymin"_s ).toDouble( &ok );
1141 if ( !ok )
1142 return QgsRectangle();
1143
1144 const double xmax = coords.value( u"xmax"_s ).toDouble( &ok );
1145 if ( !ok )
1146 return QgsRectangle();
1147
1148 const double ymax = coords.value( u"ymax"_s ).toDouble( &ok );
1149 if ( !ok )
1150 return QgsRectangle();
1151
1152 return QgsRectangle( xmin, ymin, xmax, ymax );
1153}
1154
1155
1157{
1158 QVariantMap res;
1159 if ( geometry.isNull() )
1160 return QVariantMap();
1161
1162 const QgsAbstractGeometry *geom = geometry.constGet()->simplifiedTypeRef();
1163 switch ( QgsWkbTypes::flatType( geom->wkbType() ) )
1164 {
1167 return QVariantMap();
1168
1170 res = pointToJson( qgsgeometry_cast< const QgsPoint * >( geom ) );
1171 break;
1172
1174 res = lineStringToJson( qgsgeometry_cast< const QgsLineString * >( geom ) );
1175 break;
1176
1179 res = curveToJson( qgsgeometry_cast< const QgsCurve * >( geom ) );
1180 break;
1181
1183 res = polygonToJson( qgsgeometry_cast< const QgsPolygon * >( geom ) );
1184 break;
1185
1187 res = multiPointToJson( qgsgeometry_cast< const QgsMultiPoint * >( geom ) );
1188 break;
1189
1191 res = multiLineStringToJson( qgsgeometry_cast< const QgsMultiLineString * >( geom ) );
1192 break;
1193
1195 res = multiCurveToJson( qgsgeometry_cast< const QgsMultiCurve * >( geom ) );
1196 break;
1197
1199 res = multiPolygonToJson( qgsgeometry_cast< const QgsMultiPolygon * >( geom ) );
1200 break;
1201
1203 res = curvePolygonToJson( qgsgeometry_cast< const QgsCurvePolygon * >( geom ) );
1204 break;
1205
1207 res = multiSurfaceToJson( qgsgeometry_cast< const QgsMultiSurface * >( geom ) );
1208 break;
1209
1211 return QVariantMap(); // not supported by REST API
1212
1214 return QVariantMap(); //not yet supported, but could be
1215
1216 default:
1217 return QVariantMap(); //unreachable
1218 }
1219
1220 if ( crs.isValid() )
1221 {
1222 // add spatialReference information
1223 res.insert( u"spatialReference"_s, crsToJson( crs ) );
1224 }
1225
1226 return res;
1227}
1228
1229QVariantMap QgsArcGisRestUtils::pointToJson( const QgsPoint *point )
1230{
1231 QVariantMap data;
1232 if ( point->isEmpty() )
1233 data[u"x"_s] = u"NaN"_s;
1234 else
1235 {
1236 data[u"x"_s] = point->x();
1237 data[u"y"_s] = point->y();
1238
1239 if ( point->is3D() )
1240 data[u"z"_s] = !std::isnan( point->z() ) ? QVariant( point->z() ) : QVariant( u"NaN"_s );
1241
1242 if ( point->isMeasure() )
1243 data[u"m"_s] = !std::isnan( point->m() ) ? QVariant( point->m() ) : QVariant( u"NaN"_s );
1244 }
1245 return data;
1246}
1247
1248QVariantMap QgsArcGisRestUtils::multiPointToJson( const QgsMultiPoint *multiPoint )
1249{
1250 QVariantMap data;
1251 const bool hasZ = multiPoint->is3D();
1252 const bool hasM = multiPoint->isMeasure();
1253 data[u"hasM"_s] = hasM;
1254 data[u"hasZ"_s] = hasZ;
1255
1256 QVariantList pointsList;
1257 const int size = multiPoint->numGeometries();
1258 pointsList.reserve( size );
1259
1260 QVariantList pointList;
1261 for ( int i = 0; i < size; ++i )
1262 {
1263 const QgsPoint *point = multiPoint->pointN( i );
1264
1265 pointList.clear();
1266 pointList.append( point->x() );
1267 pointList.append( point->y() );
1268 if ( hasZ )
1269 pointList.append( point->z() );
1270 if ( hasM && !std::isnan( point->m() ) )
1271 pointList.append( point->m() );
1272
1273 pointsList.push_back( pointList );
1274 }
1275
1276 data[u"points"_s] = pointsList;
1277 return data;
1278}
1279
1280QVariantList QgsArcGisRestUtils::lineStringToJsonPath( const QgsLineString *line )
1281{
1282 const bool hasZ = line->is3D();
1283 const bool hasM = line->isMeasure();
1284
1285 QVariantList pointsList;
1286 const int size = line->numPoints();
1287 pointsList.reserve( size );
1288
1289 QVariantList pointList;
1290 const double *xData = line->xData();
1291 const double *yData = line->yData();
1292 const double *zData = hasZ ? line->zData() : nullptr;
1293 const double *mData = hasM ? line->mData() : nullptr;
1294
1295 for ( int i = 0; i < size; ++i )
1296 {
1297 pointList.clear();
1298 pointList.append( *xData++ );
1299 pointList.append( *yData++ );
1300
1301 if ( hasZ )
1302 pointList.append( *zData++ );
1303
1304 if ( hasM && !std::isnan( *mData ) )
1305 pointList.append( *mData );
1306 if ( hasM )
1307 mData++;
1308
1309 pointsList.push_back( pointList );
1310 }
1311 return pointsList;
1312}
1313
1314QVariantList QgsArcGisRestUtils::curveToJsonCurve( const QgsCurve *curve, bool includeStart )
1315{
1316 const bool hasZ = curve->is3D();
1317 const bool hasM = curve->isMeasure();
1318
1319 auto pointToList = [hasZ, hasM]( const QgsPoint &point ) -> QVariantList {
1320 QVariantList pointList;
1321
1322 pointList.append( point.x() );
1323 pointList.append( point.y() );
1324
1325 if ( hasZ )
1326 pointList.append( point.z() );
1327
1328 if ( hasM && !std::isnan( point.m() ) )
1329 pointList.append( point.m() );
1330
1331 return pointList;
1332 };
1333
1334 QVariantList res;
1335 switch ( QgsWkbTypes::flatType( curve->wkbType() ) )
1336 {
1338 {
1339 QVariantList part = lineStringToJsonPath( qgsgeometry_cast< const QgsLineString *>( curve ) );
1340 if ( !part.isEmpty() && !includeStart )
1341 part.removeAt( 0 );
1342 res = part;
1343 break;
1344 }
1345
1347 {
1348 const QgsCircularString *circularString = qgsgeometry_cast<const QgsCircularString * >( curve );
1349 if ( includeStart && !circularString->isEmpty() )
1350 {
1351 res.push_back( pointToList( circularString->startPoint() ) );
1352 }
1353
1354 const int size = circularString->numPoints();
1355 for ( int i = 1; i + 1 < size; i += 2 )
1356 {
1357 // end point comes BEFORE interior point!
1358 QVariantMap curvePart;
1359 QVariantList curveList;
1360 curveList.push_back( pointToList( circularString->pointN( i + 1 ) ) );
1361
1362 curveList.push_back( pointToList( circularString->pointN( i ) ) );
1363
1364 curvePart.insert( u"c"_s, curveList );
1365 res.push_back( curvePart );
1366 }
1367 break;
1368 }
1369
1371 {
1372 const QgsCompoundCurve *compoundCurve = qgsgeometry_cast<const QgsCompoundCurve * >( curve );
1373
1374 const int size = compoundCurve->nCurves();
1375 for ( int i = 0; i < size; ++i )
1376 {
1377 const QgsCurve *subCurve = compoundCurve->curveAt( i );
1378 res.append( curveToJsonCurve( subCurve, i == 0 ) );
1379 }
1380 break;
1381 }
1382
1383 default:
1384 break;
1385 }
1386 return res;
1387}
1388
1389QVariantMap QgsArcGisRestUtils::lineStringToJson( const QgsLineString *line )
1390{
1391 QVariantMap data;
1392 const bool hasZ = line->is3D();
1393 const bool hasM = line->isMeasure();
1394 data[u"hasM"_s] = hasM;
1395 data[u"hasZ"_s] = hasZ;
1396
1397 const QVariantList pointsList = lineStringToJsonPath( line );
1398
1399 QVariantList pointsData = QVariantList();
1400 pointsData.push_back( pointsList );
1401 data[u"paths"_s] = pointsData;
1402
1403 return data;
1404}
1405
1406QVariantMap QgsArcGisRestUtils::curveToJson( const QgsCurve *curve )
1407{
1408 QVariantMap data;
1409 const bool hasZ = curve->is3D();
1410 const bool hasM = curve->isMeasure();
1411 data[u"hasM"_s] = hasM;
1412 data[u"hasZ"_s] = hasZ;
1413
1414 const QVariantList curveList = curveToJsonCurve( curve, true );
1415
1416 QVariantList curveData = QVariantList();
1417 curveData.push_back( curveList );
1418 data[u"curvePaths"_s] = curveData;
1419
1420 return data;
1421}
1422
1423QVariantMap QgsArcGisRestUtils::multiLineStringToJson( const QgsMultiLineString *multiLine )
1424{
1425 QVariantMap data;
1426 const bool hasZ = multiLine->is3D();
1427 const bool hasM = multiLine->isMeasure();
1428 data[u"hasM"_s] = hasM;
1429 data[u"hasZ"_s] = hasZ;
1430
1431 const int size = multiLine->numGeometries();
1432 QVariantList paths;
1433 paths.reserve( size );
1434 for ( int i = 0; i < size; ++i )
1435 {
1436 const QgsLineString *line = multiLine->lineStringN( i );
1437 paths.push_back( lineStringToJsonPath( line ) );
1438 }
1439
1440 data[u"paths"_s] = paths;
1441 return data;
1442}
1443
1444QVariantMap QgsArcGisRestUtils::multiCurveToJson( const QgsMultiCurve *multiCurve )
1445{
1446 QVariantMap data;
1447 const bool hasZ = multiCurve->is3D();
1448 const bool hasM = multiCurve->isMeasure();
1449 data[u"hasM"_s] = hasM;
1450 data[u"hasZ"_s] = hasZ;
1451
1452 const int size = multiCurve->numGeometries();
1453 QVariantList paths;
1454 paths.reserve( size );
1455 for ( int i = 0; i < size; ++i )
1456 {
1457 const QgsCurve *curve = multiCurve->curveN( i );
1458 paths.push_back( curveToJsonCurve( curve, true ) );
1459 }
1460
1461 data[u"curvePaths"_s] = paths;
1462 return data;
1463}
1464
1465QVariantList QgsArcGisRestUtils::polygonToJsonRings( const QgsPolygon *polygon )
1466{
1467 QVariantList rings;
1468 const int numInteriorRings = polygon->numInteriorRings();
1469 rings.reserve( numInteriorRings + 1 );
1470
1471 if ( const QgsLineString *exterior = qgsgeometry_cast< const QgsLineString * >( polygon->exteriorRing() ) )
1472 {
1473 // exterior ring MUST be clockwise
1474 switch ( exterior->orientation() )
1475 {
1477 rings.push_back( lineStringToJsonPath( exterior ) );
1478 break;
1479
1481 {
1482 std::unique_ptr< QgsLineString > reversed( exterior->reversed() );
1483 rings.push_back( lineStringToJsonPath( reversed.get() ) );
1484 break;
1485 }
1487 break;
1488 }
1489 }
1490
1491 for ( int i = 0; i < numInteriorRings; ++i )
1492 {
1493 const QgsLineString *ring = qgsgeometry_cast< const QgsLineString * >( polygon->interiorRing( i ) );
1494 // holes MUST be counter-clockwise
1495 switch ( ring->orientation() )
1496 {
1498 rings.push_back( lineStringToJsonPath( ring ) );
1499 break;
1500
1502 {
1503 std::unique_ptr< QgsLineString > reversed( ring->reversed() );
1504 rings.push_back( lineStringToJsonPath( reversed.get() ) );
1505 break;
1506 }
1508 break;
1509 }
1510 }
1511 return rings;
1512}
1513
1514QVariantList QgsArcGisRestUtils::curvePolygonToJsonRings( const QgsCurvePolygon *polygon )
1515{
1516 QVariantList rings;
1517 const int numInteriorRings = polygon->numInteriorRings();
1518 rings.reserve( numInteriorRings + 1 );
1519
1520 if ( const QgsCurve *exterior = qgsgeometry_cast< const QgsCurve * >( polygon->exteriorRing() ) )
1521 {
1522 // exterior ring MUST be clockwise
1523 switch ( exterior->orientation() )
1524 {
1526 rings.push_back( curveToJsonCurve( exterior, true ) );
1527 break;
1528
1530 {
1531 std::unique_ptr< QgsCurve > reversed( exterior->reversed() );
1532 rings.push_back( curveToJsonCurve( reversed.get(), true ) );
1533 break;
1534 }
1536 break;
1537 }
1538 }
1539
1540 for ( int i = 0; i < numInteriorRings; ++i )
1541 {
1542 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( polygon->interiorRing( i ) );
1543 // holes MUST be counter-clockwise
1544 switch ( ring->orientation() )
1545 {
1547 rings.push_back( curveToJsonCurve( ring, true ) );
1548 break;
1549
1551 {
1552 std::unique_ptr< QgsCurve > reversed( ring->reversed() );
1553 rings.push_back( curveToJsonCurve( reversed.get(), true ) );
1554 break;
1555 }
1557 break;
1558 }
1559 }
1560 return rings;
1561}
1562
1563QVariantMap QgsArcGisRestUtils::polygonToJson( const QgsPolygon *polygon )
1564{
1565 QVariantMap data;
1566 const bool hasZ = polygon->is3D();
1567 const bool hasM = polygon->isMeasure();
1568 data[u"hasM"_s] = hasM;
1569 data[u"hasZ"_s] = hasZ;
1570 data[u"rings"_s] = polygonToJsonRings( polygon );
1571 return data;
1572}
1573
1574QVariantMap QgsArcGisRestUtils::curvePolygonToJson( const QgsCurvePolygon *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"curveRings"_s] = curvePolygonToJsonRings( polygon );
1582 return data;
1583}
1584
1585QVariantMap QgsArcGisRestUtils::multiPolygonToJson( const QgsMultiPolygon *multiPolygon )
1586{
1587 QVariantMap data;
1588 const bool hasZ = multiPolygon->is3D();
1589 const bool hasM = multiPolygon->isMeasure();
1590 data[u"hasM"_s] = hasM;
1591 data[u"hasZ"_s] = hasZ;
1592
1593 const int size = multiPolygon->numGeometries();
1594 QVariantList rings;
1595 for ( int i = 0; i < size; ++i )
1596 {
1597 const QgsPolygon *polygon = multiPolygon->polygonN( i );
1598 rings.append( polygonToJsonRings( polygon ) );
1599 }
1600
1601 data[u"rings"_s] = rings;
1602 return data;
1603}
1604
1605QVariantMap QgsArcGisRestUtils::multiSurfaceToJson( const QgsMultiSurface *multiSurface )
1606{
1607 QVariantMap data;
1608 const bool hasZ = multiSurface->is3D();
1609 const bool hasM = multiSurface->isMeasure();
1610 data[u"hasM"_s] = hasM;
1611 data[u"hasZ"_s] = hasZ;
1612
1613 const int size = multiSurface->numGeometries();
1614 QVariantList rings;
1615 for ( int i = 0; i < size; ++i )
1616 {
1617 const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( multiSurface->geometryN( i ) );
1618 if ( !polygon )
1619 continue;
1620
1621 rings.append( curvePolygonToJsonRings( polygon ) );
1622 }
1623
1624 data[u"curveRings"_s] = rings;
1625 return data;
1626}
1627
1629{
1630 QVariantMap res;
1631 if ( !crs.isValid() )
1632 return res;
1633
1634 const QString authid = crs.authid();
1635 if ( !authid.isEmpty() )
1636 {
1637 const thread_local QRegularExpression rxAuthid( u"(\\w+):(\\d+)"_s );
1638 const QRegularExpressionMatch match = rxAuthid.match( authid );
1639 if ( match.hasMatch() && ( ( match.captured( 1 ).compare( "EPSG"_L1, Qt::CaseInsensitive ) == 0 ) || ( match.captured( 1 ).compare( "ESRI"_L1, Qt::CaseInsensitive ) == 0 ) ) )
1640 {
1641 const QString wkid = match.captured( 2 );
1642 res.insert( u"wkid"_s, wkid );
1643 return res;
1644 }
1645 }
1646
1647 // docs don't mention the WKT version support, so let's hope for 2.0...
1648 res.insert( u"wkt"_s, crs.toWkt( Qgis::CrsWktVariant::Wkt2_2019Simplified ) );
1649
1650 return res;
1651}
1652
1654{
1655 QVariantMap res;
1656 if ( ( flags & FeatureToJsonFlag::IncludeGeometry ) && feature.hasGeometry() )
1657 {
1658 res.insert( u"geometry"_s, geometryToJson( feature.geometry(), context, crs ) );
1659 }
1660
1661 QVariantMap attributes;
1662 const QgsFields fields = feature.fields();
1663 for ( const QgsField &field : fields )
1664 {
1665 QVariant value = feature.attribute( field.name() );
1666 if ( value.userType() == qMetaTypeId< QgsUnsetAttributeValue >() )
1667 {
1668 if ( flags.testFlag( FeatureToJsonFlag::SkipUnsetAttributes ) )
1669 continue;
1670 else
1671 value = QVariant(); // reset to null, we can't store 'QgsUnsetAttributeValue' as json
1672 }
1673
1674 if ( ( flags & FeatureToJsonFlag::IncludeNonObjectIdAttributes ) || field.name() == context.objectIdFieldName() )
1675 attributes.insert( field.name(), variantToAttributeValue( value, field.type(), context ) );
1676 }
1677 if ( !attributes.isEmpty() )
1678 {
1679 res.insert( u"attributes"_s, attributes );
1680 }
1681 return res;
1682}
1683
1684QVariant QgsArcGisRestUtils::variantToAttributeValue( const QVariant &variant, QMetaType::Type expectedType, const QgsArcGisRestContext &context )
1685{
1686 if ( QgsVariantUtils::isNull( variant ) )
1687 return QVariant();
1688
1689 switch ( expectedType )
1690 {
1691 case QMetaType::Type::QString:
1692 {
1693 const QString escaped = variant.toString().replace( '\\', "\\\\"_L1 ).replace( '"', "\\\""_L1 );
1694 return QString( QUrl::toPercentEncoding( escaped, "'" ) );
1695 }
1696
1697 case QMetaType::Type::QDateTime:
1698 case QMetaType::Type::QDate:
1699 {
1700 switch ( variant.userType() )
1701 {
1702 case QMetaType::Type::QDateTime:
1703 return variant.toDateTime().toMSecsSinceEpoch();
1704
1705 case QMetaType::Type::QDate:
1706 // for date values, assume start of day -- the REST api requires datetime values only, not plain dates
1707 if ( context.timeZone().isValid() )
1708 return QDateTime( variant.toDate(), QTime( 0, 0, 0 ), context.timeZone() ).toMSecsSinceEpoch();
1709 else
1710 return QDateTime( variant.toDate(), QTime( 0, 0, 0 ) ).toMSecsSinceEpoch();
1711
1712 default:
1713 return QVariant();
1714 }
1715 }
1716
1717 default:
1718 return variant;
1719 }
1720}
1721
1723{
1724 QVariantMap res;
1725 res.insert( u"name"_s, field.name() );
1726
1727 QString fieldType;
1728 switch ( field.type() )
1729 {
1730 case QMetaType::Type::LongLong:
1731 fieldType = u"esriFieldTypeInteger"_s;
1732 break;
1733
1734 case QMetaType::Type::Int:
1735 fieldType = u"esriFieldTypeSmallInteger"_s;
1736 break;
1737
1738 case QMetaType::Type::Double:
1739 fieldType = u"esriFieldTypeDouble"_s;
1740 break;
1741
1742 case QMetaType::Type::QString:
1743 fieldType = u"esriFieldTypeString"_s;
1744 break;
1745
1746 case QMetaType::Type::QDateTime:
1747 case QMetaType::Type::QDate:
1748 fieldType = u"esriFieldTypeDate"_s;
1749 break;
1750
1751 case QMetaType::Type::QByteArray:
1752 fieldType = u"esriFieldTypeBlob"_s;
1753 break;
1754
1755 default:
1756 // fallback to string
1757 fieldType = u"esriFieldTypeString"_s;
1758 break;
1759 }
1760 res.insert( u"type"_s, fieldType );
1761
1762 if ( !field.alias().isEmpty() )
1763 res.insert( u"alias"_s, field.alias() );
1764
1765 // nullable
1767 res.insert( u"nullable"_s, !notNullable );
1768
1769 // editable
1770 res.insert( u"editable"_s, true );
1771
1772 return res;
1773}
1774
1776{
1777 if ( type.compare( "FeatureServer"_L1, Qt::CaseInsensitive ) == 0 )
1779 else if ( type.compare( "MapServer"_L1, Qt::CaseInsensitive ) == 0 )
1781 else if ( type.compare( "ImageServer"_L1, Qt::CaseInsensitive ) == 0 )
1783 else if ( type.compare( "GlobeServer"_L1, Qt::CaseInsensitive ) == 0 )
1785 else if ( type.compare( "GPServer"_L1, Qt::CaseInsensitive ) == 0 )
1787 else if ( type.compare( "GeocodeServer"_L1, Qt::CaseInsensitive ) == 0 )
1789 else if ( type.compare( "SceneServer"_L1, Qt::CaseInsensitive ) == 0 )
1791
1793}
1794
1796{
1797 const QStringList parts = capabilities.split( ',' );
1798
1800 res.setFlag( Qgis::ArcGisRestServiceCapability::Query, parts.contains( "query"_L1, Qt::CaseInsensitive ) );
1801 res.setFlag( Qgis::ArcGisRestServiceCapability::Map, parts.contains( "map"_L1, Qt::CaseInsensitive ) );
1802 res.setFlag( Qgis::ArcGisRestServiceCapability::Update, parts.contains( "update"_L1, Qt::CaseInsensitive ) );
1803 res.setFlag( Qgis::ArcGisRestServiceCapability::Delete, parts.contains( "delete"_L1, Qt::CaseInsensitive ) );
1804 res.setFlag( Qgis::ArcGisRestServiceCapability::Create, parts.contains( "create"_L1, Qt::CaseInsensitive ) );
1805 res.setFlag( Qgis::ArcGisRestServiceCapability::Image, parts.contains( "image"_L1, Qt::CaseInsensitive ) );
1806 res.setFlag( Qgis::ArcGisRestServiceCapability::TilesOnly, parts.contains( "tilesonly"_L1, Qt::CaseInsensitive ) );
1807
1808 return res;
1809}
1810
1812{
1813 if ( pixelType.compare( "U8"_L1, Qt::CaseInsensitive ) == 0
1814 || pixelType.compare( "U4"_L1, Qt::CaseInsensitive ) == 0
1815 || pixelType.compare( "U2"_L1, Qt::CaseInsensitive ) == 0
1816 || pixelType.compare( "U1"_L1, Qt::CaseInsensitive ) == 0 )
1817 {
1818 return Qgis::DataType::Byte;
1819 }
1820 else if ( pixelType.compare( "S8"_L1, Qt::CaseInsensitive ) == 0 )
1821 {
1822 return Qgis::DataType::Int8;
1823 }
1824 else if ( pixelType.compare( "U16"_L1, Qt::CaseInsensitive ) == 0 )
1825 {
1827 }
1828 else if ( pixelType.compare( "S16"_L1, Qt::CaseInsensitive ) == 0 )
1829 {
1830 return Qgis::DataType::Int16;
1831 }
1832 else if ( pixelType.compare( "U32"_L1, Qt::CaseInsensitive ) == 0 )
1833 {
1835 }
1836 else if ( pixelType.compare( "S32"_L1, Qt::CaseInsensitive ) == 0 )
1837 {
1838 return Qgis::DataType::Int32;
1839 }
1840 else if ( pixelType.compare( "F32"_L1, Qt::CaseInsensitive ) == 0 )
1841 {
1843 }
1844 else if ( pixelType.compare( "F64"_L1, Qt::CaseInsensitive ) == 0 )
1845 {
1847 }
1848 else if ( pixelType.compare( "C64"_L1, Qt::CaseInsensitive ) == 0 )
1849 {
1850 // C64 = 32-bit real + 32-bit imaginary
1852 }
1853 else if ( pixelType.compare( "C128"_L1, Qt::CaseInsensitive ) == 0 )
1854 {
1855 // C128 = 64-bit real + 64-bit imaginary
1857 }
1858 else
1859 {
1860 QgsDebugError( u"Unknown pixelType: %1"_s.arg( pixelType ) );
1861 }
1862
1864}
1865
1867{
1868 if ( pixelType.compare( "U8"_L1, Qt::CaseInsensitive ) == 0
1869 || pixelType.compare( "U4"_L1, Qt::CaseInsensitive ) == 0
1870 || pixelType.compare( "U2"_L1, Qt::CaseInsensitive ) == 0
1871 || pixelType.compare( "U1"_L1, Qt::CaseInsensitive ) == 0
1872 || pixelType.compare( "S8"_L1, Qt::CaseInsensitive ) == 0
1873 || pixelType.compare( "U16"_L1, Qt::CaseInsensitive ) == 0
1874 || pixelType.compare( "S16"_L1, Qt::CaseInsensitive ) == 0 )
1875 {
1876 return PixelTypeLimitUsefulness { true, true };
1877 }
1878 else if ( pixelType.compare( "U32"_L1, Qt::CaseInsensitive ) == 0 )
1879 {
1880 return PixelTypeLimitUsefulness { true, false };
1881 }
1882 else if ( pixelType.compare( "S32"_L1, Qt::CaseInsensitive ) == 0
1883 || pixelType.compare( "F32"_L1, Qt::CaseInsensitive ) == 0
1884 || pixelType.compare( "F64"_L1, Qt::CaseInsensitive ) == 0
1885 || pixelType.compare( "C64"_L1, Qt::CaseInsensitive ) == 0
1886 || pixelType.compare( "C128"_L1, Qt::CaseInsensitive ) == 0 )
1887 {
1888 return PixelTypeLimitUsefulness { false, false };
1889 }
1890 else
1891 {
1892 QgsDebugError( u"Unknown pixelType: %1"_s.arg( pixelType ) );
1893 }
1894
1895 return PixelTypeLimitUsefulness { false, false };
1896}
1897
1898std::optional<std::pair<double, double> > QgsArcGisRestUtils::rangeForPixelType( const QString &pixelType )
1899{
1900 if ( pixelType.compare( "U8"_L1, Qt::CaseInsensitive ) == 0 )
1901 {
1902 return std::make_pair( 0.0, 255.0 );
1903 }
1904 else if ( pixelType.compare( "U4"_L1, Qt::CaseInsensitive ) == 0 )
1905 {
1906 return std::make_pair( 0.0, 15.0 );
1907 }
1908 else if ( pixelType.compare( "U2"_L1, Qt::CaseInsensitive ) == 0 )
1909 {
1910 return std::make_pair( 0.0, 3.0 );
1911 }
1912 else if ( pixelType.compare( "U1"_L1, Qt::CaseInsensitive ) == 0 )
1913 {
1914 return std::make_pair( 0.0, 1.0 );
1915 }
1916 else if ( pixelType.compare( "S8"_L1, Qt::CaseInsensitive ) == 0 )
1917 {
1918 return std::make_pair( -128.0, 127.0 );
1919 }
1920 else if ( pixelType.compare( "U16"_L1, Qt::CaseInsensitive ) == 0 )
1921 {
1922 return std::make_pair( 0.0, 65535.0 );
1923 }
1924 else if ( pixelType.compare( "S16"_L1, Qt::CaseInsensitive ) == 0 )
1925 {
1926 return std::make_pair( -32768.0, 32767.0 );
1927 }
1928 else if ( pixelType.compare( "U32"_L1, Qt::CaseInsensitive ) == 0 )
1929 {
1930 return std::make_pair( 0.0, static_cast<double>( std::numeric_limits<uint32_t>::max() ) );
1931 }
1932 else if ( pixelType.compare( "S32"_L1, Qt::CaseInsensitive ) == 0 )
1933 {
1934 return std::make_pair( static_cast<double>( std::numeric_limits<int32_t>::lowest() ), static_cast<double>( std::numeric_limits<int32_t>::max() ) );
1935 }
1936 else if ( pixelType.compare( "F32"_L1, Qt::CaseInsensitive ) == 0 )
1937 {
1938 return std::make_pair( static_cast<double>( std::numeric_limits<float>::lowest() ), static_cast<double>( std::numeric_limits<float>::max() ) );
1939 }
1940 else if ( pixelType.compare( "F64"_L1, Qt::CaseInsensitive ) == 0 )
1941 {
1942 return std::make_pair( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max() );
1943 }
1944 else if ( pixelType.compare( "C64"_L1, Qt::CaseInsensitive ) == 0 )
1945 {
1946 // C64 = 32-bit real + 32-bit imaginary
1947 return std::make_pair( static_cast<double>( std::numeric_limits<float>::lowest() ), static_cast<double>( std::numeric_limits<float>::max() ) );
1948 }
1949 else if ( pixelType.compare( "C128"_L1, Qt::CaseInsensitive ) == 0 )
1950 {
1951 // C128 = 64-bit real + 64-bit imaginary
1952 return std::make_pair( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max() );
1953 }
1954 else
1955 {
1956 QgsDebugError( u"Unknown pixelType: %1"_s.arg( pixelType ) );
1957 }
1958
1959 return std::nullopt;
1960}
1961
1963{
1964 if ( bandName.isEmpty() )
1965 {
1967 }
1968
1969 if ( bandName.compare( "Red"_L1, Qt::CaseInsensitive ) == 0 )
1971 else if ( bandName.compare( "Green"_L1, Qt::CaseInsensitive ) == 0 )
1973 else if ( bandName.compare( "Blue"_L1, Qt::CaseInsensitive ) == 0 )
1975 else if ( bandName.compare( "Alpha"_L1, Qt::CaseInsensitive ) == 0 )
1977 else if ( bandName.compare( "NIR"_L1, Qt::CaseInsensitive ) == 0
1978 || bandName.compare( "NearInfrared"_L1, Qt::CaseInsensitive ) == 0
1979 || bandName.compare( "NearIR"_L1, Qt::CaseInsensitive ) == 0
1980 || bandName.compare( "NarrowNIR"_L1, Qt::CaseInsensitive ) == 0 )
1982 else if ( bandName.startsWith( "SWIR"_L1, Qt::CaseInsensitive ) )
1984 else if ( bandName.startsWith( "VRE"_L1, Qt::CaseInsensitive ) || bandName.compare( "RedEdge"_L1, Qt::CaseInsensitive ) == 0 )
1986 else if ( bandName.startsWith( "Coastal"_L1, Qt::CaseInsensitive ) )
1988 else if ( bandName.startsWith( "Pan"_L1, Qt::CaseInsensitive ) )
1990 else if ( bandName.startsWith( "Thermal"_L1, Qt::CaseInsensitive ) || bandName.startsWith( "TIR"_L1, Qt::CaseInsensitive ) )
1992 else if ( bandName.compare( "Gray"_L1, Qt::CaseInsensitive ) == 0 || bandName.compare( "Grey"_L1, Qt::CaseInsensitive ) == 0 )
1994 else if ( bandName.compare( "Cyan"_L1, Qt::CaseInsensitive ) == 0 )
1996 else if ( bandName.compare( "Magenta"_L1, Qt::CaseInsensitive ) == 0 )
1998 else if ( bandName.compare( "Yellow"_L1, Qt::CaseInsensitive ) == 0 )
2000 else if ( bandName.compare( "Black"_L1, Qt::CaseInsensitive ) == 0 )
2002 else if ( bandName.compare( "Hue"_L1, Qt::CaseInsensitive ) == 0 )
2004 else if ( bandName.compare( "Saturation"_L1, Qt::CaseInsensitive ) == 0 )
2006 else if ( bandName.compare( "Lightness"_L1, Qt::CaseInsensitive ) == 0 )
2008
2009 // we don't log failures here -- a lot of bands will have non-interpretable names,
2010 // eg "Band 1"
2012}
2013
2015{
2016 ok = false;
2017 switch ( type )
2018 {
2020 return 0;
2021
2023 ok = true;
2024 return std::numeric_limits<quint8>::max();
2025
2027 ok = true;
2028 return std::numeric_limits<qint8>::lowest();
2029
2031 ok = true;
2032 return std::numeric_limits<quint16>::max();
2033
2035 ok = true;
2036 return std::numeric_limits<qint16>::lowest();
2037
2039 ok = true;
2040 return std::numeric_limits<quint32>::max();
2041
2043 ok = true;
2044 return std::numeric_limits<qint32>::lowest();
2045
2047 ok = true;
2048 return std::numeric_limits<float>::quiet_NaN();
2049
2051 ok = true;
2052 return std::numeric_limits<double>::quiet_NaN();
2053
2060 return 0;
2061 }
2063}
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1398
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
Definition qgis.h:1399
@ OnLine
Labels can be placed directly over a line feature.
Definition qgis.h:1396
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1397
@ NoOrientation
Unknown orientation or sentinel value.
Definition qgis.h:3651
@ CounterClockwise
Counter-clockwise direction.
Definition qgis.h:3650
@ Clockwise
Clockwise direction.
Definition qgis.h:3649
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
Definition qgis.h:1289
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
Definition qgis.h:1290
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature or along a line feature....
Definition qgis.h:1292
ArcGisRestServiceType
Available ArcGIS REST service types.
Definition qgis.h:4787
@ GeocodeServer
GeocodeServer.
Definition qgis.h:4793
@ SceneServer
SceneServer.
Definition qgis.h:4795
@ Unknown
Other unknown/unsupported type.
Definition qgis.h:4794
@ GlobeServer
GlobeServer.
Definition qgis.h:4791
@ ImageServer
ImageServer.
Definition qgis.h:4790
@ FeatureServer
FeatureServer.
Definition qgis.h:4788
@ AboveRight
Above right.
Definition qgis.h:1378
@ BelowLeft
Below left.
Definition qgis.h:1382
@ Above
Above center.
Definition qgis.h:1377
@ BelowRight
Below right.
Definition qgis.h:1384
@ Right
Right middle.
Definition qgis.h:1381
@ AboveLeft
Above left.
Definition qgis.h:1376
@ Below
Below center.
Definition qgis.h:1383
@ Over
Center middle.
Definition qgis.h:1380
DataType
Raster data types.
Definition qgis.h:393
@ CInt32
Complex Int32.
Definition qgis.h:404
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:401
@ CFloat64
Complex Float64.
Definition qgis.h:406
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:398
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:408
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30).
Definition qgis.h:396
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:397
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:395
@ UnknownDataType
Unknown or unspecified type.
Definition qgis.h:394
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition qgis.h:407
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:400
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:402
@ CFloat32
Complex Float32.
Definition qgis.h:405
@ CInt16
Complex Int16.
Definition qgis.h:403
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:399
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5687
QFlags< ArcGisRestServiceCapability > ArcGisRestServiceCapabilities
Available ArcGIS REST service capabilities.
Definition qgis.h:4824
@ Update
Update features.
Definition qgis.h:4811
@ Create
Create features.
Definition qgis.h:4813
@ TilesOnly
Service supports tiled image requests only.
Definition qgis.h:4815
@ Image
Image capabilities.
Definition qgis.h:4814
@ Delete
Delete features.
Definition qgis.h:4812
RasterColorInterpretation
Raster color interpretation.
Definition qgis.h:5211
@ NIRBand
Near-InfraRed (NIR) band [0.75 - 1.40 um].
Definition qgis.h:5237
@ GreenBand
Green band of RGBA image, or green spectral band [0.51 - 0.60 um].
Definition qgis.h:5216
@ SaturationBand
Saturation band of HLS image.
Definition qgis.h:5220
@ MagentaBand
Magenta band of CMYK image.
Definition qgis.h:5223
@ BlackBand
Black band of CMLY image.
Definition qgis.h:5225
@ AlphaBand
Alpha (0=transparent, 255=opaque).
Definition qgis.h:5218
@ SWIRBand
Short-Wavelength InfraRed (SWIR) band [1.40 - 3.00 um].
Definition qgis.h:5238
@ BlueBand
Blue band of RGBA image, or blue spectral band [0.45 - 0.53 um].
Definition qgis.h:5217
@ RedEdgeBand
Red-edge band [0.69 - 0.79 um].
Definition qgis.h:5236
@ YellowBand
Yellow band of CMYK image, or yellow spectral band [0.58 - 0.62 um].
Definition qgis.h:5224
@ CyanBand
Cyan band of CMYK image.
Definition qgis.h:5222
@ LightnessBand
Lightness band of HLS image.
Definition qgis.h:5221
@ CoastalBand
Coastal band [0.40 - 0.45 um].
Definition qgis.h:5235
@ HueBand
Hue band of HLS image.
Definition qgis.h:5219
@ TIRBand
Thermal InfraRed (TIR) band (MWIR or LWIR) [3 - 15 um].
Definition qgis.h:5241
@ RedBand
Red band of RGBA image, or red spectral band [0.62 - 0.69 um].
Definition qgis.h:5215
@ PanBand
Panchromatic band [0.40 - 1.00 um].
Definition qgis.h:5234
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ CompoundCurve
CompoundCurve.
Definition qgis.h:305
@ Point
Point.
Definition qgis.h:296
@ LineString
LineString.
Definition qgis.h:297
@ MultiPoint
MultiPoint.
Definition qgis.h:300
@ Polygon
Polygon.
Definition qgis.h:298
@ MultiPolygon
MultiPolygon.
Definition qgis.h:302
@ Triangle
Triangle.
Definition qgis.h:299
@ NoGeometry
No geometry.
Definition qgis.h:312
@ MultiLineString
MultiLineString.
Definition qgis.h:301
@ Unknown
Unknown.
Definition qgis.h:295
@ CircularString
CircularString.
Definition qgis.h:304
@ GeometryCollection
GeometryCollection.
Definition qgis.h:303
@ MultiCurve
MultiCurve.
Definition qgis.h:307
@ CurvePolygon
CurvePolygon.
Definition qgis.h:306
@ MultiSurface
MultiSurface.
Definition qgis.h:308
@ Wkt2_2019Simplified
WKT2_2019 with the simplification rule of WKT2_SIMPLIFIED.
Definition qgis.h:2610
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.
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...
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:781
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:8229
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7557
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...