Skip to content
Permalink
Browse files

[FEATURE][afs] Automatically set default style for layers

to match the server's rendering information.

Means that when an AFS layer is loaded into QGIS, it will
automatically have the same style applied as has been created
for that layer, matching the appearance of the layer when it
is loaded into ArcGIS.

Fixes #13349
  • Loading branch information
nyalldawson committed Mar 1, 2018
1 parent 678f658 commit 6002cc400a9246dc521b9ae4445dfd3e65447b06
@@ -5,6 +5,7 @@ INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/src/core/geometry
${CMAKE_SOURCE_DIR}/src/core/raster
${CMAKE_SOURCE_DIR}/src/core/metadata
${CMAKE_SOURCE_DIR}/src/core/symbology
${CMAKE_SOURCE_DIR}/src/gui
${CMAKE_SOURCE_DIR}/src/gui/auth
${CMAKE_BINARY_DIR}/src/ui
@@ -28,6 +28,9 @@
#include "qgssourceselectprovider.h"
#endif

#include "qgssinglesymbolrenderer.h"
#include "qgscategorizedsymbolrenderer.h"

#include <QEventLoop>
#include <QMessageBox>
#include <QNetworkRequest>
@@ -51,7 +54,7 @@ QgsAfsProvider::QgsAfsProvider( const QString &uri )

// Get layer info
QString errorTitle, errorMessage;
QVariantMap layerData = QgsArcGisRestUtils::getLayerInfo( mSharedData->mDataSource.param( QStringLiteral( "url" ) ), errorTitle, errorMessage );
const QVariantMap layerData = QgsArcGisRestUtils::getLayerInfo( mSharedData->mDataSource.param( QStringLiteral( "url" ) ), errorTitle, errorMessage );
if ( layerData.isEmpty() )
{
pushError( errorTitle + ": " + errorMessage );
@@ -74,7 +77,7 @@ QgsAfsProvider::QgsAfsProvider( const QString &uri )
mSharedData->mExtent = QgsRectangle();
}

QVariantMap layerExtentMap = layerData[QStringLiteral( "extent" )].toMap();
const QVariantMap layerExtentMap = layerData[QStringLiteral( "extent" )].toMap();
bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false;
QgsRectangle originalExtent;
originalExtent.setXMinimum( layerExtentMap[QStringLiteral( "xmin" )].toDouble( &xminOk ) );
@@ -208,6 +211,9 @@ QgsAfsProvider::QgsAfsProvider( const QString &uri )
mLayerMetadata.setRights( QStringList() << copyright );
mLayerMetadata.addLink( QgsLayerMetadata::Link( tr( "Source" ), QStringLiteral( "WWW:LINK" ), mSharedData->mDataSource.param( QStringLiteral( "url" ) ) ) );

// renderer
mRendererDataMap = layerData.value( QStringLiteral( "drawingInfo" ) ).toMap().value( QStringLiteral( "renderer" ) ).toMap();

mValid = true;
}

@@ -243,7 +249,12 @@ QgsLayerMetadata QgsAfsProvider::layerMetadata() const

QgsVectorDataProvider::Capabilities QgsAfsProvider::capabilities() const
{
return QgsVectorDataProvider::SelectAtId | QgsVectorDataProvider::ReadLayerMetadata;
QgsVectorDataProvider::Capabilities c = QgsVectorDataProvider::SelectAtId | QgsVectorDataProvider::ReadLayerMetadata;
if ( !mRendererDataMap.empty() )
{
c = c | QgsVectorDataProvider::CreateRenderer;
}
return c;
}

void QgsAfsProvider::setDataSourceUri( const QString &uri )
@@ -282,6 +293,65 @@ void QgsAfsProvider::reloadData()
mSharedData->clearCache();
}

QgsFeatureRenderer *QgsAfsProvider::createRenderer( const QVariantMap & ) const
{
const QString type = mRendererDataMap.value( QStringLiteral( "type" ) ).toString();
if ( type == QLatin1String( "simple" ) )
{
const QVariantMap symbolProps = mRendererDataMap.value( QStringLiteral( "symbol" ) ).toMap();
std::unique_ptr< QgsSymbol > symbol = QgsArcGisRestUtils::parseEsriSymbolJson( symbolProps );
if ( symbol )
return new QgsSingleSymbolRenderer( symbol.release() );
else
return nullptr;
}
else if ( type == QLatin1String( "uniqueValue" ) )
{
const QString attribute = mRendererDataMap.value( QStringLiteral( "field1" ) ).toString();
// TODO - handle field2, field3
const QVariantList categories = mRendererDataMap.value( QStringLiteral( "uniqueValueInfos" ) ).toList();
QgsCategoryList categoryList;
for ( const QVariant &category : categories )
{
const QVariantMap categoryData = category.toMap();
const QString value = categoryData.value( QStringLiteral( "value" ) ).toString();
const QString label = categoryData.value( QStringLiteral( "label" ) ).toString();
std::unique_ptr< QgsSymbol > symbol = QgsArcGisRestUtils::parseEsriSymbolJson( categoryData.value( QStringLiteral( "symbol" ) ).toMap() );
if ( symbol )
{
categoryList.append( QgsRendererCategory( value, symbol.release(), label ) );
}
}

std::unique_ptr< QgsSymbol > defaultSymbol = QgsArcGisRestUtils::parseEsriSymbolJson( mRendererDataMap.value( QStringLiteral( "defaultSymbol" ) ).toMap() );
if ( defaultSymbol )
{
categoryList.append( QgsRendererCategory( QVariant(), defaultSymbol.release(), mRendererDataMap.value( QStringLiteral( "defaultLabel" ) ).toString() ) );
}

if ( categoryList.empty() )
return nullptr;

return new QgsCategorizedSymbolRenderer( attribute, categoryList );
}
else if ( type == QLatin1String( "classBreaks" ) )
{
// currently unsupported
return nullptr;
}
else if ( type == QLatin1String( "heatmap" ) )
{
// currently unsupported
return nullptr;
}
else if ( type == QLatin1String( "vectorField" ) )
{
// currently unsupported
return nullptr;
}
return nullptr;
}


#ifdef HAVE_GUI

@@ -70,6 +70,7 @@ class QgsAfsProvider : public QgsVectorDataProvider
QString description() const override;
QString dataComment() const override;
void reloadData() override;
QgsFeatureRenderer *createRenderer( const QVariantMap &configuration = QVariantMap() ) const override;

private:
bool mValid;
@@ -78,6 +79,7 @@ class QgsAfsProvider : public QgsVectorDataProvider
QString mLayerName;
QString mLayerDescription;
QgsLayerMetadata mLayerMetadata;
QVariantMap mRendererDataMap;
};

#endif // QGSAFSPROVIDER_H
@@ -28,7 +28,11 @@
#include "geometry/qgspolygon.h"
#include "geometry/qgspoint.h"
#include "qgsfeedback.h"

#include "qgssymbol.h"
#include "qgssymbollayer.h"
#include "qgslinesymbollayer.h"
#include "qgsfillsymbollayer.h"
#include "qgsmarkersymbollayer.h"
#include <QEventLoop>
#include <QNetworkRequest>
#include <QNetworkReply>
@@ -506,6 +510,190 @@ QVariantMap QgsArcGisRestUtils::queryServiceJSON( const QUrl &url, QString &erro
return doc.object().toVariantMap();
}

std::unique_ptr<QgsSymbol> QgsArcGisRestUtils::parseEsriSymbolJson( const QVariantMap &symbolData )
{
const QString type = symbolData.value( QStringLiteral( "type" ) ).toString();
if ( type == QLatin1String( "esriSMS" ) )
{
// marker symbol
return parseEsriMarkerSymbolJson( symbolData );
}
else if ( type == QLatin1String( "esriSLS" ) )
{
// line symbol
return parseEsriLineSymbolJson( symbolData );
}
else if ( type == QLatin1String( "esriSFS" ) )
{
// fill symbol
return parseEsriFillSymbolJson( symbolData );
}
else if ( type == QLatin1String( "esriPFS" ) )
{
// picture fill - not supported
return nullptr;
}
else if ( type == QLatin1String( "esriPMS" ) )
{
// picture marker - not supported
return nullptr;
}
else if ( type == QLatin1String( "esriTS" ) )
{
// text symbol - not supported
return nullptr;
}
return nullptr;
}

std::unique_ptr<QgsLineSymbol> QgsArcGisRestUtils::parseEsriLineSymbolJson( const QVariantMap &symbolData )
{
QColor lineColor = parseEsriColorJson( symbolData.value( QStringLiteral( "color" ) ) );
if ( !lineColor.isValid() )
return nullptr;

bool ok = false;
double widthInPoints = symbolData.value( QStringLiteral( "width" ) ).toDouble( &ok );
if ( !ok )
return nullptr;

QgsSymbolLayerList layers;
Qt::PenStyle penStyle = parseEsriLineStyle( symbolData.value( QStringLiteral( "style" ) ).toString() );
std::unique_ptr< QgsSimpleLineSymbolLayer > lineLayer = qgis::make_unique< QgsSimpleLineSymbolLayer >( lineColor, widthInPoints, penStyle );
lineLayer->setWidthUnit( QgsUnitTypes::RenderPoints );
layers.append( lineLayer.release() );

std::unique_ptr< QgsLineSymbol > symbol = qgis::make_unique< QgsLineSymbol >( layers );
return symbol;
}

std::unique_ptr<QgsFillSymbol> QgsArcGisRestUtils::parseEsriFillSymbolJson( const QVariantMap &symbolData )
{
QColor fillColor = parseEsriColorJson( symbolData.value( QStringLiteral( "color" ) ) );
Qt::BrushStyle brushStyle = parseEsriFillStyle( symbolData.value( QStringLiteral( "style" ) ).toString() );

const QVariantMap outlineData = symbolData.value( QStringLiteral( "outline" ) ).toMap();
QColor lineColor = parseEsriColorJson( outlineData.value( QStringLiteral( "color" ) ) );
Qt::PenStyle penStyle = parseEsriLineStyle( outlineData.value( QStringLiteral( "style" ) ).toString() );
bool ok = false;
double penWidthInPoints = outlineData.value( QStringLiteral( "width" ) ).toDouble( &ok );

QgsSymbolLayerList layers;
std::unique_ptr< QgsSimpleFillSymbolLayer > fillLayer = qgis::make_unique< QgsSimpleFillSymbolLayer >( fillColor, brushStyle, lineColor, penStyle, penWidthInPoints );
fillLayer->setStrokeWidthUnit( QgsUnitTypes::RenderPoints );
layers.append( fillLayer.release() );

std::unique_ptr< QgsFillSymbol > symbol = qgis::make_unique< QgsFillSymbol >( layers );
return symbol;
}

QgsSimpleMarkerSymbolLayerBase::Shape parseEsriMarkerShape( const QString &style )
{
if ( style == QLatin1String( "esriSMSCircle" ) )
return QgsSimpleMarkerSymbolLayerBase::Circle;
else if ( style == QLatin1String( "esriSMSCross" ) )
return QgsSimpleMarkerSymbolLayerBase::Cross;
else if ( style == QLatin1String( "esriSMSDiamond" ) )
return QgsSimpleMarkerSymbolLayerBase::Diamond;
else if ( style == QLatin1String( "esriSMSSquare" ) )
return QgsSimpleMarkerSymbolLayerBase::Square;
else if ( style == QLatin1String( "esriSMSX" ) )
return QgsSimpleMarkerSymbolLayerBase::Cross2;
else if ( style == QLatin1String( "esriSMSTriangle" ) )
return QgsSimpleMarkerSymbolLayerBase::Triangle;
else
return QgsSimpleMarkerSymbolLayerBase::Circle;
}

std::unique_ptr<QgsMarkerSymbol> QgsArcGisRestUtils::parseEsriMarkerSymbolJson( const QVariantMap &symbolData )
{
QColor fillColor = parseEsriColorJson( symbolData.value( QStringLiteral( "color" ) ) );
bool ok = false;
const double sizeInPoints = symbolData.value( QStringLiteral( "size" ) ).toDouble( &ok );
if ( !ok )
return nullptr;
const double angleCCW = symbolData.value( QStringLiteral( "angle" ) ).toDouble( &ok );
double angleCW = 0;
if ( ok )
angleCW = -angleCCW;

QgsSimpleMarkerSymbolLayerBase::Shape shape = parseEsriMarkerShape( symbolData.value( QStringLiteral( "style" ) ).toString() );

const double xOffset = symbolData.value( QStringLiteral( "xoffset" ) ).toDouble();
const double yOffset = symbolData.value( QStringLiteral( "yoffset" ) ).toDouble();

const QVariantMap outlineData = symbolData.value( QStringLiteral( "outline" ) ).toMap();
QColor lineColor = parseEsriColorJson( outlineData.value( QStringLiteral( "color" ) ) );
Qt::PenStyle penStyle = parseEsriLineStyle( outlineData.value( QStringLiteral( "style" ) ).toString() );
double penWidthInPoints = outlineData.value( QStringLiteral( "width" ) ).toDouble( &ok );

QgsSymbolLayerList layers;
std::unique_ptr< QgsSimpleMarkerSymbolLayer > markerLayer = qgis::make_unique< QgsSimpleMarkerSymbolLayer >( shape, sizeInPoints, angleCW, QgsSymbol::ScaleArea, fillColor, lineColor );
markerLayer->setSizeUnit( QgsUnitTypes::RenderPoints );
markerLayer->setStrokeWidthUnit( QgsUnitTypes::RenderPoints );
markerLayer->setStrokeStyle( penStyle );
markerLayer->setStrokeWidth( penWidthInPoints );
markerLayer->setOffset( QPointF( xOffset, yOffset ) );
markerLayer->setOffsetUnit( QgsUnitTypes::RenderPoints );
layers.append( markerLayer.release() );

std::unique_ptr< QgsMarkerSymbol > symbol = qgis::make_unique< QgsMarkerSymbol >( layers );
return symbol;
}

QColor QgsArcGisRestUtils::parseEsriColorJson( const QVariant &colorData )
{
const QVariantList colorParts = colorData.toList();
if ( colorParts.count() < 4 )
return QColor();

int red = colorParts.at( 0 ).toInt();
int green = colorParts.at( 1 ).toInt();
int blue = colorParts.at( 2 ).toInt();
int alpha = colorParts.at( 3 ).toInt();
return QColor( red, green, blue, alpha );
}

Qt::PenStyle QgsArcGisRestUtils::parseEsriLineStyle( const QString &style )
{
if ( style == QLatin1String( "esriSLSSolid" ) )
return Qt::SolidLine;
else if ( style == QLatin1String( "esriSLSDash" ) )
return Qt::DashLine;
else if ( style == QLatin1String( "esriSLSDashDot" ) )
return Qt::DashDotLine;
else if ( style == QLatin1String( "esriSLSDashDotDot" ) )
return Qt::DashDotDotLine;
else if ( style == QLatin1String( "esriSLSDot" ) )
return Qt::DotLine;
else if ( style == QLatin1String( "esriSLSNull" ) )
return Qt::NoPen;
else
return Qt::SolidLine;
}

Qt::BrushStyle QgsArcGisRestUtils::parseEsriFillStyle( const QString &style )
{
if ( style == QLatin1String( "esriSFSBackwardDiagonal" ) )
return Qt::BDiagPattern;
else if ( style == QLatin1String( "esriSFSCross" ) )
return Qt::CrossPattern;
else if ( style == QLatin1String( "esriSFSDiagonalCross" ) )
return Qt::DiagCrossPattern;
else if ( style == QLatin1String( "esriSFSForwardDiagonal" ) )
return Qt::FDiagPattern;
else if ( style == QLatin1String( "esriSFSHorizontal" ) )
return Qt::HorPattern;
else if ( style == QLatin1String( "esriSFSNull" ) )
return Qt::NoBrush;
else if ( style == QLatin1String( "esriSFSSolid" ) )
return Qt::SolidPattern;
else if ( style == QLatin1String( "esriSFSVertical" ) )
return Qt::VerPattern;
else
return Qt::SolidPattern;
}

QUrl QgsArcGisRestUtils::parseUrl( const QUrl &url )
{
QUrl modifiedUrl( url );
@@ -27,6 +27,10 @@ class QgsRectangle;
class QgsAbstractGeometry;
class QgsCoordinateReferenceSystem;
class QgsFeedback;
class QgsSymbol;
class QgsLineSymbol;
class QgsFillSymbol;
class QgsMarkerSymbol;

class QgsArcGisRestUtils
{
@@ -46,6 +50,15 @@ class QgsArcGisRestUtils
static QByteArray queryService( const QUrl &url, QString &errorTitle, QString &errorText, QgsFeedback *feedback = nullptr );
static QVariantMap queryServiceJSON( const QUrl &url, QString &errorTitle, QString &errorText, QgsFeedback *feedback = nullptr );

static std::unique_ptr< QgsSymbol > parseEsriSymbolJson( const QVariantMap &symbolData );
static std::unique_ptr< QgsLineSymbol > parseEsriLineSymbolJson( const QVariantMap &symbolData );
static std::unique_ptr< QgsFillSymbol > parseEsriFillSymbolJson( const QVariantMap &symbolData );
static std::unique_ptr< QgsMarkerSymbol > parseEsriMarkerSymbolJson( const QVariantMap &symbolData );

static QColor parseEsriColorJson( const QVariant &colorData );
static Qt::PenStyle parseEsriLineStyle( const QString &style );
static Qt::BrushStyle parseEsriFillStyle( const QString &style );

static QUrl parseUrl( const QUrl &url );
};

0 comments on commit 6002cc4

Please sign in to comment.
You can’t perform that action at this time.