QGIS API Documentation 4.1.0-Master (376402f9aeb)
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
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 )
630{
631 QgsReadWriteContext rwContext;
632 QgsSymbolConverterContext context( rwContext );
633 return QgsSymbolConverterEsriRest().createSymbol( symbolData, context );
634}
635
636std::unique_ptr<QgsAbstractVectorLayerLabeling > QgsArcGisRestUtils::convertLabeling( const QVariantList &labelingData )
637{
638 if ( labelingData.empty() )
639 return nullptr;
640
641 QgsRuleBasedLabeling::Rule *root = new QgsRuleBasedLabeling::Rule( new QgsPalLayerSettings(), 0, 0, QString(), QString(), false );
642 root->setActive( true );
643
644 int i = 1;
645 for ( const QVariant &lbl : labelingData )
646 {
647 const QVariantMap labeling = lbl.toMap();
648
650 QgsTextFormat format;
651
652 const QString placement = labeling.value( u"labelPlacement"_s ).toString();
653 if ( placement == "esriServerPointLabelPlacementAboveCenter"_L1 )
654 {
657 }
658 else if ( placement == "esriServerPointLabelPlacementBelowCenter"_L1 )
659 {
662 }
663 else if ( placement == "esriServerPointLabelPlacementCenterCenter"_L1 )
664 {
667 }
668 else if ( placement == "esriServerPointLabelPlacementAboveLeft"_L1 )
669 {
672 }
673 else if ( placement == "esriServerPointLabelPlacementBelowLeft"_L1 )
674 {
677 }
678 else if ( placement == "esriServerPointLabelPlacementCenterLeft"_L1 )
679 {
682 }
683 else if ( placement == "esriServerPointLabelPlacementAboveRight"_L1 )
684 {
687 }
688 else if ( placement == "esriServerPointLabelPlacementBelowRight"_L1 )
689 {
692 }
693 else if ( placement == "esriServerPointLabelPlacementCenterRight"_L1 )
694 {
697 }
698 else if ( placement == "esriServerLinePlacementAboveAfter"_L1 || placement == "esriServerLinePlacementAboveStart"_L1 || placement == "esriServerLinePlacementAboveAlong"_L1 )
699 {
702 }
703 else if ( placement == "esriServerLinePlacementBelowAfter"_L1 || placement == "esriServerLinePlacementBelowStart"_L1 || placement == "esriServerLinePlacementBelowAlong"_L1 )
704 {
707 }
708 else if ( placement == "esriServerLinePlacementCenterAfter"_L1 || placement == "esriServerLinePlacementCenterStart"_L1 || placement == "esriServerLinePlacementCenterAlong"_L1 )
709 {
712 }
713 else if ( placement == "esriServerPolygonPlacementAlwaysHorizontal"_L1 )
714 {
716 }
717
718 const double minScale = labeling.value( u"minScale"_s ).toDouble();
719 const double maxScale = labeling.value( u"maxScale"_s ).toDouble();
720
721 QVariantMap symbol = labeling.value( u"symbol"_s ).toMap();
722 format.setColor( convertColor( symbol.value( u"color"_s ) ) );
723 const double haloSize = symbol.value( u"haloSize"_s ).toDouble();
724 if ( !qgsDoubleNear( haloSize, 0.0 ) )
725 {
727 buffer.setEnabled( true );
728 buffer.setSize( haloSize );
730 buffer.setColor( convertColor( symbol.value( u"haloColor"_s ) ) );
731 format.setBuffer( buffer );
732 }
733
734 const QString fontFamily = symbol.value( u"font"_s ).toMap().value( u"family"_s ).toString();
735 const QString fontStyle = symbol.value( u"font"_s ).toMap().value( u"style"_s ).toString();
736 const QString fontWeight = symbol.value( u"font"_s ).toMap().value( u"weight"_s ).toString();
737 const int fontSize = symbol.value( u"font"_s ).toMap().value( u"size"_s ).toInt();
738 QFont font( fontFamily, fontSize );
739 font.setStyleName( fontStyle );
740 font.setWeight( fontWeight == "bold"_L1 ? QFont::Bold : QFont::Normal );
741
742 format.setFont( font );
743 format.setSize( fontSize );
745
746 settings->setFormat( format );
747
748 QString where = labeling.value( u"where"_s ).toString();
749 QgsExpression exp( where );
750 // If the where clause isn't parsed as valid, don't use its
751 if ( !exp.isValid() )
752 where.clear();
753
754 settings->fieldName = convertLabelingExpression( labeling.value( u"labelExpression"_s ).toString() );
755 settings->isExpression = true;
756
757 QgsRuleBasedLabeling::Rule *child = new QgsRuleBasedLabeling::Rule( settings, maxScale, minScale, where, QObject::tr( "ASF label %1" ).arg( i++ ), false );
758 child->setActive( true );
759 root->appendChild( child );
760 }
761
762 return std::make_unique< QgsRuleBasedLabeling >( root );
763}
764
765std::unique_ptr< QgsFeatureRenderer > QgsArcGisRestUtils::convertRenderer( const QVariantMap &rendererData )
766{
767 const QString type = rendererData.value( u"type"_s ).toString();
768 if ( type == "simple"_L1 )
769 {
770 const QVariantMap symbolProps = rendererData.value( u"symbol"_s ).toMap();
771 std::unique_ptr< QgsSymbol > symbol( convertSymbol( symbolProps ) );
772 if ( symbol )
773 return std::make_unique< QgsSingleSymbolRenderer >( symbol.release() );
774 else
775 return nullptr;
776 }
777 else if ( type == "uniqueValue"_L1 )
778 {
779 const QString field1 = rendererData.value( u"field1"_s ).toString();
780 const QString field2 = rendererData.value( u"field2"_s ).toString();
781 const QString field3 = rendererData.value( u"field3"_s ).toString();
782 QString attribute;
783 if ( !field2.isEmpty() || !field3.isEmpty() )
784 {
785 const QString delimiter = rendererData.value( u"fieldDelimiter"_s ).toString();
786 if ( !field3.isEmpty() )
787 {
788 attribute = u"concat(\"%1\",'%2',\"%3\",'%4',\"%5\")"_s.arg( field1, delimiter, field2, delimiter, field3 );
789 }
790 else
791 {
792 attribute = u"concat(\"%1\",'%2',\"%3\")"_s.arg( field1, delimiter, field2 );
793 }
794 }
795 else
796 {
797 attribute = field1;
798 }
799
800 const QVariantList categories = rendererData.value( u"uniqueValueInfos"_s ).toList();
801 QgsCategoryList categoryList;
802 for ( const QVariant &category : categories )
803 {
804 const QVariantMap categoryData = category.toMap();
805 const QString value = categoryData.value( u"value"_s ).toString();
806 const QString label = categoryData.value( u"label"_s ).toString();
807 std::unique_ptr< QgsSymbol > symbol( QgsArcGisRestUtils::convertSymbol( categoryData.value( u"symbol"_s ).toMap() ) );
808 if ( symbol )
809 {
810 categoryList.append( QgsRendererCategory( value, symbol.release(), label ) );
811 }
812 }
813
814 std::unique_ptr< QgsSymbol > defaultSymbol( convertSymbol( rendererData.value( u"defaultSymbol"_s ).toMap() ) );
815 if ( defaultSymbol )
816 {
817 categoryList.append( QgsRendererCategory( QVariant(), defaultSymbol.release(), rendererData.value( u"defaultLabel"_s ).toString() ) );
818 }
819
820 if ( categoryList.empty() )
821 return nullptr;
822
823 return std::make_unique< QgsCategorizedSymbolRenderer >( attribute, categoryList );
824 }
825 else if ( type == "classBreaks"_L1 )
826 {
827 const QString attrName = rendererData.value( u"field"_s ).toString();
828
829 const QVariantList classBreakInfos = rendererData.value( u"classBreakInfos"_s ).toList();
830 const QVariantMap authoringInfo = rendererData.value( u"authoringInfo"_s ).toMap();
831 QVariantMap symbolData;
832
833 QString esriMode = authoringInfo.value( u"classificationMethod"_s ).toString();
834 if ( esriMode.isEmpty() )
835 {
836 esriMode = rendererData.value( u"classificationMethod"_s ).toString();
837 }
838
839 if ( !classBreakInfos.isEmpty() )
840 {
841 symbolData = classBreakInfos.at( 0 ).toMap().value( u"symbol"_s ).toMap();
842 }
843 std::unique_ptr< QgsSymbol > symbol( QgsArcGisRestUtils::convertSymbol( symbolData ) );
844 if ( !symbol )
845 return nullptr;
846
847 const double transparency = rendererData.value( u"transparency"_s ).toDouble();
848 const double opacity = ( 100.0 - transparency ) / 100.0;
849 symbol->setOpacity( opacity );
850
851 const QVariantList visualVariablesData = rendererData.value( u"visualVariables"_s ).toList();
852
853 for ( const QVariant &visualVariable : visualVariablesData )
854 {
855 const QVariantMap visualVariableData = visualVariable.toMap();
856 const QString variableType = visualVariableData.value( u"type"_s ).toString();
857 if ( variableType == "sizeInfo"_L1 )
858 {
859 continue;
860 }
861 else if ( variableType == "colorInfo"_L1 )
862 {
863 const QVariantList stops = visualVariableData.value( u"stops"_s ).toList();
864 if ( stops.size() < 2 )
865 continue;
866
867 // layer has continuous coloring, so convert to a symbol using color ramp assistant
868 bool ok = false;
869 const double minValue = stops.front().toMap().value( u"value"_s ).toDouble( &ok );
870 if ( !ok )
871 continue;
872 const QColor minColor = convertColor( stops.front().toMap().value( u"color"_s ) );
873
874 const double maxValue = stops.back().toMap().value( u"value"_s ).toDouble( &ok );
875 if ( !ok )
876 continue;
877 const QColor maxColor = convertColor( stops.back().toMap().value( u"color"_s ) );
878
879 QgsGradientStopsList gradientStops;
880 for ( int i = 1; i < stops.size() - 1; ++i )
881 {
882 const QVariantMap stopData = stops.at( i ).toMap();
883 const double breakpoint = stopData.value( u"value"_s ).toDouble();
884 const double scaledBreakpoint = ( breakpoint - minValue ) / ( maxValue - minValue );
885 const QColor fillColor = convertColor( stopData.value( u"color"_s ) );
886
887 gradientStops.append( QgsGradientStop( scaledBreakpoint, fillColor ) );
888 }
889
890 auto colorRamp = std::make_unique< QgsGradientColorRamp >( minColor, maxColor, false, gradientStops );
891
892 QgsProperty colorProperty = QgsProperty::fromField( attrName );
893 colorProperty.setTransformer( new QgsColorRampTransformer( minValue, maxValue, colorRamp.release() ) );
894 for ( int layer = 0; layer < symbol->symbolLayerCount(); ++layer )
895 {
896 symbol->symbolLayer( layer )->setDataDefinedProperty( QgsSymbolLayer::Property::FillColor, colorProperty );
897 }
898
899 return std::make_unique< QgsSingleSymbolRenderer >( symbol.release() );
900 }
901 else
902 {
903 QgsDebugError( u"ESRI visualVariable type %1 is not currently supported"_s.arg( variableType ) );
904 }
905 }
906
907 double lastValue = rendererData.value( u"minValue"_s ).toDouble();
908
909 auto graduatedRenderer = std::make_unique< QgsGraduatedSymbolRenderer >( attrName );
910
911 graduatedRenderer->setSourceSymbol( symbol.release() );
912
913 if ( esriMode == "esriClassifyDefinedInterval"_L1 )
914 {
916 graduatedRenderer->setClassificationMethod( method );
917 }
918 else if ( esriMode == "esriClassifyEqualInterval"_L1 )
919 {
921 graduatedRenderer->setClassificationMethod( method );
922 }
923 else if ( esriMode == "esriClassifyGeometricalInterval"_L1 )
924 {
926 graduatedRenderer->setClassificationMethod( method );
927 }
928 else if ( esriMode == "esriClassifyManual"_L1 )
929 {
931 graduatedRenderer->setClassificationMethod( method );
932 }
933 else if ( esriMode == "esriClassifyNaturalBreaks"_L1 )
934 {
936 graduatedRenderer->setClassificationMethod( method );
937 }
938 else if ( esriMode == "esriClassifyQuantile"_L1 )
939 {
941 graduatedRenderer->setClassificationMethod( method );
942 }
943 else if ( esriMode == "esriClassifyStandardDeviation"_L1 )
944 {
946 graduatedRenderer->setClassificationMethod( method );
947 }
948 else if ( !esriMode.isEmpty() )
949 {
950 QgsDebugError( u"ESRI classification mode %1 is not currently supported"_s.arg( esriMode ) );
951 }
952
953 for ( const QVariant &classBreakInfo : classBreakInfos )
954 {
955 const QVariantMap symbolData = classBreakInfo.toMap().value( u"symbol"_s ).toMap();
956 std::unique_ptr< QgsSymbol > symbol( QgsArcGisRestUtils::convertSymbol( symbolData ) );
957 double classMaxValue = classBreakInfo.toMap().value( u"classMaxValue"_s ).toDouble();
958 const QString label = classBreakInfo.toMap().value( u"label"_s ).toString();
959
960 QgsRendererRange range;
961
962 range.setLowerValue( lastValue );
963 range.setUpperValue( classMaxValue );
964 range.setLabel( label );
965 range.setSymbol( symbol.release() );
966
967 lastValue = classMaxValue;
968 graduatedRenderer->addClass( range );
969 }
970
971 return graduatedRenderer;
972 }
973 else if ( type == "heatmap"_L1 )
974 {
975 // currently unsupported
976 return nullptr;
977 }
978 else if ( type == "vectorField"_L1 )
979 {
980 // currently unsupported
981 return nullptr;
982 }
983 return nullptr;
984}
985
986QString QgsArcGisRestUtils::convertLabelingExpression( const QString &string )
987{
988 QString expression = string;
989
990 // Replace a few ArcGIS token to QGIS equivalents
991 const thread_local QRegularExpression rx1 = QRegularExpression( u"(?=([^\"\\\\]*(\\\\.|\"([^\"\\\\]*\\\\.)*[^\"\\\\]*\"))*[^\"]*$)(\\s|^)CONCAT(\\s|$)"_s );
992 expression = expression.replace( rx1, u"\\4||\\5"_s );
993
994 const thread_local QRegularExpression rx2 = QRegularExpression( u"(?=([^\"\\\\]*(\\\\.|\"([^\"\\\\]*\\\\.)*[^\"\\\\]*\"))*[^\"]*$)(\\s|^)NEWLINE(\\s|$)"_s );
995 expression = expression.replace( rx2, u"\\4'\\n'\\5"_s );
996
997 // ArcGIS's double quotes are single quotes in QGIS
998 const thread_local QRegularExpression rx3 = QRegularExpression( u"\"(.*?(?<!\\\\))\""_s );
999 expression = expression.replace( rx3, u"'\\1'"_s );
1000 const thread_local QRegularExpression rx4 = QRegularExpression( u"\\\\\""_s );
1001 expression = expression.replace( rx4, u"\""_s );
1002
1003 // ArcGIS's square brakets are double quotes in QGIS
1004 const thread_local QRegularExpression rx5 = QRegularExpression( u"\\[([^]]*)\\]"_s );
1005 expression = expression.replace( rx5, u"\"\\1\""_s );
1006
1007 return expression;
1008}
1009
1010QColor QgsArcGisRestUtils::convertColor( const QVariant &colorData )
1011{
1012 return QgsSymbolConverterEsriRest::convertColor( colorData );
1013}
1014
1015Qt::PenStyle QgsArcGisRestUtils::convertLineStyle( const QString &style )
1016{
1018}
1019
1020Qt::BrushStyle QgsArcGisRestUtils::convertFillStyle( const QString &style )
1021{
1023}
1024
1025QDateTime QgsArcGisRestUtils::convertDateTime( const QVariant &value )
1026{
1027 if ( QgsVariantUtils::isNull( value ) )
1028 return QDateTime();
1029 bool ok = false;
1030 QDateTime dt = QDateTime::fromMSecsSinceEpoch( value.toLongLong( &ok ) );
1031 if ( !ok )
1032 {
1033 QgsDebugError( u"Invalid value %1 for datetime"_s.arg( value.toString() ) );
1034 return QDateTime();
1035 }
1036 else
1037 return dt;
1038}
1039
1041{
1042 if ( QgsVariantUtils::isNull( value ) )
1043 return QgsRectangle();
1044
1045 const QVariantMap coords = value.toMap();
1046 if ( coords.isEmpty() )
1047 return QgsRectangle();
1048
1049 bool ok;
1050
1051 const double xmin = coords.value( u"xmin"_s ).toDouble( &ok );
1052 if ( !ok )
1053 return QgsRectangle();
1054
1055 const double ymin = coords.value( u"ymin"_s ).toDouble( &ok );
1056 if ( !ok )
1057 return QgsRectangle();
1058
1059 const double xmax = coords.value( u"xmax"_s ).toDouble( &ok );
1060 if ( !ok )
1061 return QgsRectangle();
1062
1063 const double ymax = coords.value( u"ymax"_s ).toDouble( &ok );
1064 if ( !ok )
1065 return QgsRectangle();
1066
1067 return QgsRectangle( xmin, ymin, xmax, ymax );
1068}
1069
1070
1072{
1073 QVariantMap res;
1074 if ( geometry.isNull() )
1075 return QVariantMap();
1076
1077 const QgsAbstractGeometry *geom = geometry.constGet()->simplifiedTypeRef();
1078 switch ( QgsWkbTypes::flatType( geom->wkbType() ) )
1079 {
1082 return QVariantMap();
1083
1085 res = pointToJson( qgsgeometry_cast< const QgsPoint * >( geom ) );
1086 break;
1087
1089 res = lineStringToJson( qgsgeometry_cast< const QgsLineString * >( geom ) );
1090 break;
1091
1094 res = curveToJson( qgsgeometry_cast< const QgsCurve * >( geom ) );
1095 break;
1096
1098 res = polygonToJson( qgsgeometry_cast< const QgsPolygon * >( geom ) );
1099 break;
1100
1102 res = multiPointToJson( qgsgeometry_cast< const QgsMultiPoint * >( geom ) );
1103 break;
1104
1106 res = multiLineStringToJson( qgsgeometry_cast< const QgsMultiLineString * >( geom ) );
1107 break;
1108
1110 res = multiCurveToJson( qgsgeometry_cast< const QgsMultiCurve * >( geom ) );
1111 break;
1112
1114 res = multiPolygonToJson( qgsgeometry_cast< const QgsMultiPolygon * >( geom ) );
1115 break;
1116
1118 res = curvePolygonToJson( qgsgeometry_cast< const QgsCurvePolygon * >( geom ) );
1119 break;
1120
1122 res = multiSurfaceToJson( qgsgeometry_cast< const QgsMultiSurface * >( geom ) );
1123 break;
1124
1126 return QVariantMap(); // not supported by REST API
1127
1129 return QVariantMap(); //not yet supported, but could be
1130
1131 default:
1132 return QVariantMap(); //unreachable
1133 }
1134
1135 if ( crs.isValid() )
1136 {
1137 // add spatialReference information
1138 res.insert( u"spatialReference"_s, crsToJson( crs ) );
1139 }
1140
1141 return res;
1142}
1143
1144QVariantMap QgsArcGisRestUtils::pointToJson( const QgsPoint *point )
1145{
1146 QVariantMap data;
1147 if ( point->isEmpty() )
1148 data[u"x"_s] = u"NaN"_s;
1149 else
1150 {
1151 data[u"x"_s] = point->x();
1152 data[u"y"_s] = point->y();
1153
1154 if ( point->is3D() )
1155 data[u"z"_s] = !std::isnan( point->z() ) ? QVariant( point->z() ) : QVariant( u"NaN"_s );
1156
1157 if ( point->isMeasure() )
1158 data[u"m"_s] = !std::isnan( point->m() ) ? QVariant( point->m() ) : QVariant( u"NaN"_s );
1159 }
1160 return data;
1161}
1162
1163QVariantMap QgsArcGisRestUtils::multiPointToJson( const QgsMultiPoint *multiPoint )
1164{
1165 QVariantMap data;
1166 const bool hasZ = multiPoint->is3D();
1167 const bool hasM = multiPoint->isMeasure();
1168 data[u"hasM"_s] = hasM;
1169 data[u"hasZ"_s] = hasZ;
1170
1171 QVariantList pointsList;
1172 const int size = multiPoint->numGeometries();
1173 pointsList.reserve( size );
1174
1175 QVariantList pointList;
1176 for ( int i = 0; i < size; ++i )
1177 {
1178 const QgsPoint *point = multiPoint->pointN( i );
1179
1180 pointList.clear();
1181 pointList.append( point->x() );
1182 pointList.append( point->y() );
1183 if ( hasZ )
1184 pointList.append( point->z() );
1185 if ( hasM && !std::isnan( point->m() ) )
1186 pointList.append( point->m() );
1187
1188 pointsList.push_back( pointList );
1189 }
1190
1191 data[u"points"_s] = pointsList;
1192 return data;
1193}
1194
1195QVariantList QgsArcGisRestUtils::lineStringToJsonPath( const QgsLineString *line )
1196{
1197 const bool hasZ = line->is3D();
1198 const bool hasM = line->isMeasure();
1199
1200 QVariantList pointsList;
1201 const int size = line->numPoints();
1202 pointsList.reserve( size );
1203
1204 QVariantList pointList;
1205 const double *xData = line->xData();
1206 const double *yData = line->yData();
1207 const double *zData = hasZ ? line->zData() : nullptr;
1208 const double *mData = hasM ? line->mData() : nullptr;
1209
1210 for ( int i = 0; i < size; ++i )
1211 {
1212 pointList.clear();
1213 pointList.append( *xData++ );
1214 pointList.append( *yData++ );
1215
1216 if ( hasZ )
1217 pointList.append( *zData++ );
1218
1219 if ( hasM && !std::isnan( *mData ) )
1220 pointList.append( *mData );
1221 if ( hasM )
1222 mData++;
1223
1224 pointsList.push_back( pointList );
1225 }
1226 return pointsList;
1227}
1228
1229QVariantList QgsArcGisRestUtils::curveToJsonCurve( const QgsCurve *curve, bool includeStart )
1230{
1231 const bool hasZ = curve->is3D();
1232 const bool hasM = curve->isMeasure();
1233
1234 auto pointToList = [hasZ, hasM]( const QgsPoint &point ) -> QVariantList {
1235 QVariantList pointList;
1236
1237 pointList.append( point.x() );
1238 pointList.append( point.y() );
1239
1240 if ( hasZ )
1241 pointList.append( point.z() );
1242
1243 if ( hasM && !std::isnan( point.m() ) )
1244 pointList.append( point.m() );
1245
1246 return pointList;
1247 };
1248
1249 QVariantList res;
1250 switch ( QgsWkbTypes::flatType( curve->wkbType() ) )
1251 {
1253 {
1254 QVariantList part = lineStringToJsonPath( qgsgeometry_cast< const QgsLineString *>( curve ) );
1255 if ( !part.isEmpty() && !includeStart )
1256 part.removeAt( 0 );
1257 res = part;
1258 break;
1259 }
1260
1262 {
1263 const QgsCircularString *circularString = qgsgeometry_cast<const QgsCircularString * >( curve );
1264 if ( includeStart && !circularString->isEmpty() )
1265 {
1266 res.push_back( pointToList( circularString->startPoint() ) );
1267 }
1268
1269 const int size = circularString->numPoints();
1270 for ( int i = 1; i + 1 < size; i += 2 )
1271 {
1272 // end point comes BEFORE interior point!
1273 QVariantMap curvePart;
1274 QVariantList curveList;
1275 curveList.push_back( pointToList( circularString->pointN( i + 1 ) ) );
1276
1277 curveList.push_back( pointToList( circularString->pointN( i ) ) );
1278
1279 curvePart.insert( u"c"_s, curveList );
1280 res.push_back( curvePart );
1281 }
1282 break;
1283 }
1284
1286 {
1287 const QgsCompoundCurve *compoundCurve = qgsgeometry_cast<const QgsCompoundCurve * >( curve );
1288
1289 const int size = compoundCurve->nCurves();
1290 for ( int i = 0; i < size; ++i )
1291 {
1292 const QgsCurve *subCurve = compoundCurve->curveAt( i );
1293 res.append( curveToJsonCurve( subCurve, i == 0 ) );
1294 }
1295 break;
1296 }
1297
1298 default:
1299 break;
1300 }
1301 return res;
1302}
1303
1304QVariantMap QgsArcGisRestUtils::lineStringToJson( const QgsLineString *line )
1305{
1306 QVariantMap data;
1307 const bool hasZ = line->is3D();
1308 const bool hasM = line->isMeasure();
1309 data[u"hasM"_s] = hasM;
1310 data[u"hasZ"_s] = hasZ;
1311
1312 const QVariantList pointsList = lineStringToJsonPath( line );
1313
1314 QVariantList pointsData = QVariantList();
1315 pointsData.push_back( pointsList );
1316 data[u"paths"_s] = pointsData;
1317
1318 return data;
1319}
1320
1321QVariantMap QgsArcGisRestUtils::curveToJson( const QgsCurve *curve )
1322{
1323 QVariantMap data;
1324 const bool hasZ = curve->is3D();
1325 const bool hasM = curve->isMeasure();
1326 data[u"hasM"_s] = hasM;
1327 data[u"hasZ"_s] = hasZ;
1328
1329 const QVariantList curveList = curveToJsonCurve( curve, true );
1330
1331 QVariantList curveData = QVariantList();
1332 curveData.push_back( curveList );
1333 data[u"curvePaths"_s] = curveData;
1334
1335 return data;
1336}
1337
1338QVariantMap QgsArcGisRestUtils::multiLineStringToJson( const QgsMultiLineString *multiLine )
1339{
1340 QVariantMap data;
1341 const bool hasZ = multiLine->is3D();
1342 const bool hasM = multiLine->isMeasure();
1343 data[u"hasM"_s] = hasM;
1344 data[u"hasZ"_s] = hasZ;
1345
1346 const int size = multiLine->numGeometries();
1347 QVariantList paths;
1348 paths.reserve( size );
1349 for ( int i = 0; i < size; ++i )
1350 {
1351 const QgsLineString *line = multiLine->lineStringN( i );
1352 paths.push_back( lineStringToJsonPath( line ) );
1353 }
1354
1355 data[u"paths"_s] = paths;
1356 return data;
1357}
1358
1359QVariantMap QgsArcGisRestUtils::multiCurveToJson( const QgsMultiCurve *multiCurve )
1360{
1361 QVariantMap data;
1362 const bool hasZ = multiCurve->is3D();
1363 const bool hasM = multiCurve->isMeasure();
1364 data[u"hasM"_s] = hasM;
1365 data[u"hasZ"_s] = hasZ;
1366
1367 const int size = multiCurve->numGeometries();
1368 QVariantList paths;
1369 paths.reserve( size );
1370 for ( int i = 0; i < size; ++i )
1371 {
1372 const QgsCurve *curve = multiCurve->curveN( i );
1373 paths.push_back( curveToJsonCurve( curve, true ) );
1374 }
1375
1376 data[u"curvePaths"_s] = paths;
1377 return data;
1378}
1379
1380QVariantList QgsArcGisRestUtils::polygonToJsonRings( const QgsPolygon *polygon )
1381{
1382 QVariantList rings;
1383 const int numInteriorRings = polygon->numInteriorRings();
1384 rings.reserve( numInteriorRings + 1 );
1385
1386 if ( const QgsLineString *exterior = qgsgeometry_cast< const QgsLineString * >( polygon->exteriorRing() ) )
1387 {
1388 // exterior ring MUST be clockwise
1389 switch ( exterior->orientation() )
1390 {
1392 rings.push_back( lineStringToJsonPath( exterior ) );
1393 break;
1394
1396 {
1397 std::unique_ptr< QgsLineString > reversed( exterior->reversed() );
1398 rings.push_back( lineStringToJsonPath( reversed.get() ) );
1399 break;
1400 }
1402 break;
1403 }
1404 }
1405
1406 for ( int i = 0; i < numInteriorRings; ++i )
1407 {
1408 const QgsLineString *ring = qgsgeometry_cast< const QgsLineString * >( polygon->interiorRing( i ) );
1409 // holes MUST be counter-clockwise
1410 switch ( ring->orientation() )
1411 {
1413 rings.push_back( lineStringToJsonPath( ring ) );
1414 break;
1415
1417 {
1418 std::unique_ptr< QgsLineString > reversed( ring->reversed() );
1419 rings.push_back( lineStringToJsonPath( reversed.get() ) );
1420 break;
1421 }
1423 break;
1424 }
1425 }
1426 return rings;
1427}
1428
1429QVariantList QgsArcGisRestUtils::curvePolygonToJsonRings( const QgsCurvePolygon *polygon )
1430{
1431 QVariantList rings;
1432 const int numInteriorRings = polygon->numInteriorRings();
1433 rings.reserve( numInteriorRings + 1 );
1434
1435 if ( const QgsCurve *exterior = qgsgeometry_cast< const QgsCurve * >( polygon->exteriorRing() ) )
1436 {
1437 // exterior ring MUST be clockwise
1438 switch ( exterior->orientation() )
1439 {
1441 rings.push_back( curveToJsonCurve( exterior, true ) );
1442 break;
1443
1445 {
1446 std::unique_ptr< QgsCurve > reversed( exterior->reversed() );
1447 rings.push_back( curveToJsonCurve( reversed.get(), true ) );
1448 break;
1449 }
1451 break;
1452 }
1453 }
1454
1455 for ( int i = 0; i < numInteriorRings; ++i )
1456 {
1457 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( polygon->interiorRing( i ) );
1458 // holes MUST be counter-clockwise
1459 switch ( ring->orientation() )
1460 {
1462 rings.push_back( curveToJsonCurve( ring, true ) );
1463 break;
1464
1466 {
1467 std::unique_ptr< QgsCurve > reversed( ring->reversed() );
1468 rings.push_back( curveToJsonCurve( reversed.get(), true ) );
1469 break;
1470 }
1472 break;
1473 }
1474 }
1475 return rings;
1476}
1477
1478QVariantMap QgsArcGisRestUtils::polygonToJson( const QgsPolygon *polygon )
1479{
1480 QVariantMap data;
1481 const bool hasZ = polygon->is3D();
1482 const bool hasM = polygon->isMeasure();
1483 data[u"hasM"_s] = hasM;
1484 data[u"hasZ"_s] = hasZ;
1485 data[u"rings"_s] = polygonToJsonRings( polygon );
1486 return data;
1487}
1488
1489QVariantMap QgsArcGisRestUtils::curvePolygonToJson( const QgsCurvePolygon *polygon )
1490{
1491 QVariantMap data;
1492 const bool hasZ = polygon->is3D();
1493 const bool hasM = polygon->isMeasure();
1494 data[u"hasM"_s] = hasM;
1495 data[u"hasZ"_s] = hasZ;
1496 data[u"curveRings"_s] = curvePolygonToJsonRings( polygon );
1497 return data;
1498}
1499
1500QVariantMap QgsArcGisRestUtils::multiPolygonToJson( const QgsMultiPolygon *multiPolygon )
1501{
1502 QVariantMap data;
1503 const bool hasZ = multiPolygon->is3D();
1504 const bool hasM = multiPolygon->isMeasure();
1505 data[u"hasM"_s] = hasM;
1506 data[u"hasZ"_s] = hasZ;
1507
1508 const int size = multiPolygon->numGeometries();
1509 QVariantList rings;
1510 for ( int i = 0; i < size; ++i )
1511 {
1512 const QgsPolygon *polygon = multiPolygon->polygonN( i );
1513 rings.append( polygonToJsonRings( polygon ) );
1514 }
1515
1516 data[u"rings"_s] = rings;
1517 return data;
1518}
1519
1520QVariantMap QgsArcGisRestUtils::multiSurfaceToJson( const QgsMultiSurface *multiSurface )
1521{
1522 QVariantMap data;
1523 const bool hasZ = multiSurface->is3D();
1524 const bool hasM = multiSurface->isMeasure();
1525 data[u"hasM"_s] = hasM;
1526 data[u"hasZ"_s] = hasZ;
1527
1528 const int size = multiSurface->numGeometries();
1529 QVariantList rings;
1530 for ( int i = 0; i < size; ++i )
1531 {
1532 const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( multiSurface->geometryN( i ) );
1533 if ( !polygon )
1534 continue;
1535
1536 rings.append( curvePolygonToJsonRings( polygon ) );
1537 }
1538
1539 data[u"curveRings"_s] = rings;
1540 return data;
1541}
1542
1544{
1545 QVariantMap res;
1546 if ( !crs.isValid() )
1547 return res;
1548
1549 const QString authid = crs.authid();
1550 if ( !authid.isEmpty() )
1551 {
1552 const thread_local QRegularExpression rxAuthid( u"(\\w+):(\\d+)"_s );
1553 const QRegularExpressionMatch match = rxAuthid.match( authid );
1554 if ( match.hasMatch() && ( ( match.captured( 1 ).compare( "EPSG"_L1, Qt::CaseInsensitive ) == 0 ) || ( match.captured( 1 ).compare( "ESRI"_L1, Qt::CaseInsensitive ) == 0 ) ) )
1555 {
1556 const QString wkid = match.captured( 2 );
1557 res.insert( u"wkid"_s, wkid );
1558 return res;
1559 }
1560 }
1561
1562 // docs don't mention the WKT version support, so let's hope for 2.0...
1563 res.insert( u"wkt"_s, crs.toWkt( Qgis::CrsWktVariant::Wkt2_2019Simplified ) );
1564
1565 return res;
1566}
1567
1569{
1570 QVariantMap res;
1571 if ( ( flags & FeatureToJsonFlag::IncludeGeometry ) && feature.hasGeometry() )
1572 {
1573 res.insert( u"geometry"_s, geometryToJson( feature.geometry(), context, crs ) );
1574 }
1575
1576 QVariantMap attributes;
1577 const QgsFields fields = feature.fields();
1578 for ( const QgsField &field : fields )
1579 {
1580 QVariant value = feature.attribute( field.name() );
1581 if ( value.userType() == qMetaTypeId< QgsUnsetAttributeValue >() )
1582 {
1583 if ( flags.testFlag( FeatureToJsonFlag::SkipUnsetAttributes ) )
1584 continue;
1585 else
1586 value = QVariant(); // reset to null, we can't store 'QgsUnsetAttributeValue' as json
1587 }
1588
1589 if ( ( flags & FeatureToJsonFlag::IncludeNonObjectIdAttributes ) || field.name() == context.objectIdFieldName() )
1590 attributes.insert( field.name(), variantToAttributeValue( value, field.type(), context ) );
1591 }
1592 if ( !attributes.isEmpty() )
1593 {
1594 res.insert( u"attributes"_s, attributes );
1595 }
1596 return res;
1597}
1598
1599QVariant QgsArcGisRestUtils::variantToAttributeValue( const QVariant &variant, QMetaType::Type expectedType, const QgsArcGisRestContext &context )
1600{
1601 if ( QgsVariantUtils::isNull( variant ) )
1602 return QVariant();
1603
1604 switch ( expectedType )
1605 {
1606 case QMetaType::Type::QString:
1607 {
1608 const QString escaped = variant.toString().replace( '\\', "\\\\"_L1 ).replace( '"', "\\\""_L1 );
1609 return QString( QUrl::toPercentEncoding( escaped, "'" ) );
1610 }
1611
1612 case QMetaType::Type::QDateTime:
1613 case QMetaType::Type::QDate:
1614 {
1615 switch ( variant.userType() )
1616 {
1617 case QMetaType::Type::QDateTime:
1618 return variant.toDateTime().toMSecsSinceEpoch();
1619
1620 case QMetaType::Type::QDate:
1621 // for date values, assume start of day -- the REST api requires datetime values only, not plain dates
1622 if ( context.timeZone().isValid() )
1623 return QDateTime( variant.toDate(), QTime( 0, 0, 0 ), context.timeZone() ).toMSecsSinceEpoch();
1624 else
1625 return QDateTime( variant.toDate(), QTime( 0, 0, 0 ) ).toMSecsSinceEpoch();
1626
1627 default:
1628 return QVariant();
1629 }
1630 }
1631
1632 default:
1633 return variant;
1634 }
1635}
1636
1638{
1639 QVariantMap res;
1640 res.insert( u"name"_s, field.name() );
1641
1642 QString fieldType;
1643 switch ( field.type() )
1644 {
1645 case QMetaType::Type::LongLong:
1646 fieldType = u"esriFieldTypeInteger"_s;
1647 break;
1648
1649 case QMetaType::Type::Int:
1650 fieldType = u"esriFieldTypeSmallInteger"_s;
1651 break;
1652
1653 case QMetaType::Type::Double:
1654 fieldType = u"esriFieldTypeDouble"_s;
1655 break;
1656
1657 case QMetaType::Type::QString:
1658 fieldType = u"esriFieldTypeString"_s;
1659 break;
1660
1661 case QMetaType::Type::QDateTime:
1662 case QMetaType::Type::QDate:
1663 fieldType = u"esriFieldTypeDate"_s;
1664 break;
1665
1666 case QMetaType::Type::QByteArray:
1667 fieldType = u"esriFieldTypeBlob"_s;
1668 break;
1669
1670 default:
1671 // fallback to string
1672 fieldType = u"esriFieldTypeString"_s;
1673 break;
1674 }
1675 res.insert( u"type"_s, fieldType );
1676
1677 if ( !field.alias().isEmpty() )
1678 res.insert( u"alias"_s, field.alias() );
1679
1680 // nullable
1682 res.insert( u"nullable"_s, !notNullable );
1683
1684 // editable
1685 res.insert( u"editable"_s, true );
1686
1687 return res;
1688}
1689
1691{
1692 if ( type.compare( "FeatureServer"_L1, Qt::CaseInsensitive ) == 0 )
1694 else if ( type.compare( "MapServer"_L1, Qt::CaseInsensitive ) == 0 )
1696 else if ( type.compare( "ImageServer"_L1, Qt::CaseInsensitive ) == 0 )
1698 else if ( type.compare( "GlobeServer"_L1, Qt::CaseInsensitive ) == 0 )
1700 else if ( type.compare( "GPServer"_L1, Qt::CaseInsensitive ) == 0 )
1702 else if ( type.compare( "GeocodeServer"_L1, Qt::CaseInsensitive ) == 0 )
1704 else if ( type.compare( "SceneServer"_L1, Qt::CaseInsensitive ) == 0 )
1706
1708}
1709
1711{
1712 const QStringList parts = capabilities.split( ',' );
1713
1715 res.setFlag( Qgis::ArcGisRestServiceCapability::Query, parts.contains( "query"_L1, Qt::CaseInsensitive ) );
1716 res.setFlag( Qgis::ArcGisRestServiceCapability::Map, parts.contains( "map"_L1, Qt::CaseInsensitive ) );
1717 res.setFlag( Qgis::ArcGisRestServiceCapability::Update, parts.contains( "update"_L1, Qt::CaseInsensitive ) );
1718 res.setFlag( Qgis::ArcGisRestServiceCapability::Delete, parts.contains( "delete"_L1, Qt::CaseInsensitive ) );
1719 res.setFlag( Qgis::ArcGisRestServiceCapability::Create, parts.contains( "create"_L1, Qt::CaseInsensitive ) );
1720 res.setFlag( Qgis::ArcGisRestServiceCapability::Image, parts.contains( "image"_L1, Qt::CaseInsensitive ) );
1721 res.setFlag( Qgis::ArcGisRestServiceCapability::TilesOnly, parts.contains( "tilesonly"_L1, Qt::CaseInsensitive ) );
1722
1723 return res;
1724}
1725
1727{
1728 if ( pixelType.compare( "U8"_L1, Qt::CaseInsensitive ) == 0
1729 || pixelType.compare( "U4"_L1, Qt::CaseInsensitive ) == 0
1730 || pixelType.compare( "U2"_L1, Qt::CaseInsensitive ) == 0
1731 || pixelType.compare( "U1"_L1, Qt::CaseInsensitive ) == 0 )
1732 {
1733 return Qgis::DataType::Byte;
1734 }
1735 else if ( pixelType.compare( "S8"_L1, Qt::CaseInsensitive ) == 0 )
1736 {
1737 return Qgis::DataType::Int8;
1738 }
1739 else if ( pixelType.compare( "U16"_L1, Qt::CaseInsensitive ) == 0 )
1740 {
1742 }
1743 else if ( pixelType.compare( "S16"_L1, Qt::CaseInsensitive ) == 0 )
1744 {
1745 return Qgis::DataType::Int16;
1746 }
1747 else if ( pixelType.compare( "U32"_L1, Qt::CaseInsensitive ) == 0 )
1748 {
1750 }
1751 else if ( pixelType.compare( "S32"_L1, Qt::CaseInsensitive ) == 0 )
1752 {
1753 return Qgis::DataType::Int32;
1754 }
1755 else if ( pixelType.compare( "F32"_L1, Qt::CaseInsensitive ) == 0 )
1756 {
1758 }
1759 else if ( pixelType.compare( "F64"_L1, Qt::CaseInsensitive ) == 0 )
1760 {
1762 }
1763 else if ( pixelType.compare( "C64"_L1, Qt::CaseInsensitive ) == 0 )
1764 {
1765 // C64 = 32-bit real + 32-bit imaginary
1767 }
1768 else if ( pixelType.compare( "C128"_L1, Qt::CaseInsensitive ) == 0 )
1769 {
1770 // C128 = 64-bit real + 64-bit imaginary
1772 }
1773 else
1774 {
1775 QgsDebugError( u"Unknown pixelType: %1"_s.arg( pixelType ) );
1776 }
1777
1779}
1780
1782{
1783 if ( bandName.isEmpty() )
1784 {
1786 }
1787
1788 if ( bandName.compare( "Red"_L1, Qt::CaseInsensitive ) == 0 )
1790 else if ( bandName.compare( "Green"_L1, Qt::CaseInsensitive ) == 0 )
1792 else if ( bandName.compare( "Blue"_L1, Qt::CaseInsensitive ) == 0 )
1794 else if ( bandName.compare( "Alpha"_L1, Qt::CaseInsensitive ) == 0 )
1796 else if ( bandName.compare( "NIR"_L1, Qt::CaseInsensitive ) == 0
1797 || bandName.compare( "NearInfrared"_L1, Qt::CaseInsensitive ) == 0
1798 || bandName.compare( "NearIR"_L1, Qt::CaseInsensitive ) == 0
1799 || bandName.compare( "NarrowNIR"_L1, Qt::CaseInsensitive ) == 0 )
1801 else if ( bandName.startsWith( "SWIR"_L1, Qt::CaseInsensitive ) )
1803 else if ( bandName.startsWith( "VRE"_L1, Qt::CaseInsensitive ) || bandName.compare( "RedEdge"_L1, Qt::CaseInsensitive ) == 0 )
1805 else if ( bandName.startsWith( "Coastal"_L1, Qt::CaseInsensitive ) )
1807 else if ( bandName.startsWith( "Pan"_L1, Qt::CaseInsensitive ) )
1809 else if ( bandName.startsWith( "Thermal"_L1, Qt::CaseInsensitive ) || bandName.startsWith( "TIR"_L1, Qt::CaseInsensitive ) )
1811 else if ( bandName.compare( "Gray"_L1, Qt::CaseInsensitive ) == 0 || bandName.compare( "Grey"_L1, Qt::CaseInsensitive ) == 0 )
1813 else if ( bandName.compare( "Cyan"_L1, Qt::CaseInsensitive ) == 0 )
1815 else if ( bandName.compare( "Magenta"_L1, Qt::CaseInsensitive ) == 0 )
1817 else if ( bandName.compare( "Yellow"_L1, Qt::CaseInsensitive ) == 0 )
1819 else if ( bandName.compare( "Black"_L1, Qt::CaseInsensitive ) == 0 )
1821 else if ( bandName.compare( "Hue"_L1, Qt::CaseInsensitive ) == 0 )
1823 else if ( bandName.compare( "Saturation"_L1, Qt::CaseInsensitive ) == 0 )
1825 else if ( bandName.compare( "Lightness"_L1, Qt::CaseInsensitive ) == 0 )
1827
1828 // we don't log failures here -- a lot of bands will have non-interpretable names,
1829 // eg "Band 1"
1831}
1832
1834{
1835 ok = false;
1836 switch ( type )
1837 {
1839 return 0;
1840
1842 ok = true;
1843 return std::numeric_limits<quint8>::max();
1844
1846 ok = true;
1847 return std::numeric_limits<qint8>::lowest();
1848
1850 ok = true;
1851 return std::numeric_limits<quint16>::max();
1852
1854 ok = true;
1855 return std::numeric_limits<qint16>::lowest();
1856
1858 ok = true;
1859 return std::numeric_limits<quint32>::max();
1860
1862 ok = true;
1863 return std::numeric_limits<qint32>::lowest();
1864
1866 ok = true;
1867 return std::numeric_limits<float>::quiet_NaN();
1868
1870 ok = true;
1871 return std::numeric_limits<double>::quiet_NaN();
1872
1879 return 0;
1880 }
1882}
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1384
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
Definition qgis.h:1385
@ OnLine
Labels can be placed directly over a line feature.
Definition qgis.h:1382
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1383
@ NoOrientation
Unknown orientation or sentinel value.
Definition qgis.h:3616
@ CounterClockwise
Counter-clockwise direction.
Definition qgis.h:3615
@ Clockwise
Clockwise direction.
Definition qgis.h:3614
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
Definition qgis.h:1275
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
Definition qgis.h:1276
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
Definition qgis.h:1278
ArcGisRestServiceType
Available ArcGIS REST service types.
Definition qgis.h:4643
@ GeocodeServer
GeocodeServer.
Definition qgis.h:4649
@ SceneServer
SceneServer.
Definition qgis.h:4651
@ Unknown
Other unknown/unsupported type.
Definition qgis.h:4650
@ GlobeServer
GlobeServer.
Definition qgis.h:4647
@ ImageServer
ImageServer.
Definition qgis.h:4646
@ FeatureServer
FeatureServer.
Definition qgis.h:4644
@ AboveRight
Above right.
Definition qgis.h:1364
@ BelowLeft
Below left.
Definition qgis.h:1368
@ Above
Above center.
Definition qgis.h:1363
@ BelowRight
Below right.
Definition qgis.h:1370
@ Right
Right middle.
Definition qgis.h:1367
@ AboveLeft
Above left.
Definition qgis.h:1362
@ Below
Below center.
Definition qgis.h:1369
@ Over
Center middle.
Definition qgis.h:1366
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:5501
QFlags< ArcGisRestServiceCapability > ArcGisRestServiceCapabilities
Available ArcGIS REST service capabilities.
Definition qgis.h:4680
@ Update
Update features.
Definition qgis.h:4667
@ Create
Create features.
Definition qgis.h:4669
@ TilesOnly
Service supports tiled image requests only.
Definition qgis.h:4671
@ Image
Image capabilities.
Definition qgis.h:4670
@ Delete
Delete features.
Definition qgis.h:4668
RasterColorInterpretation
Raster color interpretation.
Definition qgis.h:5025
@ NIRBand
Near-InfraRed (NIR) band [0.75 - 1.40 um].
Definition qgis.h:5051
@ GreenBand
Green band of RGBA image, or green spectral band [0.51 - 0.60 um].
Definition qgis.h:5030
@ SaturationBand
Saturation band of HLS image.
Definition qgis.h:5034
@ MagentaBand
Magenta band of CMYK image.
Definition qgis.h:5037
@ BlackBand
Black band of CMLY image.
Definition qgis.h:5039
@ AlphaBand
Alpha (0=transparent, 255=opaque).
Definition qgis.h:5032
@ SWIRBand
Short-Wavelength InfraRed (SWIR) band [1.40 - 3.00 um].
Definition qgis.h:5052
@ BlueBand
Blue band of RGBA image, or blue spectral band [0.45 - 0.53 um].
Definition qgis.h:5031
@ RedEdgeBand
Red-edge band [0.69 - 0.79 um].
Definition qgis.h:5050
@ YellowBand
Yellow band of CMYK image, or yellow spectral band [0.58 - 0.62 um].
Definition qgis.h:5038
@ CyanBand
Cyan band of CMYK image.
Definition qgis.h:5036
@ LightnessBand
Lightness band of HLS image.
Definition qgis.h:5035
@ CoastalBand
Coastal band [0.40 - 0.45 um].
Definition qgis.h:5049
@ HueBand
Hue band of HLS image.
Definition qgis.h:5033
@ TIRBand
Thermal InfraRed (TIR) band (MWIR or LWIR) [3 - 15 um].
Definition qgis.h:5055
@ RedBand
Red band of RGBA image, or red spectral band [0.62 - 0.69 um].
Definition qgis.h:5029
@ PanBand
Panchromatic band [0.40 - 1.00 um].
Definition qgis.h:5048
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:2579
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 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::unique_ptr< QgsSymbol > convertSymbol(const QVariantMap &definition)
Converts a symbol JSON definition to a QgsSymbol.
static std::unique_ptr< QgsFeatureRenderer > convertRenderer(const QVariantMap &rendererData)
Converts renderer JSON data to an equivalent QgsFeatureRenderer.
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 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 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.
QgsPoint startPoint() const override
Returns the starting point of the curve.
bool isEmpty() const override
Returns true if the geometry is empty.
int numPoints() const override
Returns the number of points in the curve.
QgsPoint pointN(int i) const
Returns the point at index i within the circular string.
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:286
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").
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:70
QgsGeometry geometry
Definition qgsfeature.h:71
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:46
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.
const double * yData() const
Returns a const pointer to the y vertex data.
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 linestring does not have z values.
int numPoints() const override
Returns the number of points in the curve.
QgsLineString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the linestring does not have m values.
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:391
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:766
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 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
Represents the context in which a QgsSymbolConverter conversion occurs.
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.
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:7714
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7134
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QList< QgsRendererCategory > QgsCategoryList
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
#define QgsDebugError(str)
Definition qgslogger.h:59