Skip to content
Permalink
Browse files

Add option to convert units during conversion

Because pixel based units are unfriendly for hi-dpi or print layouts
  • Loading branch information
nyalldawson committed Sep 6, 2020
1 parent 3e54b7a commit 98ba6a47ffed30a19a6d4fe21a82bd9b4be04590
@@ -39,6 +39,51 @@ Returns a list of warning messages generated during the conversion.
void clearWarnings();
%Docstring
Clears the list of warning messages.
%End

QgsUnitTypes::RenderUnit targetUnit() const;
%Docstring
Returns the target unit type.

By default this is QgsUnitTypes.RenderPixels in order to exactly match the original
style rendering. But rendering in pixels can cause issues on hidpi displays or with print
layouts, so setting a target unit of QgsUnitTypes.Millimeters or another real-world unit
type is often more appropriate.

.. seealso:: :py:func:`setTargetUnit`
%End

void setTargetUnit( QgsUnitTypes::RenderUnit targetUnit );
%Docstring
Sets the target unit type.

By default this is QgsUnitTypes.RenderPixels in order to exactly match the original
style rendering. But rendering in pixels can cause issues on hidpi displays or with print
layouts, so setting a target unit of QgsUnitTypes.Millimeters or another real-world unit
type is often more appropriate.

If setting to a non-pixel unit, be sure to call :py:func:`~QgsMapBoxGlStyleConversionContext.setPixelSizeConversionFactor` in order
to setup an appropriate pixel-to-unit conversion factor to scale converted sizes
using. E.g. if the target unit is millimeters, the size conversion factor should be
set to a pixel-to-millimeter value.

.. seealso:: :py:func:`targetUnit`
%End

double pixelSizeConversionFactor() const;
%Docstring
Returns the pixel size conversion factor, used to scale the original pixel sizes
when converting styles.

.. seealso:: :py:func:`setSizeConversionFactor`
%End

void setPixelSizeConversionFactor( double sizeConversionFactor );
%Docstring
Sets the pixel size conversion factor, used to scale the original pixel sizes
when converting styles.

.. seealso:: :py:func:`pixelSizeConversionFactor`
%End

};
@@ -75,7 +120,7 @@ Constructor for QgsMapBoxGlStyleConverter.
NoLayerList,
};

Result convert( const QVariantMap &style );
Result convert( const QVariantMap &style, QgsMapBoxGlStyleConversionContext *context = 0 );
%Docstring
Converts a JSON ``style`` map, and returns the resultant status of the conversion.

@@ -84,9 +129,11 @@ by calling :py:func:`~QgsMapBoxGlStyleConverter.errorMessage`.

After conversion, the resultant labeling and style rules can be retrieved by calling
:py:func:`~QgsMapBoxGlStyleConverter.renderer` or :py:func:`~QgsMapBoxGlStyleConverter.labeling` respectively.

The optional ``context`` argument can be set to use a specific context during the conversion.
%End

Result convert( const QString &style );
Result convert( const QString &style, QgsMapBoxGlStyleConversionContext *context = 0 );
%Docstring
Converts a JSON ``style`` string, and returns the resultant status of the conversion.

@@ -95,6 +142,8 @@ by calling :py:func:`~QgsMapBoxGlStyleConverter.errorMessage`.

After conversion, the resultant labeling and style rules can be retrieved by calling
:py:func:`~QgsMapBoxGlStyleConverter.renderer` or :py:func:`~QgsMapBoxGlStyleConverter.labeling` respectively.

The optional ``context`` argument can be set to use a specific context during the conversion.
%End

QString errorMessage() const;
@@ -134,7 +183,7 @@ or ``None`` if the style could not be converted successfully.
Opacity,
};

void parseLayers( const QVariantList &layers );
void parseLayers( const QVariantList &layers, QgsMapBoxGlStyleConversionContext *context = 0 );
%Docstring
Parse list of ``layers`` from JSON.

@@ -33,19 +33,17 @@
#include "qgsblureffect.h"


constexpr double PIXEL_RATIO = 1;

QgsMapBoxGlStyleConverter::QgsMapBoxGlStyleConverter()
{
}

QgsMapBoxGlStyleConverter::Result QgsMapBoxGlStyleConverter::convert( const QVariantMap &style )
QgsMapBoxGlStyleConverter::Result QgsMapBoxGlStyleConverter::convert( const QVariantMap &style, QgsMapBoxGlStyleConversionContext *context )
{
mError.clear();
mWarnings.clear();
if ( style.contains( QStringLiteral( "layers" ) ) )
{
parseLayers( style.value( QStringLiteral( "layers" ) ).toList() );
parseLayers( style.value( QStringLiteral( "layers" ) ).toList(), context );
}
else
{
@@ -55,16 +53,21 @@ QgsMapBoxGlStyleConverter::Result QgsMapBoxGlStyleConverter::convert( const QVar
return Success;
}

QgsMapBoxGlStyleConverter::Result QgsMapBoxGlStyleConverter::convert( const QString &style )
QgsMapBoxGlStyleConverter::Result QgsMapBoxGlStyleConverter::convert( const QString &style, QgsMapBoxGlStyleConversionContext *context )
{
return convert( QgsJsonUtils::parseJson( style ).toMap() );
return convert( QgsJsonUtils::parseJson( style ).toMap(), context );
}

QgsMapBoxGlStyleConverter::~QgsMapBoxGlStyleConverter() = default;

void QgsMapBoxGlStyleConverter::parseLayers( const QVariantList &layers )
void QgsMapBoxGlStyleConverter::parseLayers( const QVariantList &layers, QgsMapBoxGlStyleConversionContext *context )
{
QgsMapBoxGlStyleConversionContext context;
std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
if ( !context )
{
tmpContext = qgis::make_unique< QgsMapBoxGlStyleConversionContext >();
context = tmpContext.get();
}

QList<QgsVectorTileBasicRendererStyle> rendererStyles;
QList<QgsVectorTileBasicLabelingStyle> labelingStyles;
@@ -88,7 +91,7 @@ void QgsMapBoxGlStyleConverter::parseLayers( const QVariantList &layers )
QString filterExpression;
if ( jsonLayer.contains( QStringLiteral( "filter" ) ) )
{
filterExpression = parseExpression( jsonLayer.value( QStringLiteral( "filter" ) ).toList(), context );
filterExpression = parseExpression( jsonLayer.value( QStringLiteral( "filter" ) ).toList(), *context );
}

QgsVectorTileBasicRendererStyle rendererStyle;
@@ -98,15 +101,15 @@ void QgsMapBoxGlStyleConverter::parseLayers( const QVariantList &layers )
bool hasLabelingStyle = false;
if ( layerType == QLatin1String( "fill" ) )
{
hasRendererStyle = parseFillLayer( jsonLayer, rendererStyle, context );
hasRendererStyle = parseFillLayer( jsonLayer, rendererStyle, *context );
}
else if ( layerType == QLatin1String( "line" ) )
{
hasRendererStyle = parseLineLayer( jsonLayer, rendererStyle, context );
hasRendererStyle = parseLineLayer( jsonLayer, rendererStyle, *context );
}
else if ( layerType == QLatin1String( "symbol" ) )
{
parseSymbolLayer( jsonLayer, rendererStyle, hasRendererStyle, labelingStyle, hasLabelingStyle, context );
parseSymbolLayer( jsonLayer, rendererStyle, hasRendererStyle, labelingStyle, hasLabelingStyle, *context );
}
else
{
@@ -137,8 +140,8 @@ void QgsMapBoxGlStyleConverter::parseLayers( const QVariantList &layers )
labelingStyles.append( labelingStyle );
}

mWarnings.append( context.warnings() );
context.clearWarnings();
mWarnings.append( context->warnings() );
context->clearWarnings();
}

mRenderer = qgis::make_unique< QgsVectorTileBasicRenderer >();
@@ -275,8 +278,8 @@ bool QgsMapBoxGlStyleConverter::parseFillLayer( const QVariantMap &jsonLayer, Qg
QgsSimpleFillSymbolLayer *fillSymbol = dynamic_cast< QgsSimpleFillSymbolLayer * >( symbol->symbolLayer( 0 ) );

// set render units
symbol->setOutputUnit( QgsUnitTypes::RenderPixels );
fillSymbol->setOutputUnit( QgsUnitTypes::RenderPixels );
symbol->setOutputUnit( context.targetUnit() );
fillSymbol->setOutputUnit( context.targetUnit() );

if ( jsonPaint.contains( QStringLiteral( "fill-pattern" ) ) )
{
@@ -387,17 +390,17 @@ bool QgsMapBoxGlStyleConverter::parseLineLayer( const QVariantMap &jsonLayer, Qg
{
case QVariant::Int:
case QVariant::Double:
lineWidth = jsonLineWidth.toDouble();
lineWidth = jsonLineWidth.toDouble() * context.pixelSizeConversionFactor();
break;

case QVariant::Map:
lineWidth = -1;
ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeWidth, parseInterpolateByZoom( jsonLineWidth.toMap(), context, PIXEL_RATIO ) );
ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeWidth, parseInterpolateByZoom( jsonLineWidth.toMap(), context, context.pixelSizeConversionFactor() ) );
break;

case QVariant::List:
case QVariant::StringList:
ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeWidth, parseInterpolateListByZoom( jsonLineWidth.toList(), PropertyType::Numeric, context, PIXEL_RATIO ) );
ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeWidth, parseInterpolateListByZoom( jsonLineWidth.toList(), PropertyType::Numeric, context, context.pixelSizeConversionFactor() ) );
break;

default:
@@ -460,7 +463,7 @@ bool QgsMapBoxGlStyleConverter::parseLineLayer( const QVariantMap &jsonLayer, Qg
const QVariantList dashSource = jsonLineDashArray.toMap().value( QStringLiteral( "stops" ) ).toList().last().toList().value( 1 ).toList();
for ( const QVariant &v : dashSource )
{
dashVector << v.toDouble() * PIXEL_RATIO;
dashVector << v.toDouble() * context.pixelSizeConversionFactor();
}
break;
}
@@ -471,7 +474,7 @@ bool QgsMapBoxGlStyleConverter::parseLineLayer( const QVariantMap &jsonLayer, Qg
const QVariantList dashSource = jsonLineDashArray.toList();
for ( const QVariant &v : dashSource )
{
dashVector << v.toDouble() * PIXEL_RATIO;
dashVector << v.toDouble() * context.pixelSizeConversionFactor();
}
break;
}
@@ -501,8 +504,8 @@ bool QgsMapBoxGlStyleConverter::parseLineLayer( const QVariantMap &jsonLayer, Qg
QgsSimpleLineSymbolLayer *lineSymbol = dynamic_cast< QgsSimpleLineSymbolLayer * >( symbol->symbolLayer( 0 ) );

// set render units
symbol->setOutputUnit( QgsUnitTypes::RenderPixels );
lineSymbol->setOutputUnit( QgsUnitTypes::RenderPixels );
symbol->setOutputUnit( context.targetUnit() );
lineSymbol->setOutputUnit( context.targetUnit() );
lineSymbol->setPenCapStyle( penCapStyle );
lineSymbol->setPenJoinStyle( penJoinStyle );
lineSymbol->setDataDefinedProperties( ddProperties );
@@ -517,7 +520,7 @@ bool QgsMapBoxGlStyleConverter::parseLineLayer( const QVariantMap &jsonLayer, Qg
}
if ( lineWidth != -1 )
{
lineSymbol->setWidth( lineWidth * PIXEL_RATIO );
lineSymbol->setWidth( lineWidth );
}
if ( !dashVector.empty() )
{
@@ -560,18 +563,18 @@ void QgsMapBoxGlStyleConverter::parseSymbolLayer( const QVariantMap &jsonLayer,
{
case QVariant::Int:
case QVariant::Double:
textSize = jsonTextSize.toDouble();
textSize = jsonTextSize.toDouble() * context.pixelSizeConversionFactor();
break;

case QVariant::Map:
textSize = -1;
ddLabelProperties.setProperty( QgsPalLayerSettings::Size, parseInterpolateByZoom( jsonTextSize.toMap(), context ) );
ddLabelProperties.setProperty( QgsPalLayerSettings::Size, parseInterpolateByZoom( jsonTextSize.toMap(), context, context.pixelSizeConversionFactor() ) );
break;

case QVariant::List:
case QVariant::StringList:
textSize = -1;
ddLabelProperties.setProperty( QgsPalLayerSettings::Size, parseInterpolateListByZoom( jsonTextSize.toList(), PropertyType::Numeric, context ) );
ddLabelProperties.setProperty( QgsPalLayerSettings::Size, parseInterpolateListByZoom( jsonTextSize.toList(), PropertyType::Numeric, context, context.pixelSizeConversionFactor() ) );
break;

default:
@@ -690,18 +693,18 @@ void QgsMapBoxGlStyleConverter::parseSymbolLayer( const QVariantMap &jsonLayer,
{
case QVariant::Int:
case QVariant::Double:
bufferSize = jsonHaloWidth.toDouble();
bufferSize = jsonHaloWidth.toDouble() * context.pixelSizeConversionFactor();
break;

case QVariant::Map:
bufferSize = 1;
ddLabelProperties.setProperty( QgsPalLayerSettings::BufferSize, parseInterpolateByZoom( jsonHaloWidth.toMap(), context ) );
ddLabelProperties.setProperty( QgsPalLayerSettings::BufferSize, parseInterpolateByZoom( jsonHaloWidth.toMap(), context, context.pixelSizeConversionFactor() ) );
break;

case QVariant::List:
case QVariant::StringList:
bufferSize = 1;
ddLabelProperties.setProperty( QgsPalLayerSettings::BufferSize, parseInterpolateListByZoom( jsonHaloWidth.toList(), PropertyType::Numeric, context ) );
ddLabelProperties.setProperty( QgsPalLayerSettings::BufferSize, parseInterpolateListByZoom( jsonHaloWidth.toList(), PropertyType::Numeric, context, context.pixelSizeConversionFactor() ) );
break;

default:
@@ -719,7 +722,7 @@ void QgsMapBoxGlStyleConverter::parseSymbolLayer( const QVariantMap &jsonLayer,
case QVariant::Int:
case QVariant::Double:
{
haloBlurSize = jsonTextHaloBlur.toDouble();
haloBlurSize = jsonTextHaloBlur.toDouble() * context.pixelSizeConversionFactor();
break;
}

@@ -730,7 +733,7 @@ void QgsMapBoxGlStyleConverter::parseSymbolLayer( const QVariantMap &jsonLayer,
}

QgsTextFormat format;
format.setSizeUnit( QgsUnitTypes::RenderPixels );
format.setSizeUnit( context.targetUnit() );
if ( textColor.isValid() )
format.setColor( textColor );
if ( textSize >= 0 )
@@ -741,16 +744,16 @@ void QgsMapBoxGlStyleConverter::parseSymbolLayer( const QVariantMap &jsonLayer,
if ( bufferSize > 0 )
{
format.buffer().setEnabled( true );
format.buffer().setSize( bufferSize * PIXEL_RATIO );
format.buffer().setSizeUnit( QgsUnitTypes::RenderPixels );
format.buffer().setSize( bufferSize );
format.buffer().setSizeUnit( context.targetUnit() );
format.buffer().setColor( bufferColor );

if ( haloBlurSize > 0 )
{
QgsEffectStack *stack = new QgsEffectStack();
QgsBlurEffect *blur = new QgsBlurEffect() ;
blur->setEnabled( true );
blur->setBlurUnit( QgsUnitTypes::RenderPixels );
blur->setBlurUnit( context.targetUnit() );
blur->setBlurLevel( haloBlurSize );
blur->setBlurMethod( QgsBlurEffect::StackBlur );
stack->appendEffect( blur );
@@ -1470,3 +1473,23 @@ void QgsMapBoxGlStyleConversionContext::pushWarning( const QString &warning )
QgsDebugMsg( warning );
mWarnings << warning;
}

QgsUnitTypes::RenderUnit QgsMapBoxGlStyleConversionContext::targetUnit() const
{
return mTargetUnit;
}

void QgsMapBoxGlStyleConversionContext::setTargetUnit( QgsUnitTypes::RenderUnit targetUnit )
{
mTargetUnit = targetUnit;
}

double QgsMapBoxGlStyleConversionContext::pixelSizeConversionFactor() const
{
return mSizeConversionFactor;
}

void QgsMapBoxGlStyleConversionContext::setPixelSizeConversionFactor( double sizeConversionFactor )
{
mSizeConversionFactor = sizeConversionFactor;
}

0 comments on commit 98ba6a4

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