QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgsarcgisrestutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsarcgisrestutils.cpp
3 ----------------------
4 begin : Nov 25, 2015
5 copyright : (C) 2015 by Sandro Mani
7***************************************************************************
8* *
9* This program is free software; you can redistribute it and/or modify *
10* it under the terms of the GNU General Public License as published by *
11* the Free Software Foundation; either version 2 of the License, or *
12* (at your option) any later version. *
13* *
14***************************************************************************/
15
16#include "qgsarcgisrestutils.h"
17
19#include "qgscircularstring.h"
26#include "qgscolorrampimpl.h"
27#include "qgscurve.h"
28#include "qgsfields.h"
29#include "qgsfillsymbol.h"
30#include "qgsfillsymbollayer.h"
31#include "qgsgeometryengine.h"
33#include "qgslinestring.h"
34#include "qgslinesymbol.h"
35#include "qgslinesymbollayer.h"
36#include "qgslogger.h"
37#include "qgsmarkersymbol.h"
39#include "qgsmulticurve.h"
40#include "qgsmultilinestring.h"
41#include "qgsmultipoint.h"
42#include "qgsmultipolygon.h"
43#include "qgsmultisurface.h"
44#include "qgspallabeling.h"
45#include "qgspolygon.h"
47#include "qgsrectangle.h"
48#include "qgsrenderer.h"
51#include "qgssymbol.h"
52#include "qgssymbollayer.h"
53#include "qgsvariantutils.h"
55
56#include <QRegularExpression>
57#include <QString>
58#include <QUrl>
59
60#include "moc_qgsarcgisrestutils.cpp"
61
62using namespace Qt::StringLiterals;
63
64QMetaType::Type QgsArcGisRestUtils::convertFieldType( const QString &esriFieldType )
65{
66 if ( esriFieldType == "esriFieldTypeInteger"_L1 )
67 return QMetaType::Type::LongLong;
68 if ( esriFieldType == "esriFieldTypeSmallInteger"_L1 )
69 return QMetaType::Type::Int;
70 if ( esriFieldType == "esriFieldTypeDouble"_L1 )
71 return QMetaType::Type::Double;
72 if ( esriFieldType == "esriFieldTypeSingle"_L1 )
73 return QMetaType::Type::Double;
74 if ( esriFieldType == "esriFieldTypeString"_L1 )
75 return QMetaType::Type::QString;
76 if ( esriFieldType == "esriFieldTypeDate"_L1 )
77 return QMetaType::Type::QDateTime;
78 if ( esriFieldType == "esriFieldTypeGeometry"_L1 )
79 return QMetaType::Type::UnknownType; // Geometry column should not appear as field
80 if ( esriFieldType == "esriFieldTypeOID"_L1 )
81 return QMetaType::Type::LongLong;
82 if ( esriFieldType == "esriFieldTypeBlob"_L1 )
83 return QMetaType::Type::QByteArray;
84 if ( esriFieldType == "esriFieldTypeGlobalID"_L1 )
85 return QMetaType::Type::QString;
86 if ( esriFieldType == "esriFieldTypeRaster"_L1 )
87 return QMetaType::Type::QByteArray;
88 if ( esriFieldType == "esriFieldTypeGUID"_L1 )
89 return QMetaType::Type::QString;
90 if ( esriFieldType == "esriFieldTypeXML"_L1 )
91 return QMetaType::Type::QString;
92 return QMetaType::Type::UnknownType;
93}
94
96{
97 // http://resources.arcgis.com/en/help/arcobjects-cpp/componenthelp/index.html#//000w0000001p000000
98 if ( esriGeometryType == "esriGeometryNull"_L1 )
100 else if ( esriGeometryType == "esriGeometryPoint"_L1 )
102 else if ( esriGeometryType == "esriGeometryMultipoint"_L1 )
104 else if ( esriGeometryType == "esriGeometryPolyline"_L1 )
106 else if ( esriGeometryType == "esriGeometryPolygon"_L1 )
108 else if ( esriGeometryType == "esriGeometryEnvelope"_L1 )
110 // Unsupported (either by qgis, or format unspecified by the specification)
111 // esriGeometryCircularArc
112 // esriGeometryEllipticArc
113 // esriGeometryBezier3Curve
114 // esriGeometryPath
115 // esriGeometryRing
116 // esriGeometryLine
117 // esriGeometryAny
118 // esriGeometryMultiPatch
119 // esriGeometryTriangleStrip
120 // esriGeometryTriangleFan
121 // esriGeometryRay
122 // esriGeometrySphere
123 // esriGeometryTriangles
124 // esriGeometryBag
126}
127
128std::unique_ptr< QgsPoint > QgsArcGisRestUtils::convertPoint( const QVariantList &coordList, Qgis::WkbType pointType )
129{
130 int nCoords = coordList.size();
131 if ( nCoords < 2 )
132 return nullptr;
133 bool xok = false, yok = false;
134 const double x = coordList[0].toDouble( &xok );
135 const double y = coordList[1].toDouble( &yok );
136 if ( !xok || !yok )
137 return nullptr;
138 const bool hasZ = QgsWkbTypes::hasZ( pointType );
139 const double z = hasZ && nCoords >= 3 ? coordList[2].toDouble() : std::numeric_limits< double >::quiet_NaN();
140
141 // 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
142 const double m = QgsWkbTypes::hasM( pointType ) && ( ( hasZ && nCoords >= 4 ) || ( !hasZ && nCoords >= 3 ) ) ? coordList[ hasZ ? 3 : 2].toDouble() : std::numeric_limits< double >::quiet_NaN();
143 return std::make_unique< QgsPoint >( pointType, x, y, z, m );
144}
145
146std::unique_ptr< QgsCircularString > QgsArcGisRestUtils::convertCircularString( const QVariantMap &curveData, Qgis::WkbType pointType, const QgsPoint &startPoint )
147{
148 const QVariantList coordsList = curveData[u"c"_s].toList();
149 if ( coordsList.isEmpty() )
150 return nullptr;
151 const int coordsListSize = coordsList.size();
152
153 QVector<QgsPoint> points;
154 points.reserve( coordsListSize + 1 );
155 points.append( startPoint );
156
157 for ( int i = 0; i < coordsListSize - 1; )
158 {
159 // first point is end point, second is point on curve
160 // i.e. the opposite to what QGIS requires!
161 std::unique_ptr< QgsPoint > endPoint( convertPoint( coordsList.at( i ).toList(), pointType ) );
162 if ( !endPoint )
163 return nullptr;
164 i++;
165 std::unique_ptr< QgsPoint > interiorPoint( convertPoint( coordsList.at( i ).toList(), pointType ) );
166 if ( !interiorPoint )
167 return nullptr;
168 i++;
169 points << *interiorPoint;
170 points << *endPoint;
171 }
172 auto curve = std::make_unique< QgsCircularString> ();
173 curve->setPoints( points );
174 return curve;
175}
176
177std::unique_ptr< QgsCompoundCurve > QgsArcGisRestUtils::convertCompoundCurve( const QVariantList &curvesList, Qgis::WkbType pointType )
178{
179 // [[6,3],[5,3],{"b":[[3,2],[6,1],[2,4]]},[1,2],{"c": [[3,3],[1,4]]}]
180 auto compoundCurve = std::make_unique< QgsCompoundCurve >();
181
182 QVector< double > lineX;
183 QVector< double > lineY;
184 QVector< double > lineZ;
185 QVector< double > lineM;
186 int maxCurveListSize = curvesList.size();
187 lineX.resize( maxCurveListSize );
188 lineY.resize( maxCurveListSize );
189
190 const bool hasZ = QgsWkbTypes::hasZ( pointType );
191 if ( hasZ )
192 lineZ.resize( maxCurveListSize );
193 const bool hasM = QgsWkbTypes::hasM( pointType );
194 if ( hasM )
195 lineM.resize( maxCurveListSize );
196
197 double *outLineX = lineX.data();
198 double *outLineY = lineY.data();
199 double *outLineZ = lineZ.data();
200 double *outLineM = lineM.data();
201 int actualLineSize = 0;
202
203 bool xok = false;
204 bool yok = false;
205
206 int curveListIndex = 0;
207 for ( const QVariant &curveData : curvesList )
208 {
209 if ( curveData.userType() == QMetaType::Type::QVariantList )
210 {
211 const QVariantList coordList = curveData.toList();
212 const int nCoords = coordList.size();
213 if ( nCoords < 2 )
214 return nullptr;
215
216 const double x = coordList[0].toDouble( &xok );
217 const double y = coordList[1].toDouble( &yok );
218 if ( !xok || !yok )
219 return nullptr;
220
221 actualLineSize++;
222 *outLineX++ = x;
223 *outLineY++ = y;
224 if ( hasZ )
225 {
226 *outLineZ++ = nCoords >= 3 ? coordList[2].toDouble() : std::numeric_limits< double >::quiet_NaN();
227 }
228
229 if ( hasM )
230 {
231 // 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
232 *outLineM++ = ( ( hasZ && nCoords >= 4 ) || ( !hasZ && nCoords >= 3 ) ) ? coordList[ hasZ ? 3 : 2].toDouble() : std::numeric_limits< double >::quiet_NaN();
233 }
234 }
235 else if ( curveData.userType() == QMetaType::Type::QVariantMap )
236 {
237 // The last point of the linestring is the start point of this circular string
238 QgsPoint lastLineStringPoint;
239 if ( actualLineSize > 0 )
240 {
241 lastLineStringPoint = QgsPoint( lineX.at( actualLineSize - 1 ),
242 lineY.at( actualLineSize - 1 ),
243 hasZ ? lineZ.at( actualLineSize - 1 ) : std::numeric_limits< double >::quiet_NaN(),
244 hasM ? lineM.at( actualLineSize - 1 ) : std::numeric_limits< double >::quiet_NaN() );
245 }
246 std::unique_ptr< QgsCircularString > circularString( convertCircularString( curveData.toMap(), pointType, lastLineStringPoint ) );
247 if ( !circularString )
248 {
249 return nullptr;
250 }
251
252 if ( actualLineSize > 0 )
253 {
254 lineX.resize( actualLineSize );
255 lineY.resize( actualLineSize );
256 if ( hasZ )
257 lineZ.resize( actualLineSize );
258 if ( hasM )
259 lineM.resize( actualLineSize );
260
261 compoundCurve->addCurve( new QgsLineString( lineX, lineY, lineZ, lineM ) );
262 lineX.resize( maxCurveListSize - curveListIndex );
263 lineY.resize( maxCurveListSize - curveListIndex );
264 if ( hasZ )
265 lineZ.resize( maxCurveListSize - curveListIndex );
266 if ( hasM )
267 lineM.resize( maxCurveListSize - curveListIndex );
268 outLineX = lineX.data();
269 outLineY = lineY.data();
270 outLineZ = lineZ.data();
271 outLineM = lineM.data();
272 }
273
274 // If the previous curve had less than two points, remove it
275 if ( compoundCurve->curveAt( compoundCurve->nCurves() - 1 )->nCoordinates() < 2 )
276 compoundCurve->removeCurve( compoundCurve->nCurves() - 1 );
277
278 const QgsPoint endPointCircularString = circularString->endPoint();
279 compoundCurve->addCurve( circularString.release() );
280
281 // Prepare a new line string
282 actualLineSize = 1;
283 *outLineX++ = endPointCircularString.x();
284 *outLineY++ = endPointCircularString.y();
285 if ( hasZ )
286 *outLineZ++ = endPointCircularString.z();
287 if ( hasM )
288 *outLineM++ = endPointCircularString.m();
289 }
290 curveListIndex++;
291 }
292
293 if ( actualLineSize == 1 && compoundCurve->nCurves() > 0 )
294 {
295 const QgsCurve *finalCurve = compoundCurve->curveAt( compoundCurve->nCurves() - 1 );
296 const QgsPoint finalCurveEndPoint = finalCurve->endPoint();
297 if ( qgsDoubleNear( finalCurveEndPoint.x(), lineX.at( 0 ) )
298 && qgsDoubleNear( finalCurveEndPoint.y(), lineY.at( 0 ) )
299 && ( !hasZ || qgsDoubleNear( finalCurveEndPoint.z(), lineZ.at( 0 ) ) )
300 && ( !hasM || qgsDoubleNear( finalCurveEndPoint.m(), lineM.at( 0 ) ) ) )
301 {
302 actualLineSize = 0; // redundant final curve containing a duplicate vertex
303 }
304 }
305
306 if ( actualLineSize > 0 )
307 {
308 lineX.resize( actualLineSize );
309 lineY.resize( actualLineSize );
310 if ( hasZ )
311 lineZ.resize( actualLineSize );
312 if ( hasM )
313 lineM.resize( actualLineSize );
314 compoundCurve->addCurve( new QgsLineString( lineX, lineY, lineZ, lineM ) );
315 }
316
317 return compoundCurve;
318}
319
320std::unique_ptr< QgsPoint > QgsArcGisRestUtils::convertGeometryPoint( const QVariantMap &geometryData, Qgis::WkbType pointType )
321{
322 // {"x" : <x>, "y" : <y>, "z" : <z>, "m" : <m>}
323 bool xok = false, yok = false;
324 double x = geometryData[u"x"_s].toDouble( &xok );
325 double y = geometryData[u"y"_s].toDouble( &yok );
326 if ( !xok || !yok )
327 return nullptr;
328 double z = geometryData[u"z"_s].toDouble();
329 double m = geometryData[u"m"_s].toDouble();
330 return std::make_unique< QgsPoint >( pointType, x, y, z, m );
331}
332
333std::unique_ptr< QgsMultiPoint > QgsArcGisRestUtils::convertMultiPoint( const QVariantMap &geometryData, Qgis::WkbType pointType )
334{
335 // {"points" : [[ <x1>, <y1>, <z1>, <m1> ] , [ <x2>, <y2>, <z2>, <m2> ], ... ]}
336 const QVariantList coordsList = geometryData[u"points"_s].toList();
337
338 auto multiPoint = std::make_unique< QgsMultiPoint >();
339 multiPoint->reserve( coordsList.size() );
340 for ( const QVariant &coordData : coordsList )
341 {
342 const QVariantList coordList = coordData.toList();
343 std::unique_ptr< QgsPoint > p = convertPoint( coordList, pointType );
344 if ( !p )
345 {
346 continue;
347 }
348 multiPoint->addGeometry( p.release() );
349 }
350
351 // second chance -- sometimes layers are reported as multipoint but features have single
352 // point geometries. Silently handle this and upgrade to multipoint.
353 std::unique_ptr< QgsPoint > p = convertGeometryPoint( geometryData, pointType );
354 if ( p )
355 multiPoint->addGeometry( p.release() );
356
357 if ( multiPoint->numGeometries() == 0 )
358 {
359 // didn't find any points, so reset geometry to null
360 multiPoint.reset();
361 }
362 return multiPoint;
363}
364
365std::unique_ptr< QgsMultiCurve > QgsArcGisRestUtils::convertGeometryPolyline( const QVariantMap &geometryData, Qgis::WkbType pointType )
366{
367 // {"curvePaths": [[[0,0], {"c": [[3,3],[1,4]]} ]]}
368 QVariantList pathsList;
369 if ( geometryData[u"paths"_s].isValid() )
370 pathsList = geometryData[u"paths"_s].toList();
371 else if ( geometryData[u"curvePaths"_s].isValid() )
372 pathsList = geometryData[u"curvePaths"_s].toList();
373 if ( pathsList.isEmpty() )
374 return nullptr;
375 auto multiCurve = std::make_unique< QgsMultiCurve >();
376 multiCurve->reserve( pathsList.size() );
377 for ( const QVariant &pathData : std::as_const( pathsList ) )
378 {
379 std::unique_ptr< QgsCompoundCurve > curve = convertCompoundCurve( pathData.toList(), pointType );
380 if ( !curve )
381 {
382 return nullptr;
383 }
384 multiCurve->addGeometry( curve.release() );
385 }
386 return multiCurve;
387}
388
389std::unique_ptr< QgsMultiSurface > QgsArcGisRestUtils::convertGeometryPolygon( const QVariantMap &geometryData, Qgis::WkbType pointType )
390{
391 // {"curveRings": [[[0,0], {"c": [[3,3],[1,4]]} ]]}
392 QVariantList ringsList;
393 if ( geometryData[u"rings"_s].isValid() )
394 ringsList = geometryData[u"rings"_s].toList();
395 else if ( geometryData[u"ringPaths"_s].isValid() )
396 ringsList = geometryData[u"ringPaths"_s].toList();
397 if ( ringsList.isEmpty() )
398 return nullptr;
399
400 QList< QgsCompoundCurve * > curves;
401 for ( int i = 0, n = ringsList.size(); i < n; ++i )
402 {
403 std::unique_ptr< QgsCompoundCurve > curve = convertCompoundCurve( ringsList[i].toList(), pointType );
404 if ( !curve )
405 {
406 continue;
407 }
408 curves.append( curve.release() );
409 }
410 if ( curves.count() == 0 )
411 return nullptr;
412
413 auto result = std::make_unique< QgsMultiSurface >();
414 if ( curves.count() == 1 )
415 {
416 // shortcut for exterior ring only
417 auto newPolygon = std::make_unique< QgsCurvePolygon >();
418 newPolygon->setExteriorRing( curves.takeAt( 0 ) );
419 result->addGeometry( newPolygon.release() );
420 return result;
421 }
422
423 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 ); } );
424 result->reserve( curves.size() );
425 while ( !curves.isEmpty() )
426 {
427 QgsCompoundCurve *exterior = curves.takeFirst();
428 QgsCurvePolygon *newPolygon = new QgsCurvePolygon();
429 newPolygon->setExteriorRing( exterior );
430 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( newPolygon ) );
431 engine->prepareGeometry();
432
433 QMutableListIterator< QgsCompoundCurve * > it( curves );
434 while ( it.hasNext() )
435 {
436 QgsCompoundCurve *curve = it.next();
437 QgsRectangle boundingBox = newPolygon->boundingBox();
438 if ( boundingBox.intersects( curve->boundingBox() ) )
439 {
440 QgsPoint point = curve->startPoint();
441 if ( engine->contains( &point ) )
442 {
443 newPolygon->addInteriorRing( curve );
444 it.remove();
445 engine.reset( QgsGeometry::createGeometryEngine( newPolygon ) );
446 engine->prepareGeometry();
447 }
448 }
449 }
450 result->addGeometry( newPolygon );
451 }
452 if ( result->numGeometries() == 0 )
453 return nullptr;
454
455 return result;
456}
457
458std::unique_ptr< QgsPolygon > QgsArcGisRestUtils::convertEnvelope( const QVariantMap &geometryData )
459{
460 // {"xmin" : -109.55, "ymin" : 25.76, "xmax" : -86.39, "ymax" : 49.94}
461 bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false;
462 double xmin = geometryData[u"xmin"_s].toDouble( &xminOk );
463 double ymin = geometryData[u"ymin"_s].toDouble( &yminOk );
464 double xmax = geometryData[u"xmax"_s].toDouble( &xmaxOk );
465 double ymax = geometryData[u"ymax"_s].toDouble( &ymaxOk );
466 if ( !xminOk || !yminOk || !xmaxOk || !ymaxOk )
467 return nullptr;
468 auto ext = std::make_unique< QgsLineString> ();
469 ext->addVertex( QgsPoint( xmin, ymin ) );
470 ext->addVertex( QgsPoint( xmax, ymin ) );
471 ext->addVertex( QgsPoint( xmax, ymax ) );
472 ext->addVertex( QgsPoint( xmin, ymax ) );
473 ext->addVertex( QgsPoint( xmin, ymin ) );
474 auto poly = std::make_unique< QgsPolygon >();
475 poly->setExteriorRing( ext.release() );
476 return poly;
477}
478
479QgsAbstractGeometry *QgsArcGisRestUtils::convertGeometry( const QVariantMap &geometryData, const QString &esriGeometryType, bool readM, bool readZ, QgsCoordinateReferenceSystem *crs )
480{
481 Qgis::WkbType pointType = QgsWkbTypes::zmType( Qgis::WkbType::Point, readZ, readM );
482 if ( crs )
483 {
484 *crs = convertSpatialReference( geometryData[u"spatialReference"_s].toMap() );
485 }
486
487 // http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#/Geometry_Objects/02r3000000n1000000/
488 if ( esriGeometryType == "esriGeometryNull"_L1 )
489 return nullptr;
490 else if ( esriGeometryType == "esriGeometryPoint"_L1 )
491 return convertGeometryPoint( geometryData, pointType ).release();
492 else if ( esriGeometryType == "esriGeometryMultipoint"_L1 )
493 return convertMultiPoint( geometryData, pointType ).release();
494 else if ( esriGeometryType == "esriGeometryPolyline"_L1 )
495 return convertGeometryPolyline( geometryData, pointType ).release();
496 else if ( esriGeometryType == "esriGeometryPolygon"_L1 )
497 return convertGeometryPolygon( geometryData, pointType ).release();
498 else if ( esriGeometryType == "esriGeometryEnvelope"_L1 )
499 return convertEnvelope( geometryData ).release();
500 // Unsupported (either by qgis, or format unspecified by the specification)
501 // esriGeometryCircularArc
502 // esriGeometryEllipticArc
503 // esriGeometryBezier3Curve
504 // esriGeometryPath
505 // esriGeometryRing
506 // esriGeometryLine
507 // esriGeometryAny
508 // esriGeometryMultiPatch
509 // esriGeometryTriangleStrip
510 // esriGeometryTriangleFan
511 // esriGeometryRay
512 // esriGeometrySphere
513 // esriGeometryTriangles
514 // esriGeometryBag
515 return nullptr;
516}
517
519{
521
522 QString spatialReference = spatialReferenceMap[u"latestWkid"_s].toString();
523 if ( spatialReference.isEmpty() )
524 spatialReference = spatialReferenceMap[u"wkid"_s].toString();
525
526 // prefer using authority/id wherever we can
527 if ( !spatialReference.isEmpty() )
528 {
529 crs.createFromString( u"EPSG:%1"_s.arg( spatialReference ) );
530 if ( !crs.isValid() )
531 {
532 // Try as an ESRI auth
533 crs.createFromString( u"ESRI:%1"_s.arg( spatialReference ) );
534 }
535 }
536 else if ( !spatialReferenceMap[u"wkt"_s].toString().isEmpty() )
537 {
538 // otherwise fallback to WKT
539 crs.createFromWkt( spatialReferenceMap[u"wkt"_s].toString() );
540 }
541
542 if ( !crs.isValid() )
543 {
544 // If no spatial reference, just use WGS84
545 // TODO -- this needs further investigation! Most ESRI server services default to 3857, so that would likely be
546 // a safer fallback to use...
547 crs.createFromString( u"EPSG:4326"_s );
548 }
549 return crs;
550}
551
552QgsSymbol *QgsArcGisRestUtils::convertSymbol( const QVariantMap &symbolData )
553{
554 const QString type = symbolData.value( u"type"_s ).toString();
555 if ( type == "esriSMS"_L1 )
556 {
557 // marker symbol
558 return parseEsriMarkerSymbolJson( symbolData ).release();
559 }
560 else if ( type == "esriSLS"_L1 )
561 {
562 // line symbol
563 return parseEsriLineSymbolJson( symbolData ).release();
564 }
565 else if ( type == "esriSFS"_L1 )
566 {
567 // fill symbol
568 return parseEsriFillSymbolJson( symbolData ).release();
569 }
570 else if ( type == "esriPFS"_L1 )
571 {
572 return parseEsriPictureFillSymbolJson( symbolData ).release();
573 }
574 else if ( type == "esriPMS"_L1 )
575 {
576 // picture marker
577 return parseEsriPictureMarkerSymbolJson( symbolData ).release();
578 }
579 else if ( type == "esriTS"_L1 )
580 {
581 return parseEsriTextMarkerSymbolJson( symbolData ).release();
582 }
583 return nullptr;
584}
585
586std::unique_ptr<QgsLineSymbol> QgsArcGisRestUtils::parseEsriLineSymbolJson( const QVariantMap &symbolData )
587{
588 QColor lineColor = convertColor( symbolData.value( u"color"_s ) );
589 if ( !lineColor.isValid() )
590 return nullptr;
591
592 bool ok = false;
593 double widthInPoints = symbolData.value( u"width"_s ).toDouble( &ok );
594 if ( !ok )
595 return nullptr;
596
597 QgsSymbolLayerList layers;
598 Qt::PenStyle penStyle = convertLineStyle( symbolData.value( u"style"_s ).toString() );
599 auto lineLayer = std::make_unique< QgsSimpleLineSymbolLayer >( lineColor, widthInPoints, penStyle );
600 lineLayer->setWidthUnit( Qgis::RenderUnit::Points );
601 layers.append( lineLayer.release() );
602
603 auto symbol = std::make_unique< QgsLineSymbol >( layers );
604 return symbol;
605}
606
607std::unique_ptr<QgsFillSymbol> QgsArcGisRestUtils::parseEsriFillSymbolJson( const QVariantMap &symbolData )
608{
609 QColor fillColor = convertColor( symbolData.value( u"color"_s ) );
610 Qt::BrushStyle brushStyle = convertFillStyle( symbolData.value( u"style"_s ).toString() );
611
612 const QVariantMap outlineData = symbolData.value( u"outline"_s ).toMap();
613 QColor lineColor = convertColor( outlineData.value( u"color"_s ) );
614 Qt::PenStyle penStyle = convertLineStyle( outlineData.value( u"style"_s ).toString() );
615 bool ok = false;
616 double penWidthInPoints = outlineData.value( u"width"_s ).toDouble( &ok );
617
618 QgsSymbolLayerList layers;
619 auto fillLayer = std::make_unique< QgsSimpleFillSymbolLayer >( fillColor, brushStyle, lineColor, penStyle, penWidthInPoints );
620 fillLayer->setStrokeWidthUnit( Qgis::RenderUnit::Points );
621 layers.append( fillLayer.release() );
622
623 auto symbol = std::make_unique< QgsFillSymbol >( layers );
624 return symbol;
625}
626
627std::unique_ptr<QgsFillSymbol> QgsArcGisRestUtils::parseEsriPictureFillSymbolJson( const QVariantMap &symbolData )
628{
629 bool ok = false;
630
631 double widthInPixels = symbolData.value( u"width"_s ).toInt( &ok );
632 if ( !ok )
633 return nullptr;
634
635 const double xScale = symbolData.value( u"xscale"_s ).toDouble( &ok );
636 if ( !qgsDoubleNear( xScale, 0.0 ) )
637 widthInPixels *= xScale;
638
639 const double angleCCW = symbolData.value( u"angle"_s ).toDouble( &ok );
640 double angleCW = 0;
641 if ( ok )
642 angleCW = -angleCCW;
643
644 const double xOffset = symbolData.value( u"xoffset"_s ).toDouble();
645 const double yOffset = symbolData.value( u"yoffset"_s ).toDouble();
646
647 QString symbolPath( symbolData.value( u"imageData"_s ).toString() );
648 symbolPath.prepend( "base64:"_L1 );
649
650 QgsSymbolLayerList layers;
651 auto fillLayer = std::make_unique< QgsRasterFillSymbolLayer >( symbolPath );
652 fillLayer->setWidth( widthInPixels );
653 fillLayer->setAngle( angleCW );
654 fillLayer->setSizeUnit( Qgis::RenderUnit::Points );
655 fillLayer->setOffset( QPointF( xOffset, yOffset ) );
656 fillLayer->setOffsetUnit( Qgis::RenderUnit::Points );
657 layers.append( fillLayer.release() );
658
659 const QVariantMap outlineData = symbolData.value( u"outline"_s ).toMap();
660 QColor lineColor = convertColor( outlineData.value( u"color"_s ) );
661 Qt::PenStyle penStyle = convertLineStyle( outlineData.value( u"style"_s ).toString() );
662 double penWidthInPoints = outlineData.value( u"width"_s ).toDouble( &ok );
663
664 auto lineLayer = std::make_unique< QgsSimpleLineSymbolLayer >( lineColor, penWidthInPoints, penStyle );
665 lineLayer->setWidthUnit( Qgis::RenderUnit::Points );
666 layers.append( lineLayer.release() );
667
668 auto symbol = std::make_unique< QgsFillSymbol >( layers );
669 return symbol;
670}
671
672Qgis::MarkerShape QgsArcGisRestUtils::parseEsriMarkerShape( const QString &style )
673{
674 if ( style == "esriSMSCircle"_L1 )
676 else if ( style == "esriSMSCross"_L1 )
678 else if ( style == "esriSMSDiamond"_L1 )
680 else if ( style == "esriSMSSquare"_L1 )
682 else if ( style == "esriSMSX"_L1 )
684 else if ( style == "esriSMSTriangle"_L1 )
686 else
688}
689
690std::unique_ptr<QgsMarkerSymbol> QgsArcGisRestUtils::parseEsriMarkerSymbolJson( const QVariantMap &symbolData )
691{
692 QColor fillColor = convertColor( symbolData.value( u"color"_s ) );
693 bool ok = false;
694 const double sizeInPoints = symbolData.value( u"size"_s ).toDouble( &ok );
695 if ( !ok )
696 return nullptr;
697 const double angleCCW = symbolData.value( u"angle"_s ).toDouble( &ok );
698 double angleCW = 0;
699 if ( ok )
700 angleCW = -angleCCW;
701
702 Qgis::MarkerShape shape = parseEsriMarkerShape( symbolData.value( u"style"_s ).toString() );
703
704 const double xOffset = symbolData.value( u"xoffset"_s ).toDouble();
705 const double yOffset = symbolData.value( u"yoffset"_s ).toDouble();
706
707 const QVariantMap outlineData = symbolData.value( u"outline"_s ).toMap();
708 QColor lineColor = convertColor( outlineData.value( u"color"_s ) );
709 Qt::PenStyle penStyle = convertLineStyle( outlineData.value( u"style"_s ).toString() );
710 double penWidthInPoints = outlineData.value( u"width"_s ).toDouble( &ok );
711
712 QgsSymbolLayerList layers;
713 auto markerLayer = std::make_unique< QgsSimpleMarkerSymbolLayer >( shape, sizeInPoints, angleCW, Qgis::ScaleMethod::ScaleArea, fillColor, lineColor );
714 markerLayer->setSizeUnit( Qgis::RenderUnit::Points );
715 markerLayer->setStrokeWidthUnit( Qgis::RenderUnit::Points );
716 markerLayer->setStrokeStyle( penStyle );
717 markerLayer->setStrokeWidth( penWidthInPoints );
718 markerLayer->setOffset( QPointF( xOffset, yOffset ) );
719 markerLayer->setOffsetUnit( Qgis::RenderUnit::Points );
720 layers.append( markerLayer.release() );
721
722 auto symbol = std::make_unique< QgsMarkerSymbol >( layers );
723 return symbol;
724}
725
726std::unique_ptr<QgsMarkerSymbol> QgsArcGisRestUtils::parseEsriPictureMarkerSymbolJson( const QVariantMap &symbolData )
727{
728 bool ok = false;
729 const double widthInPixels = symbolData.value( u"width"_s ).toInt( &ok );
730 if ( !ok )
731 return nullptr;
732 const double heightInPixels = symbolData.value( u"height"_s ).toInt( &ok );
733 if ( !ok )
734 return nullptr;
735
736 const double angleCCW = symbolData.value( u"angle"_s ).toDouble( &ok );
737 double angleCW = 0;
738 if ( ok )
739 angleCW = -angleCCW;
740
741 const double xOffset = symbolData.value( u"xoffset"_s ).toDouble();
742 const double yOffset = symbolData.value( u"yoffset"_s ).toDouble();
743
744 //const QString contentType = symbolData.value( u"contentType"_s ).toString();
745
746 QString symbolPath( symbolData.value( u"imageData"_s ).toString() );
747 symbolPath.prepend( "base64:"_L1 );
748
749 QgsSymbolLayerList layers;
750 auto markerLayer = std::make_unique< QgsRasterMarkerSymbolLayer >( symbolPath, widthInPixels, angleCW, Qgis::ScaleMethod::ScaleArea );
751 markerLayer->setSizeUnit( Qgis::RenderUnit::Points );
752
753 // only change the default aspect ratio if the server height setting requires this
754 if ( !qgsDoubleNear( static_cast< double >( heightInPixels ) / widthInPixels, markerLayer->defaultAspectRatio() ) )
755 markerLayer->setFixedAspectRatio( static_cast< double >( heightInPixels ) / widthInPixels );
756
757 markerLayer->setOffset( QPointF( xOffset, yOffset ) );
758 markerLayer->setOffsetUnit( Qgis::RenderUnit::Points );
759 layers.append( markerLayer.release() );
760
761 auto symbol = std::make_unique< QgsMarkerSymbol >( layers );
762 return symbol;
763}
764
765std::unique_ptr<QgsMarkerSymbol> QgsArcGisRestUtils::parseEsriTextMarkerSymbolJson( const QVariantMap &symbolData )
766{
767 QgsSymbolLayerList layers;
768
769 const QString fontFamily = symbolData.value( u"font"_s ).toMap().value( u"family"_s ).toString();
770
771 const QString chr = symbolData.value( u"text"_s ).toString();
772
773 const double pointSize = symbolData.value( u"font"_s ).toMap().value( u"size"_s ).toDouble();
774
775 const QColor color = convertColor( symbolData.value( u"color"_s ) );
776
777 const double esriAngle = symbolData.value( u"angle"_s ).toDouble();
778
779 const double angle = 90.0 - esriAngle;
780
781 auto markerLayer = std::make_unique< QgsFontMarkerSymbolLayer >( fontFamily, chr, pointSize, color, angle );
782
783 QColor strokeColor = convertColor( symbolData.value( u"borderLineColor"_s ) );
784 markerLayer->setStrokeColor( strokeColor );
785
786 double borderLineSize = symbolData.value( u"borderLineSize"_s ).toDouble();
787 markerLayer->setStrokeWidth( borderLineSize );
788
789 const QString fontStyle = symbolData.value( u"font"_s ).toMap().value( u"style"_s ).toString();
790 markerLayer->setFontStyle( fontStyle );
791
792 double xOffset = symbolData.value( u"xoffset"_s ).toDouble();
793 double yOffset = symbolData.value( u"yoffset"_s ).toDouble();
794
795 markerLayer->setOffset( QPointF( xOffset, yOffset ) );
796 markerLayer->setOffsetUnit( Qgis::RenderUnit::Points );
797
798 markerLayer->setSizeUnit( Qgis::RenderUnit::Points );
799 markerLayer->setStrokeWidthUnit( Qgis::RenderUnit::Points );
800
803
804 QString horizontalAnchorPoint = symbolData.value( u"horizontalAlignment"_s ).toString();
805 QString verticalAnchorPoint = symbolData.value( u"verticalAlignment"_s ).toString();
806
807 if ( horizontalAnchorPoint == QString( "center" ) )
808 {
810 }
811 else if ( horizontalAnchorPoint == QString( "left" ) )
812 {
814 }
815 else if ( horizontalAnchorPoint == QString( "right" ) )
816 {
818 }
819
820 if ( verticalAnchorPoint == QString( "center" ) )
821 {
823 }
824 else if ( verticalAnchorPoint == QString( "top" ) )
825 {
827 }
828 else if ( verticalAnchorPoint == QString( "bottom" ) )
829 {
831 }
832
833 markerLayer->setHorizontalAnchorPoint( hAlign );
834 markerLayer->setVerticalAnchorPoint( vAlign );
835
836 layers.append( markerLayer.release() );
837
838 auto symbol = std::make_unique< QgsMarkerSymbol >( layers );
839 return symbol;
840}
841
843{
844 if ( labelingData.empty() )
845 return nullptr;
846
847 QgsRuleBasedLabeling::Rule *root = new QgsRuleBasedLabeling::Rule( new QgsPalLayerSettings(), 0, 0, QString(), QString(), false );
848 root->setActive( true );
849
850 int i = 1;
851 for ( const QVariant &lbl : labelingData )
852 {
853 const QVariantMap labeling = lbl.toMap();
854
856 QgsTextFormat format;
857
858 const QString placement = labeling.value( u"labelPlacement"_s ).toString();
859 if ( placement == "esriServerPointLabelPlacementAboveCenter"_L1 )
860 {
863 }
864 else if ( placement == "esriServerPointLabelPlacementBelowCenter"_L1 )
865 {
868 }
869 else if ( placement == "esriServerPointLabelPlacementCenterCenter"_L1 )
870 {
873 }
874 else if ( placement == "esriServerPointLabelPlacementAboveLeft"_L1 )
875 {
878 }
879 else if ( placement == "esriServerPointLabelPlacementBelowLeft"_L1 )
880 {
883 }
884 else if ( placement == "esriServerPointLabelPlacementCenterLeft"_L1 )
885 {
888 }
889 else if ( placement == "esriServerPointLabelPlacementAboveRight"_L1 )
890 {
893 }
894 else if ( placement == "esriServerPointLabelPlacementBelowRight"_L1 )
895 {
898 }
899 else if ( placement == "esriServerPointLabelPlacementCenterRight"_L1 )
900 {
903 }
904 else if ( placement == "esriServerLinePlacementAboveAfter"_L1 ||
905 placement == "esriServerLinePlacementAboveStart"_L1 ||
906 placement == "esriServerLinePlacementAboveAlong"_L1 )
907 {
910 }
911 else if ( placement == "esriServerLinePlacementBelowAfter"_L1 ||
912 placement == "esriServerLinePlacementBelowStart"_L1 ||
913 placement == "esriServerLinePlacementBelowAlong"_L1 )
914 {
917 }
918 else if ( placement == "esriServerLinePlacementCenterAfter"_L1 ||
919 placement == "esriServerLinePlacementCenterStart"_L1 ||
920 placement == "esriServerLinePlacementCenterAlong"_L1 )
921 {
924 }
925 else if ( placement == "esriServerPolygonPlacementAlwaysHorizontal"_L1 )
926 {
928 }
929
930 const double minScale = labeling.value( u"minScale"_s ).toDouble();
931 const double maxScale = labeling.value( u"maxScale"_s ).toDouble();
932
933 QVariantMap symbol = labeling.value( u"symbol"_s ).toMap();
934 format.setColor( convertColor( symbol.value( u"color"_s ) ) );
935 const double haloSize = symbol.value( u"haloSize"_s ).toDouble();
936 if ( !qgsDoubleNear( haloSize, 0.0 ) )
937 {
939 buffer.setEnabled( true );
940 buffer.setSize( haloSize );
942 buffer.setColor( convertColor( symbol.value( u"haloColor"_s ) ) );
943 format.setBuffer( buffer );
944 }
945
946 const QString fontFamily = symbol.value( u"font"_s ).toMap().value( u"family"_s ).toString();
947 const QString fontStyle = symbol.value( u"font"_s ).toMap().value( u"style"_s ).toString();
948 const QString fontWeight = symbol.value( u"font"_s ).toMap().value( u"weight"_s ).toString();
949 const int fontSize = symbol.value( u"font"_s ).toMap().value( u"size"_s ).toInt();
950 QFont font( fontFamily, fontSize );
951 font.setStyleName( fontStyle );
952 font.setWeight( fontWeight == "bold"_L1 ? QFont::Bold : QFont::Normal );
953
954 format.setFont( font );
955 format.setSize( fontSize );
957
958 settings->setFormat( format );
959
960 QString where = labeling.value( u"where"_s ).toString();
961 QgsExpression exp( where );
962 // If the where clause isn't parsed as valid, don't use its
963 if ( !exp.isValid() )
964 where.clear();
965
966 settings->fieldName = convertLabelingExpression( labeling.value( u"labelExpression"_s ).toString() );
967 settings->isExpression = true;
968
969 QgsRuleBasedLabeling::Rule *child = new QgsRuleBasedLabeling::Rule( settings, maxScale, minScale, where, QObject::tr( "ASF label %1" ).arg( i++ ), false );
970 child->setActive( true );
971 root->appendChild( child );
972 }
973
974 return new QgsRuleBasedLabeling( root );
975}
976
978{
979 const QString type = rendererData.value( u"type"_s ).toString();
980 if ( type == "simple"_L1 )
981 {
982 const QVariantMap symbolProps = rendererData.value( u"symbol"_s ).toMap();
983 std::unique_ptr< QgsSymbol > symbol( convertSymbol( symbolProps ) );
984 if ( symbol )
985 return new QgsSingleSymbolRenderer( symbol.release() );
986 else
987 return nullptr;
988 }
989 else if ( type == "uniqueValue"_L1 )
990 {
991 const QString field1 = rendererData.value( u"field1"_s ).toString();
992 const QString field2 = rendererData.value( u"field2"_s ).toString();
993 const QString field3 = rendererData.value( u"field3"_s ).toString();
994 QString attribute;
995 if ( !field2.isEmpty() || !field3.isEmpty() )
996 {
997 const QString delimiter = rendererData.value( u"fieldDelimiter"_s ).toString();
998 if ( !field3.isEmpty() )
999 {
1000 attribute = u"concat(\"%1\",'%2',\"%3\",'%4',\"%5\")"_s.arg( field1, delimiter, field2, delimiter, field3 );
1001 }
1002 else
1003 {
1004 attribute = u"concat(\"%1\",'%2',\"%3\")"_s.arg( field1, delimiter, field2 );
1005 }
1006 }
1007 else
1008 {
1009 attribute = field1;
1010 }
1011
1012 const QVariantList categories = rendererData.value( u"uniqueValueInfos"_s ).toList();
1013 QgsCategoryList categoryList;
1014 for ( const QVariant &category : categories )
1015 {
1016 const QVariantMap categoryData = category.toMap();
1017 const QString value = categoryData.value( u"value"_s ).toString();
1018 const QString label = categoryData.value( u"label"_s ).toString();
1019 std::unique_ptr< QgsSymbol > symbol( QgsArcGisRestUtils::convertSymbol( categoryData.value( u"symbol"_s ).toMap() ) );
1020 if ( symbol )
1021 {
1022 categoryList.append( QgsRendererCategory( value, symbol.release(), label ) );
1023 }
1024 }
1025
1026 std::unique_ptr< QgsSymbol > defaultSymbol( convertSymbol( rendererData.value( u"defaultSymbol"_s ).toMap() ) );
1027 if ( defaultSymbol )
1028 {
1029 categoryList.append( QgsRendererCategory( QVariant(), defaultSymbol.release(), rendererData.value( u"defaultLabel"_s ).toString() ) );
1030 }
1031
1032 if ( categoryList.empty() )
1033 return nullptr;
1034
1035 return new QgsCategorizedSymbolRenderer( attribute, categoryList );
1036 }
1037 else if ( type == "classBreaks"_L1 )
1038 {
1039 const QString attrName = rendererData.value( u"field"_s ).toString();
1040
1041 const QVariantList classBreakInfos = rendererData.value( u"classBreakInfos"_s ).toList();
1042 const QVariantMap authoringInfo = rendererData.value( u"authoringInfo"_s ).toMap();
1043 QVariantMap symbolData;
1044
1045 QString esriMode = authoringInfo.value( u"classificationMethod"_s ).toString();
1046 if ( esriMode.isEmpty() )
1047 {
1048 esriMode = rendererData.value( u"classificationMethod"_s ).toString();
1049 }
1050
1051 if ( !classBreakInfos.isEmpty() )
1052 {
1053 symbolData = classBreakInfos.at( 0 ).toMap().value( u"symbol"_s ).toMap();
1054 }
1055 std::unique_ptr< QgsSymbol > symbol( QgsArcGisRestUtils::convertSymbol( symbolData ) );
1056 if ( !symbol )
1057 return nullptr;
1058
1059 const double transparency = rendererData.value( u"transparency"_s ).toDouble();
1060 const double opacity = ( 100.0 - transparency ) / 100.0;
1061 symbol->setOpacity( opacity );
1062
1063 const QVariantList visualVariablesData = rendererData.value( u"visualVariables"_s ).toList();
1064
1065 for ( const QVariant &visualVariable : visualVariablesData )
1066 {
1067 const QVariantMap visualVariableData = visualVariable.toMap();
1068 const QString variableType = visualVariableData.value( u"type"_s ).toString();
1069 if ( variableType == "sizeInfo"_L1 )
1070 {
1071 continue;
1072 }
1073 else if ( variableType == "colorInfo"_L1 )
1074 {
1075 const QVariantList stops = visualVariableData.value( u"stops"_s ).toList();
1076 if ( stops.size() < 2 )
1077 continue;
1078
1079 // layer has continuous coloring, so convert to a symbol using color ramp assistant
1080 bool ok = false;
1081 const double minValue = stops.front().toMap().value( u"value"_s ).toDouble( &ok );
1082 if ( !ok )
1083 continue;
1084 const QColor minColor = convertColor( stops.front().toMap().value( u"color"_s ) );
1085
1086 const double maxValue = stops.back().toMap().value( u"value"_s ).toDouble( &ok );
1087 if ( !ok )
1088 continue;
1089 const QColor maxColor = convertColor( stops.back().toMap().value( u"color"_s ) );
1090
1091 QgsGradientStopsList gradientStops;
1092 for ( int i = 1; i < stops.size() - 1; ++i )
1093 {
1094 const QVariantMap stopData = stops.at( i ).toMap();
1095 const double breakpoint = stopData.value( u"value"_s ).toDouble();
1096 const double scaledBreakpoint = ( breakpoint - minValue ) / ( maxValue - minValue );
1097 const QColor fillColor = convertColor( stopData.value( u"color"_s ) );
1098
1099 gradientStops.append( QgsGradientStop( scaledBreakpoint, fillColor ) );
1100 }
1101
1102 auto colorRamp = std::make_unique< QgsGradientColorRamp >(
1103 minColor, maxColor, false, gradientStops
1104 );
1105
1106 QgsProperty colorProperty = QgsProperty::fromField( attrName );
1107 colorProperty.setTransformer(
1108 new QgsColorRampTransformer( minValue, maxValue, colorRamp.release() )
1109 );
1110 for ( int layer = 0; layer < symbol->symbolLayerCount(); ++layer )
1111 {
1112 symbol->symbolLayer( layer )->setDataDefinedProperty( QgsSymbolLayer::Property::FillColor, colorProperty );
1113 }
1114
1115 auto singleSymbolRenderer = std::make_unique< QgsSingleSymbolRenderer >( symbol.release() );
1116
1117 return singleSymbolRenderer.release();
1118 }
1119 else
1120 {
1121 QgsDebugError( u"ESRI visualVariable type %1 is not currently supported"_s.arg( variableType ) );
1122 }
1123 }
1124
1125 double lastValue = rendererData.value( u"minValue"_s ).toDouble();
1126
1127 auto graduatedRenderer = std::make_unique< QgsGraduatedSymbolRenderer >( attrName );
1128
1129 graduatedRenderer->setSourceSymbol( symbol.release() );
1130
1131 if ( esriMode == "esriClassifyDefinedInterval"_L1 )
1132 {
1134 graduatedRenderer->setClassificationMethod( method );
1135 }
1136 else if ( esriMode == "esriClassifyEqualInterval"_L1 )
1137 {
1139 graduatedRenderer->setClassificationMethod( method );
1140 }
1141 else if ( esriMode == "esriClassifyGeometricalInterval"_L1 )
1142 {
1144 graduatedRenderer->setClassificationMethod( method );
1145 }
1146 else if ( esriMode == "esriClassifyManual"_L1 )
1147 {
1149 graduatedRenderer->setClassificationMethod( method );
1150 }
1151 else if ( esriMode == "esriClassifyNaturalBreaks"_L1 )
1152 {
1154 graduatedRenderer->setClassificationMethod( method );
1155 }
1156 else if ( esriMode == "esriClassifyQuantile"_L1 )
1157 {
1159 graduatedRenderer->setClassificationMethod( method );
1160 }
1161 else if ( esriMode == "esriClassifyStandardDeviation"_L1 )
1162 {
1164 graduatedRenderer->setClassificationMethod( method );
1165 }
1166 else if ( !esriMode.isEmpty() )
1167 {
1168 QgsDebugError( u"ESRI classification mode %1 is not currently supported"_s.arg( esriMode ) );
1169 }
1170
1171 for ( const QVariant &classBreakInfo : classBreakInfos )
1172 {
1173 const QVariantMap symbolData = classBreakInfo.toMap().value( u"symbol"_s ).toMap();
1174 std::unique_ptr< QgsSymbol > symbol( QgsArcGisRestUtils::convertSymbol( symbolData ) );
1175 double classMaxValue = classBreakInfo.toMap().value( u"classMaxValue"_s ).toDouble();
1176 const QString label = classBreakInfo.toMap().value( u"label"_s ).toString();
1177
1178 QgsRendererRange range;
1179
1180 range.setLowerValue( lastValue );
1181 range.setUpperValue( classMaxValue );
1182 range.setLabel( label );
1183 range.setSymbol( symbol.release() );
1184
1185 lastValue = classMaxValue;
1186 graduatedRenderer->addClass( range );
1187 }
1188
1189 return graduatedRenderer.release();
1190 }
1191 else if ( type == "heatmap"_L1 )
1192 {
1193 // currently unsupported
1194 return nullptr;
1195 }
1196 else if ( type == "vectorField"_L1 )
1197 {
1198 // currently unsupported
1199 return nullptr;
1200 }
1201 return nullptr;
1202}
1203
1205{
1206 QString expression = string;
1207
1208 // Replace a few ArcGIS token to QGIS equivalents
1209 const thread_local QRegularExpression rx1 = QRegularExpression( u"(?=([^\"\\\\]*(\\\\.|\"([^\"\\\\]*\\\\.)*[^\"\\\\]*\"))*[^\"]*$)(\\s|^)CONCAT(\\s|$)"_s );
1210 expression = expression.replace( rx1, u"\\4||\\5"_s );
1211
1212 const thread_local QRegularExpression rx2 = QRegularExpression( u"(?=([^\"\\\\]*(\\\\.|\"([^\"\\\\]*\\\\.)*[^\"\\\\]*\"))*[^\"]*$)(\\s|^)NEWLINE(\\s|$)"_s );
1213 expression = expression.replace( rx2, u"\\4'\\n'\\5"_s );
1214
1215 // ArcGIS's double quotes are single quotes in QGIS
1216 const thread_local QRegularExpression rx3 = QRegularExpression( u"\"(.*?(?<!\\\\))\""_s );
1217 expression = expression.replace( rx3, u"'\\1'"_s );
1218 const thread_local QRegularExpression rx4 = QRegularExpression( u"\\\\\""_s );
1219 expression = expression.replace( rx4, u"\""_s );
1220
1221 // ArcGIS's square brakets are double quotes in QGIS
1222 const thread_local QRegularExpression rx5 = QRegularExpression( u"\\[([^]]*)\\]"_s );
1223 expression = expression.replace( rx5, u"\"\\1\""_s );
1224
1225 return expression;
1226}
1227
1228QColor QgsArcGisRestUtils::convertColor( const QVariant &colorData )
1229{
1230 const QVariantList colorParts = colorData.toList();
1231 if ( colorParts.count() < 4 )
1232 return QColor();
1233
1234 int red = colorParts.at( 0 ).toInt();
1235 int green = colorParts.at( 1 ).toInt();
1236 int blue = colorParts.at( 2 ).toInt();
1237 int alpha = colorParts.at( 3 ).toInt();
1238 return QColor( red, green, blue, alpha );
1239}
1240
1241Qt::PenStyle QgsArcGisRestUtils::convertLineStyle( const QString &style )
1242{
1243 if ( style == "esriSLSSolid"_L1 )
1244 return Qt::SolidLine;
1245 else if ( style == "esriSLSDash"_L1 )
1246 return Qt::DashLine;
1247 else if ( style == "esriSLSDashDot"_L1 )
1248 return Qt::DashDotLine;
1249 else if ( style == "esriSLSDashDotDot"_L1 )
1250 return Qt::DashDotDotLine;
1251 else if ( style == "esriSLSDot"_L1 )
1252 return Qt::DotLine;
1253 else if ( style == "esriSLSNull"_L1 )
1254 return Qt::NoPen;
1255 else
1256 return Qt::SolidLine;
1257}
1258
1259Qt::BrushStyle QgsArcGisRestUtils::convertFillStyle( const QString &style )
1260{
1261 if ( style == "esriSFSBackwardDiagonal"_L1 )
1262 return Qt::BDiagPattern;
1263 else if ( style == "esriSFSCross"_L1 )
1264 return Qt::CrossPattern;
1265 else if ( style == "esriSFSDiagonalCross"_L1 )
1266 return Qt::DiagCrossPattern;
1267 else if ( style == "esriSFSForwardDiagonal"_L1 )
1268 return Qt::FDiagPattern;
1269 else if ( style == "esriSFSHorizontal"_L1 )
1270 return Qt::HorPattern;
1271 else if ( style == "esriSFSNull"_L1 )
1272 return Qt::NoBrush;
1273 else if ( style == "esriSFSSolid"_L1 )
1274 return Qt::SolidPattern;
1275 else if ( style == "esriSFSVertical"_L1 )
1276 return Qt::VerPattern;
1277 else
1278 return Qt::SolidPattern;
1279}
1280
1281QDateTime QgsArcGisRestUtils::convertDateTime( const QVariant &value )
1282{
1283 if ( QgsVariantUtils::isNull( value ) )
1284 return QDateTime();
1285 bool ok = false;
1286 QDateTime dt = QDateTime::fromMSecsSinceEpoch( value.toLongLong( &ok ) );
1287 if ( !ok )
1288 {
1289 QgsDebugError( u"Invalid value %1 for datetime"_s.arg( value.toString() ) );
1290 return QDateTime();
1291 }
1292 else
1293 return dt;
1294}
1295
1297{
1298 if ( QgsVariantUtils::isNull( value ) )
1299 return QgsRectangle();
1300
1301 const QVariantMap coords = value.toMap();
1302 if ( coords.isEmpty() ) return QgsRectangle();
1303
1304 bool ok;
1305
1306 const double xmin = coords.value( u"xmin"_s ).toDouble( &ok );
1307 if ( ! ok ) return QgsRectangle();
1308
1309 const double ymin = coords.value( u"ymin"_s ).toDouble( &ok );
1310 if ( ! ok ) return QgsRectangle();
1311
1312 const double xmax = coords.value( u"xmax"_s ).toDouble( &ok );
1313 if ( ! ok ) return QgsRectangle();
1314
1315 const double ymax = coords.value( u"ymax"_s ).toDouble( &ok );
1316 if ( ! ok ) return QgsRectangle();
1317
1318 return QgsRectangle( xmin, ymin, xmax, ymax );
1319
1320}
1321
1322
1324{
1325 QVariantMap res;
1326 if ( geometry.isNull() )
1327 return QVariantMap();
1328
1329 const QgsAbstractGeometry *geom = geometry.constGet()->simplifiedTypeRef();
1330 switch ( QgsWkbTypes::flatType( geom->wkbType() ) )
1331 {
1334 return QVariantMap();
1335
1337 res = pointToJson( qgsgeometry_cast< const QgsPoint * >( geom ) );
1338 break;
1339
1341 res = lineStringToJson( qgsgeometry_cast< const QgsLineString * >( geom ) );
1342 break;
1343
1346 res = curveToJson( qgsgeometry_cast< const QgsCurve * >( geom ) );
1347 break;
1348
1350 res = polygonToJson( qgsgeometry_cast< const QgsPolygon * >( geom ) );
1351 break;
1352
1354 res = multiPointToJson( qgsgeometry_cast< const QgsMultiPoint * >( geom ) );
1355 break;
1356
1358 res = multiLineStringToJson( qgsgeometry_cast< const QgsMultiLineString * >( geom ) );
1359 break;
1360
1362 res = multiCurveToJson( qgsgeometry_cast< const QgsMultiCurve * >( geom ) );
1363 break;
1364
1366 res = multiPolygonToJson( qgsgeometry_cast< const QgsMultiPolygon * >( geom ) );
1367 break;
1368
1370 res = curvePolygonToJson( qgsgeometry_cast< const QgsCurvePolygon * >( geom ) );
1371 break;
1372
1374 res = multiSurfaceToJson( qgsgeometry_cast< const QgsMultiSurface * >( geom ) );
1375 break;
1376
1378 return QVariantMap(); // not supported by REST API
1379
1381 return QVariantMap(); //not yet supported, but could be
1382
1383 default:
1384 return QVariantMap(); //unreachable
1385
1386 }
1387
1388 if ( crs.isValid() )
1389 {
1390 // add spatialReference information
1391 res.insert( u"spatialReference"_s, crsToJson( crs ) );
1392 }
1393
1394 return res;
1395}
1396
1397QVariantMap QgsArcGisRestUtils::pointToJson( const QgsPoint *point )
1398{
1399 QVariantMap data;
1400 if ( point->isEmpty() )
1401 data[u"x"_s] = u"NaN"_s;
1402 else
1403 {
1404 data[u"x"_s] = point->x();
1405 data[u"y"_s] = point->y();
1406
1407 if ( point->is3D() )
1408 data[u"z"_s] = !std::isnan( point->z() ) ? QVariant( point->z() ) : QVariant( u"NaN"_s );
1409
1410 if ( point->isMeasure() )
1411 data[u"m"_s] = !std::isnan( point->m() ) ? QVariant( point->m() ) : QVariant( u"NaN"_s );
1412 }
1413 return data;
1414}
1415
1416QVariantMap QgsArcGisRestUtils::multiPointToJson( const QgsMultiPoint *multiPoint )
1417{
1418 QVariantMap data;
1419 const bool hasZ = multiPoint->is3D();
1420 const bool hasM = multiPoint->isMeasure();
1421 data[u"hasM"_s] = hasM;
1422 data[u"hasZ"_s] = hasZ;
1423
1424 QVariantList pointsList;
1425 const int size = multiPoint->numGeometries();
1426 pointsList.reserve( size );
1427
1428 QVariantList pointList;
1429 for ( int i = 0; i < size; ++i )
1430 {
1431 const QgsPoint *point = multiPoint->pointN( i );
1432
1433 pointList.clear();
1434 pointList.append( point->x() );
1435 pointList.append( point->y() );
1436 if ( hasZ )
1437 pointList.append( point->z() );
1438 if ( hasM && !std::isnan( point->m() ) )
1439 pointList.append( point->m() );
1440
1441 pointsList.push_back( pointList );
1442 }
1443
1444 data[u"points"_s] = pointsList;
1445 return data;
1446}
1447
1448QVariantList QgsArcGisRestUtils::lineStringToJsonPath( const QgsLineString *line )
1449{
1450 const bool hasZ = line->is3D();
1451 const bool hasM = line->isMeasure();
1452
1453 QVariantList pointsList;
1454 const int size = line->numPoints();
1455 pointsList.reserve( size );
1456
1457 QVariantList pointList;
1458 const double *xData = line->xData();
1459 const double *yData = line->yData();
1460 const double *zData = hasZ ? line->zData() : nullptr;
1461 const double *mData = hasM ? line->mData() : nullptr;
1462
1463 for ( int i = 0; i < size; ++i )
1464 {
1465 pointList.clear();
1466 pointList.append( *xData++ );
1467 pointList.append( *yData++ );
1468
1469 if ( hasZ )
1470 pointList.append( *zData++ );
1471
1472 if ( hasM && !std::isnan( *mData ) )
1473 pointList.append( *mData );
1474 if ( hasM )
1475 mData++;
1476
1477 pointsList.push_back( pointList );
1478 }
1479 return pointsList;
1480}
1481
1482QVariantList QgsArcGisRestUtils::curveToJsonCurve( const QgsCurve *curve, bool includeStart )
1483{
1484 const bool hasZ = curve->is3D();
1485 const bool hasM = curve->isMeasure();
1486
1487 auto pointToList = [hasZ, hasM]( const QgsPoint & point ) -> QVariantList
1488 {
1489 QVariantList pointList;
1490
1491 pointList.append( point.x() );
1492 pointList.append( point.y() );
1493
1494 if ( hasZ )
1495 pointList.append( point.z() );
1496
1497 if ( hasM && !std::isnan( point.m() ) )
1498 pointList.append( point.m() );
1499
1500 return pointList;
1501 };
1502
1503 QVariantList res;
1504 switch ( QgsWkbTypes::flatType( curve->wkbType() ) )
1505 {
1507 {
1508 QVariantList part = lineStringToJsonPath( qgsgeometry_cast< const QgsLineString *>( curve ) );
1509 if ( !part.isEmpty() && !includeStart )
1510 part.removeAt( 0 );
1511 res = part;
1512 break;
1513 }
1514
1516 {
1517 const QgsCircularString *circularString = qgsgeometry_cast<const QgsCircularString * >( curve );
1518 if ( includeStart && !circularString->isEmpty() )
1519 {
1520 res.push_back( pointToList( circularString->startPoint() ) );
1521 }
1522
1523 const int size = circularString->numPoints();
1524 for ( int i = 1; i + 1 < size; i += 2 )
1525 {
1526 // end point comes BEFORE interior point!
1527 QVariantMap curvePart;
1528 QVariantList curveList;
1529 curveList.push_back( pointToList( circularString->pointN( i + 1 ) ) );
1530
1531 curveList.push_back( pointToList( circularString->pointN( i ) ) );
1532
1533 curvePart.insert( u"c"_s, curveList );
1534 res.push_back( curvePart );
1535 }
1536 break;
1537 }
1538
1540 {
1541 const QgsCompoundCurve *compoundCurve = qgsgeometry_cast<const QgsCompoundCurve * >( curve );
1542
1543 const int size = compoundCurve->nCurves();
1544 for ( int i = 0; i < size; ++i )
1545 {
1546 const QgsCurve *subCurve = compoundCurve->curveAt( i );
1547 res.append( curveToJsonCurve( subCurve, i == 0 ) );
1548 }
1549 break;
1550 }
1551
1552 default:
1553 break;
1554 }
1555 return res;
1556}
1557
1558QVariantMap QgsArcGisRestUtils::lineStringToJson( const QgsLineString *line )
1559{
1560 QVariantMap data;
1561 const bool hasZ = line->is3D();
1562 const bool hasM = line->isMeasure();
1563 data[u"hasM"_s] = hasM;
1564 data[u"hasZ"_s] = hasZ;
1565
1566 const QVariantList pointsList = lineStringToJsonPath( line );
1567
1568 QVariantList pointsData = QVariantList();
1569 pointsData.push_back( pointsList );
1570 data[u"paths"_s] = pointsData;
1571
1572 return data;
1573}
1574
1575QVariantMap QgsArcGisRestUtils::curveToJson( const QgsCurve *curve )
1576{
1577 QVariantMap data;
1578 const bool hasZ = curve->is3D();
1579 const bool hasM = curve->isMeasure();
1580 data[u"hasM"_s] = hasM;
1581 data[u"hasZ"_s] = hasZ;
1582
1583 const QVariantList curveList = curveToJsonCurve( curve, true );
1584
1585 QVariantList curveData = QVariantList();
1586 curveData.push_back( curveList );
1587 data[u"curvePaths"_s] = curveData;
1588
1589 return data;
1590}
1591
1592QVariantMap QgsArcGisRestUtils::multiLineStringToJson( const QgsMultiLineString *multiLine )
1593{
1594 QVariantMap data;
1595 const bool hasZ = multiLine->is3D();
1596 const bool hasM = multiLine->isMeasure();
1597 data[u"hasM"_s] = hasM;
1598 data[u"hasZ"_s] = hasZ;
1599
1600 const int size = multiLine->numGeometries();
1601 QVariantList paths;
1602 paths.reserve( size );
1603 for ( int i = 0; i < size; ++i )
1604 {
1605 const QgsLineString *line = multiLine->lineStringN( i );
1606 paths.push_back( lineStringToJsonPath( line ) );
1607 }
1608
1609 data[u"paths"_s] = paths;
1610 return data;
1611}
1612
1613QVariantMap QgsArcGisRestUtils::multiCurveToJson( const QgsMultiCurve *multiCurve )
1614{
1615 QVariantMap data;
1616 const bool hasZ = multiCurve->is3D();
1617 const bool hasM = multiCurve->isMeasure();
1618 data[u"hasM"_s] = hasM;
1619 data[u"hasZ"_s] = hasZ;
1620
1621 const int size = multiCurve->numGeometries();
1622 QVariantList paths;
1623 paths.reserve( size );
1624 for ( int i = 0; i < size; ++i )
1625 {
1626 const QgsCurve *curve = multiCurve->curveN( i );
1627 paths.push_back( curveToJsonCurve( curve, true ) );
1628 }
1629
1630 data[u"curvePaths"_s] = paths;
1631 return data;
1632}
1633
1634QVariantList QgsArcGisRestUtils::polygonToJsonRings( const QgsPolygon *polygon )
1635{
1636 QVariantList rings;
1637 const int numInteriorRings = polygon->numInteriorRings();
1638 rings.reserve( numInteriorRings + 1 );
1639
1640 if ( const QgsLineString *exterior = qgsgeometry_cast< const QgsLineString * >( polygon->exteriorRing() ) )
1641 {
1642 // exterior ring MUST be clockwise
1643 switch ( exterior->orientation() )
1644 {
1646 rings.push_back( lineStringToJsonPath( exterior ) );
1647 break;
1648
1650 {
1651 std::unique_ptr< QgsLineString > reversed( exterior->reversed() );
1652 rings.push_back( lineStringToJsonPath( reversed.get() ) );
1653 break;
1654 }
1656 break;
1657 }
1658 }
1659
1660 for ( int i = 0; i < numInteriorRings; ++i )
1661 {
1662 const QgsLineString *ring = qgsgeometry_cast< const QgsLineString * >( polygon->interiorRing( i ) );
1663 // holes MUST be counter-clockwise
1664 switch ( ring->orientation() )
1665 {
1667 rings.push_back( lineStringToJsonPath( ring ) );
1668 break;
1669
1671 {
1672 std::unique_ptr< QgsLineString > reversed( ring->reversed() );
1673 rings.push_back( lineStringToJsonPath( reversed.get() ) );
1674 break;
1675 }
1677 break;
1678 }
1679 }
1680 return rings;
1681}
1682
1683QVariantList QgsArcGisRestUtils::curvePolygonToJsonRings( const QgsCurvePolygon *polygon )
1684{
1685 QVariantList rings;
1686 const int numInteriorRings = polygon->numInteriorRings();
1687 rings.reserve( numInteriorRings + 1 );
1688
1689 if ( const QgsCurve *exterior = qgsgeometry_cast< const QgsCurve * >( polygon->exteriorRing() ) )
1690 {
1691 // exterior ring MUST be clockwise
1692 switch ( exterior->orientation() )
1693 {
1695 rings.push_back( curveToJsonCurve( exterior, true ) );
1696 break;
1697
1699 {
1700 std::unique_ptr< QgsCurve > reversed( exterior->reversed() );
1701 rings.push_back( curveToJsonCurve( reversed.get(), true ) );
1702 break;
1703 }
1705 break;
1706 }
1707 }
1708
1709 for ( int i = 0; i < numInteriorRings; ++i )
1710 {
1711 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( polygon->interiorRing( i ) );
1712 // holes MUST be counter-clockwise
1713 switch ( ring->orientation() )
1714 {
1716 rings.push_back( curveToJsonCurve( ring, true ) );
1717 break;
1718
1720 {
1721 std::unique_ptr< QgsCurve > reversed( ring->reversed() );
1722 rings.push_back( curveToJsonCurve( reversed.get(), true ) );
1723 break;
1724 }
1726 break;
1727 }
1728 }
1729 return rings;
1730}
1731
1732QVariantMap QgsArcGisRestUtils::polygonToJson( const QgsPolygon *polygon )
1733{
1734 QVariantMap data;
1735 const bool hasZ = polygon->is3D();
1736 const bool hasM = polygon->isMeasure();
1737 data[u"hasM"_s] = hasM;
1738 data[u"hasZ"_s] = hasZ;
1739 data[u"rings"_s] = polygonToJsonRings( polygon );
1740 return data;
1741}
1742
1743QVariantMap QgsArcGisRestUtils::curvePolygonToJson( const QgsCurvePolygon *polygon )
1744{
1745 QVariantMap data;
1746 const bool hasZ = polygon->is3D();
1747 const bool hasM = polygon->isMeasure();
1748 data[u"hasM"_s] = hasM;
1749 data[u"hasZ"_s] = hasZ;
1750 data[u"curveRings"_s] = curvePolygonToJsonRings( polygon );
1751 return data;
1752}
1753
1754QVariantMap QgsArcGisRestUtils::multiPolygonToJson( const QgsMultiPolygon *multiPolygon )
1755{
1756 QVariantMap data;
1757 const bool hasZ = multiPolygon->is3D();
1758 const bool hasM = multiPolygon->isMeasure();
1759 data[u"hasM"_s] = hasM;
1760 data[u"hasZ"_s] = hasZ;
1761
1762 const int size = multiPolygon->numGeometries();
1763 QVariantList rings;
1764 for ( int i = 0; i < size; ++i )
1765 {
1766 const QgsPolygon *polygon = multiPolygon->polygonN( i );
1767 rings.append( polygonToJsonRings( polygon ) );
1768 }
1769
1770 data[u"rings"_s] = rings;
1771 return data;
1772}
1773
1774QVariantMap QgsArcGisRestUtils::multiSurfaceToJson( const QgsMultiSurface *multiSurface )
1775{
1776 QVariantMap data;
1777 const bool hasZ = multiSurface->is3D();
1778 const bool hasM = multiSurface->isMeasure();
1779 data[u"hasM"_s] = hasM;
1780 data[u"hasZ"_s] = hasZ;
1781
1782 const int size = multiSurface->numGeometries();
1783 QVariantList rings;
1784 for ( int i = 0; i < size; ++i )
1785 {
1786 const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( multiSurface->geometryN( i ) );
1787 if ( !polygon )
1788 continue;
1789
1790 rings.append( curvePolygonToJsonRings( polygon ) );
1791 }
1792
1793 data[u"curveRings"_s] = rings;
1794 return data;
1795}
1796
1798{
1799 QVariantMap res;
1800 if ( !crs.isValid() )
1801 return res;
1802
1803 const QString authid = crs.authid();
1804 if ( !authid.isEmpty() )
1805 {
1806 const thread_local QRegularExpression rxAuthid( u"(\\w+):(\\d+)"_s );
1807 const QRegularExpressionMatch match = rxAuthid.match( authid );
1808 if ( match.hasMatch()
1809 && (
1810 ( match.captured( 1 ).compare( "EPSG"_L1, Qt::CaseInsensitive ) == 0 )
1811 || ( match.captured( 1 ).compare( "ESRI"_L1, Qt::CaseInsensitive ) == 0 )
1812 )
1813 )
1814 {
1815 const QString wkid = match.captured( 2 );
1816 res.insert( u"wkid"_s, wkid );
1817 return res;
1818 }
1819 }
1820
1821 // docs don't mention the WKT version support, so let's hope for 2.0...
1822 res.insert( u"wkt"_s, crs.toWkt( Qgis::CrsWktVariant::Wkt2_2019Simplified ) );
1823
1824 return res;
1825}
1826
1828{
1829 QVariantMap res;
1830 if ( ( flags & FeatureToJsonFlag::IncludeGeometry ) && feature.hasGeometry() )
1831 {
1832 res.insert( u"geometry"_s, geometryToJson( feature.geometry(), context, crs ) );
1833 }
1834
1835 QVariantMap attributes;
1836 const QgsFields fields = feature.fields();
1837 for ( const QgsField &field : fields )
1838 {
1839 QVariant value = feature.attribute( field.name() );
1840 if ( value.userType() == qMetaTypeId< QgsUnsetAttributeValue >() )
1841 {
1842 if ( flags.testFlag( FeatureToJsonFlag::SkipUnsetAttributes ) )
1843 continue;
1844 else
1845 value = QVariant(); // reset to null, we can't store 'QgsUnsetAttributeValue' as json
1846 }
1847
1848 if ( ( flags & FeatureToJsonFlag::IncludeNonObjectIdAttributes ) || field.name() == context.objectIdFieldName() )
1849 attributes.insert( field.name(), variantToAttributeValue( value, field.type(), context ) );
1850 }
1851 if ( !attributes.isEmpty() )
1852 {
1853 res.insert( u"attributes"_s, attributes );
1854 }
1855 return res;
1856}
1857
1858QVariant QgsArcGisRestUtils::variantToAttributeValue( const QVariant &variant, QMetaType::Type expectedType, const QgsArcGisRestContext &context )
1859{
1860 if ( QgsVariantUtils::isNull( variant ) )
1861 return QVariant();
1862
1863 switch ( expectedType )
1864 {
1865 case QMetaType::Type::QString:
1866 {
1867 const QString escaped = variant.toString().replace( '\\', "\\\\"_L1 ).replace( '"', "\\\""_L1 );
1868 return QString( QUrl::toPercentEncoding( escaped, "'" ) );
1869 }
1870
1871 case QMetaType::Type::QDateTime:
1872 case QMetaType::Type::QDate:
1873 {
1874 switch ( variant.userType() )
1875 {
1876 case QMetaType::Type::QDateTime:
1877 return variant.toDateTime().toMSecsSinceEpoch();
1878
1879 case QMetaType::Type::QDate:
1880 // for date values, assume start of day -- the REST api requires datetime values only, not plain dates
1881 if ( context.timeZone().isValid() )
1882 return QDateTime( variant.toDate(), QTime( 0, 0, 0 ), context.timeZone() ).toMSecsSinceEpoch();
1883 else
1884 return QDateTime( variant.toDate(), QTime( 0, 0, 0 ) ).toMSecsSinceEpoch();
1885
1886 default:
1887 return QVariant();
1888 }
1889 }
1890
1891 default:
1892 return variant;
1893 }
1894}
1895
1897{
1898 QVariantMap res;
1899 res.insert( u"name"_s, field.name() );
1900
1901 QString fieldType;
1902 switch ( field.type() )
1903 {
1904 case QMetaType::Type::LongLong:
1905 fieldType = u"esriFieldTypeInteger"_s;
1906 break;
1907
1908 case QMetaType::Type::Int:
1909 fieldType = u"esriFieldTypeSmallInteger"_s;
1910 break;
1911
1912 case QMetaType::Type::Double:
1913 fieldType = u"esriFieldTypeDouble"_s;
1914 break;
1915
1916 case QMetaType::Type::QString:
1917 fieldType = u"esriFieldTypeString"_s;
1918 break;
1919
1920 case QMetaType::Type::QDateTime:
1921 case QMetaType::Type::QDate:
1922 fieldType = u"esriFieldTypeDate"_s;
1923 break;
1924
1925 case QMetaType::Type::QByteArray:
1926 fieldType = u"esriFieldTypeBlob"_s;
1927 break;
1928
1929 default:
1930 // fallback to string
1931 fieldType = u"esriFieldTypeString"_s;
1932 break;
1933 }
1934 res.insert( u"type"_s, fieldType );
1935
1936 if ( !field.alias().isEmpty() )
1937 res.insert( u"alias"_s, field.alias() );
1938
1939 // nullable
1941 res.insert( u"nullable"_s, !notNullable );
1942
1943 // editable
1944 res.insert( u"editable"_s, true );
1945
1946 return res;
1947}
1948
1950{
1951 if ( type.compare( "FeatureServer"_L1, Qt::CaseInsensitive ) == 0 )
1953 else if ( type.compare( "MapServer"_L1, Qt::CaseInsensitive ) == 0 )
1955 else if ( type.compare( "ImageServer"_L1, Qt::CaseInsensitive ) == 0 )
1957 else if ( type.compare( "GlobeServer"_L1, Qt::CaseInsensitive ) == 0 )
1959 else if ( type.compare( "GPServer"_L1, Qt::CaseInsensitive ) == 0 )
1961 else if ( type.compare( "GeocodeServer"_L1, Qt::CaseInsensitive ) == 0 )
1963 else if ( type.compare( "SceneServer"_L1, Qt::CaseInsensitive ) == 0 )
1965
1967}
1968
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1336
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
Definition qgis.h:1337
@ OnLine
Labels can be placed directly over a line feature.
Definition qgis.h:1334
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1335
@ NoOrientation
Unknown orientation or sentinel value.
Definition qgis.h:3494
@ CounterClockwise
Counter-clockwise direction.
Definition qgis.h:3493
@ Clockwise
Clockwise direction.
Definition qgis.h:3492
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
Definition qgis.h:1227
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
Definition qgis.h:1228
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
Definition qgis.h:1230
@ ScaleArea
Calculate scale by the area.
Definition qgis.h:644
ArcGisRestServiceType
Available ArcGIS REST service types.
Definition qgis.h:4430
@ GeocodeServer
GeocodeServer.
Definition qgis.h:4436
@ SceneServer
SceneServer.
Definition qgis.h:4438
@ Unknown
Other unknown/unsupported type.
Definition qgis.h:4437
@ GlobeServer
GlobeServer.
Definition qgis.h:4434
@ ImageServer
ImageServer.
Definition qgis.h:4433
@ FeatureServer
FeatureServer.
Definition qgis.h:4431
MarkerShape
Marker shapes.
Definition qgis.h:3139
@ Circle
Circle.
Definition qgis.h:3148
@ Triangle
Triangle.
Definition qgis.h:3144
@ Cross2
Rotated cross (lines only), 'x' shape.
Definition qgis.h:3151
@ Diamond
Diamond.
Definition qgis.h:3141
@ Square
Square.
Definition qgis.h:3140
@ Cross
Cross (lines only).
Definition qgis.h:3149
@ AboveRight
Above right.
Definition qgis.h:1316
@ BelowLeft
Below left.
Definition qgis.h:1320
@ Above
Above center.
Definition qgis.h:1315
@ BelowRight
Below right.
Definition qgis.h:1322
@ Right
Right middle.
Definition qgis.h:1319
@ AboveLeft
Above left.
Definition qgis.h:1314
@ Below
Below center.
Definition qgis.h:1321
@ Over
Center middle.
Definition qgis.h:1318
VerticalAnchorPoint
Marker symbol vertical anchor points.
Definition qgis.h:833
@ Bottom
Align to bottom of symbol.
Definition qgis.h:836
@ Center
Align to vertical center of symbol.
Definition qgis.h:835
@ Top
Align to top of symbol.
Definition qgis.h:834
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5260
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
@ CompoundCurve
CompoundCurve.
Definition qgis.h:291
@ Point
Point.
Definition qgis.h:282
@ LineString
LineString.
Definition qgis.h:283
@ MultiPoint
MultiPoint.
Definition qgis.h:286
@ Polygon
Polygon.
Definition qgis.h:284
@ MultiPolygon
MultiPolygon.
Definition qgis.h:288
@ Triangle
Triangle.
Definition qgis.h:285
@ NoGeometry
No geometry.
Definition qgis.h:298
@ MultiLineString
MultiLineString.
Definition qgis.h:287
@ Unknown
Unknown.
Definition qgis.h:281
@ CircularString
CircularString.
Definition qgis.h:290
@ GeometryCollection
GeometryCollection.
Definition qgis.h:289
@ MultiCurve
MultiCurve.
Definition qgis.h:293
@ CurvePolygon
CurvePolygon.
Definition qgis.h:292
@ MultiSurface
MultiSurface.
Definition qgis.h:294
@ Wkt2_2019Simplified
WKT2_2019 with the simplification rule of WKT2_SIMPLIFIED.
Definition qgis.h:2496
HorizontalAnchorPoint
Marker symbol horizontal anchor points.
Definition qgis.h:819
@ Center
Align to horizontal center of symbol.
Definition qgis.h:821
@ Right
Align to right side of symbol.
Definition qgis.h:822
@ Left
Align to left side of symbol.
Definition qgis.h:820
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 QVariant variantToAttributeValue(const QVariant &variant, QMetaType::Type expectedType, const QgsArcGisRestContext &context)
Converts a variant to a REST attribute value.
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 QVariantMap featureToJson(const QgsFeature &feature, const QgsArcGisRestContext &context, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem(), QgsArcGisRestUtils::FeatureToJsonFlags flags=QgsArcGisRestUtils::FeatureToJsonFlags(static_cast< int >(QgsArcGisRestUtils::FeatureToJsonFlag::IncludeGeometry)|static_cast< int >(QgsArcGisRestUtils::FeatureToJsonFlag::IncludeNonObjectIdAttributes)))
Converts a feature to an ArcGIS REST JSON representation.
static Qt::PenStyle convertLineStyle(const QString &style)
Converts an ESRI line style to a Qt pen style.
@ IncludeGeometry
Whether to include the geometry definition.
@ IncludeNonObjectIdAttributes
Whether to include any non-objectId attributes.
static QgsFeatureRenderer * convertRenderer(const QVariantMap &rendererData)
Converts renderer JSON data to an equivalent QgsFeatureRenderer.
static QMetaType::Type convertFieldType(const QString &type)
Converts an ESRI REST field type to a QVariant type.
static Qt::BrushStyle convertFillStyle(const QString &style)
Converts an ESRI fill style to a Qt brush style.
static QVariantMap crsToJson(const QgsCoordinateReferenceSystem &crs)
Converts a crs to an ArcGIS REST JSON representation.
QFlags< FeatureToJsonFlag > FeatureToJsonFlags
Flags which control the behavior of converting features to JSON.
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.
A feature renderer which represents features using a list of renderer categories.
QgsPoint startPoint() const override
Returns the starting point of the curve.
bool isEmpty() const override
Returns true if the geometry is empty.
int numPoints() const override
Returns the number of points in the curve.
QgsPoint pointN(int i) const
Returns the point at index i within the circular string.
A dummy implementation class method which does not compute any breaks.
A classification method which uses equal width intervals.
Implementation of a fixed interval classification.
A classification method for natural breaks, based on Jenks method.
A classification method which creates classes based on quantiles.
A classification method which classifies based on standard deviation of values.
QgsPropertyTransformer subclass for transforming a numeric value into a color from a color ramp.
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.
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:36
Qgis::AngularDirection orientation() const
Returns the curve's orientation, e.g.
Definition qgscurve.cpp:287
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool isValid() const
Checks if this expression is valid.
Abstract base class for all 2D vector feature renderers.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFields fields
Definition qgsfeature.h:70
QgsGeometry geometry
Definition qgsfeature.h:71
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
@ ConstraintNotNull
Field may not be null.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QMetaType::Type type
Definition qgsfield.h:63
QString name
Definition qgsfield.h:65
QString alias
Definition qgsfield.h:66
QgsFieldConstraints constraints
Definition qgsfield.h:68
Container of fields for a vector layer.
Definition qgsfields.h:46
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
Represents a color stop within a QgsGradientColorRamp color ramp.
void setPlacementFlags(Qgis::LabelLinePlacementFlags flags)
Returns the line placement flags, which dictate how line labels can be placed above or below the line...
void setQuadrant(Qgis::LabelQuadrantPosition quadrant)
Sets the quadrant in which to offset labels from the point.
Line string geometry type, with support for z-dimension and m-values.
const double * yData() const
Returns a const pointer to the y vertex data.
const double * xData() const
Returns a const pointer to the x vertex data.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the linestring does not have z values.
int numPoints() const override
Returns the number of points in the curve.
QgsLineString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the linestring does not have m values.
Multi curve geometry collection.
QgsCurve * curveN(int index)
Returns the curve with the specified index.
Multi line string geometry collection.
QgsLineString * lineStringN(int index)
Returns the line string with the specified index.
Multi point geometry collection.
QgsPoint * pointN(int index)
Returns the point with the specified index.
Multi polygon geometry collection.
QgsPolygon * polygonN(int index)
Returns the polygon with the specified index.
Multi surface geometry collection.
Contains settings for how a map layer will be labeled.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
Qgis::LabelPlacement placement
Label placement mode.
bool isExpression
true if this label is made from a expression string, e.g., FieldName || 'mm'
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
QString fieldName
Name of field (or an expression) to use for label text.
const QgsLabelPointSettings & pointSettings() const
Returns the label point settings, which contain settings related to how the label engine places and f...
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
void clear() override
Clears the geometry, ie reset it to a null geometry.
Definition qgspoint.cpp:367
double z
Definition qgspoint.h:58
double x
Definition qgspoint.h:56
bool isEmpty() const override
Returns true if the geometry is empty.
Definition qgspoint.cpp:742
double m
Definition qgspoint.h:59
double y
Definition qgspoint.h:57
Polygon geometry type.
Definition qgspolygon.h:37
A store for object properties.
void setTransformer(QgsPropertyTransformer *transformer)
Sets an optional transformer to use for manipulating the calculated values for the property.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
A 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.
Represents a value range for a QgsGraduatedSymbolRenderer.
void setUpperValue(double upperValue)
Sets the upper bound of the range.
void setSymbol(QgsSymbol *s)
Sets the symbol used for the range.
void setLabel(const QString &label)
Sets the label used for the range.
void setLowerValue(double lowerValue)
Sets the lower bound of the range.
A child rule for QgsRuleBasedLabeling.
void setActive(bool state)
Sets if this rule is active.
void appendChild(QgsRuleBasedLabeling::Rule *rule)
add child rule, take ownership, sets this as parent
Rule based labeling for a vector layer.
A feature renderer which renders all features with the same symbol.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:231
Container for settings relating to a text buffer.
void setColor(const QColor &color)
Sets the color for the buffer.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units used for the buffer size.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
void setSize(double size)
Sets the size of the buffer.
Container for all settings relating to text rendering.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setSize(double size)
Sets the size for rendered text.
void setFont(const QFont &font)
Sets the font used for rendering text.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the size of rendered text.
void setBuffer(const QgsTextBufferSettings &bufferSettings)
Sets the text's buffer settings.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static Qgis::WkbType zmType(Qgis::WkbType type, bool hasZ, bool hasM)
Returns the modified input geometry type according to hasZ / hasM.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6900
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QList< QgsRendererCategory > QgsCategoryList
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
#define QgsDebugError(str)
Definition qgslogger.h:59
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition qgssymbol.h:30