From 3ba3986ae2c411ad1f557181cec30ecbd9532b5b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 6 Apr 2016 22:27:07 +1000 Subject: [PATCH] Fix calculation of diagram size in map units when using scale limits --- .../symbology-ng/qgssymbollayerv2utils.sip | 14 +++- src/core/qgsdiagramrendererv2.cpp | 15 +---- .../symbology-ng/qgssymbollayerv2utils.cpp | 50 ++++++++++++++ src/core/symbology-ng/qgssymbollayerv2utils.h | 14 +++- tests/src/python/test_qgsmapunitscale.py | 67 +++++++++++++++++++ 5 files changed, 146 insertions(+), 14 deletions(-) diff --git a/python/core/symbology-ng/qgssymbollayerv2utils.sip b/python/core/symbology-ng/qgssymbollayerv2utils.sip index e605fdd4a1c..3ef316d1963 100644 --- a/python/core/symbology-ng/qgssymbollayerv2utils.sip +++ b/python/core/symbology-ng/qgssymbollayerv2utils.sip @@ -336,10 +336,22 @@ class QgsSymbolLayerV2Utils * @param unit units for specified size * @param scale map unit scale * @note added in QGIS 2.12 - * @see lineWidthScaleFactor + * @see lineWidthScaleFactor() + * @see convertToMapUnits() */ static double convertToPainterUnits( const QgsRenderContext&c, double size, QgsSymbolV2::OutputUnit unit, const QgsMapUnitScale& scale = QgsMapUnitScale() ); + /** Converts a size from the specied units to map units. The conversion respects the limits + * specified by the optional scale parameter. + * @param c render context + * @param size size to convert + * @param unit units for specified size + * @param scale map unit scale + * @note added in QGIS 2.16 + * @see convertToPainterUnits() + */ + static double convertToMapUnits( const QgsRenderContext&c, double size, QgsSymbolV2::OutputUnit unit, const QgsMapUnitScale& scale = QgsMapUnitScale() ); + /** Returns scale factor painter units -> pixel dimensions*/ static double pixelSizeScaleFactor( const QgsRenderContext& c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale& scale = QgsMapUnitScale() ); diff --git a/src/core/qgsdiagramrendererv2.cpp b/src/core/qgsdiagramrendererv2.cpp index cc6b5f84bab..a4efa89b38b 100644 --- a/src/core/qgsdiagramrendererv2.cpp +++ b/src/core/qgsdiagramrendererv2.cpp @@ -423,18 +423,9 @@ QSizeF QgsDiagramRendererV2::sizeMapUnits( const QgsFeature& feature, const QgsR QSizeF size = diagramSize( feature, c ); if ( size.isValid() ) { - if ( s.sizeType == QgsSymbolV2::MM ) - { - double pixelToMap = c.scaleFactor() * c.mapToPixel().mapUnitsPerPixel(); - size.rwidth() *= pixelToMap; - size.rheight() *= pixelToMap; - } - else if ( s.sizeType == QgsSymbolV2::Pixel ) - { - double pixelToMap = c.mapToPixel().mapUnitsPerPixel(); - size.rwidth() *= pixelToMap; - size.rheight() *= pixelToMap; - } + double width = QgsSymbolLayerV2Utils::convertToMapUnits( c, size.width(), s.sizeType, s.sizeScale ); + size.rheight() *= width / size.width(); + size.setWidth( width ); } return size; } diff --git a/src/core/symbology-ng/qgssymbollayerv2utils.cpp b/src/core/symbology-ng/qgssymbollayerv2utils.cpp index 3d07ad7d62d..d620ccdf8e8 100644 --- a/src/core/symbology-ng/qgssymbollayerv2utils.cpp +++ b/src/core/symbology-ng/qgssymbollayerv2utils.cpp @@ -3401,6 +3401,56 @@ double QgsSymbolLayerV2Utils::convertToPainterUnits( const QgsRenderContext &c, return convertedSize; } +double QgsSymbolLayerV2Utils::convertToMapUnits( const QgsRenderContext &c, double size, QgsSymbolV2::OutputUnit unit, const QgsMapUnitScale &scale ) +{ + double mup = c.mapToPixel().mapUnitsPerPixel(); + + switch ( unit ) + { + case QgsSymbolV2::MapUnit: + { + // check scale + double minSizeMU = -DBL_MAX; + if ( scale.minSizeMMEnabled ) + { + minSizeMU = scale.minSizeMM * c.scaleFactor() * c.rasterScaleFactor() * mup; + } + if ( !qgsDoubleNear( scale.minScale, 0.0 ) ) + { + minSizeMU = qMax( minSizeMU, size * ( scale.minScale * c.rendererScale() ) ); + } + size = qMax( size, minSizeMU ); + + double maxSizeMU = DBL_MAX; + if ( scale.maxSizeMMEnabled ) + { + maxSizeMU = scale.maxSizeMM * c.scaleFactor() * c.rasterScaleFactor() * mup; + } + if ( !qgsDoubleNear( scale.maxScale, 0.0 ) ) + { + maxSizeMU = qMin( maxSizeMU, size * ( scale.maxScale * c.rendererScale() ) ); + } + size = qMin( size, maxSizeMU ); + + return size; + } + case QgsSymbolV2::MM: + { + return size * c.scaleFactor() * c.rasterScaleFactor() * mup; + } + case QgsSymbolV2::Pixel: + { + return size * mup; + } + + case QgsSymbolV2::Mixed: + case QgsSymbolV2::Percentage: + //no sensible value + return 0.0; + } + return 0.0; +} + double QgsSymbolLayerV2Utils::pixelSizeScaleFactor( const QgsRenderContext& c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale& scale ) { switch ( u ) diff --git a/src/core/symbology-ng/qgssymbollayerv2utils.h b/src/core/symbology-ng/qgssymbollayerv2utils.h index 0bde83197c4..f24c69f069c 100644 --- a/src/core/symbology-ng/qgssymbollayerv2utils.h +++ b/src/core/symbology-ng/qgssymbollayerv2utils.h @@ -388,10 +388,22 @@ class CORE_EXPORT QgsSymbolLayerV2Utils * @param unit units for specified size * @param scale map unit scale * @note added in QGIS 2.12 - * @see lineWidthScaleFactor + * @see lineWidthScaleFactor() + * @see convertToMapUnits() */ static double convertToPainterUnits( const QgsRenderContext&c, double size, QgsSymbolV2::OutputUnit unit, const QgsMapUnitScale& scale = QgsMapUnitScale() ); + /** Converts a size from the specied units to map units. The conversion respects the limits + * specified by the optional scale parameter. + * @param c render context + * @param size size to convert + * @param unit units for specified size + * @param scale map unit scale + * @note added in QGIS 2.16 + * @see convertToPainterUnits() + */ + static double convertToMapUnits( const QgsRenderContext&c, double size, QgsSymbolV2::OutputUnit unit, const QgsMapUnitScale& scale = QgsMapUnitScale() ); + /** Returns scale factor painter units -> pixel dimensions*/ static double pixelSizeScaleFactor( const QgsRenderContext& c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale& scale = QgsMapUnitScale() ); diff --git a/tests/src/python/test_qgsmapunitscale.py b/tests/src/python/test_qgsmapunitscale.py index ae6c69b7e19..d4d33a26280 100644 --- a/tests/src/python/test_qgsmapunitscale.py +++ b/tests/src/python/test_qgsmapunitscale.py @@ -207,6 +207,73 @@ class PyQgsMapUnitScale(unittest.TestCase): size = QgsSymbolLayerV2Utils.convertToPainterUnits(r, 2, QgsSymbolV2.Pixel, c) self.assertAlmostEqual(size, 2.0, places=5) + def testConvertToMapUnits(self): + # test QgsSymbolLayerV2Utils::convertToMapUnits() using QgsMapUnitScale + + ms = QgsMapSettings() + ms.setExtent(QgsRectangle(0, 0, 100, 100)) + ms.setOutputSize(QSize(100, 50)) + ms.setOutputDpi(300) + r = QgsRenderContext.fromMapSettings(ms) + + # renderer scale should be about 1:291937841 + + # start with no min/max scale + c = QgsMapUnitScale() + + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.MapUnit, c) + self.assertEqual(size, 2.0) + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.MM, c) + self.assertAlmostEqual(size, 47.244094, places=5) + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.Pixel, c) + self.assertAlmostEqual(size, 4.0, places=5) + + # minimum size greater than the calculated size, so size should be limited to minSizeMM + c.minSizeMM = 5 + c.minSizeMMEnabled = True + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.MapUnit, c) + self.assertAlmostEqual(size, 118.1102362, places=5) + # only conversion from mapunits should be affected + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.MM, c) + self.assertAlmostEqual(size, 47.244094, places=5) + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.Pixel, c) + self.assertAlmostEqual(size, 4.0, places=5) + c.minSizeMMEnabled = False + + # maximum size less than the calculated size, so size should be limited to maxSizeMM + c.maxSizeMM = 0.05 + c.maxSizeMMEnabled = True + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.MapUnit, c) + self.assertAlmostEqual(size, 1.1811023622047245, places=5) + # only conversion from mapunits should be affected + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.MM, c) + self.assertAlmostEqual(size, 47.244094, places=5) + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.Pixel, c) + self.assertAlmostEqual(size, 4.0, places=5) + c.maxSizeMMEnabled = False + + # test with minimum scale set + c.minScale = 1 / 150000000.0 + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.MapUnit, c) + self.assertAlmostEqual(size, 15.57001821, places=5) + # only conversion from mapunits should be affected + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.MM, c) + self.assertAlmostEqual(size, 47.244094, places=5) + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.Pixel, c) + self.assertAlmostEqual(size, 4.0, places=5) + c.minScale = 0 + + # test with maximum scale set + c.maxScale = 1 / 1550000000.0 + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.MapUnit, c) + self.assertAlmostEqual(size, 1.50677595625, places=5) + # only conversion from mapunits should be affected + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.MM, c) + self.assertAlmostEqual(size, 47.244094, places=5) + size = QgsSymbolLayerV2Utils.convertToMapUnits(r, 2, QgsSymbolV2.Pixel, c) + self.assertAlmostEqual(size, 4.0, places=5) + c.maxScale = 0 + def testPixelSizeScaleFactor(self): # test QgsSymbolLayerV2Utils::pixelSizeScaleFactor() using QgsMapUnitScale