Skip to content

Commit

Permalink
Add option to convert units during conversion
Browse files Browse the repository at this point in the history
Because pixel based units are unfriendly for hi-dpi or print layouts
  • Loading branch information
nyalldawson committed Sep 6, 2020
1 parent 3e54b7a commit 98ba6a4
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 41 deletions.
Expand Up @@ -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

};
Expand Down Expand Up @@ -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.

Expand All @@ -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.

Expand All @@ -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;
Expand Down Expand Up @@ -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.

Expand Down
93 changes: 58 additions & 35 deletions src/core/vectortile/qgsmapboxglstyleconverter.cpp
Expand Up @@ -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
{
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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
{
Expand Down Expand Up @@ -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 >();
Expand Down Expand Up @@ -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" ) ) )
{
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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 );
Expand All @@ -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() )
{
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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;
}

Expand All @@ -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 )
Expand All @@ -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 );
Expand Down Expand Up @@ -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.