//the format defaults to "PNG" if not specified void QgsMapCanvas::saveAsImage( const QString& theFileName, QPixmap * theQPixmap, const QString& theFormat ) { // //check if the optional QPaintDevice was supplied // if ( theQPixmap != NULL ) { // render QPainter painter; painter.begin( theQPixmap ); QgsMapRendererCustomPainterJob job( mSettings, &painter ); job.start(); job.waitForFinished(); emit renderComplete( &painter ); painter.end(); theQPixmap->save( theFileName, theFormat.toLocal8Bit().data() ); } else //use the map view { mMap->contentImage().save( theFileName, theFormat.toLocal8Bit().data() ); } //create a world file to go with the image... QgsRectangle myRect = mapSettings().visibleExtent(); QString myHeader; // note: use 17 places of precision for all numbers output //Pixel XDim myHeader += qgsDoubleToString( mapUnitsPerPixel() ) + "\r\n"; //Rotation on y axis - hard coded myHeader += "0 \r\n"; //Rotation on x axis - hard coded myHeader += "0 \r\n"; //Pixel YDim - almost always negative - see //http://en.wikipedia.org/wiki/World_file#cite_note-2 myHeader += '-' + qgsDoubleToString( mapUnitsPerPixel() ) + "\r\n"; //Origin X (center of top left cell) myHeader += qgsDoubleToString( myRect.xMinimum() + ( mapUnitsPerPixel() / 2 ) ) + "\r\n"; //Origin Y (center of top left cell) myHeader += qgsDoubleToString( myRect.yMaximum() - ( mapUnitsPerPixel() / 2 ) ) + "\r\n"; QFileInfo myInfo = QFileInfo( theFileName ); // allow dotted names QString myWorldFileName = myInfo.absolutePath() + '/' + myInfo.completeBaseName() + '.' + theFormat + 'w'; QFile myWorldFile( myWorldFileName ); if ( !myWorldFile.open( QIODevice::WriteOnly ) ) //don't use QIODevice::Text { return; } QTextStream myStream( &myWorldFile ); myStream << myHeader; } // saveAsImage
void QgsMapSettings::updateDerived() { QgsRectangle extent = mExtent; if ( extent.isEmpty() || !extent.isFinite() ) { mValid = false; return; } // Don't allow zooms where the current extent is so small that it // can't be accurately represented using a double (which is what // currentExtent uses). Excluding 0 avoids a divide by zero and an // infinite loop when rendering to a new canvas. Excluding extents // greater than 1 avoids doing unnecessary calculations. // The scheme is to compare the width against the mean x coordinate // (and height against mean y coordinate) and only allow zooms where // the ratio indicates that there is more than about 12 significant // figures (there are about 16 significant figures in a double). if ( extent.width() > 0 && extent.height() > 0 && extent.width() < 1 && extent.height() < 1 ) { // Use abs() on the extent to avoid the case where the extent is // symmetrical about 0. double xMean = ( qAbs( extent.xMinimum() ) + qAbs( extent.xMaximum() ) ) * 0.5; double yMean = ( qAbs( extent.yMinimum() ) + qAbs( extent.yMaximum() ) ) * 0.5; double xRange = extent.width() / xMean; double yRange = extent.height() / yMean; static const double minProportion = 1e-12; if ( xRange < minProportion || yRange < minProportion ) { mValid = false; return; } } double myHeight = mSize.height(); double myWidth = mSize.width(); if ( !myWidth || !myHeight ) { mValid = false; return; } // calculate the translation and scaling parameters double mapUnitsPerPixelY = mExtent.height() / myHeight; double mapUnitsPerPixelX = mExtent.width() / myWidth; mMapUnitsPerPixel = mapUnitsPerPixelY > mapUnitsPerPixelX ? mapUnitsPerPixelY : mapUnitsPerPixelX; // calculate the actual extent of the mapCanvas double dxmin = mExtent.xMinimum(), dxmax = mExtent.xMaximum(), dymin = mExtent.yMinimum(), dymax = mExtent.yMaximum(), whitespace; if ( mapUnitsPerPixelY > mapUnitsPerPixelX ) { whitespace = (( myWidth * mMapUnitsPerPixel ) - mExtent.width() ) * 0.5; dxmin -= whitespace; dxmax += whitespace; } else { whitespace = (( myHeight * mMapUnitsPerPixel ) - mExtent.height() ) * 0.5; dymin -= whitespace; dymax += whitespace; } mVisibleExtent.set( dxmin, dymin, dxmax, dymax ); // update the scale mScaleCalculator.setDpi( mDpi ); mScale = mScaleCalculator.calculate( mVisibleExtent, mSize.width() ); mMapToPixel.setParameters( mapUnitsPerPixel(), visibleExtent().center().x(), visibleExtent().center().y(), outputSize().width(), outputSize().height(), mRotation ); #if 1 // set visible extent taking rotation in consideration if ( mRotation ) { QgsPoint p1 = mMapToPixel.toMapCoordinates( QPoint( 0, 0 ) ); QgsPoint p2 = mMapToPixel.toMapCoordinates( QPoint( 0, myHeight ) ); QgsPoint p3 = mMapToPixel.toMapCoordinates( QPoint( myWidth, 0 ) ); QgsPoint p4 = mMapToPixel.toMapCoordinates( QPoint( myWidth, myHeight ) ); dxmin = std::min( p1.x(), std::min( p2.x(), std::min( p3.x(), p4.x() ) ) ); dymin = std::min( p1.y(), std::min( p2.y(), std::min( p3.y(), p4.y() ) ) ); dxmax = std::max( p1.x(), std::max( p2.x(), std::max( p3.x(), p4.x() ) ) ); dymax = std::max( p1.y(), std::max( p2.y(), std::max( p3.y(), p4.y() ) ) ); mVisibleExtent.set( dxmin, dymin, dxmax, dymax ); } #endif QgsDebugMsg( QString( "Map units per pixel (x,y) : %1, %2" ).arg( qgsDoubleToString( mapUnitsPerPixelX ), qgsDoubleToString( mapUnitsPerPixelY ) ) ); QgsDebugMsg( QString( "Pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mSize.width() ), qgsDoubleToString( mSize.height() ) ) ); QgsDebugMsg( QString( "Extent dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mExtent.width() ), qgsDoubleToString( mExtent.height() ) ) ); QgsDebugMsg( mExtent.toString() ); QgsDebugMsg( QString( "Adjusted map units per pixel (x,y) : %1, %2" ).arg( qgsDoubleToString( mVisibleExtent.width() / myWidth ), qgsDoubleToString( mVisibleExtent.height() / myHeight ) ) ); QgsDebugMsg( QString( "Recalced pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mVisibleExtent.width() / mMapUnitsPerPixel ), qgsDoubleToString( mVisibleExtent.height() / mMapUnitsPerPixel ) ) ); QgsDebugMsg( QString( "Scale (assuming meters as map units) = 1:%1" ).arg( qgsDoubleToString( mScale ) ) ); QgsDebugMsg( QString( "Rotation: %1 degrees" ).arg( mRotation ) ); mValid = true; }