Skip to content

Commit

Permalink
Add QgsMapSettings::computeExtentForScale and QgsMapSettings::compute…
Browse files Browse the repository at this point in the history
…ScaleForExtent
  • Loading branch information
manisandro committed Jul 2, 2021
1 parent 19eaec7 commit 73c61b6
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 0 deletions.
33 changes: 33 additions & 0 deletions python/core/auto_generated/qgsmapsettings.sip.in
Expand Up @@ -664,6 +664,39 @@ transform rectangle from output CRS to layer's CRS
Returns the coordinate transform from layer's CRS to destination CRS

:return: transform - may be invalid if the transform is not needed
%End

QgsRectangle computeExtentForScale( const QgsPointXY &center, double scale ) const;
%Docstring
Compute the extent such that its ``center`` is at the specified
position (mapped to the destinatonCrs) and the zoom factor corresponds
to the specified ``scale``

:param center: the center, in map coordinates
:param scale: the desired zoom factor (the x part of 1:x)

:return: an extent which can be passed to :py:class:`QgsMapCanvas`.setExtent

.. seealso:: :py:func:`computeScaleForExtent`

.. versionadded:: 3.22
%End

double computeScaleForExtent( const QgsRectangle &extent ) const;
%Docstring
Compute the scale that corresponds to the specified ``extent``

:param extent: the extent, as passed to :py:func:`QgsMapCanvas.setExtent`

:return: the scale denominator

.. seealso:: :py:func:`computeExtentForScale`

.. note::

This function does not consider any map rotation

.. versionadded:: 3.22
%End

QgsRectangle fullExtent() const;
Expand Down
33 changes: 33 additions & 0 deletions src/core/qgsmapsettings.cpp
Expand Up @@ -436,6 +436,39 @@ QgsCoordinateTransform QgsMapSettings::layerTransform( const QgsMapLayer *layer
return QgsCoordinateTransform( layer->crs(), mDestCRS, mTransformContext );
}

QgsRectangle QgsMapSettings::computeExtentForScale( const QgsPointXY &center, double scale ) const
{
// Output width in inches
const double outputWidthInInches = outputSize().width() / outputDpi();

// Desired visible width (honouring scale)
double scaledWidthInInches = outputWidthInInches * scale;

if ( mapUnits() == QgsUnitTypes::DistanceDegrees )
{
// Start with some fraction of the current extent around the center
double delta = mExtent.width() / 100.;
QgsRectangle ext( center.x() - delta, center.y() - delta, center.x() + delta, center.y() + delta );
// Get scale at extent, and then scale extent to the desired scale
double testScale = mScaleCalculator.calculate( ext, outputSize().width() );
ext.scale( scale / testScale );
return ext;
}
else
{
// Conversion from inches to mapUnits - this is safe to use, because we know here that the map units AREN'T in degrees
double conversionFactor = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceFeet, mapUnits() ) / 12;

double delta = 0.5 * scaledWidthInInches * conversionFactor;
return QgsRectangle( center.x() - delta, center.y() - delta, center.x() + delta, center.y() + delta );
}
}

double QgsMapSettings::computeScaleForExtent( const QgsRectangle &extent ) const
{
return mScaleCalculator.calculate( extent, outputSize().width() );
}

double QgsMapSettings::layerToMapUnits( const QgsMapLayer *layer, const QgsRectangle &referenceExtent ) const
{
return layerTransform( layer ).scaleFactor( referenceExtent );
Expand Down
22 changes: 22 additions & 0 deletions src/core/qgsmapsettings.h
Expand Up @@ -600,6 +600,28 @@ class CORE_EXPORT QgsMapSettings : public QgsTemporalRangeObject
*/
QgsCoordinateTransform layerTransform( const QgsMapLayer *layer ) const;

/**
* \brief Compute the extent such that its \a center is at the specified
* position (mapped to the destinatonCrs) and the zoom factor corresponds
* to the specified \a scale
* \param center the center, in map coordinates
* \param scale the desired zoom factor (the x part of 1:x)
* \returns an extent which can be passed to QgsMapCanvas::setExtent
* \see computeScaleForExtent()
* \since QGIS 3.22
*/
QgsRectangle computeExtentForScale( const QgsPointXY &center, double scale ) const;

/**
* \brief Compute the scale that corresponds to the specified \a extent
* \param extent the extent, as passed to \see QgsMapCanvas::setExtent
* \returns the scale denominator
* \see computeExtentForScale()
* \note This function does not consider any map rotation
* \since QGIS 3.22
*/
double computeScaleForExtent( const QgsRectangle &extent ) const;

//! returns current extent of layer set
QgsRectangle fullExtent() const;

Expand Down
34 changes: 34 additions & 0 deletions tests/src/core/testqgsmapsettings.cpp
Expand Up @@ -62,6 +62,8 @@ class TestQgsMapSettings: public QObject
void testRenderedFeatureHandlers();
void testCustomRenderingFlags();
void testClippingRegions();
void testComputeExtentForScale();
void testComputeScaleForExtent();

private:
QString toString( const QPolygonF &p, int decimalPlaces = 2 ) const;
Expand Down Expand Up @@ -624,5 +626,37 @@ void TestQgsMapSettings::testClippingRegions()
QCOMPARE( settings.clippingRegions().at( 0 ).geometry().asWkt(), QStringLiteral( "Polygon ((10 0, 11 0, 11 1, 10 1, 10 0))" ) ) ;
}

void TestQgsMapSettings::testComputeExtentForScale()
{
QgsMapSettings settings;
settings.setExtent( QgsRectangle( -500., -500., 500., 500. ) ); // Just to ensure settings are valid
settings.setDestinationCrs( QgsCoordinateReferenceSystem( "EPSG:3857" ) );

settings.setOutputSize( QSize( 1000, 1000 ) );

QgsRectangle rect = settings.computeExtentForScale( QgsPoint( 0, 0 ), 500 );

// [ output width in inches ] * [scale]
double widthInches = settings.outputSize().width() / double( settings.outputDpi() ) * 500;
double widthMapUnits = widthInches * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceFeet, settings.mapUnits() ) / 12;
QGSCOMPARENEARRECTANGLE( rect, QgsRectangle( - 0.5 * widthMapUnits, - 0.5 * widthMapUnits, 0.5 * widthMapUnits, 0.5 * widthMapUnits ), 0.0001 );

}

void TestQgsMapSettings::testComputeScaleForExtent()
{
QgsMapSettings settings;
settings.setExtent( QgsRectangle( -500., -500., 500., 500. ) ); // Just to ensure settings are valid
settings.setDestinationCrs( QgsCoordinateReferenceSystem( "EPSG:3857" ) );

settings.setOutputSize( QSize( 1000, 1000 ) );

double scale = settings.computeScaleForExtent( QgsRectangle( -500., -500., 500., 500. ) );

double widthInches = 1000 * QgsUnitTypes::fromUnitToUnitFactor( settings.mapUnits(), QgsUnitTypes::DistanceFeet ) * 12;
double testScale = widthInches * settings.outputDpi() / double( settings.outputSize().width() );
QGSCOMPARENEAR( scale, testScale, 0.001 );
}

QGSTEST_MAIN( TestQgsMapSettings )
#include "testqgsmapsettings.moc"

0 comments on commit 73c61b6

Please sign in to comment.