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