QgsRectangle QgsMapSettings::fullExtent() const { // reset the map canvas extent since the extent may now be smaller // We can't use a constructor since QgsRectangle normalizes the rectangle upon construction QgsRectangle fullExtent; fullExtent.setMinimal(); // iterate through the map layers and test each layers extent // against the current min and max values QgsDebugMsgLevel( QStringLiteral( "Layer count: %1" ).arg( mLayers.count() ), 5 ); const auto constMLayers = mLayers; for ( const QgsWeakMapLayerPointer &layerPtr : constMLayers ) { if ( QgsMapLayer *lyr = layerPtr.data() ) { QgsDebugMsgLevel( "Updating extent using " + lyr->name(), 5 ); QgsDebugMsgLevel( "Input extent: " + lyr->extent().toString(), 5 ); if ( lyr->extent().isNull() ) continue; // Layer extents are stored in the coordinate system (CS) of the // layer. The extent must be projected to the canvas CS QgsRectangle extent = layerExtentToOutputExtent( lyr, lyr->extent() ); QgsDebugMsgLevel( "Output extent: " + extent.toString(), 5 ); fullExtent.combineExtentWith( extent ); } } if ( fullExtent.width() == 0.0 || fullExtent.height() == 0.0 ) { // If all of the features are at the one point, buffer the // rectangle a bit. If they are all at zero, do something a bit // more crude. if ( fullExtent.xMinimum() == 0.0 && fullExtent.xMaximum() == 0.0 && fullExtent.yMinimum() == 0.0 && fullExtent.yMaximum() == 0.0 ) { fullExtent.set( -1.0, -1.0, 1.0, 1.0 ); } else { const double padFactor = 1e-8; double widthPad = fullExtent.xMinimum() * padFactor; double heightPad = fullExtent.yMinimum() * padFactor; double xmin = fullExtent.xMinimum() - widthPad; double xmax = fullExtent.xMaximum() + widthPad; double ymin = fullExtent.yMinimum() - heightPad; double ymax = fullExtent.yMaximum() + heightPad; fullExtent.set( xmin, ymin, xmax, ymax ); } } QgsDebugMsgLevel( "Full extent: " + fullExtent.toString(), 5 ); return fullExtent; }
QgsRasterProjector::QgsRasterProjector( const QgsCoordinateReferenceSystem& theSrcCRS, const QgsCoordinateReferenceSystem& theDestCRS, const QgsRectangle& theDestExtent, int theDestRows, int theDestCols, double theMaxSrcXRes, double theMaxSrcYRes, const QgsRectangle& theExtent ) : QgsRasterInterface( nullptr ) , mSrcCRS( theSrcCRS ) , mDestCRS( theDestCRS ) , mSrcDatumTransform( -1 ) , mDestDatumTransform( -1 ) , mDestExtent( theDestExtent ) , mExtent( theExtent ) , mDestRows( theDestRows ), mDestCols( theDestCols ) , pHelperTop( nullptr ), pHelperBottom( nullptr ) , mMaxSrcXRes( theMaxSrcXRes ), mMaxSrcYRes( theMaxSrcYRes ) , mPrecision( Approximate ) , mApproximate( false ) { QgsDebugMsg( "Entered" ); QgsDebugMsg( "theDestExtent = " + theDestExtent.toString() ); calc(); }
QgsRasterProjector::QgsRasterProjector( QgsCoordinateReferenceSystem theSrcCRS, QgsCoordinateReferenceSystem theDestCRS, int theSrcDatumTransform, int theDestDatumTransform, QgsRectangle theDestExtent, int theDestRows, int theDestCols, double theMaxSrcXRes, double theMaxSrcYRes, QgsRectangle theExtent ) : QgsRasterInterface( 0 ) , mSrcCRS( theSrcCRS ) , mDestCRS( theDestCRS ) , mSrcDatumTransform( theSrcDatumTransform ) , mDestDatumTransform( theDestDatumTransform ) , mDestExtent( theDestExtent ) , mExtent( theExtent ) , mDestRows( theDestRows ), mDestCols( theDestCols ) , pHelperTop( 0 ), pHelperBottom( 0 ) , mMaxSrcXRes( theMaxSrcXRes ), mMaxSrcYRes( theMaxSrcYRes ) { QgsDebugMsg( "Entered" ); QgsDebugMsg( "theDestExtent = " + theDestExtent.toString() ); calc(); }
void QgsAlignRaster::dump() const { qDebug( "---ALIGN------------------" ); qDebug( "wkt %s", mCrsWkt.toAscii().constData() ); qDebug( "w/h %d,%d", mXSize, mYSize ); qDebug( "transform" ); qDebug( "%6.2f %6.2f %6.2f", mGeoTransform[0], mGeoTransform[1], mGeoTransform[2] ); qDebug( "%6.2f %6.2f %6.2f", mGeoTransform[3], mGeoTransform[4], mGeoTransform[5] ); QgsRectangle e = transform_to_extent( mGeoTransform, mXSize, mYSize ); qDebug( "extent %s", e.toString().toAscii().constData() ); }
QgsRectangle QgsMapSettings::outputExtentToLayerExtent( const QgsMapLayer *layer, QgsRectangle extent ) const { try { QgsCoordinateTransform ct = layerTransform( layer ); if ( ct.isValid() ) { QgsDebugMsgLevel( QStringLiteral( "sourceCrs = %1" ).arg( ct.sourceCrs().authid() ), 3 ); QgsDebugMsgLevel( QStringLiteral( "destCRS = %1" ).arg( ct.destinationCrs().authid() ), 3 ); QgsDebugMsgLevel( QStringLiteral( "extent = %1" ).arg( extent.toString() ), 3 ); extent = ct.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); } } catch ( QgsCsException &cse ) { QgsMessageLog::logMessage( QObject::tr( "Transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) ); } QgsDebugMsgLevel( QStringLiteral( "proj extent = %1" ).arg( extent.toString() ), 3 ); return extent; }
void QgsAlignRaster::RasterInfo::dump() const { qDebug( "---RASTER INFO------------------" ); qDebug( "wkt %s", mCrsWkt.toAscii().constData() ); qDebug( "w/h %d,%d", mXSize, mYSize ); qDebug( "cell x/y %f,%f", cellSize().width(), cellSize().width() ); QgsRectangle r = extent(); qDebug( "extent %s", r.toString().toAscii().constData() ); qDebug( "transform" ); qDebug( "%6.2f %6.2f %6.2f", mGeoTransform[0], mGeoTransform[1], mGeoTransform[2] ); qDebug( "%6.2f %6.2f %6.2f", mGeoTransform[3], mGeoTransform[4], mGeoTransform[5] ); }
void QgsGrassRasterProvider::readBlock( int bandNo, QgsRectangle const &viewExtent, int pixelWidth, int pixelHeight, void *block, QgsRasterBlockFeedback *feedback ) { Q_UNUSED( feedback ); QgsDebugMsg( "pixelWidth = " + QString::number( pixelWidth ) ); QgsDebugMsg( "pixelHeight = " + QString::number( pixelHeight ) ); QgsDebugMsg( "viewExtent: " + viewExtent.toString() ); clearLastError(); if ( pixelWidth <= 0 || pixelHeight <= 0 ) return; QStringList arguments; arguments.append( "map=" + mMapName + "@" + mMapset ); arguments.append( ( QStringLiteral( "window=%1,%2,%3,%4,%5,%6" ) .arg( QgsRasterBlock::printValue( viewExtent.xMinimum() ), QgsRasterBlock::printValue( viewExtent.yMinimum() ), QgsRasterBlock::printValue( viewExtent.xMaximum() ), QgsRasterBlock::printValue( viewExtent.yMaximum() ) ) .arg( pixelWidth ).arg( pixelHeight ) ) ); arguments.append( QStringLiteral( "format=value" ) ); QString cmd = QgsApplication::libexecPath() + "grass/modules/qgis.d.rast"; QByteArray data; try { data = QgsGrass::runModule( mGisdbase, mLocation, mMapset, cmd, arguments ); } catch ( QgsGrass::Exception &e ) { QString error = tr( "Cannot read raster" ) + " : " + e.what(); QgsDebugMsg( error ); appendError( error ); // We don't set mValid to false, because the raster can be recreated and work next time return; } QgsDebugMsg( QString( "%1 bytes read from modules stdout" ).arg( data.size() ) ); // byteCount() in Qt >= 4.6 //int size = image->byteCount() < data.size() ? image->byteCount() : data.size(); int size = pixelWidth * pixelHeight * dataTypeSize( bandNo ); if ( size != data.size() ) { QString error = tr( "%1 bytes expected but %2 byte were read from qgis.d.rast" ).arg( size ).arg( data.size() ); QgsDebugMsg( error ); appendError( error ); size = size < data.size() ? size : data.size(); } memcpy( block, data.data(), size ); }
QgsRectangle QgsMapSettings::outputExtentToLayerExtent( QgsMapLayer* theLayer, QgsRectangle extent ) const { if ( hasCrsTransformEnabled() ) { try { if ( const QgsCoordinateTransform* ct = layerTransform( theLayer ) ) { QgsDebugMsg( QString( "sourceCrs = " + ct->sourceCrs().authid() ) ); QgsDebugMsg( QString( "destCRS = " + ct->destCRS().authid() ) ); QgsDebugMsg( QString( "extent = " + extent.toString() ) ); extent = ct->transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); } } catch ( QgsCsException &cse ) { QgsMessageLog::logMessage( QString( "Transform error caught: %1" ).arg( cse.what() ), "CRS" ); } } QgsDebugMsg( QString( "proj extent = " + extent.toString() ) ); return extent; }
void QgsStatusBarCoordinatesWidget::showExtent() { if ( !mToggleExtentsViewButton->isChecked() ) { return; } // update the statusbar with the current extents. QgsRectangle myExtents = mMapCanvas->extent(); mLabel->setText( tr( "Extents:" ) ); mLineEdit->setText( myExtents.toString( true ) ); //ensure the label is big enough if ( mLineEdit->width() > mLineEdit->minimumWidth() ) { mLineEdit->setMinimumWidth( mLineEdit->width() ); } }
QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle rect, TransformDirection direction, const bool handle180Crossover ) const { // Calculate the bounding box of a QgsRectangle in the source CRS // when projected to the destination CRS (or the inverse). // This is done by looking at a number of points spread evenly // across the rectangle if ( mShortCircuit || !mInitialisedFlag ) return rect; if ( rect.isEmpty() ) { QgsPoint p = transform( rect.xMinimum(), rect.yMinimum(), direction ); return QgsRectangle( p, p ); } static const int numP = 8; QgsRectangle bb_rect; bb_rect.setMinimal(); // We're interfacing with C-style vectors in the // end, so let's do C-style vectors here too. double x[numP * numP]; double y[numP * numP]; double z[numP * numP]; QgsDebugMsg( "Entering transformBoundingBox..." ); // Populate the vectors double dx = rect.width() / ( double )( numP - 1 ); double dy = rect.height() / ( double )( numP - 1 ); double pointY = rect.yMinimum(); for ( int i = 0; i < numP ; i++ ) { // Start at right edge double pointX = rect.xMinimum(); for ( int j = 0; j < numP; j++ ) { x[( i*numP ) + j] = pointX; y[( i*numP ) + j] = pointY; // and the height... z[( i*numP ) + j] = 0.0; // QgsDebugMsg(QString("BBox coord: (%1, %2)").arg(x[(i*numP) + j]).arg(y[(i*numP) + j])); pointX += dx; } pointY += dy; } // Do transformation. Any exception generated must // be handled in above layers. try { transformCoords( numP * numP, x, y, z, direction ); } catch ( const QgsCsException & ) { // rethrow the exception QgsDebugMsg( "rethrowing exception" ); throw; } // Calculate the bounding box and use that for the extent for ( int i = 0; i < numP * numP; i++ ) { if ( !qIsFinite( x[i] ) || !qIsFinite( y[i] ) ) { continue; } if ( handle180Crossover ) { //if crossing the date line, temporarily add 360 degrees to -ve longitudes bb_rect.combineExtentWith( x[i] >= 0.0 ? x[i] : x[i] + 360.0, y[i] ); } else { bb_rect.combineExtentWith( x[i], y[i] ); } } if ( handle180Crossover ) { //subtract temporary addition of 360 degrees from longitudes if ( bb_rect.xMinimum() > 180.0 ) bb_rect.setXMinimum( bb_rect.xMinimum() - 360.0 ); if ( bb_rect.xMaximum() > 180.0 ) bb_rect.setXMaximum( bb_rect.xMaximum() - 360.0 ); } QgsDebugMsg( "Projected extent: " + bb_rect.toString() ); if ( bb_rect.isEmpty() ) { QgsDebugMsg( "Original extent: " + rect.toString() ); } return bb_rect; }
QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle &rect, TransformDirection direction, const bool handle180Crossover ) const { // Calculate the bounding box of a QgsRectangle in the source CRS // when projected to the destination CRS (or the inverse). // This is done by looking at a number of points spread evenly // across the rectangle if ( mShortCircuit || !mInitialisedFlag ) return rect; if ( rect.isEmpty() ) { QgsPoint p = transform( rect.xMinimum(), rect.yMinimum(), direction ); return QgsRectangle( p, p ); } // 64 points (<=2.12) is not enough, see #13665, for EPSG:4326 -> EPSG:3574 (say that it is a hard one), // are decent result from about 500 points and more. This method is called quite often, but // even with 1000 points it takes < 1ms // TODO: how to effectively and precisely reproject bounding box? const int nPoints = 1000; double d = sqrt(( rect.width() * rect.height() ) / pow( sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) ); int nXPoints = static_cast< int >( ceil( rect.width() / d ) ) + 1; int nYPoints = static_cast< int >( ceil( rect.height() / d ) ) + 1; QgsRectangle bb_rect; bb_rect.setMinimal(); // We're interfacing with C-style vectors in the // end, so let's do C-style vectors here too. QVector<double> x( nXPoints * nYPoints ); QVector<double> y( nXPoints * nYPoints ); QVector<double> z( nXPoints * nYPoints ); QgsDebugMsg( "Entering transformBoundingBox..." ); // Populate the vectors double dx = rect.width() / static_cast< double >( nXPoints - 1 ); double dy = rect.height() / static_cast< double >( nYPoints - 1 ); double pointY = rect.yMinimum(); for ( int i = 0; i < nYPoints ; i++ ) { // Start at right edge double pointX = rect.xMinimum(); for ( int j = 0; j < nXPoints; j++ ) { x[( i*nXPoints ) + j] = pointX; y[( i*nXPoints ) + j] = pointY; // and the height... z[( i*nXPoints ) + j] = 0.0; // QgsDebugMsg(QString("BBox coord: (%1, %2)").arg(x[(i*numP) + j]).arg(y[(i*numP) + j])); pointX += dx; } pointY += dy; } // Do transformation. Any exception generated must // be handled in above layers. try { transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction ); } catch ( const QgsCsException & ) { // rethrow the exception QgsDebugMsg( "rethrowing exception" ); throw; } // Calculate the bounding box and use that for the extent for ( int i = 0; i < nXPoints * nYPoints; i++ ) { if ( !qIsFinite( x[i] ) || !qIsFinite( y[i] ) ) { continue; } if ( handle180Crossover ) { //if crossing the date line, temporarily add 360 degrees to -ve longitudes bb_rect.combineExtentWith( x[i] >= 0.0 ? x[i] : x[i] + 360.0, y[i] ); } else { bb_rect.combineExtentWith( x[i], y[i] ); } } if ( handle180Crossover ) { //subtract temporary addition of 360 degrees from longitudes if ( bb_rect.xMinimum() > 180.0 ) bb_rect.setXMinimum( bb_rect.xMinimum() - 360.0 ); if ( bb_rect.xMaximum() > 180.0 ) bb_rect.setXMaximum( bb_rect.xMaximum() - 360.0 ); } QgsDebugMsg( "Projected extent: " + bb_rect.toString() ); if ( bb_rect.isEmpty() ) { QgsDebugMsg( "Original extent: " + rect.toString() ); } return bb_rect; }
QgsRectangle QgsMapSettings::fullExtent() const { QgsDebugMsg( "called." ); QgsMapLayerRegistry* registry = QgsMapLayerRegistry::instance(); // reset the map canvas extent since the extent may now be smaller // We can't use a constructor since QgsRectangle normalizes the rectangle upon construction QgsRectangle fullExtent; fullExtent.setMinimal(); // iterate through the map layers and test each layers extent // against the current min and max values QStringList::const_iterator it = mLayers.begin(); QgsDebugMsg( QString( "Layer count: %1" ).arg( mLayers.count() ) ); while ( it != mLayers.end() ) { QgsMapLayer * lyr = registry->mapLayer( *it ); if ( !lyr ) { QgsDebugMsg( QString( "WARNING: layer '%1' not found in map layer registry!" ).arg( *it ) ); } else { QgsDebugMsg( "Updating extent using " + lyr->name() ); QgsDebugMsg( "Input extent: " + lyr->extent().toString() ); if ( lyr->extent().isNull() ) { ++it; continue; } // Layer extents are stored in the coordinate system (CS) of the // layer. The extent must be projected to the canvas CS QgsRectangle extent = layerExtentToOutputExtent( lyr, lyr->extent() ); QgsDebugMsg( "Output extent: " + extent.toString() ); fullExtent.unionRect( extent ); } ++it; } if ( fullExtent.width() == 0.0 || fullExtent.height() == 0.0 ) { // If all of the features are at the one point, buffer the // rectangle a bit. If they are all at zero, do something a bit // more crude. if ( fullExtent.xMinimum() == 0.0 && fullExtent.xMaximum() == 0.0 && fullExtent.yMinimum() == 0.0 && fullExtent.yMaximum() == 0.0 ) { fullExtent.set( -1.0, -1.0, 1.0, 1.0 ); } else { const double padFactor = 1e-8; double widthPad = fullExtent.xMinimum() * padFactor; double heightPad = fullExtent.yMinimum() * padFactor; double xmin = fullExtent.xMinimum() - widthPad; double xmax = fullExtent.xMaximum() + widthPad; double ymin = fullExtent.yMinimum() - heightPad; double ymax = fullExtent.yMaximum() + heightPad; fullExtent.set( xmin, ymin, xmax, ymax ); } } QgsDebugMsg( "Full extent: " + fullExtent.toString() ); return fullExtent; }
bool QgsMapRendererJob::reprojectToLayerExtent( const QgsMapLayer *ml, const QgsCoordinateTransform &ct, QgsRectangle &extent, QgsRectangle &r2 ) { bool split = false; try { #ifdef QGISDEBUG // QgsLogger::debug<QgsRectangle>("Getting extent of canvas in layers CS. Canvas is ", extent, __FILE__, __FUNCTION__, __LINE__); #endif // Split the extent into two if the source CRS is // geographic and the extent crosses the split in // geographic coordinates (usually +/- 180 degrees, // and is assumed to be so here), and draw each // extent separately. static const double SPLIT_COORD = 180.0; if ( ml->crs().isGeographic() ) { if ( ml->type() == QgsMapLayer::VectorLayer && !ct.destinationCrs().isGeographic() ) { // if we transform from a projected coordinate system check // check if transforming back roughly returns the input // extend - otherwise render the world. QgsRectangle extent1 = ct.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); QgsRectangle extent2 = ct.transformBoundingBox( extent1, QgsCoordinateTransform::ForwardTransform ); QgsDebugMsgLevel( QString( "\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)" ) .arg( extent.toString() ).arg( extent.width() ).arg( extent.height() ) .arg( extent1.toString(), extent2.toString() ).arg( extent2.width() ).arg( extent2.height() ) .arg( std::fabs( 1.0 - extent2.width() / extent.width() ) ) .arg( std::fabs( 1.0 - extent2.height() / extent.height() ) ) , 3 ); if ( std::fabs( 1.0 - extent2.width() / extent.width() ) < 0.5 && std::fabs( 1.0 - extent2.height() / extent.height() ) < 0.5 ) { extent = extent1; } else { extent = QgsRectangle( -180.0, -90.0, 180.0, 90.0 ); } } else { // Note: ll = lower left point QgsPointXY ll = ct.transform( extent.xMinimum(), extent.yMinimum(), QgsCoordinateTransform::ReverseTransform ); // and ur = upper right point QgsPointXY ur = ct.transform( extent.xMaximum(), extent.yMaximum(), QgsCoordinateTransform::ReverseTransform ); QgsDebugMsgLevel( QString( "in:%1 (ll:%2 ur:%3)" ).arg( extent.toString(), ll.toString(), ur.toString() ), 4 ); extent = ct.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); QgsDebugMsgLevel( QString( "out:%1 (w:%2 h:%3)" ).arg( extent.toString() ).arg( extent.width() ).arg( extent.height() ), 4 ); if ( ll.x() > ur.x() ) { // the coordinates projected in reverse order than what one would expect. // we are probably looking at an area that includes longitude of 180 degrees. // we need to take into account coordinates from two intervals: (-180,x1) and (x2,180) // so let's use (-180,180). This hopefully does not add too much overhead. It is // more straightforward than rendering with two separate extents and more consistent // for rendering, labeling and caching as everything is rendered just in one go extent.setXMinimum( -SPLIT_COORD ); extent.setXMaximum( SPLIT_COORD ); } } // TODO: the above rule still does not help if using a projection that covers the whole // world. E.g. with EPSG:3857 the longitude spectrum -180 to +180 is mapped to approx. // -2e7 to +2e7. Converting extent from -5e7 to +5e7 is transformed as -90 to +90, // but in fact the extent should cover the whole world. } else // can't cross 180 { if ( ct.destinationCrs().isGeographic() && ( extent.xMinimum() <= -180 || extent.xMaximum() >= 180 || extent.yMinimum() <= -90 || extent.yMaximum() >= 90 ) ) // Use unlimited rectangle because otherwise we may end up transforming wrong coordinates. // E.g. longitude -200 to +160 would be understood as +40 to +160 due to periodicity. // We could try to clamp coords to (-180,180) for lon resp. (-90,90) for lat, // but this seems like a safer choice. extent = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX ); else extent = ct.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); } } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( "Transform error caught" ); extent = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX ); r2 = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX ); } return split; }
QgsRasterBlock * QgsRasterDataProvider::block( int theBandNo, QgsRectangle const & theExtent, int theWidth, int theHeight ) { QgsDebugMsg( QString( "theBandNo = %1 theWidth = %2 theHeight = %3" ).arg( theBandNo ).arg( theWidth ).arg( theHeight ) ); QgsDebugMsg( QString( "theExtent = %1" ).arg( theExtent.toString() ) ); QgsRasterBlock *block = new QgsRasterBlock( dataType( theBandNo ), theWidth, theHeight, noDataValue( theBandNo ) ); if ( block->isEmpty() ) { QgsDebugMsg( "Couldn't create raster block" ); return block; } // Read necessary extent only QgsRectangle tmpExtent = extent().intersect( &theExtent ); if ( tmpExtent.isEmpty() ) { QgsDebugMsg( "Extent outside provider extent" ); block->setIsNoData(); return block; } double xRes = theExtent.width() / theWidth; double yRes = theExtent.height() / theHeight; double tmpXRes, tmpYRes; double providerXRes = 0; double providerYRes = 0; if ( capabilities() & ExactResolution ) { providerXRes = extent().width() / xSize(); providerYRes = extent().height() / ySize(); tmpXRes = qMax( providerXRes, xRes ); tmpYRes = qMax( providerYRes, yRes ); if ( doubleNear( tmpXRes, xRes ) ) tmpXRes = xRes; if ( doubleNear( tmpYRes, yRes ) ) tmpYRes = yRes; } else { tmpXRes = xRes; tmpYRes = yRes; } if ( tmpExtent != theExtent || tmpXRes > xRes || tmpYRes > yRes ) { // Read smaller extent or lower resolution // Calculate row/col limits (before tmpExtent is aligned) int fromRow = qRound(( theExtent.yMaximum() - tmpExtent.yMaximum() ) / yRes ); int toRow = qRound(( theExtent.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1; int fromCol = qRound(( tmpExtent.xMinimum() - theExtent.xMinimum() ) / xRes ) ; int toCol = qRound(( tmpExtent.xMaximum() - theExtent.xMinimum() ) / xRes ) - 1; QgsDebugMsg( QString( "fromRow = %1 toRow = %2 fromCol = %3 toCol = %4" ).arg( fromRow ).arg( toRow ).arg( fromCol ).arg( toCol ) ); if ( fromRow < 0 || fromRow >= theHeight || toRow < 0 || toRow >= theHeight || fromCol < 0 || fromCol >= theWidth || toCol < 0 || toCol >= theWidth ) { // Should not happen QgsDebugMsg( "Row or column limits out of range" ); return block; } // If lower source resolution is used, the extent must beS aligned to original // resolution to avoid possible shift due to resampling if ( tmpXRes > xRes ) { int col = floor(( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes ); col = ceil(( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes ); } if ( tmpYRes > yRes ) { int row = floor(( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes ); tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes ); row = ceil(( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes ); tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes ); } int tmpWidth = qRound( tmpExtent.width() / tmpXRes ); int tmpHeight = qRound( tmpExtent.height() / tmpYRes ); tmpXRes = tmpExtent.width() / tmpWidth; tmpYRes = tmpExtent.height() / tmpHeight; QgsDebugMsg( QString( "Reading smaller block tmpWidth = %1 theHeight = %2" ).arg( tmpWidth ).arg( tmpHeight ) ); QgsDebugMsg( QString( "tmpExtent = %1" ).arg( tmpExtent.toString() ) ); block->setIsNoData(); QgsRasterBlock *tmpBlock = new QgsRasterBlock( dataType( theBandNo ), tmpWidth, tmpHeight, noDataValue( theBandNo ) ); readBlock( theBandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->data() ); int pixelSize = dataTypeSize( theBandNo ); double xMin = theExtent.xMinimum(); double yMax = theExtent.yMaximum(); double tmpXMin = tmpExtent.xMinimum(); double tmpYMax = tmpExtent.yMaximum(); for ( int row = fromRow; row <= toRow; row++ ) { double y = yMax - ( row + 0.5 ) * yRes; int tmpRow = floor(( tmpYMax - y ) / tmpYRes ); for ( int col = fromCol; col <= toCol; col++ ) { double x = xMin + ( col + 0.5 ) * xRes; int tmpCol = floor(( x - tmpXMin ) / tmpXRes ); if ( tmpRow < 0 || tmpRow >= tmpHeight || tmpCol < 0 || tmpCol >= tmpWidth ) { QgsDebugMsg( "Source row or column limits out of range" ); block->setIsNoData(); // so that the problem becomes obvious and fixed delete tmpBlock; return block; } size_t tmpIndex = tmpRow * tmpWidth + tmpCol; size_t index = row * theWidth + col; char *tmpBits = tmpBlock->bits( tmpIndex ); char *bits = block->bits( index ); if ( !tmpBits ) { QgsDebugMsg( QString( "Cannot get input block data tmpRow = %1 tmpCol = %2 tmpIndex = %3." ).arg( tmpRow ).arg( tmpCol ).arg( tmpIndex ) ); continue; } if ( !bits ) { QgsDebugMsg( "Cannot set output block data." ); continue; } memcpy( bits, tmpBits, pixelSize ); } } delete tmpBlock; } else { readBlock( theBandNo, theExtent, theWidth, theHeight, block->data() ); } // apply user no data values // TODO: there are other readBlock methods where no data are not applied block->applyNodataValues( userNoDataValue( theBandNo ) ); return block; }
bool QgsVectorLayerRenderer::render() { if ( mGeometryType == QGis::NoGeometry || mGeometryType == QGis::UnknownGeometry ) return true; if ( !mRendererV2 ) { mErrors.append( QObject::tr( "No renderer for drawing." ) ); return false; } bool usingEffect = false; if ( mRendererV2->paintEffect() && mRendererV2->paintEffect()->enabled() ) { usingEffect = true; mRendererV2->paintEffect()->begin( mContext ); } // Per feature blending mode if ( mContext.useAdvancedEffects() && mFeatureBlendMode != QPainter::CompositionMode_SourceOver ) { // set the painter to the feature blend mode, so that features drawn // on this layer will interact and blend with each other mContext.painter()->setCompositionMode( mFeatureBlendMode ); } mRendererV2->startRender( mContext, mFields ); QString rendererFilter = mRendererV2->filter( mFields ); QgsRectangle requestExtent = mContext.extent(); mRendererV2->modifyRequestExtent( requestExtent, mContext ); QgsFeatureRequest::OrderBy orderBy = mRendererV2->orderBy(); QgsFeatureRequest featureRequest = QgsFeatureRequest() .setFilterRect( requestExtent ) .setSubsetOfAttributes( mAttrNames, mFields ) .setExpressionContext( mContext.expressionContext() ) .setOrderBy( orderBy ); const QgsFeatureFilterProvider* featureFilterProvider = mContext.featureFilterProvider(); if ( featureFilterProvider ) { featureFilterProvider->filterFeatures( mLayer, featureRequest ); } if ( !rendererFilter.isEmpty() && rendererFilter != "TRUE" ) { featureRequest.combineFilterExpression( rendererFilter ); } // enable the simplification of the geometries (Using the current map2pixel context) before send it to renderer engine. if ( mSimplifyGeometry ) { double map2pixelTol = mSimplifyMethod.threshold(); bool validTransform = true; const QgsMapToPixel& mtp = mContext.mapToPixel(); map2pixelTol *= mtp.mapUnitsPerPixel(); const QgsCoordinateTransform* ct = mContext.coordinateTransform(); // resize the tolerance using the change of size of an 1-BBOX from the source CoordinateSystem to the target CoordinateSystem if ( ct && !( ct->isShortCircuited() ) ) { try { QgsPoint center = mContext.extent().center(); double rectSize = ct->sourceCrs().geographicFlag() ? 0.0008983 /* ~100/(40075014/360=111319.4833) */ : 100; QgsRectangle sourceRect = QgsRectangle( center.x(), center.y(), center.x() + rectSize, center.y() + rectSize ); QgsRectangle targetRect = ct->transform( sourceRect ); QgsDebugMsg( QString( "Simplify - SourceTransformRect=%1" ).arg( sourceRect.toString( 16 ) ) ); QgsDebugMsg( QString( "Simplify - TargetTransformRect=%1" ).arg( targetRect.toString( 16 ) ) ); if ( !sourceRect.isEmpty() && sourceRect.isFinite() && !targetRect.isEmpty() && targetRect.isFinite() ) { QgsPoint minimumSrcPoint( sourceRect.xMinimum(), sourceRect.yMinimum() ); QgsPoint maximumSrcPoint( sourceRect.xMaximum(), sourceRect.yMaximum() ); QgsPoint minimumDstPoint( targetRect.xMinimum(), targetRect.yMinimum() ); QgsPoint maximumDstPoint( targetRect.xMaximum(), targetRect.yMaximum() ); double sourceHypothenuse = sqrt( minimumSrcPoint.sqrDist( maximumSrcPoint ) ); double targetHypothenuse = sqrt( minimumDstPoint.sqrDist( maximumDstPoint ) ); QgsDebugMsg( QString( "Simplify - SourceHypothenuse=%1" ).arg( sourceHypothenuse ) ); QgsDebugMsg( QString( "Simplify - TargetHypothenuse=%1" ).arg( targetHypothenuse ) ); if ( !qgsDoubleNear( targetHypothenuse, 0.0 ) ) map2pixelTol *= ( sourceHypothenuse / targetHypothenuse ); } } catch ( QgsCsException &cse ) { QgsMessageLog::logMessage( QObject::tr( "Simplify transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) ); validTransform = false; } } if ( validTransform ) { QgsSimplifyMethod simplifyMethod; simplifyMethod.setMethodType( QgsSimplifyMethod::OptimizeForRendering ); simplifyMethod.setTolerance( map2pixelTol ); simplifyMethod.setThreshold( mSimplifyMethod.threshold() ); simplifyMethod.setForceLocalOptimization( mSimplifyMethod.forceLocalOptimization() ); featureRequest.setSimplifyMethod( simplifyMethod ); QgsVectorSimplifyMethod vectorMethod = mSimplifyMethod; mContext.setVectorSimplifyMethod( vectorMethod ); } else { QgsVectorSimplifyMethod vectorMethod; vectorMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification ); mContext.setVectorSimplifyMethod( vectorMethod ); } } else { QgsVectorSimplifyMethod vectorMethod; vectorMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification ); mContext.setVectorSimplifyMethod( vectorMethod ); } QgsFeatureIterator fit = mSource->getFeatures( featureRequest ); if (( mRendererV2->capabilities() & QgsFeatureRendererV2::SymbolLevels ) && mRendererV2->usingSymbolLevels() ) drawRendererV2Levels( fit ); else drawRendererV2( fit ); if ( usingEffect ) { mRendererV2->paintEffect()->end( mContext ); } //apply layer transparency for vector layers if ( mContext.useAdvancedEffects() && mLayerTransparency != 0 ) { // a layer transparency has been set, so update the alpha for the flattened layer // by combining it with the layer transparency QColor transparentFillColor = QColor( 0, 0, 0, 255 - ( 255 * mLayerTransparency / 100 ) ); // use destination in composition mode to merge source's alpha with destination mContext.painter()->setCompositionMode( QPainter::CompositionMode_DestinationIn ); mContext.painter()->fillRect( 0, 0, mContext.painter()->device()->width(), mContext.painter()->device()->height(), transparentFillColor ); } return true; }
void QgsRasterDataProvider::readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, QgsCoordinateReferenceSystem theSrcCRS, QgsCoordinateReferenceSystem theDestCRS, void *data ) { QgsDebugMsg( "Entered" ); QgsDebugMsg( "viewExtent = " + viewExtent.toString() ); if ( ! theSrcCRS.isValid() || ! theDestCRS.isValid() || theSrcCRS == theDestCRS ) { readBlock( bandNo, viewExtent, width, height, data ); return; } QTime time; time.start(); double mMaxSrcXRes = 0; double mMaxSrcYRes = 0; if ( capabilities() & QgsRasterDataProvider::ExactResolution ) { mMaxSrcXRes = extent().width() / xSize(); mMaxSrcYRes = extent().height() / ySize(); } QgsRasterProjector myProjector( theSrcCRS, theDestCRS, viewExtent, height, width, mMaxSrcXRes, mMaxSrcYRes, extent() ); QgsDebugMsg( QString( "create projector time (ms): %1" ).arg( time.elapsed() ) ); // TODO: init data by nulls // If we zoom out too much, projector srcRows / srcCols maybe 0, which can cause problems in providers if ( myProjector.srcRows() <= 0 || myProjector.srcCols() <= 0 ) return; // Allocate memory for not projected source data int mySize = dataTypeSize( bandNo ) / 8; void *mySrcData = malloc( mySize * myProjector.srcRows() * myProjector.srcCols() ); time.restart(); readBlock( bandNo, myProjector.srcExtent(), myProjector.srcCols(), myProjector.srcRows(), mySrcData ); QgsDebugMsg( QString( "read not projected block time (ms): %1" ).arg( time.elapsed() ) ); time.restart(); // Project data from source int mySrcRow; int mySrcCol; int mySrcOffset; int myDestOffset; for ( int r = 0; r < height; r++ ) { for ( int c = 0; c < width; c++ ) { myProjector.srcRowCol( r, c, &mySrcRow, &mySrcCol ); mySrcOffset = mySize * ( mySrcRow * myProjector.srcCols() + mySrcCol ); myDestOffset = mySize * ( r * width + c ); // retype to char is just to avoid g++ warning memcpy(( char* ) data + myDestOffset, ( char* )mySrcData + mySrcOffset, mySize ); } } QgsDebugMsg( QString( "reproject block time (ms): %1" ).arg( time.elapsed() ) ); free( mySrcData ); }
QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer* layer, QgsRenderContext& rendererContext ) : QgsMapLayerRenderer( layer->id() ) , mRasterViewPort( nullptr ) , mPipe( nullptr ) { mPainter = rendererContext.painter(); const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel(); mMapToPixel = &theQgsMapToPixel; QgsMapToPixel mapToPixel = theQgsMapToPixel; if ( mapToPixel.mapRotation() ) { // unset rotation for the sake of local computations. // Rotation will be handled by QPainter later // TODO: provide a method of QgsMapToPixel to fetch map center // in geographical units QgsPoint center = mapToPixel.toMapCoordinates( mapToPixel.mapWidth() / 2.0, mapToPixel.mapHeight() / 2.0 ); mapToPixel.setMapRotation( 0, center.x(), center.y() ); } QgsRectangle myProjectedViewExtent; QgsRectangle myProjectedLayerExtent; if ( rendererContext.coordinateTransform() ) { QgsDebugMsg( "coordinateTransform set -> project extents." ); try { myProjectedViewExtent = rendererContext.coordinateTransform()->transformBoundingBox( rendererContext.extent() ); } catch ( QgsCsException &cs ) { QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) ); myProjectedViewExtent.setMinimal(); } try { myProjectedLayerExtent = rendererContext.coordinateTransform()->transformBoundingBox( layer->extent() ); } catch ( QgsCsException &cs ) { QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) ); myProjectedLayerExtent.setMinimal(); } } else { QgsDebugMsg( "coordinateTransform not set" ); myProjectedViewExtent = rendererContext.extent(); myProjectedLayerExtent = layer->extent(); } // clip raster extent to view extent QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent ); if ( myRasterExtent.isEmpty() ) { QgsDebugMsg( "draw request outside view extent." ); // nothing to do return; } QgsDebugMsg( "theViewExtent is " + rendererContext.extent().toString() ); QgsDebugMsg( "myProjectedViewExtent is " + myProjectedViewExtent.toString() ); QgsDebugMsg( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString() ); QgsDebugMsg( "myRasterExtent is " + myRasterExtent.toString() ); // // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings // relating to the size (in pixels and coordinate system units) of the raster part that is // in view in the map window. It also stores the origin. // //this is not a class level member because every time the user pans or zooms //the contents of the rasterViewPort will change mRasterViewPort = new QgsRasterViewPort(); mRasterViewPort->mDrawnExtent = myRasterExtent; if ( rendererContext.coordinateTransform() ) { mRasterViewPort->mSrcCRS = layer->crs(); mRasterViewPort->mDestCRS = rendererContext.coordinateTransform()->destCRS(); mRasterViewPort->mSrcDatumTransform = rendererContext.coordinateTransform()->sourceDatumTransform(); mRasterViewPort->mDestDatumTransform = rendererContext.coordinateTransform()->destinationDatumTransform(); } else { mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid mRasterViewPort->mSrcDatumTransform = -1; mRasterViewPort->mDestDatumTransform = -1; } // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport) mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() ); mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() ); // align to output device grid, i.e. floor/ceil to integers // TODO: this should only be done if paint device is raster - screen, image // for other devices (pdf) it can have floating point origin // we could use floating point for raster devices as well, but respecting the // output device grid should make it more effective as the resampling is done in // the provider anyway mRasterViewPort->mTopLeftPoint.setX( floor( mRasterViewPort->mTopLeftPoint.x() ) ); mRasterViewPort->mTopLeftPoint.setY( floor( mRasterViewPort->mTopLeftPoint.y() ) ); mRasterViewPort->mBottomRightPoint.setX( ceil( mRasterViewPort->mBottomRightPoint.x() ) ); mRasterViewPort->mBottomRightPoint.setY( ceil( mRasterViewPort->mBottomRightPoint.y() ) ); // recalc myRasterExtent to aligned values myRasterExtent.set( mapToPixel.toMapCoordinatesF( mRasterViewPort->mTopLeftPoint.x(), mRasterViewPort->mBottomRightPoint.y() ), mapToPixel.toMapCoordinatesF( mRasterViewPort->mBottomRightPoint.x(), mRasterViewPort->mTopLeftPoint.y() ) ); //raster viewport top left / bottom right are already rounded to int mRasterViewPort->mWidth = static_cast<int>( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() ); mRasterViewPort->mHeight = static_cast<int>( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() ); //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue //mapToPixel.mapUnitsPerPixel() is less then 1, //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas() QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 ); QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( layer->width() ), 3 ); QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( layer->height() ), 3 ); QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 ); QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 ); QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 ); QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 ); QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 ); QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 ); QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 ); QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 ); QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 ); QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 ); // /\/\/\ - added to handle zoomed-in rasters // TODO R->mLastViewPort = *mRasterViewPort; // TODO: is it necessary? Probably WMS only? layer->dataProvider()->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() ); // copy the whole raster pipe! mPipe = new QgsRasterPipe( *layer->pipe() ); }
void QgsMapToolZoom::canvasReleaseEvent( QMouseEvent * e ) { if ( e->button() != Qt::LeftButton ) return; // We are not really dragging in this case. This is sometimes caused by // a pen based computer reporting a press, move, and release, all the // one point. if ( mDragging && ( mZoomRect.topLeft() == mZoomRect.bottomRight() ) ) { mDragging = false; delete mRubberBand; mRubberBand = 0; } if ( mDragging ) { mDragging = false; delete mRubberBand; mRubberBand = 0; // store the rectangle mZoomRect.setRight( e->pos().x() ); mZoomRect.setBottom( e->pos().y() ); const QgsMapToPixel* coordinateTransform = mCanvas->getCoordinateTransform(); // set the extent to the zoomBox QgsPoint ll = coordinateTransform->toMapCoordinates( mZoomRect.left(), mZoomRect.bottom() ); QgsPoint ur = coordinateTransform->toMapCoordinates( mZoomRect.right(), mZoomRect.top() ); QgsRectangle r; r.setXMinimum( ll.x() ); r.setYMinimum( ll.y() ); r.setXMaximum( ur.x() ); r.setYMaximum( ur.y() ); r.normalize(); // prevent zooming to an empty extent if ( r.width() == 0 || r.height() == 0 ) { return; } if ( mZoomOut ) { QgsPoint cer = r.center(); QgsRectangle extent = mCanvas->extent(); double sf; if ( mZoomRect.width() > mZoomRect.height() ) { sf = extent.width() / r.width(); } else { sf = extent.height() / r.height(); } r.expand( sf ); QgsDebugMsg( QString( "Extent scaled by %1 to %2" ).arg( sf ).arg( r.toString().toLocal8Bit().constData() ) ); QgsDebugMsg( QString( "Center of currentExtent after scaling is %1" ).arg( r.center().toString().toLocal8Bit().constData() ) ); } mCanvas->setExtent( r ); mCanvas->refresh(); } else // not dragging { // change to zoom in/out by the default multiple mCanvas->zoomWithCenter( e->x(), e->y(), !mZoomOut ); } }
void QgsGrassFeatureIterator::setSelectionRect( const QgsRectangle& rect, bool useIntersect ) { QgsDebugMsg( QString( "useIntersect = %1 rect = %2" ).arg( useIntersect ).arg( rect.toString() ) ); // TODO: selection of edited lines // Lock because functions using static/global variables are used // (e.g. static LocList in Vect_select_lines_by_box, global BranchBuf in RTreeGetBranches) QgsGrass::lock(); mSelection.fill( false ); BOUND_BOX box; box.N = rect.yMaximum(); box.S = rect.yMinimum(); box.E = rect.xMaximum(); box.W = rect.xMinimum(); box.T = PORT_DOUBLE_MAX; box.B = -PORT_DOUBLE_MAX; // Init structures struct ilist * list = Vect_new_list(); if ( !useIntersect ) { // select by bounding boxes only if ( mSource->mLayerType == QgsGrassProvider::POINT || mSource->mLayerType == QgsGrassProvider::CENTROID || mSource->mLayerType == QgsGrassProvider::LINE || mSource->mLayerType == QgsGrassProvider::FACE || mSource->mLayerType == QgsGrassProvider::BOUNDARY || mSource->mLayerType == QgsGrassProvider::TOPO_POINT || mSource->mLayerType == QgsGrassProvider::TOPO_LINE || mSource->mEditing ) { QgsDebugMsg( "Vect_select_lines_by_box" ); int type = mSource->mGrassType; if ( mSource->mEditing ) { type = GV_POINTS | GV_LINES; } QgsDebugMsg( QString( "type = %1" ).arg( type ) ); Vect_select_lines_by_box( mSource->map(), &box, type, list ); } else if ( mSource->mLayerType == QgsGrassProvider::POLYGON ) { Vect_select_areas_by_box( mSource->map(), &box, list ); } else if ( mSource->mLayerType == QgsGrassProvider::TOPO_NODE ) { Vect_select_nodes_by_box( mSource->map(), &box, list ); } } else { // check intersection struct line_pnts *polygon = Vect_new_line_struct(); // Using z coor -PORT_DOUBLE_MAX/PORT_DOUBLE_MAX we cover 3D, Vect_select_lines_by_polygon is // using dig_line_box to get the box, it is not perfect, Vect_select_lines_by_polygon // should clarify better how 2D/3D is treated Vect_append_point( polygon, rect.xMinimum(), rect.yMinimum(), -PORT_DOUBLE_MAX ); Vect_append_point( polygon, rect.xMaximum(), rect.yMinimum(), PORT_DOUBLE_MAX ); Vect_append_point( polygon, rect.xMaximum(), rect.yMaximum(), 0 ); Vect_append_point( polygon, rect.xMinimum(), rect.yMaximum(), 0 ); Vect_append_point( polygon, rect.xMinimum(), rect.yMinimum(), 0 ); if ( mSource->mLayerType == QgsGrassProvider::POINT || mSource->mLayerType == QgsGrassProvider::CENTROID || mSource->mLayerType == QgsGrassProvider::LINE || mSource->mLayerType == QgsGrassProvider::FACE || mSource->mLayerType == QgsGrassProvider::BOUNDARY || mSource->mLayerType == QgsGrassProvider::TOPO_POINT || mSource->mLayerType == QgsGrassProvider::TOPO_LINE || mSource->mEditing ) { QgsDebugMsg( "Vect_select_lines_by_polygon" ); int type = mSource->mGrassType; if ( mSource->mEditing ) { type = GV_POINTS | GV_LINES; } QgsDebugMsg( QString( "type = %1" ).arg( type ) ); Vect_select_lines_by_polygon( mSource->map(), polygon, 0, NULL, type, list ); } else if ( mSource->mLayerType == QgsGrassProvider::POLYGON ) { Vect_select_areas_by_polygon( mSource->map(), polygon, 0, NULL, list ); } else if ( mSource->mLayerType == QgsGrassProvider::TOPO_NODE ) { // There is no Vect_select_nodes_by_polygon but for nodes it is the same as by box Vect_select_nodes_by_box( mSource->map(), &box, list ); } Vect_destroy_line_struct( polygon ); } for ( int i = 0; i < list->n_values; i++ ) { int lid = list->value[i]; if ( lid < 1 || lid >= mSelection.size() ) // should not happen { QgsDebugMsg( QString( "lid %1 out of range <1,%2>" ).arg( lid ).arg( mSelection.size() ) ); continue; } mSelection.setBit( lid ); } Vect_destroy_list( list ); QgsDebugMsg( QString( " %1 features selected" ).arg( list->n_values ) ); QgsGrass::unlock(); }
osg::Image* QgsOsgEarthTileSource::createImage( const TileKey& key, ProgressCallback* progress ) { QString kname = key.str().c_str(); kname.replace( '/', '_' ); Q_UNUSED( progress ); //Get the extents of the tile int tileSize = getPixelsPerTile(); if ( tileSize <= 0 ) { QgsDebugMsg( "Tile size too small." ); return ImageUtils::createEmptyImage(); } QgsRectangle viewExtent = mQGisIface->mapCanvas()->fullExtent(); if ( mCoordTransform ) { QgsDebugMsg( QString( "vext0:%1" ).arg( viewExtent.toString( 5 ) ) ); viewExtent = mCoordTransform->transformBoundingBox( viewExtent ); } QgsDebugMsg( QString( "vext1:%1" ).arg( viewExtent.toString( 5 ) ) ); double xmin, ymin, xmax, ymax; key.getExtent().getBounds( xmin, ymin, xmax, ymax ); QgsRectangle tileExtent( xmin, ymin, xmax, ymax ); QgsDebugMsg( QString( "text0:%1" ).arg( tileExtent.toString( 5 ) ) ); if ( !viewExtent.intersects( tileExtent ) ) { QgsDebugMsg( QString( "earth tile key:%1 ext:%2: NO INTERSECT" ).arg( kname ).arg( tileExtent.toString( 5 ) ) ); return ImageUtils::createEmptyImage(); } #ifdef USE_RENDERER QImage *qImage = createQImage( tileSize, tileSize ); if ( !qImage ) { QgsDebugMsg( QString( "earth tile key:%1 ext:%2: EMPTY IMAGE" ).arg( kname ).arg( tileExtent.toString( 5 ) ) ); return ImageUtils::createEmptyImage(); } mMapRenderer->setLayerSet( mQGisIface->mapCanvas()->mapRenderer()->layerSet() ); mMapRenderer->setOutputSize( QSize( tileSize, tileSize ), qImage->logicalDpiX() ); mMapRenderer->setExtent( tileExtent ); QPainter thePainter( qImage ); mMapRenderer->render( &thePainter ); #else mMapSettings.setLayers( mQGisIface->mapCanvas()->mapSettings().layers() ); mMapSettings.setOutputSize( QSize( tileSize, tileSize ) ); mMapSettings.setOutputDpi( QgsApplication::desktop()->logicalDpiX() ); mMapSettings.setExtent( tileExtent ); mMapSettings.setBackgroundColor( QColor( 0, 0, 0, 0 ) ); QgsMapRendererSequentialJob job( mMapSettings ); job.start(); job.waitForFinished(); QImage *qImage = new QImage( job.renderedImage() ); if ( !qImage ) { QgsDebugMsg( QString( "earth tile key:%1 ext:%2: EMPTY IMAGE" ).arg( kname ).arg( tileExtent.toString( 5 ) ) ); return ImageUtils::createEmptyImage(); } Q_ASSERT( qImage->logicalDpiX() == QgsApplication::desktop()->logicalDpiX() ); Q_ASSERT( qImage->format() == QImage::Format_ARGB32_Premultiplied ); #endif QgsDebugMsg( QString( "earth tile key:%1 ext:%2" ).arg( kname ).arg( tileExtent.toString( 5 ) ) ); #if 0 qImage->save( QString( "/tmp/tile-%1.png" ).arg( kname ) ); #endif osg::ref_ptr<osg::Image> image = new osg::Image; //The pixel format is always RGBA to support transparency image->setImage( tileSize, tileSize, 1, 4, // width, height, depth, pixelFormat? GL_BGRA, GL_UNSIGNED_BYTE, //Why not GL_RGBA - Qt bug? qImage->bits(), osg::Image::NO_DELETE, 1 ); image->flipVertical(); //Create a transparent image if we don't have an image if ( !image.valid() ) { QgsDebugMsg( "image is invalid" ); return ImageUtils::createEmptyImage(); } QgsDebugMsg( "returning image" ); return image.release(); }
bool QgsVectorLayerRenderer::render() { if ( mGeometryType == QgsWkbTypes::NullGeometry || mGeometryType == QgsWkbTypes::UnknownGeometry ) return true; if ( !mRenderer ) { mErrors.append( QObject::tr( "No renderer for drawing." ) ); return false; } bool usingEffect = false; if ( mRenderer->paintEffect() && mRenderer->paintEffect()->enabled() ) { usingEffect = true; mRenderer->paintEffect()->begin( mContext ); } // Per feature blending mode if ( mContext.useAdvancedEffects() && mFeatureBlendMode != QPainter::CompositionMode_SourceOver ) { // set the painter to the feature blend mode, so that features drawn // on this layer will interact and blend with each other mContext.painter()->setCompositionMode( mFeatureBlendMode ); } mRenderer->startRender( mContext, mFields ); QString rendererFilter = mRenderer->filter( mFields ); QgsRectangle requestExtent = mContext.extent(); mRenderer->modifyRequestExtent( requestExtent, mContext ); QgsFeatureRequest featureRequest = QgsFeatureRequest() .setFilterRect( requestExtent ) .setSubsetOfAttributes( mAttrNames, mFields ) .setExpressionContext( mContext.expressionContext() ); if ( mRenderer->orderByEnabled() ) { featureRequest.setOrderBy( mRenderer->orderBy() ); } const QgsFeatureFilterProvider *featureFilterProvider = mContext.featureFilterProvider(); if ( featureFilterProvider ) { featureFilterProvider->filterFeatures( mLayer, featureRequest ); } if ( !rendererFilter.isEmpty() && rendererFilter != QLatin1String( "TRUE" ) ) { featureRequest.combineFilterExpression( rendererFilter ); } // enable the simplification of the geometries (Using the current map2pixel context) before send it to renderer engine. if ( mSimplifyGeometry ) { double map2pixelTol = mSimplifyMethod.threshold(); bool validTransform = true; const QgsMapToPixel &mtp = mContext.mapToPixel(); map2pixelTol *= mtp.mapUnitsPerPixel(); QgsCoordinateTransform ct = mContext.coordinateTransform(); // resize the tolerance using the change of size of an 1-BBOX from the source CoordinateSystem to the target CoordinateSystem if ( ct.isValid() && !ct.isShortCircuited() ) { try { QgsPointXY center = mContext.extent().center(); double rectSize = ct.sourceCrs().isGeographic() ? 0.0008983 /* ~100/(40075014/360=111319.4833) */ : 100; QgsRectangle sourceRect = QgsRectangle( center.x(), center.y(), center.x() + rectSize, center.y() + rectSize ); QgsRectangle targetRect = ct.transform( sourceRect ); QgsDebugMsgLevel( QStringLiteral( "Simplify - SourceTransformRect=%1" ).arg( sourceRect.toString( 16 ) ), 4 ); QgsDebugMsgLevel( QStringLiteral( "Simplify - TargetTransformRect=%1" ).arg( targetRect.toString( 16 ) ), 4 ); if ( !sourceRect.isEmpty() && sourceRect.isFinite() && !targetRect.isEmpty() && targetRect.isFinite() ) { QgsPointXY minimumSrcPoint( sourceRect.xMinimum(), sourceRect.yMinimum() ); QgsPointXY maximumSrcPoint( sourceRect.xMaximum(), sourceRect.yMaximum() ); QgsPointXY minimumDstPoint( targetRect.xMinimum(), targetRect.yMinimum() ); QgsPointXY maximumDstPoint( targetRect.xMaximum(), targetRect.yMaximum() ); double sourceHypothenuse = std::sqrt( minimumSrcPoint.sqrDist( maximumSrcPoint ) ); double targetHypothenuse = std::sqrt( minimumDstPoint.sqrDist( maximumDstPoint ) ); QgsDebugMsgLevel( QStringLiteral( "Simplify - SourceHypothenuse=%1" ).arg( sourceHypothenuse ), 4 ); QgsDebugMsgLevel( QStringLiteral( "Simplify - TargetHypothenuse=%1" ).arg( targetHypothenuse ), 4 ); if ( !qgsDoubleNear( targetHypothenuse, 0.0 ) ) map2pixelTol *= ( sourceHypothenuse / targetHypothenuse ); } } catch ( QgsCsException &cse ) { QgsMessageLog::logMessage( QObject::tr( "Simplify transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) ); validTransform = false; } } if ( validTransform ) { QgsSimplifyMethod simplifyMethod; simplifyMethod.setMethodType( QgsSimplifyMethod::OptimizeForRendering ); simplifyMethod.setTolerance( map2pixelTol ); simplifyMethod.setThreshold( mSimplifyMethod.threshold() ); simplifyMethod.setForceLocalOptimization( mSimplifyMethod.forceLocalOptimization() ); featureRequest.setSimplifyMethod( simplifyMethod ); QgsVectorSimplifyMethod vectorMethod = mSimplifyMethod; vectorMethod.setTolerance( map2pixelTol ); mContext.setVectorSimplifyMethod( vectorMethod ); } else { QgsVectorSimplifyMethod vectorMethod; vectorMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification ); mContext.setVectorSimplifyMethod( vectorMethod ); } } else { QgsVectorSimplifyMethod vectorMethod; vectorMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification ); mContext.setVectorSimplifyMethod( vectorMethod ); } QgsFeatureIterator fit = mSource->getFeatures( featureRequest ); // Attach an interruption checker so that iterators that have potentially // slow fetchFeature() implementations, such as in the WFS provider, can // check it, instead of relying on just the mContext.renderingStopped() check // in drawRenderer() fit.setInterruptionChecker( mInterruptionChecker.get() ); if ( ( mRenderer->capabilities() & QgsFeatureRenderer::SymbolLevels ) && mRenderer->usingSymbolLevels() ) drawRendererLevels( fit ); else drawRenderer( fit ); if ( !fit.isValid() ) { mErrors.append( QStringLiteral( "Data source invalid" ) ); } if ( usingEffect ) { mRenderer->paintEffect()->end( mContext ); } return true; }
QgsRasterProjector::QgsRasterProjector( QgsCoordinateReferenceSystem theSrcCRS, QgsCoordinateReferenceSystem theDestCRS, QgsRectangle theDestExtent, int theDestRows, int theDestCols, double theMaxSrcXRes, double theMaxSrcYRes, QgsRectangle theExtent ) : mSrcCRS( theSrcCRS ) , mDestCRS( theDestCRS ) , mCoordinateTransform( theDestCRS, theSrcCRS ) , mDestExtent( theDestExtent ) , mExtent( theExtent ) , mDestRows( theDestRows ), mDestCols( theDestCols ) , mMaxSrcXRes( theMaxSrcXRes ), mMaxSrcYRes( theMaxSrcYRes ) { QgsDebugMsg( "Entered" ); QgsDebugMsg( "theDestExtent = " + theDestExtent.toString() ); mDestXRes = mDestExtent.width() / ( mDestCols ); mDestYRes = mDestExtent.height() / ( mDestRows ); // Calculate tolerance // TODO: Think it over better // Note: we are checking on matrix each even point, that means taht the real error // in that moment is approximately half size double myDestRes = mDestXRes < mDestYRes ? mDestXRes : mDestYRes; mSqrTolerance = myDestRes * myDestRes; // Initialize the matrix by corners and middle points mCPCols = mCPRows = 3; for ( int i = 0; i < mCPRows; i++ ) { QList<QgsPoint> myRow; myRow.append( QgsPoint() ); myRow.append( QgsPoint() ); myRow.append( QgsPoint() ); mCPMatrix.insert( i, myRow ); } for ( int i = 0; i < mCPRows; i++ ) { calcRow( i ); } while ( true ) { bool myColsOK = checkCols(); if ( !myColsOK ) { insertRows(); } bool myRowsOK = checkRows(); if ( !myRowsOK ) { insertCols(); } if ( myColsOK && myRowsOK ) { QgsDebugMsg( "CP matrix within tolerance" ); mApproximate = true; break; } // What is the maximum reasonable size of transformatio matrix? // TODO: consider better when to break - ratio if ( mCPRows * mCPCols > 0.0625 * mDestRows * mDestCols ) { QgsDebugMsg( "Too large CP matrix" ); mApproximate = false; break; } } QgsDebugMsg( QString( "CPMatrix size: mCPRows = %1 mCPCols = %2" ).arg( mCPRows ).arg( mCPCols ) ); mDestRowsPerMatrixRow = ( float )mDestRows / ( mCPRows - 1 ); mDestColsPerMatrixCol = ( float )mDestCols / ( mCPCols - 1 ); //QgsDebugMsg( "CPMatrix:\n" + cpToString() ); // Calculate source dimensions calcSrcExtent(); calcSrcRowsCols(); mSrcYRes = mSrcExtent.height() / mSrcRows; mSrcXRes = mSrcExtent.width() / mSrcCols; // init helper points pHelperTop = new QgsPoint[mDestCols]; pHelperBottom = new QgsPoint[mDestCols]; calcHelper( 0, pHelperTop ); calcHelper( 1, pHelperBottom ); mHelperTopRow = 0; }
QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &boundingBox, int width, int height, QgsRasterBlockFeedback *feedback ) { QgsDebugMsgLevel( QString( "bandNo = %1 width = %2 height = %3" ).arg( bandNo ).arg( width ).arg( height ), 4 ); QgsDebugMsgLevel( QString( "boundingBox = %1" ).arg( boundingBox.toString() ), 4 ); QgsRasterBlock *block = new QgsRasterBlock( dataType( bandNo ), width, height ); if ( sourceHasNoDataValue( bandNo ) && useSourceNoDataValue( bandNo ) ) { block->setNoDataValue( sourceNoDataValue( bandNo ) ); } if ( block->isEmpty() ) { QgsDebugMsg( "Couldn't create raster block" ); return block; } // Read necessary extent only QgsRectangle tmpExtent = extent().intersect( &boundingBox ); if ( tmpExtent.isEmpty() ) { QgsDebugMsg( "Extent outside provider extent" ); block->setIsNoData(); return block; } double xRes = boundingBox.width() / width; double yRes = boundingBox.height() / height; double tmpXRes, tmpYRes; double providerXRes = 0; double providerYRes = 0; if ( capabilities() & Size ) { providerXRes = extent().width() / xSize(); providerYRes = extent().height() / ySize(); tmpXRes = std::max( providerXRes, xRes ); tmpYRes = std::max( providerYRes, yRes ); if ( qgsDoubleNear( tmpXRes, xRes ) ) tmpXRes = xRes; if ( qgsDoubleNear( tmpYRes, yRes ) ) tmpYRes = yRes; } else { tmpXRes = xRes; tmpYRes = yRes; } if ( tmpExtent != boundingBox || tmpXRes > xRes || tmpYRes > yRes ) { // Read smaller extent or lower resolution if ( !extent().contains( boundingBox ) ) { QRect subRect = QgsRasterBlock::subRect( boundingBox, width, height, extent() ); block->setIsNoDataExcept( subRect ); } // Calculate row/col limits (before tmpExtent is aligned) int fromRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMaximum() ) / yRes ); int toRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1; int fromCol = std::round( ( tmpExtent.xMinimum() - boundingBox.xMinimum() ) / xRes ); int toCol = std::round( ( tmpExtent.xMaximum() - boundingBox.xMinimum() ) / xRes ) - 1; QgsDebugMsgLevel( QString( "fromRow = %1 toRow = %2 fromCol = %3 toCol = %4" ).arg( fromRow ).arg( toRow ).arg( fromCol ).arg( toCol ), 4 ); if ( fromRow < 0 || fromRow >= height || toRow < 0 || toRow >= height || fromCol < 0 || fromCol >= width || toCol < 0 || toCol >= width ) { // Should not happen QgsDebugMsg( "Row or column limits out of range" ); return block; } // If lower source resolution is used, the extent must beS aligned to original // resolution to avoid possible shift due to resampling if ( tmpXRes > xRes ) { int col = std::floor( ( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes ); col = std::ceil( ( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes ); } if ( tmpYRes > yRes ) { int row = std::floor( ( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes ); tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes ); row = std::ceil( ( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes ); tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes ); } int tmpWidth = std::round( tmpExtent.width() / tmpXRes ); int tmpHeight = std::round( tmpExtent.height() / tmpYRes ); tmpXRes = tmpExtent.width() / tmpWidth; tmpYRes = tmpExtent.height() / tmpHeight; QgsDebugMsgLevel( QString( "Reading smaller block tmpWidth = %1 height = %2" ).arg( tmpWidth ).arg( tmpHeight ), 4 ); QgsDebugMsgLevel( QString( "tmpExtent = %1" ).arg( tmpExtent.toString() ), 4 ); QgsRasterBlock *tmpBlock = new QgsRasterBlock( dataType( bandNo ), tmpWidth, tmpHeight ); if ( sourceHasNoDataValue( bandNo ) && useSourceNoDataValue( bandNo ) ) { tmpBlock->setNoDataValue( sourceNoDataValue( bandNo ) ); } readBlock( bandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->bits(), feedback ); int pixelSize = dataTypeSize( bandNo ); double xMin = boundingBox.xMinimum(); double yMax = boundingBox.yMaximum(); double tmpXMin = tmpExtent.xMinimum(); double tmpYMax = tmpExtent.yMaximum(); for ( int row = fromRow; row <= toRow; row++ ) { double y = yMax - ( row + 0.5 ) * yRes; int tmpRow = std::floor( ( tmpYMax - y ) / tmpYRes ); for ( int col = fromCol; col <= toCol; col++ ) { double x = xMin + ( col + 0.5 ) * xRes; int tmpCol = std::floor( ( x - tmpXMin ) / tmpXRes ); if ( tmpRow < 0 || tmpRow >= tmpHeight || tmpCol < 0 || tmpCol >= tmpWidth ) { QgsDebugMsg( "Source row or column limits out of range" ); block->setIsNoData(); // so that the problem becomes obvious and fixed delete tmpBlock; return block; } qgssize tmpIndex = static_cast< qgssize >( tmpRow ) * static_cast< qgssize >( tmpWidth ) + tmpCol; qgssize index = row * static_cast< qgssize >( width ) + col; char *tmpBits = tmpBlock->bits( tmpIndex ); char *bits = block->bits( index ); if ( !tmpBits ) { QgsDebugMsg( QString( "Cannot get input block data tmpRow = %1 tmpCol = %2 tmpIndex = %3." ).arg( tmpRow ).arg( tmpCol ).arg( tmpIndex ) ); continue; } if ( !bits ) { QgsDebugMsg( "Cannot set output block data." ); continue; } memcpy( bits, tmpBits, pixelSize ); } } delete tmpBlock; } else { readBlock( bandNo, boundingBox, width, height, block->bits(), feedback ); } // apply scale and offset block->applyScaleOffset( bandScale( bandNo ), bandOffset( bandNo ) ); // apply user no data values block->applyNoDataValues( userNoDataValues( bandNo ) ); return block; }
// ------------------------ 1.1 ---------------------------------------------- bool QgsWcsCapabilities::parseDescribeCoverageDom11( QByteArray const &xml, QgsWcsCoverageSummary *coverage ) { QgsDebugMsg( "coverage->identifier = " + coverage->identifier ); if ( ! convertToDom( xml ) ) return false; QDomElement docElem = mCapabilitiesDom.documentElement(); QgsDebugMsg( "testing tagName " + docElem.tagName() ); QString tagName = stripNS( docElem.tagName() ); if ( tagName != QLatin1String( "CoverageDescriptions" ) ) { mErrorTitle = tr( "Dom Exception" ); mErrorFormat = QStringLiteral( "text/plain" ); mError = tr( "Could not get WCS capabilities in the expected format (DTD): no %1 found.\nThis might be due to an incorrect WCS Server URL.\nTag: %3\nResponse was:\n%4" ) .arg( QStringLiteral( "CoverageDescriptions" ), docElem.tagName(), QString( xml ) ); QgsLogger::debug( "Dom Exception: " + mError ); return false; } // Get image size, we can get it from BoundingBox with crs=urn:ogc:def:crs:OGC::imageCRS // but while at least one BoundingBox is mandatory, it does not have to be urn:ogc:def:crs:OGC::imageCRS // TODO: if BoundingBox with crs=urn:ogc:def:crs:OGC::imageCRS is not found, // we could calculate image size from GridCRS.GridOffsets (if available) QList<QDomElement> boundingBoxElements = domElements( docElem, QStringLiteral( "CoverageDescription.Domain.SpatialDomain.BoundingBox" ) ); QgsDebugMsg( QStringLiteral( "%1 BoundingBox found" ).arg( boundingBoxElements.size() ) ); const auto constBoundingBoxElements = boundingBoxElements; for ( const QDomElement &el : constBoundingBoxElements ) { QString authid = crsUrnToAuthId( el.attribute( QStringLiteral( "crs" ) ) ); QList<double> low = parseDoubles( domElementText( el, QStringLiteral( "LowerCorner" ) ) ); QList<double> high = parseDoubles( domElementText( el, QStringLiteral( "UpperCorner" ) ) ); if ( low.size() != 2 && high.size() != 2 ) continue; if ( el.attribute( QStringLiteral( "crs" ) ) == QLatin1String( "urn:ogc:def:crs:OGC::imageCRS" ) ) { coverage->width = ( int )( high[0] - low[0] + 1 ); coverage->height = ( int )( high[1] - low[1] + 1 ); coverage->hasSize = true; } else { QgsRectangle box; QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( authid ); if ( crs.isValid() && crs.hasAxisInverted() ) { box = QgsRectangle( low[1], low[0], high[1], high[0] ); } else { box = QgsRectangle( low[0], low[1], high[0], high[1] ); } coverage->boundingBoxes.insert( authid, box ); QgsDebugMsg( "crs: " + crs.authid() + ' ' + crs.description() + QString( " axisInverted = %1" ).arg( crs.hasAxisInverted() ) ); QgsDebugMsg( "BoundingBox: " + authid + " : " + box.toString() ); } } QgsDebugMsg( QStringLiteral( "width = %1 height = %2" ).arg( coverage->width ).arg( coverage->height ) ); // Each georectified coverage should have GridCRS QDomElement gridCRSElement = domElement( docElem, QStringLiteral( "CoverageDescription.Domain.SpatialDomain.GridCRS" ) ); if ( !gridCRSElement.isNull() ) { QString crsUrn = firstChildText( gridCRSElement, QStringLiteral( "GridBaseCRS" ) ); coverage->nativeCrs = crsUrnToAuthId( crsUrn ); QgsDebugMsg( "nativeCrs = " + coverage->nativeCrs ); // TODO: consider getting coverage size from GridOffsets (resolution) // if urn:ogc:def:crs:OGC::imageCRS BoundingBox was not found } coverage->times = domElementsTexts( docElem, QStringLiteral( "CoverageDescription.Domain.TemporalDomain.timePosition" ) ); QList<QDomElement> timePeriodElements = domElements( docElem, QStringLiteral( "CoverageDescription.Domain.TemporalDomain.timePeriod" ) ); QgsDebugMsg( QStringLiteral( "%1 timePeriod found" ).arg( timePeriodElements.size() ) ); const auto constTimePeriodElements = timePeriodElements; for ( const QDomElement &el : constTimePeriodElements ) { QString beginPosition = domElementText( el, QStringLiteral( "beginTime" ) ); QString endPosition = domElementText( el, QStringLiteral( "endTime" ) ); QString timeResolution = domElementText( el, QStringLiteral( "timeResolution" ) ); // Format used in request QString time = beginPosition + '/' + endPosition; if ( !timeResolution.isEmpty() ) { time += '/' + timeResolution; } coverage->times << time; } // NULL / no data values // TODO: handle multiple fields / ranges (?) Q_FOREACH ( const QString &text, domElementsTexts( docElem, "CoverageDescription.Range.Field.NullValue" ) ) { bool ok; double val = text.toDouble( &ok ); if ( ok ) { coverage->nullValues.append( val ); } } QStringList formats = domElementsTexts( docElem, QStringLiteral( "CoverageDescription.SupportedFormat" ) ); // There could be formats from GetCapabilities if ( !formats.isEmpty() ) { coverage->supportedFormat = formats; } QStringList crss = domElementsTexts( docElem, QStringLiteral( "CoverageDescription.SupportedCRS" ) ); QSet<QString> authids; // Set, in case one CRS is in more formats (URN, non URN) const auto constCrss = crss; for ( const QString &crs : constCrss ) { authids.insert( crsUrnToAuthId( crs ) ); } if ( !authids.isEmpty() ) { coverage->supportedCrs = authids.toList(); } coverage->described = true; return true; }