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