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