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