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