Quantum GIS API Documentation
1.7.4
|
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, ¢er ); 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 }