QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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#include "qgsfields.h"
18#include "qgslogger.h"
19#include "qgsrectangle.h"
20#include "qgspallabeling.h"
21#include "qgssymbol.h"
22#include "qgssymbollayer.h"
23#include "qgslinesymbollayer.h"
24#include "qgsfillsymbollayer.h"
25#include "qgsrenderer.h"
30#include "qgscircularstring.h"
31#include "qgsmulticurve.h"
32#include "qgspolygon.h"
33#include "qgslinestring.h"
34#include "qgscurve.h"
35#include "qgsgeometryengine.h"
36#include "qgsmultisurface.h"
37#include "qgsmultilinestring.h"
38#include "qgsmultipolygon.h"
39#include "qgsmultipoint.h"
40#include "qgsmarkersymbol.h"
41#include "qgslinesymbol.h"
42#include "qgsfillsymbol.h"
43#include "qgsvariantutils.h"
44
45#include <QRegularExpression>
46
47QVariant::Type QgsArcGisRestUtils::convertFieldType( const QString &esriFieldType )
48{
49 if ( esriFieldType == QLatin1String( "esriFieldTypeInteger" ) )
50 return QVariant::LongLong;
51 if ( esriFieldType == QLatin1String( "esriFieldTypeSmallInteger" ) )
52 return QVariant::Int;
53 if ( esriFieldType == QLatin1String( "esriFieldTypeDouble" ) )
54 return QVariant::Double;
55 if ( esriFieldType == QLatin1String( "esriFieldTypeSingle" ) )
56 return QVariant::Double;
57 if ( esriFieldType == QLatin1String( "esriFieldTypeString" ) )
58 return QVariant::String;
59 if ( esriFieldType == QLatin1String( "esriFieldTypeDate" ) )
60 return QVariant::DateTime;
61 if ( esriFieldType == QLatin1String( "esriFieldTypeGeometry" ) )
62 return QVariant::Invalid; // Geometry column should not appear as field
63 if ( esriFieldType == QLatin1String( "esriFieldTypeOID" ) )
64 return QVariant::LongLong;
65 if ( esriFieldType == QLatin1String( "esriFieldTypeBlob" ) )
66 return QVariant::ByteArray;
67 if ( esriFieldType == QLatin1String( "esriFieldTypeGlobalID" ) )
68 return QVariant::String;
69 if ( esriFieldType == QLatin1String( "esriFieldTypeRaster" ) )
70 return QVariant::ByteArray;
71 if ( esriFieldType == QLatin1String( "esriFieldTypeGUID" ) )
72 return QVariant::String;
73 if ( esriFieldType == QLatin1String( "esriFieldTypeXML" ) )
74 return QVariant::String;
75 return QVariant::Invalid;
76}
77
79{
80 // http://resources.arcgis.com/en/help/arcobjects-cpp/componenthelp/index.html#//000w0000001p000000
81 if ( esriGeometryType == QLatin1String( "esriGeometryNull" ) )
83 else if ( esriGeometryType == QLatin1String( "esriGeometryPoint" ) )
84 return QgsWkbTypes::Point;
85 else if ( esriGeometryType == QLatin1String( "esriGeometryMultipoint" ) )
87 else if ( esriGeometryType == QLatin1String( "esriGeometryPolyline" ) )
89 else if ( esriGeometryType == QLatin1String( "esriGeometryPolygon" ) )
91 else if ( esriGeometryType == QLatin1String( "esriGeometryEnvelope" ) )
93 // Unsupported (either by qgis, or format unspecified by the specification)
94 // esriGeometryCircularArc
95 // esriGeometryEllipticArc
96 // esriGeometryBezier3Curve
97 // esriGeometryPath
98 // esriGeometryRing
99 // esriGeometryLine
100 // esriGeometryAny
101 // esriGeometryMultiPatch
102 // esriGeometryTriangleStrip
103 // esriGeometryTriangleFan
104 // esriGeometryRay
105 // esriGeometrySphere
106 // esriGeometryTriangles
107 // esriGeometryBag
109}
110
111std::unique_ptr< QgsPoint > QgsArcGisRestUtils::convertPoint( const QVariantList &coordList, QgsWkbTypes::Type pointType )
112{
113 int nCoords = coordList.size();
114 if ( nCoords < 2 )
115 return nullptr;
116 bool xok = false, yok = false;
117 const double x = coordList[0].toDouble( &xok );
118 const double y = coordList[1].toDouble( &yok );
119 if ( !xok || !yok )
120 return nullptr;
121 const bool hasZ = QgsWkbTypes::hasZ( pointType );
122 const double z = hasZ && nCoords >= 3 ? coordList[2].toDouble() : std::numeric_limits< double >::quiet_NaN();
123
124 // 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
125 const double m = QgsWkbTypes::hasM( pointType ) && ( ( hasZ && nCoords >= 4 ) || ( !hasZ && nCoords >= 3 ) ) ? coordList[ hasZ ? 3 : 2].toDouble() : std::numeric_limits< double >::quiet_NaN();
126 return std::make_unique< QgsPoint >( pointType, x, y, z, m );
127}
128
129std::unique_ptr< QgsCircularString > QgsArcGisRestUtils::convertCircularString( const QVariantMap &curveData, QgsWkbTypes::Type pointType, const QgsPoint &startPoint )
130{
131 const QVariantList coordsList = curveData[QStringLiteral( "c" )].toList();
132 if ( coordsList.isEmpty() )
133 return nullptr;
134 const int coordsListSize = coordsList.size();
135
136 QVector<QgsPoint> points;
137 points.reserve( coordsListSize + 1 );
138 points.append( startPoint );
139
140 for ( int i = 0; i < coordsListSize - 1; )
141 {
142 // first point is end point, second is point on curve
143 // i.e. the opposite to what QGIS requires!
144 std::unique_ptr< QgsPoint > endPoint( convertPoint( coordsList.at( i ).toList(), pointType ) );
145 if ( !endPoint )
146 return nullptr;
147 i++;
148 std::unique_ptr< QgsPoint > interiorPoint( convertPoint( coordsList.at( i ).toList(), pointType ) );
149 if ( !interiorPoint )
150 return nullptr;
151 i++;
152 points << *interiorPoint;
153 points << *endPoint;
154 }
155 std::unique_ptr< QgsCircularString > curve = std::make_unique< QgsCircularString> ();
156 curve->setPoints( points );
157 return curve;
158}
159
160std::unique_ptr< QgsCompoundCurve > QgsArcGisRestUtils::convertCompoundCurve( const QVariantList &curvesList, QgsWkbTypes::Type pointType )
161{
162 // [[6,3],[5,3],{"b":[[3,2],[6,1],[2,4]]},[1,2],{"c": [[3,3],[1,4]]}]
163 std::unique_ptr< QgsCompoundCurve > compoundCurve = std::make_unique< QgsCompoundCurve >();
164
165 QVector< double > lineX;
166 QVector< double > lineY;
167 QVector< double > lineZ;
168 QVector< double > lineM;
169 int maxCurveListSize = curvesList.size();
170 lineX.resize( maxCurveListSize );
171 lineY.resize( maxCurveListSize );
172
173 const bool hasZ = QgsWkbTypes::hasZ( pointType );
174 if ( hasZ )
175 lineZ.resize( maxCurveListSize );
176 const bool hasM = QgsWkbTypes::hasM( pointType );
177 if ( hasM )
178 lineM.resize( maxCurveListSize );
179
180 double *outLineX = lineX.data();
181 double *outLineY = lineY.data();
182 double *outLineZ = lineZ.data();
183 double *outLineM = lineM.data();
184 int actualLineSize = 0;
185
186 bool xok = false;
187 bool yok = false;
188
189 int curveListIndex = 0;
190 for ( const QVariant &curveData : curvesList )
191 {
192 if ( curveData.type() == QVariant::List )
193 {
194 const QVariantList coordList = curveData.toList();
195 const int nCoords = coordList.size();
196 if ( nCoords < 2 )
197 return nullptr;
198
199 const double x = coordList[0].toDouble( &xok );
200 const double y = coordList[1].toDouble( &yok );
201 if ( !xok || !yok )
202 return nullptr;
203
204 actualLineSize++;
205 *outLineX++ = x;
206 *outLineY++ = y;
207 if ( hasZ )
208 {
209 *outLineZ++ = nCoords >= 3 ? coordList[2].toDouble() : std::numeric_limits< double >::quiet_NaN();
210 }
211
212 if ( hasM )
213 {
214 // 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
215 *outLineM++ = ( ( hasZ && nCoords >= 4 ) || ( !hasZ && nCoords >= 3 ) ) ? coordList[ hasZ ? 3 : 2].toDouble() : std::numeric_limits< double >::quiet_NaN();
216 }
217 }
218 else if ( curveData.type() == QVariant::Map )
219 {
220 // The last point of the linestring is the start point of this circular string
221 QgsPoint lastLineStringPoint;
222 if ( actualLineSize > 0 )
223 {
224 lastLineStringPoint = QgsPoint( lineX.at( actualLineSize - 1 ),
225 lineY.at( actualLineSize - 1 ),
226 hasZ ? lineZ.at( actualLineSize - 1 ) : std::numeric_limits< double >::quiet_NaN(),
227 hasM ? lineM.at( actualLineSize - 1 ) : std::numeric_limits< double >::quiet_NaN() );
228 }
229 std::unique_ptr< QgsCircularString > circularString( convertCircularString( curveData.toMap(), pointType, lastLineStringPoint ) );
230 if ( !circularString )
231 {
232 return nullptr;
233 }
234
235 if ( actualLineSize > 0 )
236 {
237 lineX.resize( actualLineSize );
238 lineY.resize( actualLineSize );
239 if ( hasZ )
240 lineZ.resize( actualLineSize );
241 if ( hasM )
242 lineM.resize( actualLineSize );
243
244 compoundCurve->addCurve( new QgsLineString( lineX, lineY, lineZ, lineM ) );
245 lineX.resize( maxCurveListSize - curveListIndex );
246 lineY.resize( maxCurveListSize - curveListIndex );
247 if ( hasZ )
248 lineZ.resize( maxCurveListSize - curveListIndex );
249 if ( hasM )
250 lineM.resize( maxCurveListSize - curveListIndex );
251 outLineX = lineX.data();
252 outLineY = lineY.data();
253 outLineZ = lineZ.data();
254 outLineM = lineM.data();
255 }
256
257 // If the previous curve had less than two points, remove it
258 if ( compoundCurve->curveAt( compoundCurve->nCurves() - 1 )->nCoordinates() < 2 )
259 compoundCurve->removeCurve( compoundCurve->nCurves() - 1 );
260
261 const QgsPoint endPointCircularString = circularString->endPoint();
262 compoundCurve->addCurve( circularString.release() );
263
264 // Prepare a new line string
265 actualLineSize = 1;
266 *outLineX++ = endPointCircularString.x();
267 *outLineY++ = endPointCircularString.y();
268 if ( hasZ )
269 *outLineZ++ = endPointCircularString.z();
270 if ( hasM )
271 *outLineM++ = endPointCircularString.m();
272 }
273 curveListIndex++;
274 }
275
276 if ( actualLineSize == 1 && compoundCurve->nCurves() > 0 )
277 {
278 const QgsCurve *finalCurve = compoundCurve->curveAt( compoundCurve->nCurves() - 1 );
279 const QgsPoint finalCurveEndPoint = finalCurve->endPoint();
280 if ( qgsDoubleNear( finalCurveEndPoint.x(), lineX.at( 0 ) )
281 && qgsDoubleNear( finalCurveEndPoint.y(), lineY.at( 0 ) )
282 && ( !hasZ || qgsDoubleNear( finalCurveEndPoint.z(), lineZ.at( 0 ) ) )
283 && ( !hasM || qgsDoubleNear( finalCurveEndPoint.m(), lineM.at( 0 ) ) ) )
284 {
285 actualLineSize = 0; // redundant final curve containing a duplicate vertex
286 }
287 }
288
289 if ( actualLineSize > 0 )
290 {
291 lineX.resize( actualLineSize );
292 lineY.resize( actualLineSize );
293 if ( hasZ )
294 lineZ.resize( actualLineSize );
295 if ( hasM )
296 lineM.resize( actualLineSize );
297 compoundCurve->addCurve( new QgsLineString( lineX, lineY, lineZ, lineM ) );
298 }
299
300 return compoundCurve;
301}
302
303std::unique_ptr< QgsPoint > QgsArcGisRestUtils::convertGeometryPoint( const QVariantMap &geometryData, QgsWkbTypes::Type pointType )
304{
305 // {"x" : <x>, "y" : <y>, "z" : <z>, "m" : <m>}
306 bool xok = false, yok = false;
307 double x = geometryData[QStringLiteral( "x" )].toDouble( &xok );
308 double y = geometryData[QStringLiteral( "y" )].toDouble( &yok );
309 if ( !xok || !yok )
310 return nullptr;
311 double z = geometryData[QStringLiteral( "z" )].toDouble();
312 double m = geometryData[QStringLiteral( "m" )].toDouble();
313 return std::make_unique< QgsPoint >( pointType, x, y, z, m );
314}
315
316std::unique_ptr< QgsMultiPoint > QgsArcGisRestUtils::convertMultiPoint( const QVariantMap &geometryData, QgsWkbTypes::Type pointType )
317{
318 // {"points" : [[ <x1>, <y1>, <z1>, <m1> ] , [ <x2>, <y2>, <z2>, <m2> ], ... ]}
319 const QVariantList coordsList = geometryData[QStringLiteral( "points" )].toList();
320
321 std::unique_ptr< QgsMultiPoint > multiPoint = std::make_unique< QgsMultiPoint >();
322 multiPoint->reserve( coordsList.size() );
323 for ( const QVariant &coordData : coordsList )
324 {
325 const QVariantList coordList = coordData.toList();
326 std::unique_ptr< QgsPoint > p = convertPoint( coordList, pointType );
327 if ( !p )
328 {
329 continue;
330 }
331 multiPoint->addGeometry( p.release() );
332 }
333
334 // second chance -- sometimes layers are reported as multipoint but features have single
335 // point geometries. Silently handle this and upgrade to multipoint.
336 std::unique_ptr< QgsPoint > p = convertGeometryPoint( geometryData, pointType );
337 if ( p )
338 multiPoint->addGeometry( p.release() );
339
340 if ( multiPoint->numGeometries() == 0 )
341 {
342 // didn't find any points, so reset geometry to null
343 multiPoint.reset();
344 }
345 return multiPoint;
346}
347
348std::unique_ptr< QgsMultiCurve > QgsArcGisRestUtils::convertGeometryPolyline( const QVariantMap &geometryData, QgsWkbTypes::Type pointType )
349{
350 // {"curvePaths": [[[0,0], {"c": [[3,3],[1,4]]} ]]}
351 QVariantList pathsList;
352 if ( geometryData[QStringLiteral( "paths" )].isValid() )
353 pathsList = geometryData[QStringLiteral( "paths" )].toList();
354 else if ( geometryData[QStringLiteral( "curvePaths" )].isValid() )
355 pathsList = geometryData[QStringLiteral( "curvePaths" )].toList();
356 if ( pathsList.isEmpty() )
357 return nullptr;
358 std::unique_ptr< QgsMultiCurve > multiCurve = std::make_unique< QgsMultiCurve >();
359 multiCurve->reserve( pathsList.size() );
360 for ( const QVariant &pathData : std::as_const( pathsList ) )
361 {
362 std::unique_ptr< QgsCompoundCurve > curve = convertCompoundCurve( pathData.toList(), pointType );
363 if ( !curve )
364 {
365 return nullptr;
366 }
367 multiCurve->addGeometry( curve.release() );
368 }
369 return multiCurve;
370}
371
372std::unique_ptr< QgsMultiSurface > QgsArcGisRestUtils::convertGeometryPolygon( const QVariantMap &geometryData, QgsWkbTypes::Type pointType )
373{
374 // {"curveRings": [[[0,0], {"c": [[3,3],[1,4]]} ]]}
375 QVariantList ringsList;
376 if ( geometryData[QStringLiteral( "rings" )].isValid() )
377 ringsList = geometryData[QStringLiteral( "rings" )].toList();
378 else if ( geometryData[QStringLiteral( "ringPaths" )].isValid() )
379 ringsList = geometryData[QStringLiteral( "ringPaths" )].toList();
380 if ( ringsList.isEmpty() )
381 return nullptr;
382
383 QList< QgsCompoundCurve * > curves;
384 for ( int i = 0, n = ringsList.size(); i < n; ++i )
385 {
386 std::unique_ptr< QgsCompoundCurve > curve = convertCompoundCurve( ringsList[i].toList(), pointType );
387 if ( !curve )
388 {
389 continue;
390 }
391 curves.append( curve.release() );
392 }
393 if ( curves.count() == 0 )
394 return nullptr;
395
396 std::unique_ptr< QgsMultiSurface > result = std::make_unique< QgsMultiSurface >();
397 if ( curves.count() == 1 )
398 {
399 // shortcut for exterior ring only
400 std::unique_ptr< QgsCurvePolygon > newPolygon = std::make_unique< QgsCurvePolygon >();
401 newPolygon->setExteriorRing( curves.takeAt( 0 ) );
402 result->addGeometry( newPolygon.release() );
403 return result;
404 }
405
406 std::sort( curves.begin(), curves.end(), []( const QgsCompoundCurve * a, const QgsCompoundCurve * b )->bool{ double a_area = 0.0; double b_area = 0.0; a->sumUpArea( a_area ); b->sumUpArea( b_area ); return std::abs( a_area ) > std::abs( b_area ); } );
407 result->reserve( curves.size() );
408 while ( !curves.isEmpty() )
409 {
410 QgsCompoundCurve *exterior = curves.takeFirst();
411 QgsCurvePolygon *newPolygon = new QgsCurvePolygon();
412 newPolygon->setExteriorRing( exterior );
413 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( newPolygon ) );
414 engine->prepareGeometry();
415
416 QMutableListIterator< QgsCompoundCurve * > it( curves );
417 while ( it.hasNext() )
418 {
419 QgsCompoundCurve *curve = it.next();
420 QgsRectangle boundingBox = newPolygon->boundingBox();
421 if ( boundingBox.intersects( curve->boundingBox() ) )
422 {
423 QgsPoint point = curve->startPoint();
424 if ( engine->contains( &point ) )
425 {
426 newPolygon->addInteriorRing( curve );
427 it.remove();
428 engine.reset( QgsGeometry::createGeometryEngine( newPolygon ) );
429 engine->prepareGeometry();
430 }
431 }
432 }
433 result->addGeometry( newPolygon );
434 }
435 if ( result->numGeometries() == 0 )
436 return nullptr;
437
438 return result;
439}
440
441std::unique_ptr< QgsPolygon > QgsArcGisRestUtils::convertEnvelope( const QVariantMap &geometryData )
442{
443 // {"xmin" : -109.55, "ymin" : 25.76, "xmax" : -86.39, "ymax" : 49.94}
444 bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false;
445 double xmin = geometryData[QStringLiteral( "xmin" )].toDouble( &xminOk );
446 double ymin = geometryData[QStringLiteral( "ymin" )].toDouble( &yminOk );
447 double xmax = geometryData[QStringLiteral( "xmax" )].toDouble( &xmaxOk );
448 double ymax = geometryData[QStringLiteral( "ymax" )].toDouble( &ymaxOk );
449 if ( !xminOk || !yminOk || !xmaxOk || !ymaxOk )
450 return nullptr;
451 std::unique_ptr< QgsLineString > ext = std::make_unique< QgsLineString> ();
452 ext->addVertex( QgsPoint( xmin, ymin ) );
453 ext->addVertex( QgsPoint( xmax, ymin ) );
454 ext->addVertex( QgsPoint( xmax, ymax ) );
455 ext->addVertex( QgsPoint( xmin, ymax ) );
456 ext->addVertex( QgsPoint( xmin, ymin ) );
457 std::unique_ptr< QgsPolygon > poly = std::make_unique< QgsPolygon >();
458 poly->setExteriorRing( ext.release() );
459 return poly;
460}
461
462QgsAbstractGeometry *QgsArcGisRestUtils::convertGeometry( const QVariantMap &geometryData, const QString &esriGeometryType, bool readM, bool readZ, QgsCoordinateReferenceSystem *crs )
463{
464 QgsWkbTypes::Type pointType = QgsWkbTypes::zmType( QgsWkbTypes::Point, readZ, readM );
465 if ( crs )
466 {
467 *crs = convertSpatialReference( geometryData[QStringLiteral( "spatialReference" )].toMap() );
468 }
469
470 // http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#/Geometry_Objects/02r3000000n1000000/
471 if ( esriGeometryType == QLatin1String( "esriGeometryNull" ) )
472 return nullptr;
473 else if ( esriGeometryType == QLatin1String( "esriGeometryPoint" ) )
474 return convertGeometryPoint( geometryData, pointType ).release();
475 else if ( esriGeometryType == QLatin1String( "esriGeometryMultipoint" ) )
476 return convertMultiPoint( geometryData, pointType ).release();
477 else if ( esriGeometryType == QLatin1String( "esriGeometryPolyline" ) )
478 return convertGeometryPolyline( geometryData, pointType ).release();
479 else if ( esriGeometryType == QLatin1String( "esriGeometryPolygon" ) )
480 return convertGeometryPolygon( geometryData, pointType ).release();
481 else if ( esriGeometryType == QLatin1String( "esriGeometryEnvelope" ) )
482 return convertEnvelope( geometryData ).release();
483 // Unsupported (either by qgis, or format unspecified by the specification)
484 // esriGeometryCircularArc
485 // esriGeometryEllipticArc
486 // esriGeometryBezier3Curve
487 // esriGeometryPath
488 // esriGeometryRing
489 // esriGeometryLine
490 // esriGeometryAny
491 // esriGeometryMultiPatch
492 // esriGeometryTriangleStrip
493 // esriGeometryTriangleFan
494 // esriGeometryRay
495 // esriGeometrySphere
496 // esriGeometryTriangles
497 // esriGeometryBag
498 return nullptr;
499}
500
502{
504
505 QString spatialReference = spatialReferenceMap[QStringLiteral( "latestWkid" )].toString();
506 if ( spatialReference.isEmpty() )
507 spatialReference = spatialReferenceMap[QStringLiteral( "wkid" )].toString();
508
509 // prefer using authority/id wherever we can
510 if ( !spatialReference.isEmpty() )
511 {
512 crs.createFromString( QStringLiteral( "EPSG:%1" ).arg( spatialReference ) );
513 if ( !crs.isValid() )
514 {
515 // Try as an ESRI auth
516 crs.createFromString( QStringLiteral( "ESRI:%1" ).arg( spatialReference ) );
517 }
518 }
519 else if ( !spatialReferenceMap[QStringLiteral( "wkt" )].toString().isEmpty() )
520 {
521 // otherwise fallback to WKT
522 crs.createFromWkt( spatialReferenceMap[QStringLiteral( "wkt" )].toString() );
523 }
524
525 if ( !crs.isValid() )
526 {
527 // If no spatial reference, just use WGS84
528 // TODO -- this needs further investigation! Most ESRI server services default to 3857, so that would likely be
529 // a safer fallback to use...
530 crs.createFromString( QStringLiteral( "EPSG:4326" ) );
531 }
532 return crs;
533}
534
535QgsSymbol *QgsArcGisRestUtils::convertSymbol( const QVariantMap &symbolData )
536{
537 const QString type = symbolData.value( QStringLiteral( "type" ) ).toString();
538 if ( type == QLatin1String( "esriSMS" ) )
539 {
540 // marker symbol
541 return parseEsriMarkerSymbolJson( symbolData ).release();
542 }
543 else if ( type == QLatin1String( "esriSLS" ) )
544 {
545 // line symbol
546 return parseEsriLineSymbolJson( symbolData ).release();
547 }
548 else if ( type == QLatin1String( "esriSFS" ) )
549 {
550 // fill symbol
551 return parseEsriFillSymbolJson( symbolData ).release();
552 }
553 else if ( type == QLatin1String( "esriPFS" ) )
554 {
555 return parseEsriPictureFillSymbolJson( symbolData ).release();
556 }
557 else if ( type == QLatin1String( "esriPMS" ) )
558 {
559 // picture marker
560 return parseEsriPictureMarkerSymbolJson( symbolData ).release();
561 }
562 else if ( type == QLatin1String( "esriTS" ) )
563 {
564 // text symbol - not supported
565 return nullptr;
566 }
567 return nullptr;
568}
569
570std::unique_ptr<QgsLineSymbol> QgsArcGisRestUtils::parseEsriLineSymbolJson( const QVariantMap &symbolData )
571{
572 QColor lineColor = convertColor( symbolData.value( QStringLiteral( "color" ) ) );
573 if ( !lineColor.isValid() )
574 return nullptr;
575
576 bool ok = false;
577 double widthInPoints = symbolData.value( QStringLiteral( "width" ) ).toDouble( &ok );
578 if ( !ok )
579 return nullptr;
580
581 QgsSymbolLayerList layers;
582 Qt::PenStyle penStyle = convertLineStyle( symbolData.value( QStringLiteral( "style" ) ).toString() );
583 std::unique_ptr< QgsSimpleLineSymbolLayer > lineLayer = std::make_unique< QgsSimpleLineSymbolLayer >( lineColor, widthInPoints, penStyle );
584 lineLayer->setWidthUnit( QgsUnitTypes::RenderPoints );
585 layers.append( lineLayer.release() );
586
587 std::unique_ptr< QgsLineSymbol > symbol = std::make_unique< QgsLineSymbol >( layers );
588 return symbol;
589}
590
591std::unique_ptr<QgsFillSymbol> QgsArcGisRestUtils::parseEsriFillSymbolJson( const QVariantMap &symbolData )
592{
593 QColor fillColor = convertColor( symbolData.value( QStringLiteral( "color" ) ) );
594 Qt::BrushStyle brushStyle = convertFillStyle( symbolData.value( QStringLiteral( "style" ) ).toString() );
595
596 const QVariantMap outlineData = symbolData.value( QStringLiteral( "outline" ) ).toMap();
597 QColor lineColor = convertColor( outlineData.value( QStringLiteral( "color" ) ) );
598 Qt::PenStyle penStyle = convertLineStyle( outlineData.value( QStringLiteral( "style" ) ).toString() );
599 bool ok = false;
600 double penWidthInPoints = outlineData.value( QStringLiteral( "width" ) ).toDouble( &ok );
601
602 QgsSymbolLayerList layers;
603 std::unique_ptr< QgsSimpleFillSymbolLayer > fillLayer = std::make_unique< QgsSimpleFillSymbolLayer >( fillColor, brushStyle, lineColor, penStyle, penWidthInPoints );
604 fillLayer->setStrokeWidthUnit( QgsUnitTypes::RenderPoints );
605 layers.append( fillLayer.release() );
606
607 std::unique_ptr< QgsFillSymbol > symbol = std::make_unique< QgsFillSymbol >( layers );
608 return symbol;
609}
610
611std::unique_ptr<QgsFillSymbol> QgsArcGisRestUtils::parseEsriPictureFillSymbolJson( const QVariantMap &symbolData )
612{
613 bool ok = false;
614
615 double widthInPixels = symbolData.value( QStringLiteral( "width" ) ).toInt( &ok );
616 if ( !ok )
617 return nullptr;
618
619 const double xScale = symbolData.value( QStringLiteral( "xscale" ) ).toDouble( &ok );
620 if ( !qgsDoubleNear( xScale, 0.0 ) )
621 widthInPixels *= xScale;
622
623 const double angleCCW = symbolData.value( QStringLiteral( "angle" ) ).toDouble( &ok );
624 double angleCW = 0;
625 if ( ok )
626 angleCW = -angleCCW;
627
628 const double xOffset = symbolData.value( QStringLiteral( "xoffset" ) ).toDouble();
629 const double yOffset = symbolData.value( QStringLiteral( "yoffset" ) ).toDouble();
630
631 QString symbolPath( symbolData.value( QStringLiteral( "imageData" ) ).toString() );
632 symbolPath.prepend( QLatin1String( "base64:" ) );
633
634 QgsSymbolLayerList layers;
635 std::unique_ptr< QgsRasterFillSymbolLayer > fillLayer = std::make_unique< QgsRasterFillSymbolLayer >( symbolPath );
636 fillLayer->setWidth( widthInPixels );
637 fillLayer->setAngle( angleCW );
638 fillLayer->setWidthUnit( QgsUnitTypes::RenderPoints );
639 fillLayer->setOffset( QPointF( xOffset, yOffset ) );
640 fillLayer->setOffsetUnit( QgsUnitTypes::RenderPoints );
641 layers.append( fillLayer.release() );
642
643 const QVariantMap outlineData = symbolData.value( QStringLiteral( "outline" ) ).toMap();
644 QColor lineColor = convertColor( outlineData.value( QStringLiteral( "color" ) ) );
645 Qt::PenStyle penStyle = convertLineStyle( outlineData.value( QStringLiteral( "style" ) ).toString() );
646 double penWidthInPoints = outlineData.value( QStringLiteral( "width" ) ).toDouble( &ok );
647
648 std::unique_ptr< QgsSimpleLineSymbolLayer > lineLayer = std::make_unique< QgsSimpleLineSymbolLayer >( lineColor, penWidthInPoints, penStyle );
649 lineLayer->setWidthUnit( QgsUnitTypes::RenderPoints );
650 layers.append( lineLayer.release() );
651
652 std::unique_ptr< QgsFillSymbol > symbol = std::make_unique< QgsFillSymbol >( layers );
653 return symbol;
654}
655
656Qgis::MarkerShape QgsArcGisRestUtils::parseEsriMarkerShape( const QString &style )
657{
658 if ( style == QLatin1String( "esriSMSCircle" ) )
660 else if ( style == QLatin1String( "esriSMSCross" ) )
662 else if ( style == QLatin1String( "esriSMSDiamond" ) )
664 else if ( style == QLatin1String( "esriSMSSquare" ) )
666 else if ( style == QLatin1String( "esriSMSX" ) )
668 else if ( style == QLatin1String( "esriSMSTriangle" ) )
670 else
672}
673
674std::unique_ptr<QgsMarkerSymbol> QgsArcGisRestUtils::parseEsriMarkerSymbolJson( const QVariantMap &symbolData )
675{
676 QColor fillColor = convertColor( symbolData.value( QStringLiteral( "color" ) ) );
677 bool ok = false;
678 const double sizeInPoints = symbolData.value( QStringLiteral( "size" ) ).toDouble( &ok );
679 if ( !ok )
680 return nullptr;
681 const double angleCCW = symbolData.value( QStringLiteral( "angle" ) ).toDouble( &ok );
682 double angleCW = 0;
683 if ( ok )
684 angleCW = -angleCCW;
685
686 Qgis::MarkerShape shape = parseEsriMarkerShape( symbolData.value( QStringLiteral( "style" ) ).toString() );
687
688 const double xOffset = symbolData.value( QStringLiteral( "xoffset" ) ).toDouble();
689 const double yOffset = symbolData.value( QStringLiteral( "yoffset" ) ).toDouble();
690
691 const QVariantMap outlineData = symbolData.value( QStringLiteral( "outline" ) ).toMap();
692 QColor lineColor = convertColor( outlineData.value( QStringLiteral( "color" ) ) );
693 Qt::PenStyle penStyle = convertLineStyle( outlineData.value( QStringLiteral( "style" ) ).toString() );
694 double penWidthInPoints = outlineData.value( QStringLiteral( "width" ) ).toDouble( &ok );
695
696 QgsSymbolLayerList layers;
697 std::unique_ptr< QgsSimpleMarkerSymbolLayer > markerLayer = std::make_unique< QgsSimpleMarkerSymbolLayer >( shape, sizeInPoints, angleCW, Qgis::ScaleMethod::ScaleArea, fillColor, lineColor );
698 markerLayer->setSizeUnit( QgsUnitTypes::RenderPoints );
699 markerLayer->setStrokeWidthUnit( QgsUnitTypes::RenderPoints );
700 markerLayer->setStrokeStyle( penStyle );
701 markerLayer->setStrokeWidth( penWidthInPoints );
702 markerLayer->setOffset( QPointF( xOffset, yOffset ) );
703 markerLayer->setOffsetUnit( QgsUnitTypes::RenderPoints );
704 layers.append( markerLayer.release() );
705
706 std::unique_ptr< QgsMarkerSymbol > symbol = std::make_unique< QgsMarkerSymbol >( layers );
707 return symbol;
708}
709
710std::unique_ptr<QgsMarkerSymbol> QgsArcGisRestUtils::parseEsriPictureMarkerSymbolJson( const QVariantMap &symbolData )
711{
712 bool ok = false;
713 const double widthInPixels = symbolData.value( QStringLiteral( "width" ) ).toInt( &ok );
714 if ( !ok )
715 return nullptr;
716 const double heightInPixels = symbolData.value( QStringLiteral( "height" ) ).toInt( &ok );
717 if ( !ok )
718 return nullptr;
719
720 const double angleCCW = symbolData.value( QStringLiteral( "angle" ) ).toDouble( &ok );
721 double angleCW = 0;
722 if ( ok )
723 angleCW = -angleCCW;
724
725 const double xOffset = symbolData.value( QStringLiteral( "xoffset" ) ).toDouble();
726 const double yOffset = symbolData.value( QStringLiteral( "yoffset" ) ).toDouble();
727
728 //const QString contentType = symbolData.value( QStringLiteral( "contentType" ) ).toString();
729
730 QString symbolPath( symbolData.value( QStringLiteral( "imageData" ) ).toString() );
731 symbolPath.prepend( QLatin1String( "base64:" ) );
732
733 QgsSymbolLayerList layers;
734 std::unique_ptr< QgsRasterMarkerSymbolLayer > markerLayer = std::make_unique< QgsRasterMarkerSymbolLayer >( symbolPath, widthInPixels, angleCW, Qgis::ScaleMethod::ScaleArea );
735 markerLayer->setSizeUnit( QgsUnitTypes::RenderPoints );
736
737 // only change the default aspect ratio if the server height setting requires this
738 if ( !qgsDoubleNear( static_cast< double >( heightInPixels ) / widthInPixels, markerLayer->defaultAspectRatio() ) )
739 markerLayer->setFixedAspectRatio( static_cast< double >( heightInPixels ) / widthInPixels );
740
741 markerLayer->setOffset( QPointF( xOffset, yOffset ) );
742 markerLayer->setOffsetUnit( QgsUnitTypes::RenderPoints );
743 layers.append( markerLayer.release() );
744
745 std::unique_ptr< QgsMarkerSymbol > symbol = std::make_unique< QgsMarkerSymbol >( layers );
746 return symbol;
747}
748
750{
751 if ( labelingData.empty() )
752 return nullptr;
753
754 QgsRuleBasedLabeling::Rule *root = new QgsRuleBasedLabeling::Rule( new QgsPalLayerSettings(), 0, 0, QString(), QString(), false );
755 root->setActive( true );
756
757 int i = 1;
758 for ( const QVariant &lbl : labelingData )
759 {
760 const QVariantMap labeling = lbl.toMap();
761
763 QgsTextFormat format;
764
765 const QString placement = labeling.value( QStringLiteral( "labelPlacement" ) ).toString();
766 if ( placement == QLatin1String( "esriServerPointLabelPlacementAboveCenter" ) )
767 {
769 settings->quadOffset = Qgis::LabelQuadrantPosition::Above;
770 }
771 else if ( placement == QLatin1String( "esriServerPointLabelPlacementBelowCenter" ) )
772 {
774 settings->quadOffset = Qgis::LabelQuadrantPosition::Below;
775 }
776 else if ( placement == QLatin1String( "esriServerPointLabelPlacementCenterCenter" ) )
777 {
779 settings->quadOffset = Qgis::LabelQuadrantPosition::Over;
780 }
781 else if ( placement == QLatin1String( "esriServerPointLabelPlacementAboveLeft" ) )
782 {
784 settings->quadOffset = Qgis::LabelQuadrantPosition::AboveLeft;
785 }
786 else if ( placement == QLatin1String( "esriServerPointLabelPlacementBelowLeft" ) )
787 {
789 settings->quadOffset = Qgis::LabelQuadrantPosition::BelowLeft;
790 }
791 else if ( placement == QLatin1String( "esriServerPointLabelPlacementCenterLeft" ) )
792 {
794 settings->quadOffset = Qgis::LabelQuadrantPosition::Left;
795 }
796 else if ( placement == QLatin1String( "esriServerPointLabelPlacementAboveRight" ) )
797 {
799 settings->quadOffset = Qgis::LabelQuadrantPosition::AboveRight;
800 }
801 else if ( placement == QLatin1String( "esriServerPointLabelPlacementBelowRight" ) )
802 {
804 settings->quadOffset = Qgis::LabelQuadrantPosition::BelowRight;
805 }
806 else if ( placement == QLatin1String( "esriServerPointLabelPlacementCenterRight" ) )
807 {
809 settings->quadOffset = Qgis::LabelQuadrantPosition::Right;
810 }
811 else if ( placement == QLatin1String( "esriServerLinePlacementAboveAfter" ) ||
812 placement == QLatin1String( "esriServerLinePlacementAboveStart" ) ||
813 placement == QLatin1String( "esriServerLinePlacementAboveAlong" ) )
814 {
816 settings->lineSettings().setPlacementFlags( QgsLabeling::LinePlacementFlag::AboveLine | QgsLabeling::LinePlacementFlag::MapOrientation );
817 }
818 else if ( placement == QLatin1String( "esriServerLinePlacementBelowAfter" ) ||
819 placement == QLatin1String( "esriServerLinePlacementBelowStart" ) ||
820 placement == QLatin1String( "esriServerLinePlacementBelowAlong" ) )
821 {
823 settings->lineSettings().setPlacementFlags( QgsLabeling::LinePlacementFlag::BelowLine | QgsLabeling::LinePlacementFlag::MapOrientation );
824 }
825 else if ( placement == QLatin1String( "esriServerLinePlacementCenterAfter" ) ||
826 placement == QLatin1String( "esriServerLinePlacementCenterStart" ) ||
827 placement == QLatin1String( "esriServerLinePlacementCenterAlong" ) )
828 {
830 settings->lineSettings().setPlacementFlags( QgsLabeling::LinePlacementFlag::OnLine | QgsLabeling::LinePlacementFlag::MapOrientation );
831 }
832 else if ( placement == QLatin1String( "esriServerPolygonPlacementAlwaysHorizontal" ) )
833 {
835 }
836
837 const double minScale = labeling.value( QStringLiteral( "minScale" ) ).toDouble();
838 const double maxScale = labeling.value( QStringLiteral( "maxScale" ) ).toDouble();
839
840 QVariantMap symbol = labeling.value( QStringLiteral( "symbol" ) ).toMap();
841 format.setColor( convertColor( symbol.value( QStringLiteral( "color" ) ) ) );
842 const double haloSize = symbol.value( QStringLiteral( "haloSize" ) ).toDouble();
843 if ( !qgsDoubleNear( haloSize, 0.0 ) )
844 {
846 buffer.setEnabled( true );
847 buffer.setSize( haloSize );
849 buffer.setColor( convertColor( symbol.value( QStringLiteral( "haloColor" ) ) ) );
850 format.setBuffer( buffer );
851 }
852
853 const QString fontFamily = symbol.value( QStringLiteral( "font" ) ).toMap().value( QStringLiteral( "family" ) ).toString();
854 const QString fontStyle = symbol.value( QStringLiteral( "font" ) ).toMap().value( QStringLiteral( "style" ) ).toString();
855 const QString fontWeight = symbol.value( QStringLiteral( "font" ) ).toMap().value( QStringLiteral( "weight" ) ).toString();
856 const int fontSize = symbol.value( QStringLiteral( "font" ) ).toMap().value( QStringLiteral( "size" ) ).toInt();
857 QFont font( fontFamily, fontSize );
858 font.setStyleName( fontStyle );
859 font.setWeight( fontWeight == QLatin1String( "bold" ) ? QFont::Bold : QFont::Normal );
860
861 format.setFont( font );
862 format.setSize( fontSize );
864
865 settings->setFormat( format );
866
867 QString where = labeling.value( QStringLiteral( "where" ) ).toString();
868 QgsExpression exp( where );
869 // If the where clause isn't parsed as valid, don't use its
870 if ( !exp.isValid() )
871 where.clear();
872
873 settings->fieldName = convertLabelingExpression( labeling.value( QStringLiteral( "labelExpression" ) ).toString() );
874 settings->isExpression = true;
875
876 QgsRuleBasedLabeling::Rule *child = new QgsRuleBasedLabeling::Rule( settings, maxScale, minScale, where, QObject::tr( "ASF label %1" ).arg( i++ ), false );
877 child->setActive( true );
878 root->appendChild( child );
879 }
880
881 return new QgsRuleBasedLabeling( root );
882}
883
885{
886 const QString type = rendererData.value( QStringLiteral( "type" ) ).toString();
887 if ( type == QLatin1String( "simple" ) )
888 {
889 const QVariantMap symbolProps = rendererData.value( QStringLiteral( "symbol" ) ).toMap();
890 std::unique_ptr< QgsSymbol > symbol( convertSymbol( symbolProps ) );
891 if ( symbol )
892 return new QgsSingleSymbolRenderer( symbol.release() );
893 else
894 return nullptr;
895 }
896 else if ( type == QLatin1String( "uniqueValue" ) )
897 {
898 const QString field1 = rendererData.value( QStringLiteral( "field1" ) ).toString();
899 const QString field2 = rendererData.value( QStringLiteral( "field2" ) ).toString();
900 const QString field3 = rendererData.value( QStringLiteral( "field3" ) ).toString();
901 QString attribute;
902 if ( !field2.isEmpty() || !field3.isEmpty() )
903 {
904 const QString delimiter = rendererData.value( QStringLiteral( "fieldDelimiter" ) ).toString();
905 if ( !field3.isEmpty() )
906 {
907 attribute = QStringLiteral( "concat(\"%1\",'%2',\"%3\",'%4',\"%5\")" ).arg( field1, delimiter, field2, delimiter, field3 );
908 }
909 else
910 {
911 attribute = QStringLiteral( "concat(\"%1\",'%2',\"%3\")" ).arg( field1, delimiter, field2 );
912 }
913 }
914 else
915 {
916 attribute = field1;
917 }
918
919 const QVariantList categories = rendererData.value( QStringLiteral( "uniqueValueInfos" ) ).toList();
920 QgsCategoryList categoryList;
921 for ( const QVariant &category : categories )
922 {
923 const QVariantMap categoryData = category.toMap();
924 const QString value = categoryData.value( QStringLiteral( "value" ) ).toString();
925 const QString label = categoryData.value( QStringLiteral( "label" ) ).toString();
926 std::unique_ptr< QgsSymbol > symbol( QgsArcGisRestUtils::convertSymbol( categoryData.value( QStringLiteral( "symbol" ) ).toMap() ) );
927 if ( symbol )
928 {
929 categoryList.append( QgsRendererCategory( value, symbol.release(), label ) );
930 }
931 }
932
933 std::unique_ptr< QgsSymbol > defaultSymbol( convertSymbol( rendererData.value( QStringLiteral( "defaultSymbol" ) ).toMap() ) );
934 if ( defaultSymbol )
935 {
936 categoryList.append( QgsRendererCategory( QVariant(), defaultSymbol.release(), rendererData.value( QStringLiteral( "defaultLabel" ) ).toString() ) );
937 }
938
939 if ( categoryList.empty() )
940 return nullptr;
941
942 return new QgsCategorizedSymbolRenderer( attribute, categoryList );
943 }
944 else if ( type == QLatin1String( "classBreaks" ) )
945 {
946 // currently unsupported
947 return nullptr;
948 }
949 else if ( type == QLatin1String( "heatmap" ) )
950 {
951 // currently unsupported
952 return nullptr;
953 }
954 else if ( type == QLatin1String( "vectorField" ) )
955 {
956 // currently unsupported
957 return nullptr;
958 }
959 return nullptr;
960}
961
962QString QgsArcGisRestUtils::convertLabelingExpression( const QString &string )
963{
964 QString expression = string;
965
966 // Replace a few ArcGIS token to QGIS equivalents
967 const thread_local QRegularExpression rx1 = QRegularExpression( QStringLiteral( "(?=([^\"\\\\]*(\\\\.|\"([^\"\\\\]*\\\\.)*[^\"\\\\]*\"))*[^\"]*$)(\\s|^)CONCAT(\\s|$)" ) );
968 expression = expression.replace( rx1, QStringLiteral( "\\4||\\5" ) );
969
970 const thread_local QRegularExpression rx2 = QRegularExpression( QStringLiteral( "(?=([^\"\\\\]*(\\\\.|\"([^\"\\\\]*\\\\.)*[^\"\\\\]*\"))*[^\"]*$)(\\s|^)NEWLINE(\\s|$)" ) );
971 expression = expression.replace( rx2, QStringLiteral( "\\4'\\n'\\5" ) );
972
973 // ArcGIS's double quotes are single quotes in QGIS
974 const thread_local QRegularExpression rx3 = QRegularExpression( QStringLiteral( "\"(.*?(?<!\\\\))\"" ) );
975 expression = expression.replace( rx3, QStringLiteral( "'\\1'" ) );
976 const thread_local QRegularExpression rx4 = QRegularExpression( QStringLiteral( "\\\\\"" ) );
977 expression = expression.replace( rx4, QStringLiteral( "\"" ) );
978
979 // ArcGIS's square brakets are double quotes in QGIS
980 const thread_local QRegularExpression rx5 = QRegularExpression( QStringLiteral( "\\[([^]]*)\\]" ) );
981 expression = expression.replace( rx5, QStringLiteral( "\"\\1\"" ) );
982
983 return expression;
984}
985
986QColor QgsArcGisRestUtils::convertColor( const QVariant &colorData )
987{
988 const QVariantList colorParts = colorData.toList();
989 if ( colorParts.count() < 4 )
990 return QColor();
991
992 int red = colorParts.at( 0 ).toInt();
993 int green = colorParts.at( 1 ).toInt();
994 int blue = colorParts.at( 2 ).toInt();
995 int alpha = colorParts.at( 3 ).toInt();
996 return QColor( red, green, blue, alpha );
997}
998
999Qt::PenStyle QgsArcGisRestUtils::convertLineStyle( const QString &style )
1000{
1001 if ( style == QLatin1String( "esriSLSSolid" ) )
1002 return Qt::SolidLine;
1003 else if ( style == QLatin1String( "esriSLSDash" ) )
1004 return Qt::DashLine;
1005 else if ( style == QLatin1String( "esriSLSDashDot" ) )
1006 return Qt::DashDotLine;
1007 else if ( style == QLatin1String( "esriSLSDashDotDot" ) )
1008 return Qt::DashDotDotLine;
1009 else if ( style == QLatin1String( "esriSLSDot" ) )
1010 return Qt::DotLine;
1011 else if ( style == QLatin1String( "esriSLSNull" ) )
1012 return Qt::NoPen;
1013 else
1014 return Qt::SolidLine;
1015}
1016
1017Qt::BrushStyle QgsArcGisRestUtils::convertFillStyle( const QString &style )
1018{
1019 if ( style == QLatin1String( "esriSFSBackwardDiagonal" ) )
1020 return Qt::BDiagPattern;
1021 else if ( style == QLatin1String( "esriSFSCross" ) )
1022 return Qt::CrossPattern;
1023 else if ( style == QLatin1String( "esriSFSDiagonalCross" ) )
1024 return Qt::DiagCrossPattern;
1025 else if ( style == QLatin1String( "esriSFSForwardDiagonal" ) )
1026 return Qt::FDiagPattern;
1027 else if ( style == QLatin1String( "esriSFSHorizontal" ) )
1028 return Qt::HorPattern;
1029 else if ( style == QLatin1String( "esriSFSNull" ) )
1030 return Qt::NoBrush;
1031 else if ( style == QLatin1String( "esriSFSSolid" ) )
1032 return Qt::SolidPattern;
1033 else if ( style == QLatin1String( "esriSFSVertical" ) )
1034 return Qt::VerPattern;
1035 else
1036 return Qt::SolidPattern;
1037}
1038
1039QDateTime QgsArcGisRestUtils::convertDateTime( const QVariant &value )
1040{
1041 if ( QgsVariantUtils::isNull( value ) )
1042 return QDateTime();
1043 bool ok = false;
1044 QDateTime dt = QDateTime::fromMSecsSinceEpoch( value.toLongLong( &ok ) );
1045 if ( !ok )
1046 {
1047 QgsDebugMsg( QStringLiteral( "Invalid value %1 for datetime" ).arg( value.toString() ) );
1048 return QDateTime();
1049 }
1050 else
1051 return dt;
1052}
1053
1055{
1056 QVariantMap res;
1057 if ( geometry.isNull() )
1058 return QVariantMap();
1059
1060 const QgsAbstractGeometry *geom = geometry.constGet()->simplifiedTypeRef();
1061 switch ( QgsWkbTypes::flatType( geom->wkbType() ) )
1062 {
1065 return QVariantMap();
1066
1067 case QgsWkbTypes::Point:
1068 res = pointToJson( qgsgeometry_cast< const QgsPoint * >( geom ) );
1069 break;
1070
1072 res = lineStringToJson( qgsgeometry_cast< const QgsLineString * >( geom ) );
1073 break;
1074
1077 res = curveToJson( qgsgeometry_cast< const QgsCurve * >( geom ) );
1078 break;
1079
1081 res = polygonToJson( qgsgeometry_cast< const QgsPolygon * >( geom ) );
1082 break;
1083
1085 res = multiPointToJson( qgsgeometry_cast< const QgsMultiPoint * >( geom ) );
1086 break;
1087
1089 res = multiLineStringToJson( qgsgeometry_cast< const QgsMultiLineString * >( geom ) );
1090 break;
1091
1093 res = multiCurveToJson( qgsgeometry_cast< const QgsMultiCurve * >( geom ) );
1094 break;
1095
1097 res = multiPolygonToJson( qgsgeometry_cast< const QgsMultiPolygon * >( geom ) );
1098 break;
1099
1101 res = curvePolygonToJson( qgsgeometry_cast< const QgsCurvePolygon * >( geom ) );
1102 break;
1103
1105 res = multiSurfaceToJson( qgsgeometry_cast< const QgsMultiSurface * >( geom ) );
1106 break;
1107
1109 return QVariantMap(); // not supported by REST API
1110
1112 return QVariantMap(); //not yet supported, but could be
1113
1114 default:
1115 return QVariantMap(); //unreachable
1116
1117 }
1118
1119 if ( crs.isValid() )
1120 {
1121 // add spatialReference information
1122 res.insert( QStringLiteral( "spatialReference" ), crsToJson( crs ) );
1123 }
1124
1125 return res;
1126}
1127
1128QVariantMap QgsArcGisRestUtils::pointToJson( const QgsPoint *point )
1129{
1130 QVariantMap data;
1131 if ( point->isEmpty() )
1132 data[QStringLiteral( "x" )] = QStringLiteral( "NaN" );
1133 else
1134 {
1135 data[QStringLiteral( "x" )] = point->x();
1136 data[QStringLiteral( "y" )] = point->y();
1137
1138 if ( point->is3D() )
1139 data[QStringLiteral( "z" )] = !std::isnan( point->z() ) ? QVariant( point->z() ) : QVariant( QStringLiteral( "NaN" ) );
1140
1141 if ( point->isMeasure() )
1142 data[QStringLiteral( "m" )] = !std::isnan( point->m() ) ? QVariant( point->m() ) : QVariant( QStringLiteral( "NaN" ) );
1143 }
1144 return data;
1145}
1146
1147QVariantMap QgsArcGisRestUtils::multiPointToJson( const QgsMultiPoint *multiPoint )
1148{
1149 QVariantMap data;
1150 const bool hasZ = multiPoint->is3D();
1151 const bool hasM = multiPoint->isMeasure();
1152 data[QStringLiteral( "hasM" )] = hasM;
1153 data[QStringLiteral( "hasZ" )] = hasZ;
1154
1155 QVariantList pointsList;
1156 const int size = multiPoint->numGeometries();
1157 pointsList.reserve( size );
1158
1159 QVariantList pointList;
1160 for ( int i = 0; i < size; ++i )
1161 {
1162 const QgsPoint *point = multiPoint->pointN( i );
1163
1164 pointList.clear();
1165 pointList.append( point->x() );
1166 pointList.append( point->y() );
1167 if ( hasZ )
1168 pointList.append( point->z() );
1169 if ( hasM && !std::isnan( point->m() ) )
1170 pointList.append( point->m() );
1171
1172 pointsList.push_back( pointList );
1173 }
1174
1175 data[QStringLiteral( "points" )] = pointsList;
1176 return data;
1177}
1178
1179QVariantList QgsArcGisRestUtils::lineStringToJsonPath( const QgsLineString *line )
1180{
1181 const bool hasZ = line->is3D();
1182 const bool hasM = line->isMeasure();
1183
1184 QVariantList pointsList;
1185 const int size = line->numPoints();
1186 pointsList.reserve( size );
1187
1188 QVariantList pointList;
1189 const double *xData = line->xData();
1190 const double *yData = line->yData();
1191 const double *zData = hasZ ? line->zData() : nullptr;
1192 const double *mData = hasM ? line->mData() : nullptr;
1193
1194 for ( int i = 0; i < size; ++i )
1195 {
1196 pointList.clear();
1197 pointList.append( *xData++ );
1198 pointList.append( *yData++ );
1199
1200 if ( hasZ )
1201 pointList.append( *zData++ );
1202
1203 if ( hasM && !std::isnan( *mData ) )
1204 pointList.append( *mData );
1205 if ( hasM )
1206 mData++;
1207
1208 pointsList.push_back( pointList );
1209 }
1210 return pointsList;
1211}
1212
1213QVariantList QgsArcGisRestUtils::curveToJsonCurve( const QgsCurve *curve, bool includeStart )
1214{
1215 const bool hasZ = curve->is3D();
1216 const bool hasM = curve->isMeasure();
1217
1218 auto pointToList = [hasZ, hasM]( const QgsPoint & point ) -> QVariantList
1219 {
1220 QVariantList pointList;
1221
1222 pointList.append( point.x() );
1223 pointList.append( point.y() );
1224
1225 if ( hasZ )
1226 pointList.append( point.z() );
1227
1228 if ( hasM && !std::isnan( point.m() ) )
1229 pointList.append( point.m() );
1230
1231 return pointList;
1232 };
1233
1234 QVariantList res;
1235 switch ( QgsWkbTypes::flatType( curve->wkbType() ) )
1236 {
1238 {
1239 QVariantList part = lineStringToJsonPath( qgsgeometry_cast< const QgsLineString *>( curve ) );
1240 if ( !part.isEmpty() && !includeStart )
1241 part.removeAt( 0 );
1242 res = part;
1243 break;
1244 }
1245
1247 {
1248 const QgsCircularString *circularString = qgsgeometry_cast<const QgsCircularString * >( curve );
1249 if ( includeStart && !circularString->isEmpty() )
1250 {
1251 res.push_back( pointToList( circularString->startPoint() ) );
1252 }
1253
1254 const int size = circularString->numPoints();
1255 for ( int i = 1; i + 1 < size; i += 2 )
1256 {
1257 // end point comes BEFORE interior point!
1258 QVariantMap curvePart;
1259 QVariantList curveList;
1260 curveList.push_back( pointToList( circularString->pointN( i + 1 ) ) );
1261
1262 curveList.push_back( pointToList( circularString->pointN( i ) ) );
1263
1264 curvePart.insert( QStringLiteral( "c" ), curveList );
1265 res.push_back( curvePart );
1266 }
1267 break;
1268 }
1269
1271 {
1272 const QgsCompoundCurve *compoundCurve = qgsgeometry_cast<const QgsCompoundCurve * >( curve );
1273
1274 const int size = compoundCurve->nCurves();
1275 for ( int i = 0; i < size; ++i )
1276 {
1277 const QgsCurve *subCurve = compoundCurve->curveAt( i );
1278 res.append( curveToJsonCurve( subCurve, i == 0 ) );
1279 }
1280 break;
1281 }
1282
1283 default:
1284 break;
1285 }
1286 return res;
1287}
1288
1289QVariantMap QgsArcGisRestUtils::lineStringToJson( const QgsLineString *line )
1290{
1291 QVariantMap data;
1292 const bool hasZ = line->is3D();
1293 const bool hasM = line->isMeasure();
1294 data[QStringLiteral( "hasM" )] = hasM;
1295 data[QStringLiteral( "hasZ" )] = hasZ;
1296
1297 const QVariantList pointsList = lineStringToJsonPath( line );
1298
1299 QVariantList pointsData = QVariantList();
1300 pointsData.push_back( pointsList );
1301 data[QStringLiteral( "paths" )] = pointsData;
1302
1303 return data;
1304}
1305
1306QVariantMap QgsArcGisRestUtils::curveToJson( const QgsCurve *curve )
1307{
1308 QVariantMap data;
1309 const bool hasZ = curve->is3D();
1310 const bool hasM = curve->isMeasure();
1311 data[QStringLiteral( "hasM" )] = hasM;
1312 data[QStringLiteral( "hasZ" )] = hasZ;
1313
1314 const QVariantList curveList = curveToJsonCurve( curve, true );
1315
1316 QVariantList curveData = QVariantList();
1317 curveData.push_back( curveList );
1318 data[QStringLiteral( "curvePaths" )] = curveData;
1319
1320 return data;
1321}
1322
1323QVariantMap QgsArcGisRestUtils::multiLineStringToJson( const QgsMultiLineString *multiLine )
1324{
1325 QVariantMap data;
1326 const bool hasZ = multiLine->is3D();
1327 const bool hasM = multiLine->isMeasure();
1328 data[QStringLiteral( "hasM" )] = hasM;
1329 data[QStringLiteral( "hasZ" )] = hasZ;
1330
1331 const int size = multiLine->numGeometries();
1332 QVariantList paths;
1333 paths.reserve( size );
1334 for ( int i = 0; i < size; ++i )
1335 {
1336 const QgsLineString *line = multiLine->lineStringN( i );
1337 paths.push_back( lineStringToJsonPath( line ) );
1338 }
1339
1340 data[QStringLiteral( "paths" )] = paths;
1341 return data;
1342}
1343
1344QVariantMap QgsArcGisRestUtils::multiCurveToJson( const QgsMultiCurve *multiCurve )
1345{
1346 QVariantMap data;
1347 const bool hasZ = multiCurve->is3D();
1348 const bool hasM = multiCurve->isMeasure();
1349 data[QStringLiteral( "hasM" )] = hasM;
1350 data[QStringLiteral( "hasZ" )] = hasZ;
1351
1352 const int size = multiCurve->numGeometries();
1353 QVariantList paths;
1354 paths.reserve( size );
1355 for ( int i = 0; i < size; ++i )
1356 {
1357 const QgsCurve *curve = multiCurve->curveN( i );
1358 paths.push_back( curveToJsonCurve( curve, true ) );
1359 }
1360
1361 data[QStringLiteral( "curvePaths" )] = paths;
1362 return data;
1363}
1364
1365QVariantList QgsArcGisRestUtils::polygonToJsonRings( const QgsPolygon *polygon )
1366{
1367 QVariantList rings;
1368 const int numInteriorRings = polygon->numInteriorRings();
1369 rings.reserve( numInteriorRings + 1 );
1370
1371 if ( const QgsLineString *exterior = qgsgeometry_cast< const QgsLineString * >( polygon->exteriorRing() ) )
1372 {
1373 // exterior ring MUST be clockwise
1374 switch ( exterior->orientation() )
1375 {
1377 rings.push_back( lineStringToJsonPath( exterior ) );
1378 break;
1379
1381 {
1382 std::unique_ptr< QgsLineString > reversed( exterior->reversed() );
1383 rings.push_back( lineStringToJsonPath( reversed.get() ) );
1384 break;
1385 }
1386 }
1387 }
1388
1389 for ( int i = 0; i < numInteriorRings; ++i )
1390 {
1391 const QgsLineString *ring = qgsgeometry_cast< const QgsLineString * >( polygon->interiorRing( i ) );
1392 // holes MUST be counter-clockwise
1393 switch ( ring->orientation() )
1394 {
1396 rings.push_back( lineStringToJsonPath( ring ) );
1397 break;
1398
1400 {
1401 std::unique_ptr< QgsLineString > reversed( ring->reversed() );
1402 rings.push_back( lineStringToJsonPath( reversed.get() ) );
1403 break;
1404 }
1405 }
1406 }
1407 return rings;
1408}
1409
1410QVariantList QgsArcGisRestUtils::curvePolygonToJsonRings( const QgsCurvePolygon *polygon )
1411{
1412 QVariantList rings;
1413 const int numInteriorRings = polygon->numInteriorRings();
1414 rings.reserve( numInteriorRings + 1 );
1415
1416 if ( const QgsCurve *exterior = qgsgeometry_cast< const QgsCurve * >( polygon->exteriorRing() ) )
1417 {
1418 // exterior ring MUST be clockwise
1419 switch ( exterior->orientation() )
1420 {
1422 rings.push_back( curveToJsonCurve( exterior, true ) );
1423 break;
1424
1426 {
1427 std::unique_ptr< QgsCurve > reversed( exterior->reversed() );
1428 rings.push_back( curveToJsonCurve( reversed.get(), true ) );
1429 break;
1430 }
1431 }
1432 }
1433
1434 for ( int i = 0; i < numInteriorRings; ++i )
1435 {
1436 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( polygon->interiorRing( i ) );
1437 // holes MUST be counter-clockwise
1438 switch ( ring->orientation() )
1439 {
1441 rings.push_back( curveToJsonCurve( ring, true ) );
1442 break;
1443
1445 {
1446 std::unique_ptr< QgsCurve > reversed( ring->reversed() );
1447 rings.push_back( curveToJsonCurve( reversed.get(), true ) );
1448 break;
1449 }
1450 }
1451 }
1452 return rings;
1453}
1454
1455QVariantMap QgsArcGisRestUtils::polygonToJson( const QgsPolygon *polygon )
1456{
1457 QVariantMap data;
1458 const bool hasZ = polygon->is3D();
1459 const bool hasM = polygon->isMeasure();
1460 data[QStringLiteral( "hasM" )] = hasM;
1461 data[QStringLiteral( "hasZ" )] = hasZ;
1462 data[QStringLiteral( "rings" )] = polygonToJsonRings( polygon );
1463 return data;
1464}
1465
1466QVariantMap QgsArcGisRestUtils::curvePolygonToJson( const QgsCurvePolygon *polygon )
1467{
1468 QVariantMap data;
1469 const bool hasZ = polygon->is3D();
1470 const bool hasM = polygon->isMeasure();
1471 data[QStringLiteral( "hasM" )] = hasM;
1472 data[QStringLiteral( "hasZ" )] = hasZ;
1473 data[QStringLiteral( "curveRings" )] = curvePolygonToJsonRings( polygon );
1474 return data;
1475}
1476
1477QVariantMap QgsArcGisRestUtils::multiPolygonToJson( const QgsMultiPolygon *multiPolygon )
1478{
1479 QVariantMap data;
1480 const bool hasZ = multiPolygon->is3D();
1481 const bool hasM = multiPolygon->isMeasure();
1482 data[QStringLiteral( "hasM" )] = hasM;
1483 data[QStringLiteral( "hasZ" )] = hasZ;
1484
1485 const int size = multiPolygon->numGeometries();
1486 QVariantList rings;
1487 for ( int i = 0; i < size; ++i )
1488 {
1489 const QgsPolygon *polygon = multiPolygon->polygonN( i );
1490 rings.append( polygonToJsonRings( polygon ) );
1491 }
1492
1493 data[QStringLiteral( "rings" )] = rings;
1494 return data;
1495}
1496
1497QVariantMap QgsArcGisRestUtils::multiSurfaceToJson( const QgsMultiSurface *multiSurface )
1498{
1499 QVariantMap data;
1500 const bool hasZ = multiSurface->is3D();
1501 const bool hasM = multiSurface->isMeasure();
1502 data[QStringLiteral( "hasM" )] = hasM;
1503 data[QStringLiteral( "hasZ" )] = hasZ;
1504
1505 const int size = multiSurface->numGeometries();
1506 QVariantList rings;
1507 for ( int i = 0; i < size; ++i )
1508 {
1509 const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( multiSurface->geometryN( i ) );
1510 if ( !polygon )
1511 continue;
1512
1513 rings.append( curvePolygonToJsonRings( polygon ) );
1514 }
1515
1516 data[QStringLiteral( "curveRings" )] = rings;
1517 return data;
1518}
1519
1521{
1522 QVariantMap res;
1523 if ( !crs.isValid() )
1524 return res;
1525
1526 const QString authid = crs.authid();
1527 if ( !authid.isEmpty() )
1528 {
1529 const thread_local QRegularExpression rxAuthid( QStringLiteral( "(\\w+):(\\d+)" ) );
1530 const QRegularExpressionMatch match = rxAuthid.match( authid );
1531 if ( match.hasMatch()
1532 && (
1533 ( match.captured( 1 ).compare( QLatin1String( "EPSG" ), Qt::CaseInsensitive ) == 0 )
1534 || ( match.captured( 1 ).compare( QLatin1String( "ESRI" ), Qt::CaseInsensitive ) == 0 )
1535 )
1536 )
1537 {
1538 const QString wkid = match.captured( 2 );
1539 res.insert( QStringLiteral( "wkid" ), wkid );
1540 return res;
1541 }
1542 }
1543
1544 // docs don't mention the WKT version support, so let's hope for 2.0...
1545 res.insert( QStringLiteral( "wkt" ), crs.toWkt( QgsCoordinateReferenceSystem::WKT2_2019_SIMPLIFIED ) );
1546
1547 return res;
1548}
1549
1550QVariantMap QgsArcGisRestUtils::featureToJson( const QgsFeature &feature, const QgsArcGisRestContext &context, const QgsCoordinateReferenceSystem &crs, QgsArcGisRestUtils::FeatureToJsonFlags flags )
1551{
1552 QVariantMap res;
1553 if ( ( flags & FeatureToJsonFlag::IncludeGeometry ) && feature.hasGeometry() )
1554 {
1555 res.insert( QStringLiteral( "geometry" ), geometryToJson( feature.geometry(), context, crs ) );
1556 }
1557
1558 QVariantMap attributes;
1559 const QgsFields fields = feature.fields();
1560 for ( const QgsField &field : fields )
1561 {
1563 attributes.insert( field.name(), variantToAttributeValue( feature.attribute( field.name() ), field.type(), context ) );
1564 }
1565 if ( !attributes.isEmpty() )
1566 {
1567 res.insert( QStringLiteral( "attributes" ), attributes );
1568 }
1569 return res;
1570}
1571
1572QVariant QgsArcGisRestUtils::variantToAttributeValue( const QVariant &variant, QVariant::Type expectedType, const QgsArcGisRestContext &context )
1573{
1574 if ( QgsVariantUtils::isNull( variant ) )
1575 return QVariant();
1576
1577 switch ( expectedType )
1578 {
1579 case QVariant::DateTime:
1580 case QVariant::Date:
1581 {
1582 switch ( variant.type() )
1583 {
1584 case QVariant::DateTime:
1585 return variant.toDateTime().toMSecsSinceEpoch();
1586
1587 case QVariant::Date:
1588 // for date values, assume start of day -- the REST api requires datetime values only, not plain dates
1589 if ( context.timeZone().isValid() )
1590 return QDateTime( variant.toDate(), QTime( 0, 0, 0 ), context.timeZone() ).toMSecsSinceEpoch();
1591 else
1592 return QDateTime( variant.toDate(), QTime( 0, 0, 0 ) ).toMSecsSinceEpoch();
1593
1594 default:
1595 return QVariant();
1596 }
1597 }
1598
1599 default:
1600 return variant;
1601 }
1602}
1603
1605{
1606 QVariantMap res;
1607 res.insert( QStringLiteral( "name" ), field.name() );
1608
1609 QString fieldType;
1610 switch ( field.type() )
1611 {
1612 case QVariant::LongLong:
1613 fieldType = QStringLiteral( "esriFieldTypeInteger" );
1614 break;
1615
1616 case QVariant::Int:
1617 fieldType = QStringLiteral( "esriFieldTypeSmallInteger" );
1618 break;
1619
1620 case QVariant::Double:
1621 fieldType = QStringLiteral( "esriFieldTypeDouble" );
1622 break;
1623
1624 case QVariant::String:
1625 fieldType = QStringLiteral( "esriFieldTypeString" );
1626 break;
1627
1628 case QVariant::DateTime:
1629 case QVariant::Date:
1630 fieldType = QStringLiteral( "esriFieldTypeDate" );
1631 break;
1632
1633 case QVariant::ByteArray:
1634 fieldType = QStringLiteral( "esriFieldTypeBlob" );
1635 break;
1636
1637 default:
1638 // fallback to string
1639 fieldType = QStringLiteral( "esriFieldTypeString" );
1640 break;
1641 }
1642 res.insert( QStringLiteral( "type" ), fieldType );
1643
1644 if ( !field.alias().isEmpty() )
1645 res.insert( QStringLiteral( "alias" ), field.alias() );
1646
1647 // nullable
1648 const bool notNullable = field.constraints().constraints() & QgsFieldConstraints::Constraint::ConstraintNotNull;
1649 res.insert( QStringLiteral( "nullable" ), !notNullable );
1650
1651 // editable
1652 res.insert( QStringLiteral( "editable" ), true );
1653
1654 return res;
1655}
1656
1658{
1659 if ( type.compare( QLatin1String( "FeatureServer" ), Qt::CaseInsensitive ) == 0 )
1660 return Qgis::ArcGisRestServiceType::FeatureServer;
1661 else if ( type.compare( QLatin1String( "MapServer" ), Qt::CaseInsensitive ) == 0 )
1662 return Qgis::ArcGisRestServiceType::MapServer;
1663 else if ( type.compare( QLatin1String( "ImageServer" ), Qt::CaseInsensitive ) == 0 )
1664 return Qgis::ArcGisRestServiceType::ImageServer;
1665 else if ( type.compare( QLatin1String( "GlobeServer" ), Qt::CaseInsensitive ) == 0 )
1667 else if ( type.compare( QLatin1String( "GPServer" ), Qt::CaseInsensitive ) == 0 )
1669 else if ( type.compare( QLatin1String( "GeocodeServer" ), Qt::CaseInsensitive ) == 0 )
1671
1673}
1674
@ CounterClockwise
Counter-clockwise direction.
@ Clockwise
Clockwise direction.
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
@ ScaleArea
Calculate scale by the area.
ArcGisRestServiceType
Available ArcGIS REST service types.
Definition: qgis.h:2182
@ GeocodeServer
GeocodeServer.
@ Unknown
Other unknown/unsupported type.
MarkerShape
Marker shapes.
Definition: qgis.h:1532
@ Triangle
Triangle.
@ Cross2
Rotated cross (lines only), 'x' shape.
@ Cross
Cross (lines only)
Abstract base class for all geometries.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual const QgsAbstractGeometry * simplifiedTypeRef() const SIP_HOLDGIL
Returns a reference to the simplest lossless representation of this geometry, e.g.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
Abstract base class - its implementations define different approaches to the labeling of a vector lay...
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 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 QString convertLabelingExpression(const QString &string)
Converts an ESRI labeling expression to a QGIS expression string.
static QgsSymbol * convertSymbol(const QVariantMap &definition)
Converts a symbol JSON definition to a QgsSymbol.
static QVariantMap geometryToJson(const QgsGeometry &geometry, const QgsArcGisRestContext &context, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem())
Converts a geometry to an ArcGIS REST JSON representation.
static Qgis::ArcGisRestServiceType serviceTypeFromString(const QString &type)
Converts a string value to a REST service type.
static QgsAbstractGeometry * convertGeometry(const QVariantMap &geometry, const QString &esriGeometryType, bool hasM, bool hasZ, QgsCoordinateReferenceSystem *crs=nullptr)
Converts an ESRI REST geometry JSON definition to a QgsAbstractGeometry.
static QVariant variantToAttributeValue(const QVariant &variant, QVariant::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)))
Flags which control the behavior of converting features to JSON.
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 QgsFeatureRenderer * convertRenderer(const QVariantMap &rendererData)
Converts renderer JSON data to an equivalent QgsFeatureRenderer.
static Qt::BrushStyle convertFillStyle(const QString &style)
Converts an ESRI fill style to a Qt brush style.
static QVariant::Type convertFieldType(const QString &type)
Converts an ESRI REST field type to a QVariant type.
static QgsWkbTypes::Type convertGeometryType(const QString &type)
Converts an ESRI REST geometry type to a WKB type.
static QVariantMap crsToJson(const QgsCoordinateReferenceSystem &crs)
Converts a crs to an ArcGIS REST JSON representation.
static QgsAbstractVectorLayerLabeling * convertLabeling(const QVariantList &data)
Converts labeling JSON data to an equivalent QGIS vector labeling.
static QColor convertColor(const QVariant &data)
Converts ESRI JSON color data to a QColor object.
Circular string geometry type.
QgsPoint pointN(int i) const SIP_HOLDGIL
Returns the point at index i within the circular string.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
Compound curve geometry type.
QgsCompoundCurve * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
const QgsCurve * curveAt(int i) const SIP_HOLDGIL
Returns the curve at the specified index.
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
int nCurves() const SIP_HOLDGIL
Returns the number of curves in the geometry.
This class 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.
@ WKT2_2019_SIMPLIFIED
WKT2_2019 with the simplification rule of WKT2_SIMPLIFIED.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Curve polygon geometry type.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
virtual void setExteriorRing(QgsCurve *ring)
Sets the exterior ring of the polygon.
virtual void addInteriorRing(QgsCurve *ring)
Adds an interior ring to the geometry (takes ownership)
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with 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
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
Definition: qgscurve.cpp:238
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.
Class for 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:56
QgsFields fields
Definition: qgsfeature.h:66
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:233
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:338
Q_GADGET Constraints constraints
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString name
Definition: qgsfield.h:60
QVariant::Type type
Definition: qgsfield.h:58
QString alias
Definition: qgsfield.h:61
QgsFieldConstraints constraints
Definition: qgsfield.h:63
Container of fields for a vector layer.
Definition: qgsfields.h:45
int numGeometries() const SIP_HOLDGIL
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.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
void setPlacementFlags(QgsLabeling::LinePlacementFlags flags)
Returns the line placement flags, which dictate how line labels can be placed above or below the line...
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
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 SIP_HOLDGIL
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.
Definition: qgsmulticurve.h:30
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.
Definition: qgsmultipoint.h:30
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.
Qgis::LabelQuadrantPosition quadOffset
Sets the quadrant in which to offset labels from feature.
bool isExpression
true if this label is made from a expression string, e.g., FieldName || 'mm'
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
QString fieldName
Name of field (or an expression) to use for label text.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
void clear() override
Clears the geometry, ie reset it to a null geometry.
Definition: qgspoint.cpp:359
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspoint.cpp:767
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
Polygon geometry type.
Definition: qgspolygon.h:34
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool intersects(const QgsRectangle &rect) const SIP_HOLDGIL
Returns true when rectangle intersects with other rectangle.
Definition: qgsrectangle.h:349
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
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
Rule based labeling for a vector layer.
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
Definition: qgssurface.h:43
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:93
Container for settings relating to a text buffer.
void setColor(const QColor &color)
Sets the color for the buffer.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the buffer size.
void setSize(double size)
Sets the size of the buffer.
Container for all settings relating to text rendering.
Definition: qgstextformat.h:41
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 setBuffer(const QgsTextBufferSettings &bufferSettings)
Sets the text's buffer settings.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the size of rendered text.
@ RenderPoints
Points (e.g., for font sizes)
Definition: qgsunittypes.h:173
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
@ GeometryCollection
Definition: qgswkbtypes.h:79
static Type zmType(Type type, bool hasZ, bool hasM) SIP_HOLDGIL
Returns the modified input geometry type according to hasZ / hasM.
Definition: qgswkbtypes.h:831
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2527
QList< QgsRendererCategory > QgsCategoryList
const QgsField & field
Definition: qgsfield.h:463
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:29
const QgsCoordinateReferenceSystem & crs