Skip to content

Commit ce8a9ba

Browse files
author
Sandro Santilli
committedDec 7, 2014
Add support for map rotation (hub #9330)
Includes widget to show and set map rotation. Handle rotation in vector and raster renderers. Ensure correct behavior of panning and zooming actions. Drop compile-time defines for ARM and ANDROID, leaving only the qreal based function to transform in place. Update expected test results after eye comparison.
1 parent b774853 commit ce8a9ba

File tree

28 files changed

+486
-125
lines changed

28 files changed

+486
-125
lines changed
 

‎src/app/qgisapp.cpp

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <QApplication>
2525
#include <QBitmap>
2626
#include <QCheckBox>
27+
#include <QSpinBox>
2728
#include <QClipboard>
2829
#include <QColor>
2930
#include <QCursor>
@@ -1732,6 +1733,37 @@ void QgisApp::createStatusBar()
17321733
statusBar()->addPermanentWidget( mScaleEdit, 0 );
17331734
connect( mScaleEdit, SIGNAL( scaleChanged() ), this, SLOT( userScale() ) );
17341735

1736+
// add a widget to show/set current rotation
1737+
mRotationLabel = new QLabel( QString(), statusBar() );
1738+
mRotationLabel->setObjectName( "mRotationLabel" );
1739+
mRotationLabel->setFont( myFont );
1740+
mRotationLabel->setMinimumWidth( 10 );
1741+
mRotationLabel->setMaximumHeight( 20 );
1742+
mRotationLabel->setMargin( 3 );
1743+
mRotationLabel->setAlignment( Qt::AlignCenter );
1744+
mRotationLabel->setFrameStyle( QFrame::NoFrame );
1745+
mRotationLabel->setText( tr( "Rotation:" ) );
1746+
mRotationLabel->setToolTip( tr( "Current clockwise map rotation in degrees" ) );
1747+
statusBar()->addPermanentWidget( mRotationLabel, 0 );
1748+
1749+
mRotationEdit = new QSpinBox( statusBar() );
1750+
mRotationEdit->setObjectName( "mRotationEdit" );
1751+
mRotationEdit->setMaximumWidth( 100 );
1752+
mRotationEdit->setMaximumHeight( 20 );
1753+
mRotationEdit->setRange(-180, 180);
1754+
mRotationEdit->setWrapping(true);
1755+
mRotationEdit->setSingleStep(5.0);
1756+
mRotationEdit->setFont( myFont );
1757+
mRotationEdit->setWhatsThis( tr( "Shows the current map clockwise rotation "
1758+
"in degrees. It also allows editing to set "
1759+
"the rotation") );
1760+
mRotationEdit->setToolTip( tr( "Current clockwise map rotation in degrees" ) );
1761+
statusBar()->addPermanentWidget( mRotationEdit, 0 );
1762+
connect( mRotationEdit, SIGNAL( valueChanged(int) ), this, SLOT( userRotation() ) );
1763+
1764+
showRotation();
1765+
1766+
17351767
// render suppression status bar widget
17361768
mRenderSuppressionCBox = new QCheckBox( tr( "Render" ), statusBar() );
17371769
mRenderSuppressionCBox->setObjectName( "mRenderSuppressionCBox" );
@@ -1975,6 +2007,8 @@ void QgisApp::setupConnections()
19752007
this, SLOT( showExtents() ) );
19762008
connect( mMapCanvas, SIGNAL( scaleChanged( double ) ),
19772009
this, SLOT( showScale( double ) ) );
2010+
connect( mMapCanvas, SIGNAL( rotationChanged( double ) ),
2011+
this, SLOT( showRotation() ) );
19782012
connect( mMapCanvas, SIGNAL( scaleChanged( double ) ),
19792013
this, SLOT( updateMouseCoordinatePrecision() ) );
19802014
connect( mMapCanvas, SIGNAL( mapToolSet( QgsMapTool *, QgsMapTool * ) ),
@@ -6857,14 +6891,14 @@ void QgisApp::userCenter()
68576891
if ( !yOk )
68586892
return;
68596893

6860-
QgsRectangle r = mMapCanvas->extent();
6894+
mMapCanvas->setCenter( QgsPoint( x, y ) );
6895+
mMapCanvas->refresh();
6896+
}
68616897

6862-
mMapCanvas->setExtent(
6863-
QgsRectangle(
6864-
x - r.width() / 2.0, y - r.height() / 2.0,
6865-
x + r.width() / 2.0, y + r.height() / 2.0
6866-
)
6867-
);
6898+
void QgisApp::userRotation()
6899+
{
6900+
double degrees = mRotationEdit->value();
6901+
mMapCanvas->setRotation(degrees);
68686902
mMapCanvas->refresh();
68696903
}
68706904

@@ -8752,6 +8786,13 @@ void QgisApp::showExtents()
87528786
}
87538787
} // QgisApp::showExtents
87548788

8789+
void QgisApp::showRotation()
8790+
{
8791+
// update the statusbar with the current rotation.
8792+
double myrotation = mMapCanvas->rotation();
8793+
mRotationEdit->setValue( myrotation );
8794+
} // QgisApp::showRotation
8795+
87558796

87568797
void QgisApp::updateMouseCoordinatePrecision()
87578798
{

‎src/app/qgisapp.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class QProgressBar;
3030
class QPushButton;
3131
class QRect;
3232
class QSettings;
33+
class QSpinBox;
3334
class QSplashScreen;
3435
class QStringList;
3536
class QToolButton;
@@ -687,6 +688,9 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
687688
void userScale();
688689
//! Slot to handle user center input;
689690
void userCenter();
691+
//! Slot to handle user rotation input;
692+
//! @note added in 2.8
693+
void userRotation();
690694
//! Remove a layer from the map and legend
691695
void removeLayer();
692696
/** Duplicate map layer(s) in legend */
@@ -1021,6 +1025,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
10211025
void showProgress( int theProgress, int theTotalSteps );
10221026
void extentsViewToggled( bool theFlag );
10231027
void showExtents();
1028+
void showRotation();
10241029
void showStatusMessage( QString theMessage );
10251030
void displayMapToolMessage( QString message, QgsMessageBar::MessageLevel level = QgsMessageBar::INFO );
10261031
void removeMapToolMessage();
@@ -1430,6 +1435,12 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
14301435
QLineEdit * mCoordsEdit;
14311436
//! The validator for the mCoordsEdit
14321437
QValidator * mCoordsEditValidator;
1438+
//! Widget that will live on the statusbar to display "Rotation"
1439+
QLabel * mRotationLabel;
1440+
//! Widget that will live in the statusbar to display and edit rotation
1441+
QSpinBox * mRotationEdit;
1442+
//! The validator for the mCoordsEdit
1443+
QValidator * mRotationEditValidator;
14331444
//! Widget that will live in the statusbar to show progress of operations
14341445
QProgressBar * mProgressBar;
14351446
//! Widget used to suppress rendering

‎src/core/qgsmaprenderer.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
QgsMapRenderer::QgsMapRenderer()
4343
{
4444
mScale = 1.0;
45+
mRotation = 0.0;
4546
mScaleCalculator = new QgsScaleCalculator;
4647
mDistArea = new QgsDistanceArea;
4748

@@ -121,6 +122,18 @@ bool QgsMapRenderer::setExtent( const QgsRectangle& extent )
121122
return true;
122123
}
123124

125+
void QgsMapRenderer::setRotation( double rotation )
126+
{
127+
mRotation = rotation;
128+
// TODO: adjust something ?
129+
130+
emit rotationChanged( rotation );
131+
}
132+
133+
double QgsMapRenderer::rotation( ) const
134+
{
135+
return mRotation;
136+
}
124137

125138

126139
void QgsMapRenderer::setOutputSize( QSize size, int dpi )

‎src/core/qgsmaprenderer.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,15 @@ class CORE_EXPORT QgsMapRenderer : public QObject
184184
//! returns current extent
185185
QgsRectangle extent() const;
186186

187+
//! sets rotation
188+
//! value in clockwise degrees
189+
//! @note added in 2.8
190+
void setRotation( double degrees );
191+
192+
//! returns current rotation in clockwise degrees
193+
//! @note added in 2.8
194+
double rotation() const;
195+
187196
const QgsMapToPixel* coordinateTransform() { return &( mRenderContext.mapToPixel() ); }
188197

189198
//! Scale denominator
@@ -353,6 +362,10 @@ class CORE_EXPORT QgsMapRenderer : public QObject
353362
//! @note added in 2.4
354363
void extentsChanged();
355364

365+
//! emitted when the current rotation gets changed
366+
//! @note added in 2.8
367+
void rotationChanged( double );
368+
356369
//! Notifies higher level components to show the datum transform dialog and add a QgsLayerCoordinateTransformInfo for that layer
357370
void datumTransformInfoRequested( const QgsMapLayer* ml, const QString& srcAuthId, const QString& destAuthId ) const;
358371

@@ -376,6 +389,9 @@ class CORE_EXPORT QgsMapRenderer : public QObject
376389
//! Map scale denominator at its current zoom level
377390
double mScale;
378391

392+
//! Map rotation
393+
double mRotation;
394+
379395
//! scale calculator
380396
QgsScaleCalculator * mScaleCalculator;
381397

‎src/core/qgsmapsettings.cpp

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ QgsMapSettings::QgsMapSettings()
3434
: mDpi( qt_defaultDpiX() ) // DPI that will be used by default for QImage instances
3535
, mSize( QSize( 0, 0 ) )
3636
, mExtent()
37+
, mRotation( 0.0 )
3738
, mProjectionsEnabled( false )
3839
, mDestCRS( GEOCRS_ID, QgsCoordinateReferenceSystem::InternalCrsId ) // WGS 84
3940
, mDatumTransformStore( mDestCRS )
@@ -61,6 +62,21 @@ void QgsMapSettings::setExtent( const QgsRectangle& extent )
6162
updateDerived();
6263
}
6364

65+
double QgsMapSettings::rotation() const
66+
{
67+
return mRotation;
68+
}
69+
70+
void QgsMapSettings::setRotation( double degrees )
71+
{
72+
if ( mRotation == degrees ) return;
73+
74+
mRotation = degrees;
75+
76+
// TODO: update extent while keeping scale ?
77+
updateDerived();
78+
}
79+
6480

6581
void QgsMapSettings::updateDerived()
6682
{
@@ -142,14 +158,30 @@ void QgsMapSettings::updateDerived()
142158
mScale = mScaleCalculator.calculate( mVisibleExtent, mSize.width() );
143159

144160
mMapToPixel = QgsMapToPixel( mapUnitsPerPixel(), outputSize().height(), visibleExtent().yMinimum(), visibleExtent().xMinimum() );
161+
mMapToPixel.setMapRotation( mRotation, visibleExtent().center().x(), visibleExtent().center().y() );
162+
163+
#if 1 // set visible extent taking rotation in consideration
164+
if ( mRotation ) {
165+
QgsPoint p1 = mMapToPixel.toMapCoordinates( QPoint(0,0) );
166+
QgsPoint p2 = mMapToPixel.toMapCoordinates( QPoint(0,myHeight) );
167+
QgsPoint p3 = mMapToPixel.toMapCoordinates( QPoint(myWidth,0) );
168+
QgsPoint p4 = mMapToPixel.toMapCoordinates( QPoint(myWidth,myHeight) );
169+
dxmin = std::min(p1.x(), std::min(p2.x(), std::min(p3.x(), p4.x())));
170+
dymin = std::min(p1.y(), std::min(p2.y(), std::min(p3.y(), p4.y())));
171+
dxmax = std::max(p1.x(), std::max(p2.x(), std::max(p3.x(), p4.x())));
172+
dymax = std::max(p1.y(), std::max(p2.y(), std::max(p3.y(), p4.y())));
173+
mVisibleExtent.set( dxmin, dymin, dxmax, dymax );
174+
}
175+
#endif
145176

146177
QgsDebugMsg( QString( "Map units per pixel (x,y) : %1, %2" ).arg( qgsDoubleToString( mapUnitsPerPixelX ) ).arg( qgsDoubleToString( mapUnitsPerPixelY ) ) );
147-
QgsDebugMsg( QString( "Pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( myWidth ) ).arg( qgsDoubleToString( myHeight ) ) );
178+
QgsDebugMsg( QString( "Pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mSize.width() ) ).arg( qgsDoubleToString( mSize.height() ) ) );
148179
QgsDebugMsg( QString( "Extent dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mExtent.width() ) ).arg( qgsDoubleToString( mExtent.height() ) ) );
149180
QgsDebugMsg( mExtent.toString() );
150181
QgsDebugMsg( QString( "Adjusted map units per pixel (x,y) : %1, %2" ).arg( qgsDoubleToString( mVisibleExtent.width() / myWidth ) ).arg( qgsDoubleToString( mVisibleExtent.height() / myHeight ) ) );
151182
QgsDebugMsg( QString( "Recalced pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mVisibleExtent.width() / mMapUnitsPerPixel ) ).arg( qgsDoubleToString( mVisibleExtent.height() / mMapUnitsPerPixel ) ) );
152183
QgsDebugMsg( QString( "Scale (assuming meters as map units) = 1:%1" ).arg( qgsDoubleToString( mScale ) ) );
184+
QgsDebugMsg( QString( "Rotation: %1 degrees" ).arg( mRotation ) );
153185

154186
mValid = true;
155187
}
@@ -512,6 +544,14 @@ void QgsMapSettings::readXML( QDomNode& theNode )
512544
QgsRectangle aoi = QgsXmlUtils::readRectangle( extentNode.toElement() );
513545
setExtent( aoi );
514546

547+
// set rotation
548+
QDomNode rotationNode = theNode.namedItem( "rotation" );
549+
QString rotationVal = rotationNode.toElement().text();
550+
if ( ! rotationVal.isEmpty() ) {
551+
double rot = rotationVal.toDouble();
552+
setRotation( rot );
553+
}
554+
515555
mDatumTransformStore.readXML( theNode );
516556
}
517557

@@ -525,6 +565,13 @@ void QgsMapSettings::writeXML( QDomNode& theNode, QDomDocument& theDoc )
525565
// Write current view extents
526566
theNode.appendChild( QgsXmlUtils::writeRectangle( extent(), theDoc ) );
527567

568+
// Write current view rotation
569+
QDomElement rotNode = theDoc.createElement( "rotation" );
570+
rotNode.appendChild(
571+
theDoc.createTextNode( qgsDoubleToString( rotation() ) )
572+
);
573+
theNode.appendChild(rotNode);
574+
528575
// projections enabled
529576
QDomElement projNode = theDoc.createElement( "projections" );
530577
projNode.appendChild( theDoc.createTextNode( QString::number( hasCrsTransformEnabled() ) ) );

‎src/core/qgsmapsettings.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ class CORE_EXPORT QgsMapSettings
7070
//! Set the size of the resulting map image
7171
void setOutputSize( const QSize& size );
7272

73+
//! Return the rotation of the resulting map image
74+
//! Units are clockwise degrees
75+
//! @note added in 2.8
76+
double rotation() const;
77+
//! Set the rotation of the resulting map image
78+
//! Units are clockwise degrees
79+
//! TODO: define relation between extent and rotation
80+
//! @note added in 2.8
81+
void setRotation( double degrees );
82+
7383
//! Return DPI used for conversion between real world units (e.g. mm) and pixels
7484
//! Default value is 96
7585
int outputDpi() const;
@@ -217,6 +227,8 @@ class CORE_EXPORT QgsMapSettings
217227

218228
QgsRectangle mExtent;
219229

230+
double mRotation;
231+
220232
QStringList mLayers;
221233

222234
bool mProjectionsEnabled;

‎src/core/qgsmaptopixel.cpp

Lines changed: 83 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,80 @@
1919
#include <QPoint>
2020
#include <QTextStream>
2121
#include <QVector>
22+
#include <QTransform>
2223

2324
#include "qgslogger.h"
2425

2526
QgsMapToPixel::QgsMapToPixel( double mapUnitsPerPixel,
26-
double ymax,
27+
double height,
2728
double ymin,
2829
double xmin )
2930
: mMapUnitsPerPixel( mapUnitsPerPixel )
30-
, yMax( ymax )
31+
, mHeight( height )
3132
, yMin( ymin )
3233
, xMin( xmin )
34+
, mMapRotation( 0 )
3335
{
3436
}
3537

3638
QgsMapToPixel::~QgsMapToPixel()
3739
{
3840
}
3941

42+
int QgsMapToPixel::mapHeight() const
43+
{
44+
return mHeight;
45+
}
46+
47+
int QgsMapToPixel::mapWidth() const
48+
{
49+
return ((xCenter-xMin)*2)/mMapUnitsPerPixel;
50+
}
51+
52+
QTransform QgsMapToPixel::getMatrix() const
53+
{
54+
double cy = mapHeight()/2.0;
55+
double cx = mapWidth()/2.0;
56+
double rotation = mapRotation();
57+
58+
#if 0 // debugging
59+
QgsDebugMsg(QString("XXX %7 -- xCent:%1 yCent:%2 mWidth:%3 mHeight:%4 uPP:%5 rot:%6")
60+
.arg(xCenter).arg(yCenter).arg(mWidth).arg(mHeight)
61+
.arg(mMapUnitsPerPixel).arg(rotation).arg((quintptr)this,QT_POINTER_SIZE *2, 15, QChar('0')));
62+
#endif
63+
64+
// NOTE: operations are done in the reverse order in which
65+
// they are configured, so translation to geographical
66+
// center happens first, then scaling, then rotation
67+
// and finally translation to output viewport center
68+
69+
if ( ! rotation ) {
70+
// Returning a simplified matrix in hope it'll give expected
71+
// results from an existing test, see
72+
// https://travis-ci.org/qgis/QGIS/builds/42508945
73+
return QTransform::fromScale( 1.0/mMapUnitsPerPixel, -1.0/mMapUnitsPerPixel )
74+
.translate( -xMin, - ( yMin + mHeight*mMapUnitsPerPixel ) );
75+
}
76+
77+
return QTransform::fromTranslate( cx, cy )
78+
.rotate( rotation )
79+
.scale( 1/mMapUnitsPerPixel, -1/mMapUnitsPerPixel )
80+
.translate ( -xCenter, -yCenter )
81+
;
82+
}
83+
4084
QgsPoint QgsMapToPixel::toMapPoint( double x, double y ) const
4185
{
42-
double mx = x * mMapUnitsPerPixel + xMin;
43-
double my = -1 * (( y - yMax ) * mMapUnitsPerPixel - yMin );
44-
return QgsPoint( mx, my );
86+
bool invertible;
87+
QTransform matrix = getMatrix().inverted(&invertible);
88+
assert(invertible);
89+
double mx, my;
90+
matrix.map(x, y, &mx, &my);
91+
QgsPoint ret( mx, my );
92+
93+
//QgsDebugMsg(QString("XXX toMapPoint x:%1 y:%2 -> x:%3 y:%4").arg(x).arg(y).arg(mx).arg(my));
94+
95+
return ret;
4596
}
4697

4798
QgsPoint QgsMapToPixel::toMapCoordinates( QPoint p ) const
@@ -70,9 +121,24 @@ double QgsMapToPixel::mapUnitsPerPixel() const
70121
return mMapUnitsPerPixel;
71122
}
72123

73-
void QgsMapToPixel::setYMaximum( double ymax )
124+
void QgsMapToPixel::setMapRotation( double degrees, double cx, double cy )
125+
{
126+
mMapRotation = degrees;
127+
xCenter = cx;
128+
yCenter = cy;
129+
assert(xCenter >= xMin);
130+
assert(yCenter >= yMin);
131+
//assert(yCenter <= yMin + mHeight*mMapUnitsPerPixel;
132+
}
133+
134+
double QgsMapToPixel::mapRotation() const
74135
{
75-
yMax = ymax;
136+
return mMapRotation;
137+
}
138+
139+
void QgsMapToPixel::setViewportHeight( double height )
140+
{
141+
mHeight = height;
76142
}
77143

78144
void QgsMapToPixel::setYMinimum( double ymin )
@@ -90,15 +156,17 @@ void QgsMapToPixel::setParameters( double mapUnitsPerPixel, double xmin, double
90156
mMapUnitsPerPixel = mapUnitsPerPixel;
91157
xMin = xmin;
92158
yMin = ymin;
93-
yMax = ymax;
159+
mHeight = ymax;
94160

95161
}
96162

97163
QString QgsMapToPixel::showParameters()
98164
{
99165
QString rep;
100166
QTextStream( &rep ) << "Map units/pixel: " << mMapUnitsPerPixel
101-
<< " X minimum: " << xMin << " Y minimum: " << yMin << " Y maximum: " << yMax;
167+
<< " X minimum: " << xMin << " Y minimum: " << yMin
168+
<< " Height: " << mHeight << " Rotation: " << mMapRotation
169+
<< " X center: " << xCenter << " Y center: " << yCenter;
102170
return rep;
103171

104172
}
@@ -132,41 +200,13 @@ void QgsMapToPixel::transform( QgsPoint* p ) const
132200
p->set( x, y );
133201
}
134202

135-
void QgsMapToPixel::transformInPlace( double& x, double& y ) const
136-
{
137-
x = ( x - xMin ) / mMapUnitsPerPixel;
138-
y = yMax - ( y - yMin ) / mMapUnitsPerPixel;
139-
}
140-
141-
#ifdef QT_ARCH_ARM
142203
void QgsMapToPixel::transformInPlace( qreal& x, qreal& y ) const
143204
{
144-
x = ( x - xMin ) / mMapUnitsPerPixel;
145-
y = yMax - ( y - yMin ) / mMapUnitsPerPixel;
146-
}
147-
#endif
148-
149-
void QgsMapToPixel::transformInPlace( QVector<double>& x,
150-
QVector<double>& y ) const
151-
{
152-
assert( x.size() == y.size() );
153-
for ( int i = 0; i < x.size(); ++i )
154-
transformInPlace( x[i], y[i] );
155-
}
205+
// Map 2 Pixel
156206

157-
#ifdef ANDROID
158-
void QgsMapToPixel::transformInPlace( float& x, float& y ) const
159-
{
160-
x = ( x - xMin ) / mMapUnitsPerPixel;
161-
y = yMax - ( y - yMin ) / mMapUnitsPerPixel;
207+
QTransform matrix = getMatrix();
208+
double mx, my;
209+
matrix.map(x, y, &mx, &my);
210+
//QgsDebugMsg(QString("XXX transformInPlace X : %1-->%2, Y: %3 -->%4").arg(x).arg(mx).arg(y).arg(my));
211+
x = mx; y = my;
162212
}
163-
164-
void QgsMapToPixel::transformInPlace( QVector<float>& x,
165-
QVector<float>& y ) const
166-
{
167-
assert( x.size() == y.size() );
168-
for ( unsigned int i = 0; i < x.size(); ++i )
169-
transformInPlace( x[i], y[i] );
170-
}
171-
#endif
172-

‎src/core/qgsmaptopixel.h

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define QGSMAPTOPIXEL
1919

2020
#include "qgspoint.h"
21+
#include <QTransform>
2122
#include <vector>
2223

2324
#include <cassert>
@@ -35,11 +36,11 @@ class CORE_EXPORT QgsMapToPixel
3536
public:
3637
/* Constructor
3738
* @param mapUnitsPerPixel Map units per pixel
38-
* @param ymax Maximum y value of the map canvas
39+
* @param height Map canvas height, in pixels
3940
* @param ymin Minimum y value of the map canvas
4041
* @param xmin Minimum x value of the map canvas
4142
*/
42-
QgsMapToPixel( double mapUnitsPerPixel = 0, double ymax = 0, double ymin = 0,
43+
QgsMapToPixel( double mapUnitsPerPixel = 0, double height = 0, double ymin = 0,
4344
double xmin = 0 );
4445
//! destructor
4546
~QgsMapToPixel();
@@ -65,22 +66,19 @@ class CORE_EXPORT QgsMapToPixel
6566
/* Transform device coordinates to map coordinates. Modifies the
6667
given coordinates in place. Intended as a fast way to do the
6768
transform. */
68-
void transformInPlace( double& x, double& y ) const;
69-
#ifdef QT_ARCH_ARM
7069
void transformInPlace( qreal& x, qreal& y ) const;
71-
#endif
7270

7371
/* Transform device coordinates to map coordinates. Modifies the
7472
given coordinates in place. Intended as a fast way to do the
7573
transform.
7674
@note not available in python bindings
7775
*/
78-
void transformInPlace( QVector<double>& x, QVector<double>& y ) const;
79-
80-
#ifdef ANDROID
81-
void transformInPlace( float& x, float& y ) const;
82-
void transformInPlace( QVector<float>& x, QVector<float>& y ) const;
83-
#endif
76+
template <class T>
77+
void transformInPlace( QVector<T>& x, QVector<T>& y ) const {
78+
assert( x.size() == y.size() );
79+
for ( int i = 0; i < x.size(); ++i )
80+
transformInPlace( x[i], y[i] );
81+
}
8482

8583
QgsPoint toMapCoordinates( int x, int y ) const;
8684

@@ -102,8 +100,30 @@ class CORE_EXPORT QgsMapToPixel
102100
//! Return current map units per pixel
103101
double mapUnitsPerPixel() const;
104102

103+
//! Return current map width in pixels
104+
int mapWidth() const;
105+
106+
//! Return current map height in pixels
107+
int mapHeight() const;
108+
109+
//! Set map rotation in degrees (clockwise)
110+
//! @param degrees clockwise rotation in degrees
111+
//! @param cx X ordinate of map center in geographical units
112+
//! @param cy Y ordinate of map center in geographical units
113+
//! @note added in 2.8
114+
void setMapRotation( double degrees, double cx, double cy );
115+
116+
//! Return current map rotation in degrees
117+
//! @note added in 2.8
118+
double mapRotation() const;
119+
105120
//! Set maximum y value
106-
void setYMaximum( double ymax );
121+
// @deprecated in 2.8, use setViewportHeight
122+
// @note this really sets the viewport height, not ymax
123+
void setYMaximum( double yMax ) { setViewportHeight(yMax); }
124+
//! Set viewport height
125+
//! @note added in 2.8
126+
void setViewportHeight( double height );
107127
//! Set minimum y value
108128
void setYMinimum( double ymin );
109129
//! set minimum x value
@@ -112,17 +132,25 @@ class CORE_EXPORT QgsMapToPixel
112132
* @param mapUnitsPerPixel Map units per pixel
113133
* @param xmin Minimum x value
114134
* @param ymin Minimum y value
115-
* @param ymax Maximum y value
135+
* @param height Map height, in pixels
116136
*/
117-
void setParameters( double mapUnitsPerPixel, double xmin, double ymin, double ymax );
137+
void setParameters( double mapUnitsPerPixel, double xmin, double ymin, double height );
118138
//! String representation of the parameters used in the transform
119139
QString showParameters();
120140

121141
private:
122142
double mMapUnitsPerPixel;
123-
double yMax;
143+
double mHeight;
124144
double yMin;
125145
double xMin;
146+
//! Map rotation around Z axis on map center as clockwise degrees
147+
//! @note added in 2.8
148+
double mMapRotation;
149+
double xCenter;
150+
double yCenter;
151+
152+
// Matrix to map from map (geographical) to screen (pixels) units
153+
QTransform getMatrix() const;
126154
};
127155

128156

‎src/core/qgsrectangle.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <QRectF>
2222
#include <QString>
2323
#include <QTextStream>
24+
#include <QTransform>
2425
#include <QRegExp>
2526
#include <qnumeric.h>
2627

‎src/core/raster/qgsrasterdrawer.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,13 @@ void QgsRasterDrawer::draw( QPainter* p, QgsRasterViewPort* viewPort, const QgsM
8989
}
9090
}
9191

92-
drawImage( p, viewPort, img, topLeftCol, topLeftRow );
92+
drawImage( p, viewPort, img, topLeftCol, topLeftRow, theQgsMapToPixel );
9393

9494
delete block;
9595
}
9696
}
9797

98-
void QgsRasterDrawer::drawImage( QPainter* p, QgsRasterViewPort* viewPort, const QImage& img, int topLeftCol, int topLeftRow ) const
98+
void QgsRasterDrawer::drawImage( QPainter* p, QgsRasterViewPort* viewPort, const QImage& img, int topLeftCol, int topLeftRow, const QgsMapToPixel* theQgsMapToPixel ) const
9999
{
100100
if ( !p || !viewPort )
101101
{
@@ -112,7 +112,41 @@ void QgsRasterDrawer::drawImage( QPainter* p, QgsRasterViewPort* viewPort, const
112112
// which should not harm anything
113113
p->setBrush( QBrush( QColor( Qt::white ), Qt::NoBrush ) );
114114

115+
int w = theQgsMapToPixel->mapWidth();
116+
int h = theQgsMapToPixel->mapHeight();
117+
118+
if ( theQgsMapToPixel ) {
119+
double rotation = theQgsMapToPixel->mapRotation();
120+
if ( rotation ) {
121+
// both viewPort and image sizes are dependent on scale
122+
double cx = w/2.0;
123+
double cy = h/2.0;
124+
p->translate( cx, cy );
125+
p->rotate( rotation );
126+
p->translate( -cx, -cy );
127+
}
128+
}
129+
115130
p->drawImage( tlPoint, img );
131+
132+
#if 0
133+
// For debugging:
134+
QRectF br = QRectF(tlPoint, img.size());
135+
QPointF c = br.center();
136+
double rad = std::max(br.width(),br.height())/10;
137+
p->drawRoundedRect( br, rad, rad );
138+
p->drawLine( QLineF(br.x(), br.y(), br.x()+br.width(), br.y()+br.height()) );
139+
p->drawLine( QLineF(br.x()+br.width(), br.y(), br.x(), br.y()+br.height()) );
140+
141+
double nw = br.width()*0.5; double nh = br.height()*0.5;
142+
br = QRectF(c-QPointF(nw/2,nh/2), QSize(nw, nh));
143+
p->drawRoundedRect( br, rad, rad );
144+
145+
nw = br.width()*0.5; nh = br.height()*0.5;
146+
br = QRectF(c-QPointF(nw/2,nh/2), QSize(nw, nh));
147+
p->drawRoundedRect( br, rad, rad );
148+
#endif
149+
116150
p->restore();
117151
}
118152

‎src/core/raster/qgsrasterdrawer.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ class CORE_EXPORT QgsRasterDrawer
4444
@param viewPort view port to draw to
4545
@param img image to draw
4646
@param topLeftCol Left position relative to left border of viewport
47-
@param topLeftRow Top position relative to top border of viewport*/
48-
void drawImage( QPainter* p, QgsRasterViewPort* viewPort, const QImage& img, int topLeftCol, int topLeftRow ) const;
47+
@param topLeftRow Top position relative to top border of viewport
48+
@param mapToPixel map to device coordinate transformation info */
49+
void drawImage( QPainter* p, QgsRasterViewPort* viewPort, const QImage& img, int topLeftCol, int topLeftRow, const QgsMapToPixel* mapToPixel=0 ) const;
4950

5051
private:
5152
QgsRasterIterator* mIterator;

‎src/core/raster/qgsrasterlayerrenderer.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,19 @@ QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer* layer, QgsRender
3131
const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();
3232
mMapToPixel = &theQgsMapToPixel;
3333

34+
QgsMapToPixel mapToPixel = theQgsMapToPixel;
35+
if ( mapToPixel.mapRotation() ) {
36+
// unset rotation for the sake of local computations.
37+
// Rotation will be handled by QPainter later
38+
// TODO: provide a method of QgsMapToPixel to fetch map center
39+
// in geographical units
40+
QgsPoint center = mapToPixel.toMapCoordinates(
41+
mapToPixel.mapWidth()/2.0,
42+
mapToPixel.mapHeight()/2.0
43+
);
44+
mapToPixel.setMapRotation(0, center.x(), center.y());
45+
}
46+
3447
QgsRectangle myProjectedViewExtent;
3548
QgsRectangle myProjectedLayerExtent;
3649

@@ -104,8 +117,8 @@ QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer* layer, QgsRender
104117
}
105118

106119
// get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
107-
mRasterViewPort->mTopLeftPoint = theQgsMapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
108-
mRasterViewPort->mBottomRightPoint = theQgsMapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
120+
mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
121+
mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
109122

110123
// align to output device grid, i.e. floor/ceil to integers
111124
// TODO: this should only be done if paint device is raster - screen, image
@@ -119,22 +132,21 @@ QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer* layer, QgsRender
119132
mRasterViewPort->mBottomRightPoint.setY( ceil( mRasterViewPort->mBottomRightPoint.y() ) );
120133
// recalc myRasterExtent to aligned values
121134
myRasterExtent.set(
122-
theQgsMapToPixel.toMapCoordinatesF( mRasterViewPort->mTopLeftPoint.x(),
135+
mapToPixel.toMapCoordinatesF( mRasterViewPort->mTopLeftPoint.x(),
123136
mRasterViewPort->mBottomRightPoint.y() ),
124-
theQgsMapToPixel.toMapCoordinatesF( mRasterViewPort->mBottomRightPoint.x(),
137+
mapToPixel.toMapCoordinatesF( mRasterViewPort->mBottomRightPoint.x(),
125138
mRasterViewPort->mTopLeftPoint.y() )
126139
);
127140

128141
//raster viewport top left / bottom right are already rounded to int
129142
mRasterViewPort->mWidth = static_cast<int>( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() );
130143
mRasterViewPort->mHeight = static_cast<int>( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() );
131144

132-
133145
//the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue
134-
//theQgsMapToPixel.mapUnitsPerPixel() is less then 1,
146+
//mapToPixel.mapUnitsPerPixel() is less then 1,
135147
//so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
136148

137-
QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( theQgsMapToPixel.mapUnitsPerPixel() ), 3 );
149+
QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 );
138150
QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( layer->width() ), 3 );
139151
QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( layer->height() ), 3 );
140152
QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );

‎src/gui/qgsmapcanvas.cpp

Lines changed: 68 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ QgsMapCanvasRendererSync::QgsMapCanvasRendererSync( QgsMapCanvas* canvas, QgsMap
9999
connect( mCanvas, SIGNAL( mapUnitsChanged() ), this, SLOT( onMapUnitsC2R() ) );
100100
connect( mRenderer, SIGNAL( mapUnitsChanged() ), this, SLOT( onMapUnitsR2C() ) );
101101

102+
connect( mCanvas, SIGNAL( rotationChanged( double ) ), this, SLOT( onMapRotationC2R() ) );
103+
connect( mRenderer, SIGNAL( rotationChanged( double ) ), this, SLOT( onMapRotationR2C() ) );
104+
102105
connect( mCanvas, SIGNAL( hasCrsTransformEnabledChanged( bool ) ), this, SLOT( onCrsTransformC2R() ) );
103106
connect( mRenderer, SIGNAL( hasCrsTransformEnabled( bool ) ), this, SLOT( onCrsTransformR2C() ) );
104107

@@ -142,6 +145,16 @@ void QgsMapCanvasRendererSync::onMapUnitsR2C()
142145
mCanvas->setMapUnits( mRenderer->mapUnits() );
143146
}
144147

148+
void QgsMapCanvasRendererSync::onMapRotationR2C()
149+
{
150+
mCanvas->setRotation( mRenderer->rotation() );
151+
}
152+
153+
void QgsMapCanvasRendererSync::onMapRotationC2R()
154+
{
155+
mRenderer->setRotation( mCanvas->rotation() );
156+
}
157+
145158
void QgsMapCanvasRendererSync::onCrsTransformC2R()
146159
{
147160
mRenderer->setProjectionsEnabled( mCanvas->mapSettings().hasCrsTransformEnabled() );
@@ -712,7 +725,8 @@ void QgsMapCanvas::rendererJobFinished()
712725

713726
p.end();
714727

715-
mMap->setContent( img, mSettings.visibleExtent() );
728+
QgsRectangle rect = mSettings.visibleExtent();
729+
mMap->setContent( img, rect );
716730
}
717731

718732
// now we are in a slot called from mJob - do not delete it immediately
@@ -818,10 +832,8 @@ void QgsMapCanvas::setExtent( QgsRectangle const & r )
818832

819833
if ( r.isEmpty() )
820834
{
821-
QgsDebugMsg( "Empty extent - keeping old extent with new center!" );
822-
QgsRectangle e( QgsPoint( r.center().x() - current.width() / 2.0, r.center().y() - current.height() / 2.0 ),
823-
QgsPoint( r.center().x() + current.width() / 2.0, r.center().y() + current.height() / 2.0 ) );
824-
mSettings.setExtent( e );
835+
QgsDebugMsg( "Empty extent - keeping old scale with new center!" );
836+
setCenter( r.center() );
825837
}
826838
else
827839
{
@@ -857,6 +869,47 @@ void QgsMapCanvas::setExtent( QgsRectangle const & r )
857869

858870
} // setExtent
859871

872+
void QgsMapCanvas::setCenter( const QgsPoint& center )
873+
{
874+
QgsRectangle r = mapSettings().extent();
875+
double x = center.x();
876+
double y = center.y();
877+
setExtent(
878+
QgsRectangle(
879+
x - r.width() / 2.0, y - r.height() / 2.0,
880+
x + r.width() / 2.0, y + r.height() / 2.0
881+
)
882+
);
883+
} // setCenter
884+
885+
QgsPoint QgsMapCanvas::center() const
886+
{
887+
QgsRectangle r = mapSettings().extent();
888+
return r.center();
889+
}
890+
891+
892+
double QgsMapCanvas::rotation() const
893+
{
894+
return mapSettings().rotation();
895+
} // rotation
896+
897+
void QgsMapCanvas::setRotation( double degrees )
898+
{
899+
double current = rotation();
900+
901+
if ( degrees == current )
902+
return;
903+
904+
mSettings.setRotation( degrees );
905+
emit rotationChanged( degrees );
906+
emit extentsChanged(); // visible extent changes with rotation
907+
908+
// notify canvas items of change (needed?)
909+
updateCanvasItemPositions();
910+
911+
} // setRotation
912+
860913

861914
void QgsMapCanvas::updateScale()
862915
{
@@ -1309,16 +1362,12 @@ void QgsMapCanvas::wheelEvent( QWheelEvent *e )
13091362
// zoom map to mouse cursor
13101363
double scaleFactor = e->delta() > 0 ? 1 / mWheelZoomFactor : mWheelZoomFactor;
13111364

1312-
QgsPoint oldCenter( mapSettings().visibleExtent().center() );
1365+
QgsPoint oldCenter = center();
13131366
QgsPoint mousePos( getCoordinateTransform()->toMapPoint( e->x(), e->y() ) );
13141367
QgsPoint newCenter( mousePos.x() + (( oldCenter.x() - mousePos.x() ) * scaleFactor ),
13151368
mousePos.y() + (( oldCenter.y() - mousePos.y() ) * scaleFactor ) );
13161369

1317-
// same as zoomWithCenter (no coordinate transformations are needed)
1318-
QgsRectangle extent = mapSettings().visibleExtent();
1319-
extent.scale( scaleFactor, &newCenter );
1320-
setExtent( extent );
1321-
refresh();
1370+
zoomByFactor( scaleFactor, &newCenter );
13221371
break;
13231372
}
13241373

@@ -1600,37 +1649,12 @@ void QgsMapCanvas::panActionEnd( QPoint releasePoint )
16001649
QgsPoint start = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
16011650
QgsPoint end = getCoordinateTransform()->toMapCoordinates( releasePoint );
16021651

1603-
double dx = qAbs( end.x() - start.x() );
1604-
double dy = qAbs( end.y() - start.y() );
1605-
1606-
// modify the extent
1607-
QgsRectangle r = mapSettings().visibleExtent();
1608-
1609-
if ( end.x() < start.x() )
1610-
{
1611-
r.setXMinimum( r.xMinimum() + dx );
1612-
r.setXMaximum( r.xMaximum() + dx );
1613-
}
1614-
else
1615-
{
1616-
r.setXMinimum( r.xMinimum() - dx );
1617-
r.setXMaximum( r.xMaximum() - dx );
1618-
}
1619-
1620-
if ( end.y() < start.y() )
1621-
{
1622-
r.setYMaximum( r.yMaximum() + dy );
1623-
r.setYMinimum( r.yMinimum() + dy );
1624-
1625-
}
1626-
else
1627-
{
1628-
r.setYMaximum( r.yMaximum() - dy );
1629-
r.setYMinimum( r.yMinimum() - dy );
1630-
1631-
}
1632-
1633-
setExtent( r );
1652+
// modify the center
1653+
double dx = end.x() - start.x();
1654+
double dy = end.y() - start.y();
1655+
QgsPoint c = center();
1656+
c.set( c.x() - dx, c.y() - dy );
1657+
setCenter( c );
16341658

16351659
refresh();
16361660
}
@@ -1715,6 +1739,7 @@ void QgsMapCanvas::readProject( const QDomDocument & doc )
17151739
setCrsTransformEnabled( tmpSettings.hasCrsTransformEnabled() );
17161740
setDestinationCrs( tmpSettings.destinationCrs() );
17171741
setExtent( tmpSettings.extent() );
1742+
setRotation( tmpSettings.rotation() );
17181743
mSettings.datumTransformStore() = tmpSettings.datumTransformStore();
17191744

17201745
clearExtentHistory(); // clear the extent history on project load
@@ -1815,7 +1840,7 @@ void QgsMapCanvas::getDatumTransformInfo( const QgsMapLayer* ml, const QString&
18151840

18161841
void QgsMapCanvas::zoomByFactor( double scaleFactor, const QgsPoint* center )
18171842
{
1818-
QgsRectangle r = mapSettings().visibleExtent();
1843+
QgsRectangle r = mapSettings().extent();
18191844
r.scale( scaleFactor, center );
18201845
setExtent( r );
18211846
refresh();

‎src/gui/qgsmapcanvas.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,21 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
201201
//! Set the extent of the map canvas
202202
void setExtent( const QgsRectangle &r );
203203

204+
//! Get the current map canvas rotation in clockwise degrees
205+
double rotation() const;
206+
207+
//! Set the rotation of the map canvas in clockwise degrees
208+
//! @note added in 2.8
209+
void setRotation( double degrees );
210+
211+
//! Set the center of the map canvas, in geographical coordinates
212+
//! @note added in 2.8
213+
void setCenter( const QgsPoint& center );
214+
215+
//! Get map center, in geographical coordinates
216+
//! @note added in 2.8
217+
QgsPoint center() const;
218+
204219
//! Zoom to the full extent of all layers
205220
void zoomToFullExtent();
206221

@@ -429,6 +444,10 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
429444
//! Emitted when the extents of the map change
430445
void extentsChanged();
431446

447+
//! Emitted when the rotation of the map changes
448+
//! @note added in 2.8
449+
void rotationChanged( double );
450+
432451
/** Emitted when the canvas has rendered.
433452
434453
Passes a pointer to the painter on which the map was drawn. This is
@@ -648,6 +667,11 @@ class QgsMapCanvasRendererSync : public QObject
648667
void onMapUnitsC2R();
649668
void onMapUnitsR2C();
650669

670+
//! @note added in 2.8
671+
void onMapRotationC2R();
672+
//! @note added in 2.8
673+
void onMapRotationR2C();
674+
651675
void onCrsTransformC2R();
652676
void onCrsTransformR2C();
653677

‎src/gui/qgsmapcanvasitem.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,19 @@ QPointF QgsMapCanvasItem::toCanvasCoordinates( const QgsPoint& point )
6767
return QPointF( x, y ) + mPanningOffset;
6868
}
6969

70+
QRectF QgsMapCanvasItem::toCanvasCoordinates( const QRectF& rect )
71+
{
72+
QPointF tl( toCanvasCoordinates( rect.topLeft() ) );
73+
QPointF bl( toCanvasCoordinates( rect.bottomLeft() ) );
74+
QPointF br( toCanvasCoordinates( rect.bottomRight() ) );
75+
QPointF tr( toCanvasCoordinates( rect.topRight() ) );
76+
double xmin = std::min(tl.x(), std::min(bl.x(), std::min(br.x(), tr.x())));
77+
double ymin = std::min(tl.y(), std::min(bl.y(), std::min(br.y(), tr.y())));
78+
double xmax = std::max(tl.x(), std::max(bl.x(), std::max(br.x(), tr.x())));
79+
double ymax = std::max(tl.y(), std::max(bl.y(), std::max(br.y(), tr.y())));
80+
return QRectF( QPointF( xmin, ymin ), QPointF( xmax, ymax ) );
81+
}
82+
7083

7184
QgsRectangle QgsMapCanvasItem::rect() const
7285
{
@@ -82,14 +95,13 @@ void QgsMapCanvasItem::setRect( const QgsRectangle& rect )
8295
QRectF r; // empty rect by default
8396
if ( !mRect.isEmpty() )
8497
{
85-
r.setTopLeft( toCanvasCoordinates( QgsPoint( mRect.xMinimum(), mRect.yMinimum() ) ) );
86-
r.setBottomRight( toCanvasCoordinates( QgsPoint( mRect.xMaximum(), mRect.yMaximum() ) ) );
98+
r = toCanvasCoordinates( mRect.toRectF() );
8799
r = r.normalized();
88100
}
89101

90102
// set position in canvas where the item will have coordinate (0,0)
91103
prepareGeometryChange();
92-
setPos( r.topLeft() );
104+
setPos( r.topLeft() ); // TODO: compute from (0,0) using toMapCoordinates ?
93105
mItemSize = QSizeF( r.width() + 2, r.height() + 2 );
94106

95107
// QgsDebugMsg(QString("[%1,%2]-[%3x%4]").arg((int) r.left()).arg((int) r.top()).arg((int) r.width()).arg((int) r.height()));

‎src/gui/qgsmapcanvasitem.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class GUI_EXPORT QgsMapCanvasItem : public QGraphicsItem
7676

7777
//! transformation from map coordinates to screen coordinates
7878
QPointF toCanvasCoordinates( const QgsPoint& point );
79+
QRectF toCanvasCoordinates( const QRectF& rect );
7980

8081
protected:
8182

‎src/gui/qgsmapcanvasmap.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,36 @@ void QgsMapCanvasMap::paint( QPainter* painter )
5050
if ( mImage.size() != QSize( w, h ) )
5151
{
5252
QgsDebugMsg( QString( "map paint DIFFERENT SIZE: img %1,%2 item %3,%4" ).arg( mImage.width() ).arg( mImage.height() ).arg( w ).arg( h ) );
53+
int tX = (w-mImage.width())/2.0;
54+
int tY = (h-mImage.height())/2.0;
55+
int fX = 0;
56+
int fY = 0;
57+
int fW = w;
58+
int fH = h;
59+
painter->drawImage(tX, tY, mImage, fX, fY, fW, fH);
5360
}
54-
painter->drawImage( QRect( 0, 0, w, h ), mImage );
61+
else
62+
{
63+
painter->drawImage( QRect( 0, 0, w, h ), mImage );
64+
}
65+
66+
// For debugging:
67+
#if 0
68+
QRectF br = boundingRect();
69+
QPointF c = br.center();
70+
double rad = std::max(br.width(),br.height())/10;
71+
painter->drawRoundedRect( br, rad, rad );
72+
painter->drawLine( QLineF(0, 0, br.width(), br.height()) );
73+
painter->drawLine( QLineF(br.width(), 0, 0, br.height()) );
74+
75+
double nw = br.width()*0.5; double nh = br.height()*0.5;
76+
br = QRectF(c-QPointF(nw/2,nh/2), QSize(nw, nh));
77+
painter->drawRoundedRect( br, rad, rad );
78+
79+
nw = br.width()*0.5; nh = br.height()*0.5;
80+
br = QRectF(c-QPointF(nw/2,nh/2), QSize(nw, nh));
81+
painter->drawRoundedRect( br, rad, rad );
82+
#endif
5583
}
5684

5785
QPaintDevice& QgsMapCanvasMap::paintDevice()

‎src/gui/qgsmaptoolpan.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ void QgsMapToolPan::canvasReleaseEvent( QMouseEvent * e )
5757
{
5858
// transform the mouse pos to map coordinates
5959
QgsPoint center = mCanvas->getCoordinateTransform()->toMapPoint( e->x(), e->y() );
60-
mCanvas->setExtent( QgsRectangle( center, center ) );
60+
mCanvas->setCenter( center );
6161
mCanvas->refresh();
6262
}
6363
}

‎tests/README

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Running tests
2+
=============
3+
4+
You can run all tests using "make check".
5+
6+
Individual tests can be run using ctest.
7+
For example if the output of "make check" ends like this:
8+
9+
The following tests FAILED:
10+
77 - PyQgsLocalServer (Failed)
11+
12+
You could re-run the failing test with:
13+
14+
ctest -V -R PyQgsLocalServer
15+
Loading

0 commit comments

Comments
 (0)
Failed to load comments.