Merge pull request #665 from Oslandia/overview_centering

[2.1] Overview centering
This commit is contained in:
Paolo Cavallini 2013-09-05 02:22:42 -07:00
commit 135ef36569
9 changed files with 181 additions and 38 deletions

View File

@ -300,7 +300,12 @@ class QgsComposerMap : QgsComposerItem
/**Sets flag if overview frame should be inverted
@note this function was added in version 1.9*/
void setOverviewInverted( bool inverted );
bool overviewInverted() const;
bool overviewInverted() const;
/** Returns true if the extent is forced to center on the overview */
bool overviewCentered() const;
/** Set the overview's centering mode */
void setOverviewCentered( bool centered );
/**Sets mId to a number not yet used in the composition. mId is kept if it is not in use.
Usually, this function is called before adding the composer map to the composition*/

View File

@ -303,6 +303,8 @@ void QgsComposerMapWidget::updateGuiElements()
mOverviewBlendModeComboBox->setBlendMode( mComposerMap->overviewBlendMode() );
//overview inverted
mOverviewInvertCheckbox->setChecked( mComposerMap->overviewInverted() );
//center overview
mOverviewCenterCheckbox->setChecked( mComposerMap->overviewCentered() );
//grid
if ( mComposerMap->gridEnabled() )
@ -452,6 +454,7 @@ void QgsComposerMapWidget::blockAllSignals( bool b )
mOverviewFrameStyleButton->blockSignals( b );
mOverviewBlendModeComboBox->blockSignals( b );
mOverviewInvertCheckbox->blockSignals( b );
mOverviewCenterCheckbox->blockSignals( b );
}
void QgsComposerMapWidget::on_mUpdatePreviewButton_clicked()
@ -591,6 +594,18 @@ void QgsComposerMapWidget::on_mOverviewInvertCheckbox_toggled( bool state )
}
}
void QgsComposerMapWidget::on_mOverviewCenterCheckbox_toggled( bool state )
{
if ( mComposerMap )
{
mComposerMap->setOverviewCentered( state );
}
mComposerMap->beginCommand( tr( "Overview centering mode changed" ) );
mComposerMap->cache();
mComposerMap->update();
mComposerMap->endCommand();
}
void QgsComposerMapWidget::on_mGridCheckBox_toggled( bool state )
{
if ( !mComposerMap )

View File

@ -46,6 +46,7 @@ class QgsComposerMapWidget: public QWidget, private Ui::QgsComposerMapWidgetBase
void on_mOverviewFrameStyleButton_clicked();
void on_mOverviewBlendModeComboBox_currentIndexChanged( int index );
void on_mOverviewInvertCheckbox_toggled( bool state );
void on_mOverviewCenterCheckbox_toggled( bool state );
void on_mXMinLineEdit_editingFinished();
void on_mXMaxLineEdit_editingFinished();

View File

@ -41,7 +41,8 @@
QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int width, int height )
: QgsComposerItem( x, y, width, height, composition ), mKeepLayerSet( false ),
mOverviewFrameMapId( -1 ), mOverviewBlendMode( QPainter::CompositionMode_SourceOver ), mOverviewInverted( false ), mGridEnabled( false ), mGridStyle( Solid ),
mOverviewFrameMapId( -1 ), mOverviewBlendMode( QPainter::CompositionMode_SourceOver ), mOverviewInverted( false ), mOverviewCentered( false ),
mGridEnabled( false ), mGridStyle( Solid ),
mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationFontColor( QColor( 0, 0, 0 ) ),
mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), mGridBlendMode( QPainter::CompositionMode_SourceOver ),
mLeftGridAnnotationPosition( OutsideMapFrame ), mRightGridAnnotationPosition( OutsideMapFrame ),
@ -86,7 +87,8 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int w
QgsComposerMap::QgsComposerMap( QgsComposition *composition )
: QgsComposerItem( 0, 0, 10, 10, composition ), mKeepLayerSet( false ), mOverviewFrameMapId( -1 ),
mOverviewBlendMode( QPainter::CompositionMode_SourceOver ), mOverviewInverted( false ), mGridEnabled( false ), mGridStyle( Solid ),
mOverviewBlendMode( QPainter::CompositionMode_SourceOver ), mOverviewInverted( false ), mOverviewCentered( false ),
mGridEnabled( false ), mGridStyle( Solid ),
mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationFontColor( QColor( 0, 0, 0 ) ),
mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), mGridBlendMode( QPainter::CompositionMode_SourceOver ),
mLeftGridAnnotationPosition( OutsideMapFrame ), mRightGridAnnotationPosition( OutsideMapFrame ),
@ -116,6 +118,26 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition )
initGridAnnotationFormatFromProject();
}
void QgsComposerMap::extentCenteredOnOverview( QgsRectangle& extent ) const
{
extent = mExtent;
if ( ! mOverviewCentered ) {
return;
}
if ( mOverviewFrameMapId != -1 ) {
const QgsComposerMap* overviewFrameMap = mComposition->getComposerMapById( mOverviewFrameMapId );
QgsRectangle otherExtent = overviewFrameMap->extent();
QgsPoint center = otherExtent.center();
QgsRectangle movedExtent( center.x() - mExtent.width() / 2,
center.y() - mExtent.height() / 2,
center.x() - mExtent.width() / 2 + mExtent.width(),
center.y() - mExtent.height() / 2 + mExtent.height() );
extent = movedExtent;
}
}
QgsComposerMap::~QgsComposerMap()
{
delete mOverviewFrameMapSymbol;
@ -297,6 +319,10 @@ void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* i
QgsRectangle requestRectangle;
requestedExtent( requestRectangle );
QgsRectangle cExtent;
extentCenteredOnOverview( cExtent );
double horizontalVScaleFactor = horizontalViewScaleFactor();
if ( horizontalVScaleFactor < 0 )
{
@ -305,15 +331,15 @@ void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* i
double imagePixelWidth = mExtent.width() / requestRectangle.width() * mCacheImage.width() ; //how many pixels of the image are for the map extent?
double scale = rect().width() / imagePixelWidth;
QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
QgsPoint rotationPoint = QgsPoint(( cExtent.xMaximum() + cExtent.xMinimum() ) / 2.0, ( cExtent.yMaximum() + cExtent.yMinimum() ) / 2.0 );
//shift such that rotation point is at 0/0 point in the coordinate system
double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
//shift such that top left point of the extent at point 0/0 in item coordinate system
double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
double xTopLeftShift = ( rotationPoint.x() - cExtent.xMinimum() ) * mapUnitsToMM();
double yTopLeftShift = ( cExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
painter->save();
@ -351,16 +377,20 @@ void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* i
QgsRectangle requestRectangle;
requestedExtent( requestRectangle );
QgsRectangle cExtent;
extentCenteredOnOverview( cExtent );
QSizeF theSize( requestRectangle.width() * mapUnitsToMM(), requestRectangle.height() * mapUnitsToMM() );
QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
QgsPoint rotationPoint = QgsPoint(( cExtent.xMaximum() + cExtent.xMinimum() ) / 2.0, ( cExtent.yMaximum() + cExtent.yMinimum() ) / 2.0 );
//shift such that rotation point is at 0/0 point in the coordinate system
double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
//shift such that top left point of the extent at point 0/0 in item coordinate system
double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
double xTopLeftShift = ( rotationPoint.x() - cExtent.xMinimum() ) * mapUnitsToMM();
double yTopLeftShift = ( cExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
painter->save();
painter->translate( mXOffset, mYOffset );
painter->translate( xTopLeftShift, yTopLeftShift );
@ -757,6 +787,9 @@ bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
{
overviewFrameElem.setAttribute( "overviewInverted", "false" );
}
overviewFrameElem.setAttribute( "overviewCentered", mOverviewCentered ? "true" : "false" );
QDomElement overviewFrameStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mOverviewFrameMapSymbol, doc );
overviewFrameElem.appendChild( overviewFrameStyleElem );
composerMapElem.appendChild( overviewFrameElem );
@ -874,6 +907,15 @@ bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& d
setOverviewInverted( false );
}
if ( overviewFrameElem.attribute( "overviewCentered" ).compare( "true", Qt::CaseInsensitive ) == 0 )
{
setOverviewCentered( true );
}
else
{
setOverviewCentered( false );
}
QDomElement overviewFrameSymbolElem = overviewFrameElem.firstChildElement( "symbol" );
if ( !overviewFrameSymbolElem.isNull() )
{
@ -1727,63 +1769,70 @@ double QgsComposerMap::maxExtension() const
return maxExtension + mAnnotationFrameDistance + gridFrameDist;
}
void QgsComposerMap::mapPolygon( QPolygonF& poly ) const
void QgsComposerMap::mapPolygon( const QgsRectangle& extent, QPolygonF& poly ) const
{
poly.clear();
if ( mRotation == 0 )
{
poly << QPointF( mExtent.xMinimum(), mExtent.yMaximum() );
poly << QPointF( mExtent.xMaximum(), mExtent.yMaximum() );
poly << QPointF( mExtent.xMaximum(), mExtent.yMinimum() );
poly << QPointF( mExtent.xMinimum(), mExtent.yMinimum() );
poly << QPointF( extent.xMinimum(), extent.yMaximum() );
poly << QPointF( extent.xMaximum(), extent.yMaximum() );
poly << QPointF( extent.xMaximum(), extent.yMinimum() );
poly << QPointF( extent.xMinimum(), extent.yMinimum() );
return;
}
//there is rotation
QgsPoint rotationPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
QgsPoint rotationPoint(( extent.xMaximum() + extent.xMinimum() ) / 2.0, ( extent.yMaximum() + extent.yMinimum() ) / 2.0 );
double dx, dy; //x-, y- shift from rotation point to corner point
//top left point
dx = rotationPoint.x() - mExtent.xMinimum();
dy = rotationPoint.y() - mExtent.yMaximum();
dx = rotationPoint.x() - extent.xMinimum();
dy = rotationPoint.y() - extent.yMaximum();
rotate( mRotation, dx, dy );
poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
//top right point
dx = rotationPoint.x() - mExtent.xMaximum();
dy = rotationPoint.y() - mExtent.yMaximum();
dx = rotationPoint.x() - extent.xMaximum();
dy = rotationPoint.y() - extent.yMaximum();
rotate( mRotation, dx, dy );
poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
//bottom right point
dx = rotationPoint.x() - mExtent.xMaximum();
dy = rotationPoint.y() - mExtent.yMinimum();
dx = rotationPoint.x() - extent.xMaximum();
dy = rotationPoint.y() - extent.yMinimum();
rotate( mRotation, dx, dy );
poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
//bottom left point
dx = rotationPoint.x() - mExtent.xMinimum();
dy = rotationPoint.y() - mExtent.yMinimum();
dx = rotationPoint.x() - extent.xMinimum();
dy = rotationPoint.y() - extent.yMinimum();
rotate( mRotation, dx, dy );
poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
}
void QgsComposerMap::mapPolygon( QPolygonF& poly ) const
{
return mapPolygon( mExtent, poly );
}
void QgsComposerMap::requestedExtent( QgsRectangle& extent ) const
{
QgsRectangle newExtent;
extentCenteredOnOverview( newExtent );
if ( mRotation == 0 )
{
extent = mExtent;
return;
extent = newExtent;
}
else
{
QPolygonF poly;
mapPolygon( newExtent, poly );
QRectF bRect = poly.boundingRect();
extent.setXMinimum( bRect.left() );
extent.setXMaximum( bRect.right() );
extent.setYMinimum( bRect.top() );
extent.setYMaximum( bRect.bottom() );
}
QPolygonF poly;
mapPolygon( poly );
QRectF bRect = poly.boundingRect();
extent.setXMinimum( bRect.left() );
extent.setXMaximum( bRect.right() );
extent.setYMinimum( bRect.top() );
extent.setYMaximum( bRect.bottom() );
return;
}
double QgsComposerMap::mapUnitsToMM() const
@ -1836,6 +1885,12 @@ void QgsComposerMap::setOverviewInverted( bool inverted )
update();
}
void QgsComposerMap::setOverviewCentered( bool centered )
{
mOverviewCentered = centered;
update();
}
void QgsComposerMap::setGridLineSymbol( QgsLineSymbolV2* symbol )
{
delete mGridLineSymbol;
@ -2165,7 +2220,8 @@ void QgsComposerMap::drawOverviewMapExtent( QPainter* p )
}
QgsRectangle otherExtent = overviewFrameMap->extent();
QgsRectangle thisExtent = extent();
QgsRectangle thisExtent;
extentCenteredOnOverview( thisExtent );
QgsRectangle intersectRect = thisExtent.intersect( &otherExtent );
QgsRenderContext context;

View File

@ -332,6 +332,11 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
/** Sets the overview's inversion mode*/
void setOverviewInverted( bool inverted );
/** Returns true if the extent is forced to center on the overview */
bool overviewCentered() const { return mOverviewCentered; }
/** Set the overview's centering mode */
void setOverviewCentered( bool centered );
void setGridLineSymbol( QgsLineSymbolV2* symbol );
QgsLineSymbolV2* gridLineSymbol() { return mGridLineSymbol; }
@ -408,6 +413,8 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
/**Blend mode for overview*/
QPainter::CompositionMode mOverviewBlendMode;
bool mOverviewInverted;
/** Centering mode for overview */
bool mOverviewCentered;
/**Establishes signal/slot connection for update in case of layer change*/
void connectUpdateSlot();
@ -503,6 +510,9 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
@param poly out: the result polygon with the four corner points. The points are clockwise, starting at the top-left point
@return true in case of success*/
void mapPolygon( QPolygonF& poly ) const;
/** mapPolygon variant using a given extent */
void mapPolygon( const QgsRectangle& extent, QPolygonF& poly ) const;
/**Calculates the extent to request and the yShift of the top-left point in case of rotation.*/
void requestedExtent( QgsRectangle& extent ) const;
/**Scales a composer map shift (in MM) and rotates it by mRotation
@ -525,6 +535,11 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
void createDefaultOverviewFrameSymbol();
void createDefaultGridLineSymbol();
void initGridAnnotationFormatFromProject();
/**
* Returns the extent, centered on the overview frame
*/
void extentCenteredOnOverview( QgsRectangle& extent ) const;
};
#endif

View File

@ -54,9 +54,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>438</width>
<height>1454</height>
<y>-376</y>
<width>439</width>
<height>1439</height>
</rect>
</property>
<property name="sizePolicy">
@ -697,6 +697,13 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="mOverviewCenterCheckbox">
<property name="text">
<string>Center on overview</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -41,6 +41,7 @@ class TestQgsComposerMap: public QObject
void overviewMapInvert(); //test if invert of overview map frame works
void uniqueId(); //test if map id is adapted when doing copy paste
void zebraStyle(); //test zebra map border style
void overviewMapCenter(); //test if centering of overview map frame works
private:
QgsComposition* mComposition;
@ -217,5 +218,22 @@ void TestQgsComposerMap::zebraStyle()
QVERIFY( testResult );
}
void TestQgsComposerMap::overviewMapCenter()
{
QgsComposerMap* overviewMapCenter = new QgsComposerMap( mComposition, 20, 130, 70, 70 );
overviewMapCenter->setFrameEnabled( true );
mComposition->addComposerMap( overviewMapCenter );
mComposerMap->setNewExtent( QgsRectangle( 785462.375+5000, 3341423.125, 789262.375+5000, 3343323.125 ) ); //zoom in
mComposerMap->setGridEnabled( false );
overviewMapCenter->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3350923.125 ) );
overviewMapCenter->setOverviewFrameMap( mComposerMap->id() );
overviewMapCenter->setOverviewCentered( true );
QgsCompositionChecker checker( "Composer map overview center", mComposition, QString( QString( TEST_DATA_DIR ) + QDir::separator() +
"control_images" + QDir::separator() + "expected_composermap" + QDir::separator() + "composermap_landsat_overview_center.png" ) );
bool testResult = checker.testComposition();
mComposition->removeComposerItem( overviewMapCenter );
QVERIFY( testResult );
}
QTEST_MAIN( TestQgsComposerMap )
#include "moc_testqgscomposermap.cxx"

View File

@ -181,6 +181,32 @@ class TestQgsComposerMap(TestCase):
self.mComposition.removeComposerItem(overviewMap)
assert myTestResult == True, myMessage
def testOverviewMapCenter(self):
overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70)
overviewMap.setFrameEnabled(True)
self.mComposition.addComposerMap(overviewMap)
# zoom in
myRectangle = QgsRectangle(785462.375+5000, 3341423.125,
789262.375+5000, 3343323.125)
self.mComposerMap.setNewExtent(myRectangle)
myRectangle2 = QgsRectangle(781662.375, 3339523.125,
793062.375, 3350923.125)
overviewMap.setNewExtent(myRectangle2)
overviewMap.setOverviewFrameMap(self.mComposerMap.id())
overviewMap.setOverviewInverted(False)
overviewMap.setOverviewCentered(True)
checker = QgsCompositionChecker()
myPngPath = os.path.join(TEST_DATA_DIR,
'control_images',
'expected_composermap',
'composermap_landsat_overview_center.png')
myTestResult, myMessage = checker.testComposition(
'Composer map overview centered',
self.mComposition,
myPngPath)
self.mComposition.removeComposerItem(overviewMap)
assert myTestResult == True, myMessage
# Fails because addItemsFromXML has been commented out in sip
@expectedFailure
def testuniqueId(self):

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB