diff --git a/python/core/composer/qgscomposermap.sip b/python/core/composer/qgscomposermap.sip index 7a335d3b771..0555feb8238 100644 --- a/python/core/composer/qgscomposermap.sip +++ b/python/core/composer/qgscomposermap.sip @@ -127,6 +127,10 @@ class QgsComposerMap : QgsComposerItem //! @note not available in python bindings // QgsRectangle* currentMapExtent(); + QgsCoordinateReferenceSystem crs() const; + QgsCoordinateReferenceSystem presetCrs() const; + void setCrs( const QgsCoordinateReferenceSystem& crs ); + PreviewMode previewMode() const; void setPreviewMode( PreviewMode m ); diff --git a/python/core/composer/qgscomposerutils.sip b/python/core/composer/qgscomposerutils.sip index 6f8a9c3df83..f12234b5310 100644 --- a/python/core/composer/qgscomposerutils.sip +++ b/python/core/composer/qgscomposerutils.sip @@ -249,5 +249,5 @@ class QgsComposerUtils * QgsComposition::referenceMap(). * @note added in QGIS 3.0 */ - static QgsRenderContext createRenderContext( QgsComposition* composition, QPainter& painter ); + static QgsRenderContext createRenderContext( QgsComposition* composition, QPainter* painter ); }; diff --git a/python/gui/qgscomposerview.sip b/python/gui/qgscomposerview.sip index 2d7f82184e1..dffb882d2eb 100644 --- a/python/gui/qgscomposerview.sip +++ b/python/gui/qgscomposerview.sip @@ -133,6 +133,9 @@ class QgsComposerView : QGraphicsView */ void setPreviewMode( QgsPreviewEffect::PreviewMode mode ); + void setMapCanvas( QgsMapCanvas* canvas ); + QgsMapCanvas* mapCanvas() const; + protected: void mousePressEvent( QMouseEvent* ); void mouseReleaseEvent( QMouseEvent* ); diff --git a/src/app/composer/qgscomposer.cpp b/src/app/composer/qgscomposer.cpp index c3dae52f2b1..6f80070a9ca 100644 --- a/src/app/composer/qgscomposer.cpp +++ b/src/app/composer/qgscomposer.cpp @@ -4027,6 +4027,7 @@ void QgsComposer::createComposerView() delete mView; mView = new QgsComposerView(); + mView->setMapCanvas( mQgis->mapCanvas() ); mView->setContentsMargins( 0, 0, 0, 0 ); mView->setHorizontalRuler( mHorizontalRuler ); mView->setVerticalRuler( mVerticalRuler ); diff --git a/src/app/composer/qgscomposermapgridwidget.cpp b/src/app/composer/qgscomposermapgridwidget.cpp index 9ee86981aab..b7450e2af69 100644 --- a/src/app/composer/qgscomposermapgridwidget.cpp +++ b/src/app/composer/qgscomposermapgridwidget.cpp @@ -1035,7 +1035,7 @@ void QgsComposerMapGridWidget::on_mMapGridCRSButton_clicked() QgsGenericProjectionSelector crsDialog( this ); QgsCoordinateReferenceSystem crs = mComposerMapGrid->crs(); - QString currentAuthId = crs.isValid() ? crs.authid() : mComposerMap->composition()->mapSettings().destinationCrs().authid(); + QString currentAuthId = crs.isValid() ? crs.authid() : mComposerMap->crs().authid(); crsDialog.setSelectedAuthId( currentAuthId ); if ( crsDialog.exec() == QDialog::Accepted ) diff --git a/src/app/composer/qgscomposermapwidget.cpp b/src/app/composer/qgscomposermapwidget.cpp index fa1968fa0ff..0e92bbf8180 100644 --- a/src/app/composer/qgscomposermapwidget.cpp +++ b/src/app/composer/qgscomposermapwidget.cpp @@ -37,6 +37,7 @@ #include "qgsmapthemecollection.h" #include "qgsmapthemes.h" #include "qgisgui.h" +#include "qgscsexception.h" #include @@ -63,6 +64,9 @@ QgsComposerMapWidget::QgsComposerMapWidget( QgsComposerMap* composerMap ) mPreviewModeComboBox->insertItem( 1, tr( "Render" ) ); mPreviewModeComboBox->insertItem( 2, tr( "Rectangle" ) ); + mCrsSelector->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, true ); + mCrsSelector->setNotSetText( tr( "Use project CRS" ) ); + // follow preset combo mFollowVisibilityPresetCombo->setModel( new QStringListModel( mFollowVisibilityPresetCombo ) ); connect( mFollowVisibilityPresetCombo, SIGNAL( currentIndexChanged( int ) ), this, SLOT( followVisibilityPresetSelected( int ) ) ); @@ -99,6 +103,8 @@ QgsComposerMapWidget::QgsComposerMapWidget( QgsComposerMap* composerMap ) connect( mOverviewFrameMapComboBox, SIGNAL( itemChanged( QgsComposerItem* ) ), this, SLOT( overviewMapChanged( QgsComposerItem* ) ) ); } + connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, &QgsComposerMapWidget::mapCrsChanged ); + updateGuiElements(); loadGridEntries(); loadOverviewEntries(); @@ -260,6 +266,42 @@ void QgsComposerMapWidget::cleanUpOverviewFrameStyleSelector( QgsPanelWidget* co mComposerMap->endCommand(); } +void QgsComposerMapWidget::mapCrsChanged( const QgsCoordinateReferenceSystem& crs ) +{ + if ( !mComposerMap ) + { + return; + } + + if ( mComposerMap->presetCrs() == crs ) + return; + + // try to reproject to maintain extent + QgsCoordinateReferenceSystem oldCrs = mComposerMap->crs(); + + bool updateExtent = false; + QgsRectangle newExtent; + try + { + QgsCoordinateTransform xForm( oldCrs, crs.isValid() ? crs : QgsProject::instance()->crs() ); + QgsRectangle prevExtent = *mComposerMap->currentMapExtent(); + newExtent = xForm.transformBoundingBox( prevExtent ); + updateExtent = true; + } + catch ( QgsCsException & ) + { + //transform failed, don't update extent + } + + mComposerMap->beginCommand( tr( "Map CRS changed" ) ); + mComposerMap->setCrs( crs ); + if ( updateExtent ) + mComposerMap->zoomToExtent( newExtent ); + mComposerMap->endCommand(); + mComposerMap->cache(); + mComposerMap->update(); +} + void QgsComposerMapWidget::on_mAtlasCheckBox_toggled( bool checked ) { if ( !mComposerMap ) @@ -471,6 +513,23 @@ void QgsComposerMapWidget::on_mSetToMapCanvasExtentButton_clicked() QgsRectangle newExtent = QgisApp::instance()->mapCanvas()->mapSettings().visibleExtent(); + //transform? + if ( QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs() + != mComposerMap->crs() ) + { + try + { + QgsCoordinateTransform xForm( QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs(), + mComposerMap->crs() ); + newExtent = xForm.transformBoundingBox( newExtent ); + } + catch ( QgsCsException & ) + { + //transform failed, better not proceed + return; + } + } + mComposerMap->beginCommand( tr( "Map extent changed" ) ); mComposerMap->zoomToExtent( newExtent ); mComposerMap->endCommand(); @@ -487,6 +546,23 @@ void QgsComposerMapWidget::on_mViewExtentInCanvasButton_clicked() if ( !currentMapExtent.isEmpty() ) { + //transform? + if ( QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs() + != mComposerMap->crs() ) + { + try + { + QgsCoordinateTransform xForm( mComposerMap->crs(), + QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs() ); + currentMapExtent = xForm.transformBoundingBox( currentMapExtent ); + } + catch ( QgsCsException & ) + { + //transform failed, better not proceed + return; + } + } + QgisApp::instance()->mapCanvas()->setExtent( currentMapExtent ); QgisApp::instance()->mapCanvas()->refresh(); } @@ -532,6 +608,8 @@ void QgsComposerMapWidget::updateGuiElements() blockAllSignals( true ); + whileBlocking( mCrsSelector )->setCrs( mComposerMap->presetCrs() ); + //width, height, scale double scale = mComposerMap->scale(); diff --git a/src/app/composer/qgscomposermapwidget.h b/src/app/composer/qgscomposermapwidget.h index 0b239041ed9..47c4d7dfc48 100644 --- a/src/app/composer/qgscomposermapwidget.h +++ b/src/app/composer/qgscomposermapwidget.h @@ -121,6 +121,8 @@ class QgsComposerMapWidget: public QgsComposerItemBaseWidget, private Ui::QgsCom void updateOverviewFrameStyleFromWidget(); void cleanUpOverviewFrameStyleSelector( QgsPanelWidget* container ); + void mapCrsChanged( const QgsCoordinateReferenceSystem& crs ); + private: QgsComposerMap* mComposerMap; diff --git a/src/core/composer/qgsatlascomposition.cpp b/src/core/composer/qgsatlascomposition.cpp index 0e13c9bf4d5..8790d1925e2 100644 --- a/src/core/composer/qgsatlascomposition.cpp +++ b/src/core/composer/qgsatlascomposition.cpp @@ -439,7 +439,7 @@ void QgsAtlasComposition::computeExtent( QgsComposerMap* map ) // QgsGeometry::boundingBox is expressed in the geometry"s native CRS // We have to transform the grometry to the destination CRS and ask for the bounding box // Note: we cannot directly take the transformation of the bounding box, since transformations are not linear - mTransformedFeatureBounds = currentGeometry( map->composition()->mapSettings().destinationCrs() ).boundingBox(); + mTransformedFeatureBounds = currentGeometry( map->crs() ).boundingBox(); } void QgsAtlasComposition::prepareMap( QgsComposerMap* map ) @@ -481,7 +481,7 @@ void QgsAtlasComposition::prepareMap( QgsComposerMap* map ) if ( map->atlasScalingMode() == QgsComposerMap::Fixed || map->atlasScalingMode() == QgsComposerMap::Predefined || isPointLayer ) { QgsScaleCalculator calc; - calc.setMapUnits( composition()->mapSettings().mapUnits() ); + calc.setMapUnits( map->crs().mapUnits() ); calc.setDpi( 25.4 ); double originalScale = calc.calculate( mOrigExtent, map->rect().width() ); double geomCenterX = ( xa1 + xa2 ) / 2.0; diff --git a/src/core/composer/qgscomposerarrow.cpp b/src/core/composer/qgscomposerarrow.cpp index 315e54794b2..7f9e926859b 100644 --- a/src/core/composer/qgscomposerarrow.cpp +++ b/src/core/composer/qgscomposerarrow.cpp @@ -182,7 +182,7 @@ void QgsComposerArrow::drawLine( QPainter *painter ) painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); //scale painter from mm to dots //setup render context - QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, *painter ); + QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, painter ); context.setForceVectorOutput( true ); QgsExpressionContext expressionContext = createExpressionContext(); diff --git a/src/core/composer/qgscomposerattributetablev2.cpp b/src/core/composer/qgscomposerattributetablev2.cpp index 54c6c645780..8680bba9311 100644 --- a/src/core/composer/qgscomposerattributetablev2.cpp +++ b/src/core/composer/qgscomposerattributetablev2.cpp @@ -422,7 +422,7 @@ bool QgsComposerAttributeTableV2::getTableContents( QgsComposerTableContents &co if ( layer ) { //transform back to layer CRS - QgsCoordinateTransform coordTransform( layer->crs(), mComposition->mapSettings().destinationCrs() ); + QgsCoordinateTransform coordTransform( layer->crs(), mComposerMap->crs() ); try { selectionRect = coordTransform.transformBoundingBox( selectionRect, QgsCoordinateTransform::ReverseTransform ); diff --git a/src/core/composer/qgscomposerhtml.cpp b/src/core/composer/qgscomposerhtml.cpp index 31bbdbca570..40a7a01d61d 100644 --- a/src/core/composer/qgscomposerhtml.cpp +++ b/src/core/composer/qgscomposerhtml.cpp @@ -27,6 +27,7 @@ #include "qgsdistancearea.h" #include "qgsjsonutils.h" #include "qgsmapsettings.h" +#include "qgscomposermap.h" #include "qgswebpage.h" #include "qgswebframe.h" @@ -542,7 +543,9 @@ void QgsComposerHtml::setExpressionContext( const QgsFeature &feature, QgsVector else if ( mComposition ) { //set to composition's mapsettings' crs - mDistanceArea->setSourceCrs( mComposition->mapSettings().destinationCrs().srsid() ); + QgsComposerMap* referenceMap = mComposition->referenceMap(); + if ( referenceMap ) + mDistanceArea->setSourceCrs( referenceMap->crs().srsid() ); } if ( mComposition ) { diff --git a/src/core/composer/qgscomposerlabel.cpp b/src/core/composer/qgscomposerlabel.cpp index 0801aeceb21..d6c52705671 100644 --- a/src/core/composer/qgscomposerlabel.cpp +++ b/src/core/composer/qgscomposerlabel.cpp @@ -27,6 +27,7 @@ #include "qgsfontutils.h" #include "qgsexpressioncontext.h" #include "qgsmapsettings.h" +#include "qgscomposermap.h" #include "qgswebview.h" #include "qgswebframe.h" @@ -261,8 +262,10 @@ void QgsComposerLabel::refreshExpressionContext() } else { - //set to composition's mapsettings' crs - mDistanceArea->setSourceCrs( mComposition->mapSettings().destinationCrs().srsid() ); + //set to composition's reference map's crs + QgsComposerMap* referenceMap = mComposition->referenceMap(); + if ( referenceMap ) + mDistanceArea->setSourceCrs( referenceMap->crs().srsid() ); } mDistanceArea->setEllipsoidalMode( true ); mDistanceArea->setEllipsoid( mComposition->project()->ellipsoid() ); diff --git a/src/core/composer/qgscomposerlegend.cpp b/src/core/composer/qgscomposerlegend.cpp index 20db111265d..93dfde19a3a 100644 --- a/src/core/composer/qgscomposerlegend.cpp +++ b/src/core/composer/qgscomposerlegend.cpp @@ -111,10 +111,10 @@ void QgsComposerLegend::paint( QPainter* painter, const QStyleOptionGraphicsItem mSettings.setMmPerMapUnit( mComposerMap->mapUnitsToMM() ); // use a temporary QgsMapSettings to find out real map scale - QgsMapSettings ms = mComposerMap->composition()->mapSettings(); - ms.setOutputSize( QSizeF( mComposerMap->rect().width() * dotsPerMM, mComposerMap->rect().height() * dotsPerMM ).toSize() ); - ms.setExtent( *mComposerMap->currentMapExtent() ); - ms.setOutputDpi( dpi ); + QSizeF mapSizePixels = QSizeF( mComposerMap->rect().width() * dotsPerMM, mComposerMap->rect().height() * dotsPerMM ); + QgsRectangle mapExtent = *mComposerMap->currentMapExtent(); + + QgsMapSettings ms = mComposerMap->mapSettings( mapExtent, mapSizePixels, dpi ); mSettings.setMapScale( ms.scale() ); } mInitialMapScaleCalculated = true; @@ -704,7 +704,7 @@ void QgsComposerLegend::doUpdateFilterByMap() QgsGeometry filterPolygon; if ( mInAtlas ) { - filterPolygon = composition()->atlasComposition().currentGeometry( composition()->mapSettings().destinationCrs() ); + filterPolygon = composition()->atlasComposition().currentGeometry( mComposerMap->crs() ); } mLegendModel->setLegendFilter( &ms, /* useExtent */ mInAtlas || mLegendFilterByMap, filterPolygon, /* useExpressions */ true ); } diff --git a/src/core/composer/qgscomposermap.cpp b/src/core/composer/qgscomposermap.cpp index 8afd9427f87..42c5ae78854 100644 --- a/src/core/composer/qgscomposermap.cpp +++ b/src/core/composer/qgscomposermap.cpp @@ -85,9 +85,6 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int w int bgBlueInt = project->readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), 255 ); setBackgroundColor( QColor( bgRedInt, bgGreenInt, bgBlueInt ) ); - //calculate mExtent based on width/height ratio and map canvas extent - mExtent = mComposition->mapSettings().visibleExtent(); - init(); setSceneRect( QRectF( x, y, width, height ) ); @@ -200,17 +197,17 @@ void QgsComposerMap::draw( QPainter *painter, const QgsRectangle& extent, QSizeF QgsMapSettings QgsComposerMap::mapSettings( const QgsRectangle& extent, QSizeF size, int dpi ) const { - const QgsMapSettings &ms = mComposition->mapSettings(); - QgsExpressionContext expressionContext = createExpressionContext(); + QgsCoordinateReferenceSystem renderCrs = crs(); QgsMapSettings jobMapSettings; + jobMapSettings.setDestinationCrs( renderCrs ); + jobMapSettings.setCrsTransformEnabled( true ); jobMapSettings.setExtent( extent ); jobMapSettings.setOutputSize( size.toSize() ); jobMapSettings.setOutputDpi( dpi ); - jobMapSettings.setMapUnits( ms.mapUnits() ); + jobMapSettings.setMapUnits( renderCrs.mapUnits() ); jobMapSettings.setBackgroundColor( Qt::transparent ); - jobMapSettings.setOutputImageFormat( ms.outputImageFormat() ); jobMapSettings.setRotation( mEvaluatedMapRotation ); //set layers to render @@ -233,10 +230,6 @@ QgsMapSettings QgsComposerMap::mapSettings( const QgsRectangle& extent, QSizeF s } jobMapSettings.setLayers( layers ); jobMapSettings.setLayerStyleOverrides( layerStyleOverridesToRender( expressionContext ) ); - jobMapSettings.setDestinationCrs( ms.destinationCrs() ); - jobMapSettings.setCrsTransformEnabled( ms.hasCrsTransformEnabled() ); - jobMapSettings.setFlags( ms.flags() ); - jobMapSettings.setFlag( QgsMapSettings::DrawSelection, false ); if ( mComposition->plotStyle() == QgsComposition::Print || mComposition->plotStyle() == QgsComposition::Postscript ) @@ -249,11 +242,13 @@ QgsMapSettings QgsComposerMap::mapSettings( const QgsRectangle& extent, QSizeF s jobMapSettings.setExpressionContext( context ); // composer-specific overrides of flags - jobMapSettings.setFlag( QgsMapSettings::ForceVectorOutput ); // force vector output (no caching of marker images etc.) + jobMapSettings.setFlag( QgsMapSettings::ForceVectorOutput, true ); // force vector output (no caching of marker images etc.) + jobMapSettings.setFlag( QgsMapSettings::Antialiasing, true ); jobMapSettings.setFlag( QgsMapSettings::DrawEditingInfo, false ); + jobMapSettings.setFlag( QgsMapSettings::DrawSelection, false ); jobMapSettings.setFlag( QgsMapSettings::UseAdvancedEffects, mComposition->useAdvancedEffects() ); // respect the composition's useAdvancedEffects flag - jobMapSettings.datumTransformStore() = ms.datumTransformStore(); + jobMapSettings.datumTransformStore().setDestinationCrs( renderCrs ); return jobMapSettings; } @@ -625,7 +620,7 @@ QMap QgsComposerMap::layerStyleOverridesToRender( const QgsExp double QgsComposerMap::scale() const { QgsScaleCalculator calculator; - calculator.setMapUnits( mComposition->mapSettings().mapUnits() ); + calculator.setMapUnits( crs().mapUnits() ); calculator.setDpi( 25.4 ); //QGraphicsView units are mm return calculator.calculate( *currentMapExtent(), rect().width() ); } @@ -717,7 +712,7 @@ void QgsComposerMap::zoomContent( const double factor, const QPointF point, cons //and also apply to the map's original extent (see #9602) //we can't use the scaleRatio calculated earlier, as the scale can vary depending on extent for geographic coordinate systems QgsScaleCalculator calculator; - calculator.setMapUnits( mComposition->mapSettings().mapUnits() ); + calculator.setMapUnits( crs().mapUnits() ); calculator.setDpi( 25.4 ); //QGraphicsView units are mm double scaleRatio = scale() / calculator.calculate( mExtent, rect().width() ); mExtent.scale( scaleRatio ); @@ -777,9 +772,14 @@ void QgsComposerMap::setNewExtent( const QgsRectangle& extent ) void QgsComposerMap::zoomToExtent( const QgsRectangle &extent ) { QgsRectangle newExtent = extent; + QgsRectangle currentExtent = *currentMapExtent(); //Make sure the width/height ratio is the same as the current composer map extent. //This is to keep the map item frame size fixed - double currentWidthHeightRatio = currentMapExtent()->width() / currentMapExtent()->height(); + double currentWidthHeightRatio = 1.0; + if ( !currentExtent.isNull() ) + currentWidthHeightRatio = currentExtent.width() / currentExtent.height(); + else + currentWidthHeightRatio = rect().width() / rect().height(); double newWidthHeightRatio = newExtent.width() / newExtent.height(); if ( currentWidthHeightRatio < newWidthHeightRatio ) @@ -871,6 +871,20 @@ QgsRectangle* QgsComposerMap::currentMapExtent() } } +QgsCoordinateReferenceSystem QgsComposerMap::crs() const +{ + if ( mCrs.isValid() ) + return mCrs; + else if ( mComposition && mComposition->project() ) + return mComposition->project()->crs(); + return QgsCoordinateReferenceSystem(); +} + +void QgsComposerMap::setCrs( const QgsCoordinateReferenceSystem& crs ) +{ + mCrs = crs; +} + const QgsRectangle* QgsComposerMap::currentMapExtent() const { //const version @@ -905,7 +919,7 @@ void QgsComposerMap::setNewScale( double scaleDenominator, bool forceUpdate ) //and also apply to the map's original extent (see #9602) //we can't use the scaleRatio calculated earlier, as the scale can vary depending on extent for geographic coordinate systems QgsScaleCalculator calculator; - calculator.setMapUnits( mComposition->mapSettings().mapUnits() ); + calculator.setMapUnits( crs().mapUnits() ); calculator.setDpi( 25.4 ); //QGraphicsView units are mm scaleRatio = scaleDenominator / calculator.calculate( mExtent, rect().width() ); mExtent.scale( scaleRatio ); @@ -1261,6 +1275,13 @@ bool QgsComposerMap::writeXml( QDomElement& elem, QDomDocument & doc ) const extentElem.setAttribute( QStringLiteral( "ymax" ), qgsDoubleToString( mExtent.yMaximum() ) ); composerMapElem.appendChild( extentElem ); + if ( mCrs.isValid() ) + { + QDomElement crsElem = doc.createElement( QStringLiteral( "crs" ) ); + mCrs.writeXml( crsElem, doc ); + composerMapElem.appendChild( crsElem ); + } + // follow map theme composerMapElem.setAttribute( QStringLiteral( "followPreset" ), mFollowVisibilityPreset ? "true" : "false" ); composerMapElem.setAttribute( QStringLiteral( "followPresetName" ), mFollowVisibilityPresetName ); @@ -1362,6 +1383,17 @@ bool QgsComposerMap::readXml( const QDomElement& itemElem, const QDomDocument& d setNewExtent( QgsRectangle( xmin, ymin, xmax, ymax ) ); } + QDomNodeList crsNodeList = itemElem.elementsByTagName( QStringLiteral( "crs" ) ); + if ( !crsNodeList.isEmpty() ) + { + QDomElement crsElem = crsNodeList.at( 0 ).toElement(); + mCrs.readXml( crsElem ); + } + else + { + mCrs = QgsCoordinateReferenceSystem(); + } + //map rotation if ( !qgsDoubleNear( itemElem.attribute( QStringLiteral( "mapRotation" ), QStringLiteral( "0" ) ).toDouble(), 0.0 ) ) { @@ -1826,9 +1858,10 @@ QgsExpressionContext QgsComposerMap::createExpressionContext() const if ( mComposition ) { - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs" ), mComposition->mapSettings().destinationCrs().authid(), true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_definition" ), mComposition->mapSettings().destinationCrs().toProj4(), true ) ); - scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_units" ), QgsUnitTypes::toString( mComposition->mapSettings().mapUnits() ), true ) ); + QgsCoordinateReferenceSystem mapCrs = crs(); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs" ), mapCrs.authid(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_crs_definition" ), mapCrs.toProj4(), true ) ); + scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_units" ), QgsUnitTypes::toString( mapCrs.mapUnits() ), true ) ); } context.appendScope( scope ); @@ -1979,12 +2012,12 @@ QPointF QgsComposerMap::composerMapPosForItem( const QgsAnnotation* annotation ) mapX = annotation->mapPosition().x(); mapY = annotation->mapPosition().y(); - QgsCoordinateReferenceSystem crs = annotation->mapPositionCrs(); + QgsCoordinateReferenceSystem annotationCrs = annotation->mapPositionCrs(); - if ( crs != mComposition->mapSettings().destinationCrs() ) + if ( annotationCrs != crs() ) { //need to reproject - QgsCoordinateTransform t( crs, mComposition->mapSettings().destinationCrs() ); + QgsCoordinateTransform t( annotationCrs, crs() ); double z = 0.0; t.transformInPlace( mapX, mapY, z ); } diff --git a/src/core/composer/qgscomposermap.h b/src/core/composer/qgscomposermap.h index 8cd7863f19b..30cabce7ed1 100644 --- a/src/core/composer/qgscomposermap.h +++ b/src/core/composer/qgscomposermap.h @@ -21,6 +21,7 @@ #include "qgis_core.h" #include "qgscomposeritem.h" #include "qgsrectangle.h" +#include "qgscoordinatereferencesystem.h" #include #include @@ -166,6 +167,38 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem //! @note not available in python bindings QgsRectangle* currentMapExtent(); + /** + * Returns coordinate reference system used for rendering the map. + * This will match the presetCrs() if that is set, or if a preset + * CRS is not set then the map's CRS will follow the composition's + * project's CRS. + * @note added in QGIS 3.0 + * @see presetCrs() + * @see setCrs() + */ + QgsCoordinateReferenceSystem crs() const; + + /** + * Returns the map's preset coordinate reference system. If set, this + * CRS will be used to render the map regardless of any project CRS + * setting. If the returned CRS is not valid then the project CRS + * will be used to render the map. + * @note added in QGIS 3.0 + * @see crs() + * @see setCrs() + */ + QgsCoordinateReferenceSystem presetCrs() const { return mCrs; } + + /** + * Sets the map's preset coordinate reference system. If a valid CRS is + * set, this CRS will be used to render the map regardless of any project CRS + * setting. If the CRS is not valid then the project CRS will be used to render the map. + * @see crs() + * @see presetCrs() + * @note added in QGIS 3.0 + */ + void setCrs( const QgsCoordinateReferenceSystem& crs ); + PreviewMode previewMode() const {return mPreviewMode;} void setPreviewMode( PreviewMode m ); @@ -447,6 +480,9 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem // so that full rectangle in paper is used. QgsRectangle mExtent; + //! Map CRS + QgsCoordinateReferenceSystem mCrs; + // Current temporary map region in map units. This is overwritten when atlas feature changes. It's also // used when the user changes the map extent and an atlas preview is enabled. This allows the user // to manually tweak each atlas preview page without affecting the actual original map extent. diff --git a/src/core/composer/qgscomposermapgrid.cpp b/src/core/composer/qgscomposermapgrid.cpp index e9218f2839d..342976b3271 100644 --- a/src/core/composer/qgscomposermapgrid.cpp +++ b/src/core/composer/qgscomposermapgrid.cpp @@ -636,14 +636,8 @@ void QgsComposerMapGrid::draw( QPainter* p ) p->scale( 1 / dotsPerMM, 1 / dotsPerMM ); //scale painter from mm to dots //setup render context - QgsMapSettings ms = mComposerMap->composition()->mapSettings(); - //context units should be in dots - ms.setOutputSize( QSizeF( mComposerMap->rect().width() * dotsPerMM, mComposerMap->rect().height() * dotsPerMM ).toSize() ); - ms.setExtent( *mComposerMap->currentMapExtent() ); - ms.setOutputDpi( p->device()->logicalDpiX() ); - QgsRenderContext context = QgsRenderContext::fromMapSettings( ms ); + QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, p ); context.setForceVectorOutput( true ); - context.setPainter( p ); QgsExpressionContext expressionContext = createExpressionContext(); context.setExpressionContext( expressionContext ); @@ -651,7 +645,7 @@ void QgsComposerMapGrid::draw( QPainter* p ) QList< QPair< double, QLineF > > horizontalLines; //is grid in a different crs than map? - if ( mGridUnit == MapUnit && mCRS.isValid() && mCRS != ms.destinationCrs() ) + if ( mGridUnit == MapUnit && mCRS.isValid() && mCRS != mComposerMap->crs() ) { drawGridCrsTransform( context, dotsPerMM, horizontalLines, verticalLines ); } @@ -1416,7 +1410,7 @@ QString QgsComposerMapGrid::gridAnnotationString( double value, QgsComposerMapGr } else if ( mComposerMap && mComposerMap->composition() ) { - geographic = mComposerMap->composition()->mapSettings().destinationCrs().isGeographic(); + geographic = mComposerMap->crs().isGeographic(); } if ( geographic && coord == QgsComposerMapGrid::Longitude && @@ -2062,8 +2056,7 @@ void QgsComposerMapGrid::calculateMaxExtension( double& top, double& right, doub } //setup render context - QgsMapSettings ms = mComposerMap->composition()->mapSettings(); - QgsRenderContext context = QgsRenderContext::fromMapSettings( ms ); + QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, nullptr ); QgsExpressionContext expressionContext = createExpressionContext(); context.setExpressionContext( expressionContext ); @@ -2072,7 +2065,7 @@ void QgsComposerMapGrid::calculateMaxExtension( double& top, double& right, doub //collect grid lines QList< QPair< double, QLineF > > verticalLines; QList< QPair< double, QLineF > > horizontalLines; - if ( mGridUnit == MapUnit && mCRS.isValid() && mCRS != ms.destinationCrs() ) + if ( mGridUnit == MapUnit && mCRS.isValid() && mCRS != mComposerMap->crs() ) { drawGridCrsTransform( context, 0, horizontalLines, verticalLines, false ); } @@ -2340,7 +2333,7 @@ int QgsComposerMapGrid::crsGridParams( QgsRectangle& crsRect, QgsCoordinateTrans try { - QgsCoordinateTransform tr( mComposerMap->composition()->mapSettings().destinationCrs(), mCRS ); + QgsCoordinateTransform tr( mComposerMap->crs(), mCRS ); QPolygonF mapPolygon = mComposerMap->transformedMapPolygon(); QRectF mbr = mapPolygon.boundingRect(); QgsRectangle mapBoundingRect( mbr.left(), mbr.bottom(), mbr.right(), mbr.top() ); @@ -2372,7 +2365,7 @@ int QgsComposerMapGrid::crsGridParams( QgsRectangle& crsRect, QgsCoordinateTrans } inverseTransform.setSourceCrs( mCRS ); - inverseTransform.setDestinationCrs( mComposerMap->composition()->mapSettings().destinationCrs() ); + inverseTransform.setDestinationCrs( mComposerMap->crs() ); } catch ( QgsCsException & cse ) { diff --git a/src/core/composer/qgscomposermapoverview.cpp b/src/core/composer/qgscomposermapoverview.cpp index de5d3413fa5..04cad148a1c 100644 --- a/src/core/composer/qgscomposermapoverview.cpp +++ b/src/core/composer/qgscomposermapoverview.cpp @@ -22,6 +22,7 @@ #include "qgssymbol.h" #include "qgsmapsettings.h" #include "qgspainting.h" +#include "qgscomposerutils.h" #include @@ -91,14 +92,8 @@ void QgsComposerMapOverview::draw( QPainter *painter ) double dotsPerMM = painter->device()->logicalDpiX() / 25.4; //setup render context - QgsMapSettings ms = mComposerMap->composition()->mapSettings(); - //context units should be in dots - ms.setOutputSize( QSizeF( mComposerMap->rect().width() * dotsPerMM, mComposerMap->rect().height() * dotsPerMM ).toSize() ); - ms.setExtent( *mComposerMap->currentMapExtent() ); - ms.setOutputDpi( painter->device()->logicalDpiX() ); - QgsRenderContext context = QgsRenderContext::fromMapSettings( ms ); + QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, painter ); context.setForceVectorOutput( true ); - context.setPainter( painter ); QgsExpressionContext expressionContext = createExpressionContext(); context.setExpressionContext( expressionContext ); diff --git a/src/core/composer/qgscomposernodesitem.cpp b/src/core/composer/qgscomposernodesitem.cpp index 8d2a9bc83d9..c3ab475ecda 100644 --- a/src/core/composer/qgscomposernodesitem.cpp +++ b/src/core/composer/qgscomposernodesitem.cpp @@ -137,7 +137,7 @@ void QgsComposerNodesItem::drawNodes( QPainter *painter ) const symbol.data()->setSize( rectSize ); symbol.data()->setAngle( 45 ); - QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, *painter ); + QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, painter ); context.setForceVectorOutput( true ); QgsExpressionContext expressionContext = createExpressionContext(); @@ -168,7 +168,7 @@ void QgsComposerNodesItem::drawSelectedNode( QPainter *painter ) const symbol.reset( QgsMarkerSymbol::createSimple( properties ) ); symbol.data()->setSize( rectSize ); - QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, *painter ); + QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, painter ); context.setForceVectorOutput( true ); QgsExpressionContext expressionContext = createExpressionContext(); diff --git a/src/core/composer/qgscomposerpicture.cpp b/src/core/composer/qgscomposerpicture.cpp index 6f802be2e43..fdc3de53325 100644 --- a/src/core/composer/qgscomposerpicture.cpp +++ b/src/core/composer/qgscomposerpicture.cpp @@ -426,7 +426,7 @@ void QgsComposerPicture::updateMapRotation() case TrueNorth: { QgsPoint center = mRotationMap->currentMapExtent()->center(); - QgsCoordinateReferenceSystem crs = mComposition->mapSettings().destinationCrs(); + QgsCoordinateReferenceSystem crs = mRotationMap->crs(); try { diff --git a/src/core/composer/qgscomposerpolygon.cpp b/src/core/composer/qgscomposerpolygon.cpp index d1cb2f4bd20..e9077552e90 100644 --- a/src/core/composer/qgscomposerpolygon.cpp +++ b/src/core/composer/qgscomposerpolygon.cpp @@ -73,7 +73,7 @@ void QgsComposerPolygon::_draw( QPainter *painter ) //setup painter scaling to dots so that raster symbology is drawn to scale const double dotsPerMM = painter->device()->logicalDpiX() / 25.4; - QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, *painter ); + QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, painter ); context.setForceVectorOutput( true ); context.setExpressionContext( createExpressionContext() ); diff --git a/src/core/composer/qgscomposerpolyline.cpp b/src/core/composer/qgscomposerpolyline.cpp index ae72ced2255..3d2efc6b171 100644 --- a/src/core/composer/qgscomposerpolyline.cpp +++ b/src/core/composer/qgscomposerpolyline.cpp @@ -100,7 +100,7 @@ void QgsComposerPolyline::_draw( QPainter *painter ) { double dotsPerMM = painter->device()->logicalDpiX() / 25.4; - QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, *painter ); + QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, painter ); context.setForceVectorOutput( true ); QgsExpressionContext expressionContext = createExpressionContext(); diff --git a/src/core/composer/qgscomposerscalebar.cpp b/src/core/composer/qgscomposerscalebar.cpp index 2e3d3c7a070..83c5db173f9 100644 --- a/src/core/composer/qgscomposerscalebar.cpp +++ b/src/core/composer/qgscomposerscalebar.cpp @@ -303,7 +303,7 @@ double QgsComposerScaleBar::mapWidth() const { QgsDistanceArea da; da.setEllipsoidalMode( true ); - da.setSourceCrs( mComposition->mapSettings().destinationCrs().srsid() ); + da.setSourceCrs( mComposerMap->crs().srsid() ); da.setEllipsoid( mComposition->project()->ellipsoid() ); QgsUnitTypes::DistanceUnit units = QgsUnitTypes::DistanceMeters; diff --git a/src/core/composer/qgscomposershape.cpp b/src/core/composer/qgscomposershape.cpp index db093fc2923..3f64574ed99 100644 --- a/src/core/composer/qgscomposershape.cpp +++ b/src/core/composer/qgscomposershape.cpp @@ -179,7 +179,7 @@ void QgsComposerShape::drawShapeUsingSymbol( QPainter* p ) double dotsPerMM = p->device()->logicalDpiX() / 25.4; //setup render context - QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, *p ); + QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, p ); context.setForceVectorOutput( true ); QgsExpressionContext expressionContext = createExpressionContext(); context.setExpressionContext( expressionContext ); diff --git a/src/core/composer/qgscomposerutils.cpp b/src/core/composer/qgscomposerutils.cpp index 321e70120cd..6ce9d6e4236 100644 --- a/src/core/composer/qgscomposerutils.cpp +++ b/src/core/composer/qgscomposerutils.cpp @@ -548,15 +548,16 @@ void QgsComposerUtils::drawText( QPainter *painter, const QRectF &rect, const QS painter->restore(); } -QgsRenderContext QgsComposerUtils::createRenderContext( QgsComposition* composition, QPainter &painter ) +QgsRenderContext QgsComposerUtils::createRenderContext( QgsComposition* composition, QPainter* painter ) { QgsComposerMap* referenceMap = composition ? composition->referenceMap() : nullptr; if ( !referenceMap ) { - return QgsRenderContext::fromQPainter( &painter ); + return QgsRenderContext::fromQPainter( painter ); } - int dpi = painter.device()->logicalDpiX(); + // default to 88 dpi if no painter specified + int dpi = ( painter && painter->device() ) ? painter->device()->logicalDpiX() : 88; double dotsPerMM = dpi / 25.4; // get map settings from reference map @@ -565,6 +566,7 @@ QgsRenderContext QgsComposerUtils::createRenderContext( QgsComposition* composit QgsMapSettings ms = referenceMap->mapSettings( extent, mapSizeMM * dotsPerMM, dpi ); QgsRenderContext context = QgsRenderContext::fromMapSettings( ms ); - context.setPainter( &painter ); + if ( painter ) + context.setPainter( painter ); return context; } diff --git a/src/core/composer/qgscomposerutils.h b/src/core/composer/qgscomposerutils.h index 106bde3b8e2..6d1385ce4e9 100644 --- a/src/core/composer/qgscomposerutils.h +++ b/src/core/composer/qgscomposerutils.h @@ -272,7 +272,7 @@ class CORE_EXPORT QgsComposerUtils * QgsComposition::referenceMap(). * @note added in QGIS 3.0 */ - static QgsRenderContext createRenderContext( QgsComposition* composition, QPainter& painter ); + static QgsRenderContext createRenderContext( QgsComposition* composition, QPainter* painter ); }; diff --git a/src/core/composer/qgscomposition.cpp b/src/core/composer/qgscomposition.cpp index cc6958954fc..af469ee9959 100644 --- a/src/core/composer/qgscomposition.cpp +++ b/src/core/composer/qgscomposition.cpp @@ -2832,6 +2832,12 @@ bool QgsComposition::exportAsPDF( const QString& file ) void QgsComposition::georeferenceOutput( const QString& file, QgsComposerMap* map, const QRectF& exportRegion, double dpi ) const { + if ( !map ) + map = referenceMap(); + + if ( !map ) + return; // no reference map + if ( dpi < 0 ) dpi = printResolution(); @@ -2850,7 +2856,7 @@ void QgsComposition::georeferenceOutput( const QString& file, QgsComposerMap* ma //TODO - metadata can be set here, e.g.: GDALSetMetadataItem( outputDS, "AUTHOR", "me", nullptr ); #endif - GDALSetProjection( outputDS, mMapSettings.destinationCrs().toWkt().toLocal8Bit().constData() ); + GDALSetProjection( outputDS, map->crs().toWkt().toLocal8Bit().constData() ); GDALClose( outputDS ); } CPLSetConfigOption( "GDAL_PDF_DPI", nullptr ); diff --git a/src/core/composer/qgspaperitem.cpp b/src/core/composer/qgspaperitem.cpp index eb975795ecf..5603ca0372b 100644 --- a/src/core/composer/qgspaperitem.cpp +++ b/src/core/composer/qgspaperitem.cpp @@ -156,7 +156,7 @@ void QgsPaperItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* ite double dotsPerMM = painter->device()->logicalDpiX() / 25.4; //setup render context - QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, *painter ); + QgsRenderContext context = QgsComposerUtils::createRenderContext( mComposition, painter ); context.setForceVectorOutput( true ); QgsExpressionContext expressionContext = createExpressionContext(); diff --git a/src/gui/qgscomposerview.cpp b/src/gui/qgscomposerview.cpp index 4d2ecd33834..bf68bcff945 100644 --- a/src/gui/qgscomposerview.cpp +++ b/src/gui/qgscomposerview.cpp @@ -44,7 +44,7 @@ #include "qgscomposerattributetablev2.h" #include "qgsaddremovemultiframecommand.h" #include "qgspaperitem.h" -#include "qgsmapcanvas.h" //for QgsMapCanvas::WheelAction +#include "qgsmapcanvas.h" #include "qgscursors.h" #include "qgscomposerutils.h" @@ -997,6 +997,9 @@ void QgsComposerView::mouseReleaseEvent( QMouseEvent* e ) else { QgsComposerMap* composerMap = new QgsComposerMap( composition(), mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() ); + if ( mCanvas ) + composerMap->zoomToExtent( mCanvas->mapSettings().visibleExtent() ); + composition()->addComposerMap( composerMap ); composition()->setAllDeselected(); @@ -2103,6 +2106,16 @@ void QgsComposerView::setPreviewMode( QgsPreviewEffect::PreviewMode mode ) mPreviewEffect->setMode( mode ); } +void QgsComposerView::setMapCanvas( QgsMapCanvas* canvas ) +{ + mCanvas = canvas; +} + +QgsMapCanvas*QgsComposerView::mapCanvas() const +{ + return mCanvas; +} + void QgsComposerView::paintEvent( QPaintEvent* event ) { if ( mPaintingEnabled ) diff --git a/src/gui/qgscomposerview.h b/src/gui/qgscomposerview.h index 45f3fac2a05..1cdcd38b4af 100644 --- a/src/gui/qgscomposerview.h +++ b/src/gui/qgscomposerview.h @@ -39,6 +39,7 @@ class QgsComposerScaleBar; class QgsComposerShape; class QgsComposerNodesItem; class QgsComposerAttributeTableV2; +class QgsMapCanvas; /** \ingroup gui * Widget to display the composer items. Manages the composer tools and the @@ -170,6 +171,20 @@ class GUI_EXPORT QgsComposerView: public QGraphicsView */ void setPreviewMode( QgsPreviewEffect::PreviewMode mode ); + /** Sets the map canvas associated with the view. This allows the + * view to retrieve map settings from the canvas. + * @note added in QGIS 3.0 + * @see mapCanvas() + */ + void setMapCanvas( QgsMapCanvas* canvas ); + + /** + * Returns the map canvas associated with the view. + * @see setMapCanvas() + * @note added in QGIS 3.0 + */ + QgsMapCanvas* mapCanvas() const; + protected: void mousePressEvent( QMouseEvent* ) override; void mouseReleaseEvent( QMouseEvent* ) override; @@ -218,6 +233,8 @@ class GUI_EXPORT QgsComposerView: public QGraphicsView QgsComposerRuler* mHorizontalRuler; QgsComposerRuler* mVerticalRuler; + QgsMapCanvas* mCanvas = nullptr; + //! Draw a shape on the canvas void addShape( Tool currentTool ); diff --git a/src/gui/qgsprojectionselectionwidget.cpp b/src/gui/qgsprojectionselectionwidget.cpp index f264d3d1351..09781b4d85a 100644 --- a/src/gui/qgsprojectionselectionwidget.cpp +++ b/src/gui/qgsprojectionselectionwidget.cpp @@ -223,6 +223,7 @@ void QgsProjectionSelectionWidget::comboIndexChanged( int idx ) } case QgsProjectionSelectionWidget::CrsNotSet: emit cleared(); + emit crsChanged( QgsCoordinateReferenceSystem() ); return; } } diff --git a/src/server/qgswmsconfigparser.cpp b/src/server/qgswmsconfigparser.cpp index cde490fba7f..3175c9d552a 100644 --- a/src/server/qgswmsconfigparser.cpp +++ b/src/server/qgswmsconfigparser.cpp @@ -79,6 +79,12 @@ QgsComposition* QgsWmsConfigParser::createPrintComposition( const QString& compo continue; } + // Change CRS of map set to "project CRS" to match requested CRS + // (if map has a valid preset crs then we keep this crs and don't use the + // requested crs for this map item) + if ( mapSettings.destinationCrs().isValid() && !currentMap->presetCrs().isValid() ) + currentMap->setCrs( mapSettings.destinationCrs() ); + QStringList coordList = extent.split( QStringLiteral( "," ) ); if ( coordList.size() < 4 ) { diff --git a/src/ui/composer/qgscomposermapwidgetbase.ui b/src/ui/composer/qgscomposermapwidgetbase.ui index 1bcfb7ed0c8..d9cc3a17917 100644 --- a/src/ui/composer/qgscomposermapwidgetbase.ui +++ b/src/ui/composer/qgscomposermapwidgetbase.ui @@ -64,8 +64,8 @@ 0 0 - 364 - 1248 + 366 + 1241 @@ -90,37 +90,6 @@ false - - - - - - - 0 - 0 - - - - - - - - Update preview - - - - - - - - - Scale - - - mScaleLineEdit - - - @@ -167,13 +136,58 @@ - + Draw map canvas items + + + + + + + 0 + 0 + + + + + + + + Update preview + + + + + + + + + Scale + + + mScaleLineEdit + + + + + + + CRS + + + + + + + Qt::StrongFocus + + + @@ -805,6 +819,12 @@ QComboBox
qgscomposeritemcombobox.h
+ + QgsProjectionSelectionWidget + QWidget +
qgsprojectionselectionwidget.h
+ 1 +
groupBox @@ -815,6 +835,7 @@ mScaleDDBtn mMapRotationSpinBox mMapRotationDDBtn + mCrsSelector mDrawCanvasItemsCheckBox mFollowVisibilityPresetCheckBox mFollowVisibilityPresetCombo @@ -858,6 +879,8 @@ mOverviewBlendModeComboBox mOverviewInvertCheckbox mOverviewCenterCheckbox + mDrawGridCheckBox + mGridPropertiesButton diff --git a/tests/src/core/testqgsatlascomposition.cpp b/tests/src/core/testqgsatlascomposition.cpp index f8486775250..452a9d5b23d 100644 --- a/tests/src/core/testqgsatlascomposition.cpp +++ b/tests/src/core/testqgsatlascomposition.cpp @@ -146,7 +146,7 @@ void TestQgsAtlasComposition::init() // select epsg:2154 QgsCoordinateReferenceSystem crs; crs.createFromSrid( 2154 ); - mMapSettings->setDestinationCrs( crs ); + QgsProject::instance()->setCrs( crs ); mComposition = new QgsComposition( *mMapSettings, QgsProject::instance() ); mComposition->setPaperSize( 297, 210 ); //A4 landscape diff --git a/tests/src/core/testqgscomposerscalebar.cpp b/tests/src/core/testqgscomposerscalebar.cpp index 6076094fe0c..efa220412a2 100644 --- a/tests/src/core/testqgscomposerscalebar.cpp +++ b/tests/src/core/testqgscomposerscalebar.cpp @@ -75,6 +75,10 @@ void TestQgsComposerScaleBar::initTestCase() // so we enforce C locale to make sure we get expected result QLocale::setDefault( QLocale::c() ); + //reproject to WGS84 + QgsCoordinateReferenceSystem destCRS; + destCRS.createFromId( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ); + QgsProject::instance()->setCrs( destCRS ); QgsProject::instance()->setEllipsoid( QStringLiteral( "WGS84" ) ); mMapSettings = new QgsMapSettings(); @@ -89,12 +93,6 @@ void TestQgsComposerScaleBar::initTestCase() //create composition with composer map mMapSettings->setLayers( QList() << mRasterLayer ); - //reproject to WGS84 - QgsCoordinateReferenceSystem destCRS; - destCRS.createFromId( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ); - mMapSettings->setDestinationCrs( destCRS ); - mMapSettings->setCrsTransformEnabled( true ); - mComposition = new QgsComposition( *mMapSettings, QgsProject::instance() ); mComposition->setPaperSize( 297, 210 ); //A4 landscape mComposerMap = new QgsComposerMap( mComposition, 20, 20, 150, 150 ); diff --git a/tests/src/core/testqgscomposerutils.cpp b/tests/src/core/testqgscomposerutils.cpp index e560b50c8f7..79143805eeb 100644 --- a/tests/src/core/testqgscomposerutils.cpp +++ b/tests/src/core/testqgscomposerutils.cpp @@ -712,19 +712,29 @@ void TestQgsComposerUtils::createRenderContext() QPainter p( &testImage ); // no composition - QgsRenderContext rc = QgsComposerUtils::createRenderContext( nullptr, p ); + QgsRenderContext rc = QgsComposerUtils::createRenderContext( nullptr, &p ); QGSCOMPARENEAR( rc.scaleFactor(), 150 / 25.4, 0.001 ); QCOMPARE( rc.painter(), &p ); + // no composition, no painter + rc = QgsComposerUtils::createRenderContext( nullptr, nullptr ); + QGSCOMPARENEAR( rc.scaleFactor(), 88 / 25.4, 0.001 ); + QVERIFY( !rc.painter() ); + //create composition with no reference map QgsRectangle extent( 2000, 2800, 2500, 2900 ); QgsMapSettings ms; ms.setExtent( extent ); QgsComposition* composition = new QgsComposition( ms, QgsProject::instance() ); - rc = QgsComposerUtils::createRenderContext( composition, p ); + rc = QgsComposerUtils::createRenderContext( composition, &p ); QGSCOMPARENEAR( rc.scaleFactor(), 150 / 25.4, 0.001 ); QCOMPARE( rc.painter(), &p ); + // composition, no map, no painter + rc = QgsComposerUtils::createRenderContext( composition, nullptr ); + QGSCOMPARENEAR( rc.scaleFactor(), 88 / 25.4, 0.001 ); + QVERIFY( !rc.painter() ); + // add a reference map QgsComposerMap* map = new QgsComposerMap( composition ); map->setNewExtent( extent ); @@ -732,11 +742,17 @@ void TestQgsComposerUtils::createRenderContext() composition->addComposerMap( map ); composition->setReferenceMap( map ); - rc = QgsComposerUtils::createRenderContext( composition, p ); + rc = QgsComposerUtils::createRenderContext( composition, &p ); QGSCOMPARENEAR( rc.scaleFactor(), 150 / 25.4, 0.001 ); - QGSCOMPARENEAR( rc.rendererScale(), map->scale(), 100000 ); + QGSCOMPARENEAR( rc.rendererScale(), map->scale(), 1000000 ); QCOMPARE( rc.painter(), &p ); + // composition, reference map, no painter + rc = QgsComposerUtils::createRenderContext( composition, nullptr ); + QGSCOMPARENEAR( rc.scaleFactor(), 88 / 25.4, 0.001 ); + QGSCOMPARENEAR( rc.rendererScale(), map->scale(), 1000000 ); + QVERIFY( !rc.painter() ); + p.end(); } diff --git a/tests/src/core/testqgscomposition.cpp b/tests/src/core/testqgscomposition.cpp index 4798d8dedea..3c7ea7f7358 100644 --- a/tests/src/core/testqgscomposition.cpp +++ b/tests/src/core/testqgscomposition.cpp @@ -618,6 +618,7 @@ void TestQgsComposition::itemVariablesFunction() QgsComposerMap* map = new QgsComposerMap( composition ); map->setNewExtent( extent ); map->setSceneRect( QRectF( 30, 60, 200, 100 ) ); + map->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) ); composition->addComposerMap( map ); map->setId( "map_id" ); diff --git a/tests/src/python/test_qgsatlascomposition.py b/tests/src/python/test_qgsatlascomposition.py index 91dc700cc2b..080abd62f35 100644 --- a/tests/src/python/test_qgsatlascomposition.py +++ b/tests/src/python/test_qgsatlascomposition.py @@ -46,13 +46,11 @@ class TestQgsAtlasComposition(unittest.TestCase): self.mapSettings = QgsMapSettings() layerStringList = [mVectorLayer] self.mapSettings.setLayers(layerStringList) - self.mapSettings.setCrsTransformEnabled(True) - self.mapSettings.setMapUnits(QgsUnitTypes.DistanceMeters) # select epsg:2154 crs = QgsCoordinateReferenceSystem() crs.createFromSrid(2154) - self.mapSettings.setDestinationCrs(crs) + QgsProject.instance().setCrs(crs) self.mComposition = QgsComposition(self.mapSettings, QgsProject.instance()) self.mComposition.setPaperSize(297, 210) diff --git a/tests/src/python/test_qgscomposermap.py b/tests/src/python/test_qgscomposermap.py index 8a13d1476d4..e37c4612889 100644 --- a/tests/src/python/test_qgscomposermap.py +++ b/tests/src/python/test_qgscomposermap.py @@ -23,10 +23,12 @@ from qgis.PyQt.QtGui import QPainter from qgis.core import (QgsComposerMap, QgsRectangle, QgsRasterLayer, + QgsVectorLayer, QgsComposition, QgsMapSettings, QgsProject, QgsMultiBandColorRenderer, + QgsCoordinateReferenceSystem ) from qgis.testing import start_app, unittest @@ -44,18 +46,25 @@ class TestQgsComposerMap(unittest.TestCase): unittest.TestCase.__init__(self, methodName) myPath = os.path.join(TEST_DATA_DIR, 'rgb256x256.png') rasterFileInfo = QFileInfo(myPath) - mRasterLayer = QgsRasterLayer(rasterFileInfo.filePath(), - rasterFileInfo.completeBaseName()) + self.raster_layer = QgsRasterLayer(rasterFileInfo.filePath(), + rasterFileInfo.completeBaseName()) rasterRenderer = QgsMultiBandColorRenderer( - mRasterLayer.dataProvider(), 1, 2, 3) - mRasterLayer.setRenderer(rasterRenderer) + self.raster_layer.dataProvider(), 1, 2, 3) + self.raster_layer.setRenderer(rasterRenderer) + + myPath = os.path.join(TEST_DATA_DIR, 'points.shp') + vector_file_info = QFileInfo(myPath) + self.vector_layer = QgsVectorLayer(vector_file_info.filePath(), + vector_file_info.completeBaseName(), 'ogr') + assert self.vector_layer.isValid() + #pipe = mRasterLayer.pipe() #assert pipe.set(rasterRenderer), 'Cannot set pipe renderer' - QgsProject.instance().addMapLayers([mRasterLayer]) + QgsProject.instance().addMapLayers([self.raster_layer, self.vector_layer]) # create composition with composer map self.mMapSettings = QgsMapSettings() - self.mMapSettings.setLayers([mRasterLayer]) + self.mMapSettings.setLayers([self.raster_layer]) self.mMapSettings.setCrsTransformEnabled(False) self.mComposition = QgsComposition(self.mMapSettings, QgsProject.instance()) self.mComposition.setPaperSize(297, 210) @@ -131,6 +140,49 @@ class TestQgsComposerMap(unittest.TestCase): self.mComposition.removeComposerItem(overviewMap) assert myTestResult, myMessage + def testMapCrs(self): + # create composition with composer map + map_settings = QgsMapSettings() + map_settings.setLayers([self.vector_layer]) + composition = QgsComposition(map_settings, QgsProject.instance()) + composition.setPaperSize(297, 210) + + # check that new maps inherit project CRS + QgsProject.instance().setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) + map = QgsComposerMap(composition, 20, 20, 200, 100) + map.setFrameEnabled(True) + rectangle = QgsRectangle(-13838977, 2369660, -8672298, 6250909) + map.setNewExtent(rectangle) + composition.addComposerMap(map) + + self.assertEqual(map.crs().authid(), 'EPSG:4326') + self.assertFalse(map.presetCrs().isValid()) + + # overwrite CRS + map.setCrs(QgsCoordinateReferenceSystem('EPSG:3857')) + self.assertEqual(map.crs().authid(), 'EPSG:3857') + self.assertEqual(map.presetCrs().authid(), 'EPSG:3857') + checker = QgsCompositionChecker('composermap_crs3857', composition) + checker.setControlPathPrefix("composer_map") + result, message = checker.testComposition() + self.assertTrue(result, message) + + # overwrite CRS + map.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) + self.assertEqual(map.presetCrs().authid(), 'EPSG:4326') + self.assertEqual(map.crs().authid(), 'EPSG:4326') + rectangle = QgsRectangle(-124, 17, -78, 52) + map.zoomToExtent(rectangle) + checker = QgsCompositionChecker('composermap_crs4326', composition) + checker.setControlPathPrefix("composer_map") + result, message = checker.testComposition() + self.assertTrue(result, message) + + # change back to project CRS + map.setCrs(QgsCoordinateReferenceSystem()) + self.assertEqual(map.crs().authid(), 'EPSG:4326') + self.assertFalse(map.presetCrs().isValid()) + # Fails because addItemsFromXml has been commented out in sip @unittest.expectedFailure def testuniqueId(self): diff --git a/tests/src/python/test_qgscomposerpicture.py b/tests/src/python/test_qgscomposerpicture.py index 1cc0e7d6b99..2f4942596e8 100644 --- a/tests/src/python/test_qgscomposerpicture.py +++ b/tests/src/python/test_qgscomposerpicture.py @@ -119,10 +119,10 @@ class TestQgsComposerPicture(unittest.TestCase): """Test syncing picture to true north""" mapSettings = QgsMapSettings() - mapSettings.setDestinationCrs(QgsCoordinateReferenceSystem.fromEpsgId(3575)) composition = QgsComposition(mapSettings, QgsProject.instance()) composerMap = QgsComposerMap(composition) + composerMap.setCrs(QgsCoordinateReferenceSystem.fromEpsgId(3575)) composerMap.setNewExtent(QgsRectangle(-2126029.962, -2200807.749, -119078.102, -757031.156)) composition.addComposerMap(composerMap) diff --git a/tests/testdata/control_images/composer_map/expected_composermap_crs3857/expected_composermap_crs3857.png b/tests/testdata/control_images/composer_map/expected_composermap_crs3857/expected_composermap_crs3857.png new file mode 100644 index 00000000000..cd92a0bbf6f Binary files /dev/null and b/tests/testdata/control_images/composer_map/expected_composermap_crs3857/expected_composermap_crs3857.png differ diff --git a/tests/testdata/control_images/composer_map/expected_composermap_crs4326/expected_composermap_crs4326.png b/tests/testdata/control_images/composer_map/expected_composermap_crs4326/expected_composermap_crs4326.png new file mode 100644 index 00000000000..494997d073f Binary files /dev/null and b/tests/testdata/control_images/composer_map/expected_composermap_crs4326/expected_composermap_crs4326.png differ