Quantum GIS API Documentation  1.7.4
src/gui/qgsmapcanvas.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   qgsmapcanvas.cpp  -  description
00003   -------------------
00004 begin                : Sun Jun 30 2002
00005 copyright            : (C) 2002 by Gary E.Sherman
00006 email                : sherman at mrcc.com
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 /* $Id: qgsmapcanvas.cpp 5400 2006-04-30 20:14:08Z wonder $ */
00018 
00019 
00020 #include <QtGlobal>
00021 #include <QApplication>
00022 #include <QCursor>
00023 #include <QDir>
00024 #include <QFile>
00025 #include <QGraphicsItem>
00026 #include <QGraphicsScene>
00027 #include <QGraphicsView>
00028 #include <QKeyEvent>
00029 #include <QMouseEvent>
00030 #include <QPainter>
00031 #include <QPaintEvent>
00032 #include <QPixmap>
00033 #include <QRect>
00034 #include <QTextStream>
00035 #include <QResizeEvent>
00036 #include <QString>
00037 #include <QStringList>
00038 #include <QWheelEvent>
00039 
00040 #include "qgis.h"
00041 #include "qgslogger.h"
00042 #include "qgsmapcanvas.h"
00043 #include "qgsmapcanvasmap.h"
00044 #include "qgsmaplayer.h"
00045 #include "qgsmaplayerregistry.h"
00046 #include "qgsmaptoolpan.h"
00047 #include "qgsmaptoolzoom.h"
00048 #include "qgsmaptopixel.h"
00049 #include "qgsmapoverviewcanvas.h"
00050 #include "qgsmaprenderer.h"
00051 #include "qgsmessageviewer.h"
00052 #include "qgsproject.h"
00053 #include "qgsrubberband.h"
00054 #include "qgsvectorlayer.h"
00055 #include <math.h>
00056 
00058 class QgsMapCanvas::CanvasProperties
00059 {
00060   public:
00061 
00062     CanvasProperties() : mouseButtonDown( false ), panSelectorDown( false ) { }
00063 
00065     bool mouseButtonDown;
00066 
00068     QPoint mouseLastXY;
00069 
00071     QPoint rubberStartPoint;
00072 
00074     bool panSelectorDown;
00075 
00076 };
00077 
00078 
00079 
00080 QgsMapCanvas::QgsMapCanvas( QWidget * parent, const char *name )
00081     : QGraphicsView( parent )
00082     , mCanvasProperties( new CanvasProperties )
00083     , mNewSize( QSize() )
00084     , mPainting( false )
00085     , mAntiAliasing( false )
00086 {
00087   //disable the update that leads to the resize crash
00088   if ( viewport() )
00089   {
00090     viewport()->setAttribute( Qt::WA_PaintOnScreen, true );
00091   }
00092 
00093   mScene = new QGraphicsScene();
00094   setScene( mScene );
00095   setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
00096   setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
00097   mLastExtentIndex = -1;
00098   mCurrentLayer = NULL;
00099   mMapOverview = NULL;
00100   mMapTool = NULL;
00101   mLastNonZoomMapTool = NULL;
00102 
00103   mDrawing = false;
00104   mFrozen = false;
00105   mDirty = true;
00106 
00107   setWheelAction( WheelZoom );
00108 
00109   // by default, the canvas is rendered
00110   mRenderFlag = true;
00111 
00112   setMouseTracking( true );
00113   setFocusPolicy( Qt::StrongFocus );
00114 
00115   mMapRenderer = new QgsMapRenderer;
00116 
00117   // create map canvas item which will show the map
00118   mMap = new QgsMapCanvasMap( this );
00119   mScene->addItem( mMap );
00120   mScene->update(); // porting??
00121 
00122   moveCanvasContents( true );
00123 
00124   //connect(mMapRenderer, SIGNAL(updateMap()), this, SLOT(updateMap()));
00125   connect( mMapRenderer, SIGNAL( drawError( QgsMapLayer* ) ), this, SLOT( showError( QgsMapLayer* ) ) );
00126 
00127   // project handling
00128   connect( QgsProject::instance(), SIGNAL( readProject( const QDomDocument & ) ),
00129            this, SLOT( readProject( const QDomDocument & ) ) );
00130   connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument & ) ),
00131            this, SLOT( writeProject( QDomDocument & ) ) );
00132   mMap->resize( size() );
00133 } // QgsMapCanvas ctor
00134 
00135 
00136 QgsMapCanvas::~QgsMapCanvas()
00137 {
00138   if ( mMapTool )
00139   {
00140     mMapTool->deactivate();
00141     mMapTool = NULL;
00142   }
00143   mLastNonZoomMapTool = NULL;
00144 
00145   // delete canvas items prior to deleteing the canvas
00146   // because they might try to update canvas when it's
00147   // already being destructed, ends with segfault
00148   QList<QGraphicsItem*> list = mScene->items();
00149   QList<QGraphicsItem*>::iterator it = list.begin();
00150   while ( it != list.end() )
00151   {
00152     QGraphicsItem* item = *it;
00153     delete item;
00154     it++;
00155   }
00156 
00157   delete mScene;
00158 
00159   delete mMapRenderer;
00160   // mCanvasProperties auto-deleted via std::auto_ptr
00161   // CanvasProperties struct has its own dtor for freeing resources
00162 
00163 } // dtor
00164 
00165 void QgsMapCanvas::enableAntiAliasing( bool theFlag )
00166 {
00167   mAntiAliasing = theFlag;
00168   mMap->enableAntiAliasing( theFlag );
00169   if ( mMapOverview )
00170     mMapOverview->enableAntiAliasing( theFlag );
00171 } // anti aliasing
00172 
00173 void QgsMapCanvas::useImageToRender( bool theFlag )
00174 {
00175   mMap->useImageToRender( theFlag );
00176   refresh(); // redraw the map on change - prevents black map view
00177 }
00178 
00179 QgsMapCanvasMap* QgsMapCanvas::map()
00180 {
00181   return mMap;
00182 }
00183 
00184 QgsMapRenderer* QgsMapCanvas::mapRenderer()
00185 {
00186   return mMapRenderer;
00187 }
00188 
00189 
00190 QgsMapLayer* QgsMapCanvas::layer( int index )
00191 {
00192   QStringList& layers = mMapRenderer->layerSet();
00193   if ( index >= 0 && index < ( int ) layers.size() )
00194     return QgsMapLayerRegistry::instance()->mapLayer( layers[index] );
00195   else
00196     return NULL;
00197 }
00198 
00199 
00200 void QgsMapCanvas::setCurrentLayer( QgsMapLayer* layer )
00201 {
00202   mCurrentLayer = layer;
00203 }
00204 
00205 double QgsMapCanvas::scale()
00206 {
00207   return mMapRenderer->scale();
00208 } // scale
00209 
00210 void QgsMapCanvas::setDirty( bool dirty )
00211 {
00212   mDirty = dirty;
00213 }
00214 
00215 bool QgsMapCanvas::isDirty() const
00216 {
00217   return mDirty;
00218 }
00219 
00220 
00221 
00222 bool QgsMapCanvas::isDrawing()
00223 {
00224   return mDrawing;
00225 } // isDrawing
00226 
00227 
00228 // return the current coordinate transform based on the extents and
00229 // device size
00230 const QgsMapToPixel * QgsMapCanvas::getCoordinateTransform()
00231 {
00232   return mMapRenderer->coordinateTransform();
00233 }
00234 
00235 void QgsMapCanvas::setLayerSet( QList<QgsMapCanvasLayer> &layers )
00236 {
00237   if ( mDrawing )
00238   {
00239     return;
00240   }
00241 
00242   // create layer set
00243   QStringList layerSet, layerSetOverview;
00244 
00245   int i;
00246   for ( i = 0; i < layers.size(); i++ )
00247   {
00248     QgsMapCanvasLayer &lyr = layers[i];
00249     if ( !lyr.layer() )
00250     {
00251       continue;
00252     }
00253 
00254     if ( lyr.isVisible() )
00255     {
00256       layerSet.push_back( lyr.layer()->id() );
00257     }
00258     if ( lyr.isInOverview() )
00259     {
00260       layerSetOverview.push_back( lyr.layer()->id() );
00261     }
00262   }
00263 
00264   QStringList& layerSetOld = mMapRenderer->layerSet();
00265 
00266   bool layerSetChanged = layerSetOld != layerSet;
00267 
00268   // update only if needed
00269   if ( layerSetChanged )
00270   {
00271     for ( i = 0; i < layerCount(); i++ )
00272     {
00273       // Add check if vector layer when disconnecting from selectionChanged slot
00274       // Ticket #811 - racicot
00275       QgsMapLayer *currentLayer = layer( i );
00276       disconnect( currentLayer, SIGNAL( repaintRequested() ), this, SLOT( refresh() ) );
00277       disconnect( currentLayer, SIGNAL( screenUpdateRequested() ), this, SLOT( updateMap() ) );
00278       QgsVectorLayer *isVectLyr = qobject_cast<QgsVectorLayer *>( currentLayer );
00279       if ( isVectLyr )
00280       {
00281         disconnect( currentLayer, SIGNAL( selectionChanged() ), this, SLOT( selectionChangedSlot() ) );
00282       }
00283     }
00284 
00285     mMapRenderer->setLayerSet( layerSet );
00286 
00287     for ( i = 0; i < layerCount(); i++ )
00288     {
00289       // Add check if vector layer when connecting to selectionChanged slot
00290       // Ticket #811 - racicot
00291       QgsMapLayer *currentLayer = layer( i );
00292       connect( currentLayer, SIGNAL( repaintRequested() ), this, SLOT( refresh() ) );
00293       connect( currentLayer, SIGNAL( screenUpdateRequested() ), this, SLOT( updateMap() ) );
00294       QgsVectorLayer *isVectLyr = qobject_cast<QgsVectorLayer *>( currentLayer );
00295       if ( isVectLyr )
00296       {
00297         connect( currentLayer, SIGNAL( selectionChanged() ), this, SLOT( selectionChangedSlot() ) );
00298       }
00299     }
00300   }
00301 
00302 
00303   if ( mMapOverview )
00304   {
00305     mMapOverview->updateFullExtent( fullExtent() );
00306 
00307     QStringList& layerSetOvOld = mMapOverview->layerSet();
00308     if ( layerSetOvOld != layerSetOverview )
00309     {
00310       mMapOverview->setLayerSet( layerSetOverview );
00311     }
00312 
00313     // refresh overview maplayers even if layer set is the same
00314     // because full extent might have changed
00315     updateOverview();
00316   }
00317 
00318   if ( layerSetChanged )
00319   {
00320     QgsDebugMsg( "Layers have changed, refreshing" );
00321     emit layersChanged();
00322 
00323     refresh();
00324   }
00325 
00326 } // setLayerSet
00327 
00328 void QgsMapCanvas::enableOverviewMode( QgsMapOverviewCanvas* overview )
00329 {
00330   if ( mMapOverview )
00331   {
00332     // disconnect old map overview if exists
00333     disconnect( mMapRenderer, SIGNAL( hasCrsTransformEnabled( bool ) ),
00334                 mMapOverview, SLOT( hasCrsTransformEnabled( bool ) ) );
00335     disconnect( mMapRenderer, SIGNAL( destinationSrsChanged() ),
00336                 mMapOverview, SLOT( destinationSrsChanged() ) );
00337 
00338     // map overview is not owned by map canvas so don't delete it...
00339   }
00340 
00341   mMapOverview = overview;
00342 
00343   if ( overview )
00344   {
00345     // connect to the map render to copy its projection settings
00346     connect( mMapRenderer, SIGNAL( hasCrsTransformEnabled( bool ) ),
00347              overview,     SLOT( hasCrsTransformEnabled( bool ) ) );
00348     connect( mMapRenderer, SIGNAL( destinationSrsChanged() ),
00349              overview,     SLOT( destinationSrsChanged() ) );
00350   }
00351 }
00352 
00353 
00354 void QgsMapCanvas::updateOverview()
00355 {
00356   // redraw overview
00357   if ( mMapOverview )
00358   {
00359     mMapOverview->refresh();
00360   }
00361 }
00362 
00363 
00364 QgsMapLayer* QgsMapCanvas::currentLayer()
00365 {
00366   return mCurrentLayer;
00367 }
00368 
00369 
00370 void QgsMapCanvas::refresh()
00371 {
00372   // we can't draw again if already drawing...
00373   if ( mDrawing )
00374     return;
00375 
00376   mDrawing = true;
00377 
00378   if ( mRenderFlag && !mFrozen )
00379   {
00380     clear();
00381 
00382     // Tell the user we're going to be a while
00383     QApplication::setOverrideCursor( Qt::WaitCursor );
00384 
00385     emit renderStarting();
00386 
00387     mMap->render();
00388 
00389     mDirty = false;
00390 
00391     // notify any listeners that rendering is complete
00392     QPainter p;
00393     p.begin( &mMap->paintDevice() );
00394     emit renderComplete( &p );
00395     p.end();
00396 
00397     // notifies current map tool
00398     if ( mMapTool )
00399       mMapTool->renderComplete();
00400 
00401     // Tell the user we've finished going to be a while
00402     QApplication::restoreOverrideCursor();
00403   }
00404 
00405   mDrawing = false;
00406 } // refresh
00407 
00408 void QgsMapCanvas::updateMap()
00409 {
00410   if ( mMap )
00411   {
00412     mMap->updateContents();
00413   }
00414 }
00415 
00416 //the format defaults to "PNG" if not specified
00417 void QgsMapCanvas::saveAsImage( QString theFileName, QPixmap * theQPixmap, QString theFormat )
00418 {
00419   //
00420   //check if the optional QPaintDevice was supplied
00421   //
00422   if ( theQPixmap != NULL )
00423   {
00424     // render
00425     QPainter painter;
00426     painter.begin( theQPixmap );
00427     mMapRenderer->render( &painter );
00428     emit renderComplete( &painter );
00429     painter.end();
00430 
00431     theQPixmap->save( theFileName, theFormat.toLocal8Bit().data() );
00432   }
00433   else //use the map view
00434   {
00435     QPixmap *pixmap = dynamic_cast<QPixmap *>( &mMap->paintDevice() );
00436     if ( !pixmap )
00437       return;
00438 
00439     pixmap->save( theFileName, theFormat.toLocal8Bit().data() );
00440   }
00441   //create a world file to go with the image...
00442   QgsRectangle myRect = mMapRenderer->extent();
00443   QString myHeader;
00444   // note: use 17 places of precision for all numbers output
00445   //Pixel XDim
00446   myHeader += QString::number( mapUnitsPerPixel(), 'g', 17 ) + "\r\n";
00447   //Rotation on y axis - hard coded
00448   myHeader += "0 \r\n";
00449   //Rotation on x axis - hard coded
00450   myHeader += "0 \r\n";
00451   //Pixel YDim - almost always negative - see
00452   //http://en.wikipedia.org/wiki/World_file#cite_note-2
00453   myHeader += "-" + QString::number( mapUnitsPerPixel(), 'g', 17 ) + "\r\n";
00454   //Origin X (center of top left cell)
00455   myHeader += QString::number( myRect.xMinimum() + ( mapUnitsPerPixel() / 2 ), 'g', 17 ) + "\r\n";
00456   //Origin Y (center of top left cell)
00457   myHeader += QString::number( myRect.yMaximum() - ( mapUnitsPerPixel() / 2 ), 'g', 17 ) + "\r\n";
00458   QFileInfo myInfo  = QFileInfo( theFileName );
00459   // allow dotted names
00460   QString myWorldFileName = myInfo.absolutePath() + "/" + myInfo.completeBaseName() + "." + theFormat + "w";
00461   QFile myWorldFile( myWorldFileName );
00462   if ( !myWorldFile.open( QIODevice::WriteOnly ) ) //don't use QIODevice::Text
00463   {
00464     return;
00465   }
00466   QTextStream myStream( &myWorldFile );
00467   myStream << myHeader;
00468 } // saveAsImage
00469 
00470 
00471 
00472 QgsRectangle QgsMapCanvas::extent() const
00473 {
00474   return mMapRenderer->extent();
00475 } // extent
00476 
00477 QgsRectangle QgsMapCanvas::fullExtent() const
00478 {
00479   return mMapRenderer->fullExtent();
00480 } // extent
00481 
00482 void QgsMapCanvas::updateFullExtent()
00483 {
00484   // projection settings have changed
00485 
00486   QgsDebugMsg( "updating full extent" );
00487 
00488   mMapRenderer->updateFullExtent();
00489   if ( mMapOverview )
00490   {
00491     mMapOverview->updateFullExtent( fullExtent() );
00492     updateOverview();
00493   }
00494   refresh();
00495 }
00496 
00497 void QgsMapCanvas::setExtent( QgsRectangle const & r )
00498 {
00499   if ( mDrawing )
00500   {
00501     return;
00502   }
00503 
00504   QgsRectangle current = extent();
00505 
00506   if ( r.isEmpty() )
00507   {
00508     QgsDebugMsg( "Empty extent - keeping old extent with new center!" );
00509     QgsRectangle e( QgsPoint( r.center().x() - current.width() / 2.0, r.center().y() - current.height() / 2.0 ),
00510                     QgsPoint( r.center().x() + current.width() / 2.0, r.center().y() + current.height() / 2.0 ) );
00511     mMapRenderer->setExtent( e );
00512   }
00513   else
00514   {
00515     mMapRenderer->setExtent( r );
00516   }
00517   emit extentsChanged();
00518   updateScale();
00519   if ( mMapOverview )
00520     mMapOverview->drawExtentRect();
00521   if ( mLastExtent.size() > 20 ) mLastExtent.removeAt( 0 );
00522 
00523   //clear all extent items after current index
00524   for ( int i = mLastExtent.size() - 1; i > mLastExtentIndex; i-- )
00525   {
00526     mLastExtent.removeAt( i );
00527   }
00528 
00529   mLastExtent.append( extent() ) ;
00530 
00531   // adjust history to no more than 20
00532   if ( mLastExtent.size() > 20 )
00533   {
00534     mLastExtent.removeAt( 0 );
00535   }
00536 
00537   // the last item is the current extent
00538   mLastExtentIndex = mLastExtent.size() - 1;
00539 
00540   // update controls' enabled state
00541   emit zoomLastStatusChanged( mLastExtentIndex > 0 );
00542   emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
00543   // notify canvas items of change
00544   updateCanvasItemPositions();
00545 
00546 } // setExtent
00547 
00548 
00549 void QgsMapCanvas::updateScale()
00550 {
00551   double scale = mMapRenderer->scale();
00552 
00553   emit scaleChanged( scale );
00554 }
00555 
00556 
00557 void QgsMapCanvas::clear()
00558 {
00559   // Indicate to the next paint event that we need to rebuild the canvas contents
00560   setDirty( true );
00561 
00562 } // clear
00563 
00564 
00565 
00566 void QgsMapCanvas::zoomToFullExtent()
00567 {
00568   if ( mDrawing )
00569   {
00570     return;
00571   }
00572 
00573   QgsRectangle extent = fullExtent();
00574   // If the full extent is an empty set, don't do the zoom
00575   if ( !extent.isEmpty() )
00576   {
00577     // Add a 5% margin around the full extent
00578     extent.scale( 1.05 );
00579     setExtent( extent );
00580   }
00581   refresh();
00582 
00583 } // zoomToFullExtent
00584 
00585 
00586 
00587 void QgsMapCanvas::zoomToPreviousExtent()
00588 {
00589   if ( mDrawing )
00590   {
00591     return;
00592   }
00593 
00594   if ( mLastExtentIndex > 0 )
00595   {
00596     mLastExtentIndex--;
00597     mMapRenderer->setExtent( mLastExtent[mLastExtentIndex] );
00598     emit extentsChanged();
00599     updateScale();
00600     if ( mMapOverview )
00601       mMapOverview->drawExtentRect();
00602     refresh();
00603     // update controls' enabled state
00604     emit zoomLastStatusChanged( mLastExtentIndex > 0 );
00605     emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
00606     // notify canvas items of change
00607     updateCanvasItemPositions();
00608   }
00609 
00610 } // zoomToPreviousExtent
00611 
00612 void QgsMapCanvas::zoomToNextExtent()
00613 {
00614   if ( mDrawing )
00615   {
00616     return;
00617   }
00618   if ( mLastExtentIndex < mLastExtent.size() - 1 )
00619   {
00620     mLastExtentIndex++;
00621     mMapRenderer->setExtent( mLastExtent[mLastExtentIndex] );
00622     emit extentsChanged();
00623     updateScale();
00624     if ( mMapOverview )
00625       mMapOverview->drawExtentRect();
00626     refresh();
00627     // update controls' enabled state
00628     emit zoomLastStatusChanged( mLastExtentIndex > 0 );
00629     emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
00630     // notify canvas items of change
00631     updateCanvasItemPositions();
00632   }
00633 }// zoomToNextExtent
00634 
00635 void QgsMapCanvas::clearExtentHistory()
00636 {
00637   mLastExtent.clear(); // clear the zoom history list
00638   mLastExtent.append( extent() ) ; // set the current extent in the list
00639   mLastExtentIndex = mLastExtent.size() - 1;
00640   // update controls' enabled state
00641   emit zoomLastStatusChanged( mLastExtentIndex > 0 );
00642   emit zoomNextStatusChanged( mLastExtentIndex < mLastExtent.size() - 1 );
00643 }// clearExtentHistory
00644 
00645 
00646 bool QgsMapCanvas::hasCrsTransformEnabled()
00647 {
00648   return mMapRenderer->hasCrsTransformEnabled();
00649 }
00650 
00651 void QgsMapCanvas::mapUnitsChanged()
00652 {
00653   // We assume that if the map units have changed, the changed value
00654   // will be accessible from QgsMapRenderer
00655 
00656   // And then force a redraw of the scale number in the status bar
00657   updateScale();
00658 
00659   // And then redraw the map to force the scale bar to update
00660   // itself. This is less than ideal as the entire map gets redrawn
00661   // just to get the scale bar to redraw itself. If we ask the scale
00662   // bar to redraw itself without redrawing the map, the existing
00663   // scale bar is not removed, and we end up with two scale bars in
00664   // the same location. This can perhaps be fixed when/if the scale
00665   // bar is done as a transparent layer on top of the map canvas.
00666   refresh();
00667 }
00668 
00669 void QgsMapCanvas::zoomToSelected( QgsVectorLayer* layer )
00670 {
00671   if ( mDrawing )
00672   {
00673     return;
00674   }
00675 
00676   if ( layer == NULL )
00677   {
00678     // use current layer by default
00679     layer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
00680   }
00681 
00682   if ( layer == NULL )
00683   {
00684     return;
00685   }
00686 
00687   if ( layer->selectedFeatureCount() == 0 )
00688   {
00689     return;
00690   }
00691 
00692   QgsRectangle rect = mMapRenderer->layerExtentToOutputExtent( layer, layer->boundingBoxOfSelected() );
00693 
00694   // no selected features, only one selected point feature
00695   //or two point features with the same x- or y-coordinates
00696   if ( rect.isEmpty() )
00697   {
00698     // zoom in
00699     QgsPoint c = rect.center();
00700     rect = extent();
00701     rect.expand( 0.25, &c );
00702   }
00703   //zoom to an area
00704   else
00705   {
00706     // Expand rect to give a bit of space around the selected
00707     // objects so as to keep them clear of the map boundaries
00708     // The same 5% should apply to all margins.
00709     rect.scale( 1.05 );
00710   }
00711 
00712   setExtent( rect );
00713   refresh();
00714 } // zoomToSelected
00715 
00716 void QgsMapCanvas::keyPressEvent( QKeyEvent * e )
00717 {
00718 
00719   if ( mDrawing )
00720   {
00721     e->ignore();
00722   }
00723 
00724   emit keyPressed( e );
00725 
00726   if ( mCanvasProperties->mouseButtonDown || mCanvasProperties->panSelectorDown )
00727     return;
00728 
00729   QPainter paint;
00730   QPen     pen( Qt::gray );
00731   QgsPoint ll, ur;
00732 
00733   if ( ! mCanvasProperties->mouseButtonDown )
00734   {
00735     // Don't want to interfer with mouse events
00736 
00737     QgsRectangle currentExtent = mMapRenderer->extent();
00738     double dx = qAbs(( currentExtent.xMaximum() - currentExtent.xMinimum() ) / 4 );
00739     double dy = qAbs(( currentExtent.yMaximum() - currentExtent.yMinimum() ) / 4 );
00740 
00741     switch ( e->key() )
00742     {
00743       case Qt::Key_Left:
00744         QgsDebugMsg( "Pan left" );
00745 
00746         currentExtent.setXMinimum( currentExtent.xMinimum() - dx );
00747         currentExtent.setXMaximum( currentExtent.xMaximum() - dx );
00748         setExtent( currentExtent );
00749         refresh();
00750         break;
00751 
00752       case Qt::Key_Right:
00753         QgsDebugMsg( "Pan right" );
00754 
00755         currentExtent.setXMinimum( currentExtent.xMinimum() + dx );
00756         currentExtent.setXMaximum( currentExtent.xMaximum() + dx );
00757         setExtent( currentExtent );
00758         refresh();
00759         break;
00760 
00761       case Qt::Key_Up:
00762         QgsDebugMsg( "Pan up" );
00763 
00764         currentExtent.setYMaximum( currentExtent.yMaximum() + dy );
00765         currentExtent.setYMinimum( currentExtent.yMinimum() + dy );
00766         setExtent( currentExtent );
00767         refresh();
00768         break;
00769 
00770       case Qt::Key_Down:
00771         QgsDebugMsg( "Pan down" );
00772 
00773         currentExtent.setYMaximum( currentExtent.yMaximum() - dy );
00774         currentExtent.setYMinimum( currentExtent.yMinimum() - dy );
00775         setExtent( currentExtent );
00776         refresh();
00777         break;
00778 
00779 
00780 
00781       case Qt::Key_Space:
00782         QgsDebugMsg( "Pressing pan selector" );
00783 
00784         //mCanvasProperties->dragging = true;
00785         if ( ! e->isAutoRepeat() )
00786         {
00787           mCanvasProperties->panSelectorDown = true;
00788           mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
00789         }
00790         break;
00791 
00792       case Qt::Key_PageUp:
00793         QgsDebugMsg( "Zoom in" );
00794         zoomIn();
00795         break;
00796 
00797       case Qt::Key_PageDown:
00798         QgsDebugMsg( "Zoom out" );
00799         zoomOut();
00800         break;
00801 
00802       default:
00803         // Pass it on
00804         if ( mMapTool )
00805         {
00806           mMapTool->keyPressEvent( e );
00807         }
00808         e->ignore();
00809 
00810         QgsDebugMsg( "Ignoring key: " + QString::number( e->key() ) );
00811 
00812     }
00813   }
00814 } //keyPressEvent()
00815 
00816 void QgsMapCanvas::keyReleaseEvent( QKeyEvent * e )
00817 {
00818   QgsDebugMsg( "keyRelease event" );
00819 
00820   if ( mDrawing )
00821   {
00822     return;
00823   }
00824 
00825   switch ( e->key() )
00826   {
00827     case Qt::Key_Space:
00828       if ( !e->isAutoRepeat() && mCanvasProperties->panSelectorDown )
00829       {
00830         QgsDebugMsg( "Releasing pan selector" );
00831 
00832         mCanvasProperties->panSelectorDown = false;
00833         panActionEnd( mCanvasProperties->mouseLastXY );
00834       }
00835       break;
00836 
00837     default:
00838       // Pass it on
00839       if ( mMapTool )
00840       {
00841         mMapTool->keyReleaseEvent( e );
00842       }
00843 
00844       e->ignore();
00845 
00846       QgsDebugMsg( "Ignoring key release: " + QString::number( e->key() ) );
00847   }
00848 
00849   emit keyReleased( e );
00850 
00851 } //keyReleaseEvent()
00852 
00853 
00854 void QgsMapCanvas::mouseDoubleClickEvent( QMouseEvent * e )
00855 {
00856   if ( mDrawing )
00857   {
00858     return;
00859   }
00860 
00861   // call handler of current map tool
00862   if ( mMapTool )
00863     mMapTool->canvasDoubleClickEvent( e );
00864 } // mouseDoubleClickEvent
00865 
00866 
00867 void QgsMapCanvas::mousePressEvent( QMouseEvent * e )
00868 {
00869   if ( mDrawing )
00870   {
00871     return;
00872   }
00873 
00874   //use middle mouse button for panning, map tools won't receive any events in that case
00875   if ( e->button() == Qt::MidButton )
00876   {
00877     mCanvasProperties->panSelectorDown = true;
00878     mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
00879   }
00880   else
00881   {
00882 
00883     // call handler of current map tool
00884     if ( mMapTool )
00885       mMapTool->canvasPressEvent( e );
00886   }
00887 
00888   if ( mCanvasProperties->panSelectorDown )
00889   {
00890     return;
00891   }
00892 
00893   mCanvasProperties->mouseButtonDown = true;
00894   mCanvasProperties->rubberStartPoint = e->pos();
00895 
00896 } // mousePressEvent
00897 
00898 
00899 void QgsMapCanvas::mouseReleaseEvent( QMouseEvent * e )
00900 {
00901   if ( mDrawing )
00902   {
00903     return;
00904   }
00905 
00906   //use middle mouse button for panning, map tools won't receive any events in that case
00907   if ( e->button() == Qt::MidButton )
00908   {
00909     mCanvasProperties->panSelectorDown = false;
00910     panActionEnd( mCanvasProperties->mouseLastXY );
00911   }
00912   else
00913   {
00914     // call handler of current map tool
00915     if ( mMapTool )
00916     {
00917       // right button was pressed in zoom tool? return to previous non zoom tool
00918       if ( e->button() == Qt::RightButton && mMapTool->isTransient() )
00919       {
00920         QgsDebugMsg( "Right click in map tool zoom or pan, last tool is " +
00921                      QString( mLastNonZoomMapTool ? "not null." : "null." ) );
00922 
00923         QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCurrentLayer );
00924 
00925         // change to older non-zoom tool
00926         if ( mLastNonZoomMapTool
00927              && ( !mLastNonZoomMapTool->isEditTool() || ( vlayer && vlayer->isEditable() ) ) )
00928         {
00929           QgsMapTool* t = mLastNonZoomMapTool;
00930           mLastNonZoomMapTool = NULL;
00931           setMapTool( t );
00932         }
00933         return;
00934       }
00935       mMapTool->canvasReleaseEvent( e );
00936     }
00937   }
00938 
00939 
00940   mCanvasProperties->mouseButtonDown = false;
00941 
00942   if ( mCanvasProperties->panSelectorDown )
00943     return;
00944 
00945 } // mouseReleaseEvent
00946 
00947 void QgsMapCanvas::resizeEvent( QResizeEvent * e )
00948 {
00949   mNewSize = e->size();
00950 }
00951 
00952 void QgsMapCanvas::paintEvent( QPaintEvent *e )
00953 {
00954   if ( mNewSize.isValid() )
00955   {
00956     if ( mPainting || mDrawing )
00957     {
00958       //cancel current render progress
00959       if ( mMapRenderer )
00960       {
00961         QgsRenderContext* theRenderContext = mMapRenderer->rendererContext();
00962         if ( theRenderContext )
00963         {
00964           theRenderContext->setRenderingStopped( true );
00965         }
00966       }
00967       return;
00968     }
00969 
00970     mPainting = true;
00971 
00972     while ( mNewSize.isValid() )
00973     {
00974       QSize lastSize = mNewSize;
00975       mNewSize = QSize();
00976 
00977       //set map size before scene size helps keep scene indexes updated properly
00978       // this was the cause of rubberband artifacts
00979       mMap->resize( lastSize );
00980       mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
00981 
00982       // notify canvas items of change
00983       updateCanvasItemPositions();
00984 
00985       updateScale();
00986 
00987       refresh();
00988 
00989       emit extentsChanged();
00990     }
00991 
00992     mPainting = false;
00993   }
00994 
00995   QGraphicsView::paintEvent( e );
00996 } // paintEvent
00997 
00998 void QgsMapCanvas::updateCanvasItemPositions()
00999 {
01000   QList<QGraphicsItem*> list = mScene->items();
01001   QList<QGraphicsItem*>::iterator it = list.begin();
01002   while ( it != list.end() )
01003   {
01004     QgsMapCanvasItem* item = dynamic_cast<QgsMapCanvasItem *>( *it );
01005 
01006     if ( item )
01007     {
01008       item->updatePosition();
01009     }
01010 
01011     it++;
01012   }
01013 }
01014 
01015 
01016 void QgsMapCanvas::wheelEvent( QWheelEvent *e )
01017 {
01018   // Zoom the map canvas in response to a mouse wheel event. Moving the
01019   // wheel forward (away) from the user zooms in
01020 
01021   QgsDebugMsg( "Wheel event delta " + QString::number( e->delta() ) );
01022 
01023   if ( mDrawing )
01024   {
01025     return;
01026   }
01027 
01028   switch ( mWheelAction )
01029   {
01030     case WheelZoom:
01031       // zoom without changing extent
01032       if ( e->delta() > 0 )
01033         zoomIn();
01034       else
01035         zoomOut();
01036       break;
01037 
01038     case WheelZoomAndRecenter:
01039       // zoom and don't change extent
01040       zoomWithCenter( e->x(), e->y(), e->delta() > 0 );
01041       break;
01042 
01043     case WheelZoomToMouseCursor:
01044     {
01045       // zoom map to mouse cursor
01046       double scaleFactor = e->delta() > 0 ? 1 / mWheelZoomFactor : mWheelZoomFactor;
01047 
01048       QgsPoint oldCenter( mMapRenderer->extent().center() );
01049       QgsPoint mousePos( getCoordinateTransform()->toMapPoint( e->x(), e->y() ) );
01050       QgsPoint newCenter( mousePos.x() + (( oldCenter.x() - mousePos.x() ) * scaleFactor ),
01051                           mousePos.y() + (( oldCenter.y() - mousePos.y() ) * scaleFactor ) );
01052 
01053       // same as zoomWithCenter (no coordinate transformations are needed)
01054       QgsRectangle extent = mMapRenderer->extent();
01055       extent.scale( scaleFactor, &newCenter );
01056       setExtent( extent );
01057       refresh();
01058       break;
01059     }
01060 
01061     case WheelNothing:
01062       // well, nothing!
01063       break;
01064   }
01065 }
01066 
01067 void QgsMapCanvas::setWheelAction( WheelAction action, double factor )
01068 {
01069   mWheelAction = action;
01070   mWheelZoomFactor = factor;
01071 }
01072 
01073 void QgsMapCanvas::zoomIn()
01074 {
01075   zoomByFactor( 1 / mWheelZoomFactor );
01076 }
01077 
01078 void QgsMapCanvas::zoomOut()
01079 {
01080   zoomByFactor( mWheelZoomFactor );
01081 }
01082 
01083 void QgsMapCanvas::zoomScale( double newScale )
01084 {
01085   zoomByFactor( newScale / scale() );
01086 }
01087 
01088 void QgsMapCanvas::zoomWithCenter( int x, int y, bool zoomIn )
01089 {
01090   if ( mDrawing )
01091   {
01092     return;
01093   }
01094 
01095   double scaleFactor = ( zoomIn ? 1 / mWheelZoomFactor : mWheelZoomFactor );
01096 
01097   // transform the mouse pos to map coordinates
01098   QgsPoint center  = getCoordinateTransform()->toMapPoint( x, y );
01099   QgsRectangle r = mMapRenderer->extent();
01100   r.scale( scaleFactor, &center );
01101   setExtent( r );
01102   refresh();
01103 }
01104 
01105 void QgsMapCanvas::mouseMoveEvent( QMouseEvent * e )
01106 {
01107   if ( mDrawing )
01108   {
01109     return;
01110   }
01111 
01112   mCanvasProperties->mouseLastXY = e->pos();
01113 
01114   if ( mCanvasProperties->panSelectorDown )
01115   {
01116     panAction( e );
01117   }
01118   else
01119   {
01120     // call handler of current map tool
01121     if ( mMapTool )
01122       mMapTool->canvasMoveEvent( e );
01123   }
01124 
01125   // show x y on status bar
01126   QPoint xy = e->pos();
01127   QgsPoint coord = getCoordinateTransform()->toMapCoordinates( xy );
01128   emit xyCoordinates( coord );
01129 } // mouseMoveEvent
01130 
01131 
01132 
01134 void QgsMapCanvas::setMapTool( QgsMapTool* tool )
01135 {
01136   if ( !tool )
01137     return;
01138 
01139   if ( mMapTool )
01140     mMapTool->deactivate();
01141 
01142   if ( tool->isTransient() && mMapTool && !mMapTool->isTransient() )
01143   {
01144     // if zoom or pan tool will be active, save old tool
01145     // to bring it back on right click
01146     // (but only if it wasn't also zoom or pan tool)
01147     mLastNonZoomMapTool = mMapTool;
01148   }
01149   else
01150   {
01151     mLastNonZoomMapTool = NULL;
01152   }
01153 
01154   // set new map tool and activate it
01155   mMapTool = tool;
01156   if ( mMapTool )
01157     mMapTool->activate();
01158 
01159   emit mapToolSet( mMapTool );
01160 } // setMapTool
01161 
01162 void QgsMapCanvas::unsetMapTool( QgsMapTool* tool )
01163 {
01164   if ( mMapTool && mMapTool == tool )
01165   {
01166     mMapTool->deactivate();
01167     mMapTool = NULL;
01168     emit mapToolSet( NULL );
01169     setCursor( Qt::ArrowCursor );
01170   }
01171 
01172   if ( mLastNonZoomMapTool && mLastNonZoomMapTool == tool )
01173   {
01174     mLastNonZoomMapTool = NULL;
01175   }
01176 }
01177 
01179 void QgsMapCanvas::setCanvasColor( const QColor & theColor )
01180 {
01181   // background of map's pixmap
01182   mMap->setBackgroundColor( theColor );
01183 
01184   // background of the QGraphicsView
01185   QBrush bgBrush( theColor );
01186   setBackgroundBrush( bgBrush );
01187 #if 0
01188   QPalette palette;
01189   palette.setColor( backgroundRole(), theColor );
01190   setPalette( palette );
01191 #endif
01192 
01193   // background of QGraphicsScene
01194   mScene->setBackgroundBrush( bgBrush );
01195 } // setBackgroundColor
01196 
01197 QColor QgsMapCanvas::canvasColor() const
01198 {
01199   return mScene->backgroundBrush().color();
01200 }
01201 
01202 int QgsMapCanvas::layerCount() const
01203 {
01204   return mMapRenderer->layerSet().size();
01205 } // layerCount
01206 
01207 
01208 QList<QgsMapLayer*> QgsMapCanvas::layers() const
01209 {
01210   QList<QgsMapLayer*> lst;
01211   foreach( QString layerID, mMapRenderer->layerSet() )
01212   {
01213     QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerID );
01214     if ( layer )
01215       lst.append( layer );
01216   }
01217   return lst;
01218 }
01219 
01220 
01221 void QgsMapCanvas::layerStateChange()
01222 {
01223   // called when a layer has changed visibility setting
01224 
01225   refresh();
01226 
01227 } // layerStateChange
01228 
01229 
01230 
01231 void QgsMapCanvas::freeze( bool frz )
01232 {
01233   mFrozen = frz;
01234 } // freeze
01235 
01236 bool QgsMapCanvas::isFrozen()
01237 {
01238   return mFrozen;
01239 } // freeze
01240 
01241 
01242 QPixmap& QgsMapCanvas::canvasPixmap()
01243 {
01244   QPixmap *pixmap = dynamic_cast<QPixmap *>( &canvasPaintDevice() );
01245   if ( pixmap )
01246   {
01247     return *pixmap;
01248   }
01249 
01250   qWarning( "QgsMapCanvas::canvasPixmap() deprecated - returning static pixmap instance - use QgsMapCanvas::paintDevice()" );
01251 
01252   static QPixmap staticPixmap;
01253 
01254   QImage *image = dynamic_cast<QImage *>( &mMap->paintDevice() );
01255   if ( image )
01256   {
01257     staticPixmap = QPixmap::fromImage( *image );
01258   }
01259   else
01260   {
01261     staticPixmap = QPixmap( canvasPaintDevice().width(), canvasPaintDevice().height() );
01262   }
01263 
01264   return staticPixmap;
01265 } // canvasPixmap
01266 
01267 QPaintDevice &QgsMapCanvas::canvasPaintDevice()
01268 {
01269   return mMap->paintDevice();
01270 }
01271 
01272 double QgsMapCanvas::mapUnitsPerPixel() const
01273 {
01274   return mMapRenderer->mapUnitsPerPixel();
01275 } // mapUnitsPerPixel
01276 
01277 
01278 void QgsMapCanvas::setMapUnits( QGis::UnitType u )
01279 {
01280   QgsDebugMsg( "Setting map units to " + QString::number( static_cast<int>( u ) ) );
01281   mMapRenderer->setMapUnits( u );
01282 }
01283 
01284 
01285 QGis::UnitType QgsMapCanvas::mapUnits() const
01286 {
01287   return mMapRenderer->mapUnits();
01288 }
01289 
01290 
01291 void QgsMapCanvas::setRenderFlag( bool theFlag )
01292 {
01293   mRenderFlag = theFlag;
01294   if ( mMapRenderer )
01295   {
01296     QgsRenderContext* rc = mMapRenderer->rendererContext();
01297     if ( rc )
01298     {
01299       rc->setRenderingStopped( !theFlag );
01300     }
01301   }
01302 
01303   if ( mRenderFlag )
01304   {
01305     refresh();
01306   }
01307 }
01308 
01309 void QgsMapCanvas::connectNotify( const char * signal )
01310 {
01311   QgsDebugMsg( "QgsMapCanvas connected to " + QString( signal ) );
01312 } //connectNotify
01313 
01314 
01315 
01316 QgsMapTool* QgsMapCanvas::mapTool()
01317 {
01318   return mMapTool;
01319 }
01320 
01321 void QgsMapCanvas::panActionEnd( QPoint releasePoint )
01322 {
01323   if ( mDrawing )
01324   {
01325     return;
01326   }
01327 
01328   // move map image and other items to standard position
01329   moveCanvasContents( true ); // true means reset
01330 
01331   // use start and end box points to calculate the extent
01332   QgsPoint start = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
01333   QgsPoint end = getCoordinateTransform()->toMapCoordinates( releasePoint );
01334 
01335   double dx = qAbs( end.x() - start.x() );
01336   double dy = qAbs( end.y() - start.y() );
01337 
01338   // modify the extent
01339   QgsRectangle r = mMapRenderer->extent();
01340 
01341   if ( end.x() < start.x() )
01342   {
01343     r.setXMinimum( r.xMinimum() + dx );
01344     r.setXMaximum( r.xMaximum() + dx );
01345   }
01346   else
01347   {
01348     r.setXMinimum( r.xMinimum() - dx );
01349     r.setXMaximum( r.xMaximum() - dx );
01350   }
01351 
01352   if ( end.y() < start.y() )
01353   {
01354     r.setYMaximum( r.yMaximum() + dy );
01355     r.setYMinimum( r.yMinimum() + dy );
01356 
01357   }
01358   else
01359   {
01360     r.setYMaximum( r.yMaximum() - dy );
01361     r.setYMinimum( r.yMinimum() - dy );
01362 
01363   }
01364 
01365   setExtent( r );
01366   refresh();
01367 }
01368 
01369 void QgsMapCanvas::panAction( QMouseEvent * e )
01370 {
01371   if ( mDrawing )
01372   {
01373     return;
01374   }
01375 
01376   // move all map canvas items
01377   moveCanvasContents();
01378 
01379   // update canvas
01380   //updateContents(); // TODO: need to update?
01381 }
01382 
01383 void QgsMapCanvas::moveCanvasContents( bool reset )
01384 {
01385   if ( mDrawing )
01386   {
01387     return;
01388   }
01389 
01390   QPoint pnt( 0, 0 );
01391   if ( !reset )
01392     pnt += mCanvasProperties->mouseLastXY - mCanvasProperties->rubberStartPoint;
01393 
01394   mMap->setPanningOffset( pnt );
01395 
01396   QList<QGraphicsItem*> list = mScene->items();
01397   QList<QGraphicsItem*>::iterator it = list.begin();
01398   while ( it != list.end() )
01399   {
01400     QGraphicsItem* item = *it;
01401 
01402     if ( item != mMap )
01403     {
01404       // this tells map canvas item to draw with offset
01405       QgsMapCanvasItem* canvasItem = dynamic_cast<QgsMapCanvasItem *>( item );
01406       if ( canvasItem )
01407         canvasItem->setPanningOffset( pnt );
01408     }
01409 
01410     it++;
01411   }
01412 
01413   // show items
01414   updateCanvasItemPositions();
01415 
01416 }
01417 
01418 void QgsMapCanvas::showError( QgsMapLayer * mapLayer )
01419 {
01420 #if 0
01421   QMessageBox::warning(
01422     this,
01423     mapLayer->lastErrorTitle(),
01424     tr( "Could not draw %1 because:\n%2", "COMMENTED OUT" ).arg( mapLayer->name() ).arg( mapLayer->lastError() )
01425   );
01426 #endif
01427 
01428   QgsMessageViewer * mv = new QgsMessageViewer( this );
01429   mv->setWindowTitle( mapLayer->lastErrorTitle() );
01430   mv->setMessageAsPlainText( tr( "Could not draw %1 because:\n%2" )
01431                              .arg( mapLayer->name() ).arg( mapLayer->lastError() ) );
01432   mv->exec();
01433   //MH
01434   //QgsMessageViewer automatically sets delete on close flag
01435   //so deleting mv would lead to a segfault
01436 }
01437 
01438 QPoint QgsMapCanvas::mouseLastXY()
01439 {
01440   return mCanvasProperties->mouseLastXY;
01441 }
01442 
01443 void QgsMapCanvas::readProject( const QDomDocument & doc )
01444 {
01445   QDomNodeList nodes = doc.elementsByTagName( "mapcanvas" );
01446   if ( nodes.count() )
01447   {
01448     QDomNode node = nodes.item( 0 );
01449     mMapRenderer->readXML( node );
01450     clearExtentHistory(); // clear the extent history on project load
01451   }
01452   else
01453   {
01454     QgsDebugMsg( "Couldn't read mapcanvas information from project" );
01455   }
01456 
01457 }
01458 
01459 void QgsMapCanvas::writeProject( QDomDocument & doc )
01460 {
01461   // create node "mapcanvas" and call mMapRenderer->writeXML()
01462 
01463   QDomNodeList nl = doc.elementsByTagName( "qgis" );
01464   if ( !nl.count() )
01465   {
01466     QgsDebugMsg( "Unable to find qgis element in project file" );
01467     return;
01468   }
01469   QDomNode qgisNode = nl.item( 0 );  // there should only be one, so zeroth element ok
01470 
01471   QDomElement mapcanvasNode = doc.createElement( "mapcanvas" );
01472   qgisNode.appendChild( mapcanvasNode );
01473   mMapRenderer->writeXML( mapcanvasNode, doc );
01474 
01475 
01476 }
01477 
01478 void QgsMapCanvas::zoomByFactor( double scaleFactor )
01479 {
01480   if ( mDrawing )
01481   {
01482     return;
01483   }
01484 
01485   QgsRectangle r = mMapRenderer->extent();
01486   r.scale( scaleFactor );
01487   setExtent( r );
01488   refresh();
01489 }
01490 
01491 void QgsMapCanvas::selectionChangedSlot()
01492 {
01493   // Find out which layer it was that sent the signal.
01494   QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
01495   emit selectionChanged( layer );
01496   refresh();
01497 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines