Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NoData support to QgsVirtualRasterProvider #50590

Closed
wants to merge 7 commits into from

Conversation

kikitte
Copy link

@kikitte kikitte commented Oct 18, 2022

This PR adds nodata support to the QgsVirtualRasterProvider, Fixes #46813.

During this work, another problem relevant to QgsVirtualRasterProvider is identification, that is the identification result of
virtual raster layer always shows its real value instead of 'no data' string if value equals the nodata value. Currently QgsVirtualRasterProvider inherits from QgsRasterDataProvider, and QgsRasterDataProvider::identify just check the block reading result without checking the value.

std::unique_ptr< QgsRasterBlock > bandBlock( block( i, pixelExtent, 1, 1 ) );
if ( bandBlock )
{
const double value = bandBlock->value( 0 );
results.insert( i, value );
}
else
{
results.insert( i, QVariant() );
}
if the value is nodata value, an QVariant() should be inserted, the logic from QgsGdalProvider may be more reasonable.
std::unique_ptr< QgsRasterBlock > bandBlock( block( i, pixelExtent, w, h ) );
if ( !bandBlock )
{
return QgsRasterIdentifyResult( ERR( tr( "Cannot read data" ) ) );
}
double value = bandBlock->value( r, c );
if ( ( sourceHasNoDataValue( i ) && useSourceNoDataValue( i ) &&
( std::isnan( value ) || qgsDoubleNear( value, sourceNoDataValue( i ) ) ) ) ||
( QgsRasterRange::contains( value, userNoDataValues( i ) ) ) )
{
results.insert( i, QVariant() ); // null QVariant represents no data
}
else
{
if ( sourceDataType( i ) == Qgis::DataType::Float32 )
{
// Insert a float QVariant so that QgsMapToolIdentify::identifyRasterLayer()
// can print a string without an excessive precision
results.insert( i, static_cast<float>( value ) );
}
else
results.insert( i, value );
}

So, I think there are two ways to fix this problem, the first way is override the identify method in QgsVirtualRasterProvider with nodata value checking, the second way is modify the logic of the QgsRasterDataProvider::identify just like QgsGdalProvider does. I prefer the second way because it is in the right direction, but the first way is error-prone. Hopes to hear your thoughts!

@github-actions github-actions bot added this to the 3.28.0 milestone Oct 18, 2022
@nyalldawson
Copy link
Collaborator

@kikitte thank you! Can you add a unit test covering this too?

@kikitte
Copy link
Author

kikitte commented Oct 22, 2022

@nyalldawson Yes, and the identify method is add to the virtualrasterprovider too.

@github-actions
Copy link

github-actions bot commented Nov 5, 2022

The QGIS project highly values your contribution and would love to see this work merged! Unfortunately this PR has not had any activity in the last 14 days and is being automatically marked as "stale". If you think this pull request should be merged, please check

  • that all unit tests are passing

  • that all comments by reviewers have been addressed

  • that there is enough information for reviewers, in particular

    • link to any issues which this pull request fixes

    • add a description of workflows which this pull request fixes

    • add screenshots if applicable

  • that you have written unit tests where possible
    In case you should have any uncertainty, please leave a comment and we will be happy to help you proceed with this pull request.
    If there is no further activity on this pull request, it will be closed in a week.

@github-actions github-actions bot added the stale Uh oh! Seems this work is abandoned, and the PR is about to close. label Nov 5, 2022
@kikitte kikitte closed this Nov 5, 2022
@kikitte
Copy link
Author

kikitte commented Nov 22, 2022

Hi @nyalldawson, do you have any idea about this PR? This PR add nodata related code to QgsVirtualRasterProvider and fix the identification logic, that is the value returned by QgsRasterDataProvider::identify should be compared with the QgsVirtualRasterProvider's nodata vlaue. I am willing to hear your suggestions.
This PR should also fix another issue #47812

@kikitte kikitte reopened this Nov 22, 2022
@github-actions github-actions bot removed the stale Uh oh! Seems this work is abandoned, and the PR is about to close. label Nov 22, 2022
@github-actions
Copy link

github-actions bot commented Dec 7, 2022

The QGIS project highly values your contribution and would love to see this work merged! Unfortunately this PR has not had any activity in the last 14 days and is being automatically marked as "stale". If you think this pull request should be merged, please check

  • that all unit tests are passing

  • that all comments by reviewers have been addressed

  • that there is enough information for reviewers, in particular

    • link to any issues which this pull request fixes

    • add a description of workflows which this pull request fixes

    • add screenshots if applicable

  • that you have written unit tests where possible
    In case you should have any uncertainty, please leave a comment and we will be happy to help you proceed with this pull request.
    If there is no further activity on this pull request, it will be closed in a week.

@github-actions github-actions bot added the stale Uh oh! Seems this work is abandoned, and the PR is about to close. label Dec 7, 2022
@github-actions
Copy link

While we hate to see this happen, this PR has been automatically closed because it has not had any activity in the last 21 days. If this pull request should be reconsidered, please follow the guidelines in the previous comment and reopen this pull request. Or, if you have any further questions, just ask! We love to help, and if there's anything the QGIS project can do to help push this PR forward please let us know how we can assist.

@github-actions github-actions bot closed this Dec 15, 2022
@elpaso elpaso reopened this Jan 27, 2023
Copy link
Contributor

@elpaso elpaso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except for less autos this looks good to me, excellent job!

src/providers/virtualraster/qgsvirtualrasterprovider.cpp Outdated Show resolved Hide resolved
@elpaso
Copy link
Contributor

elpaso commented Jan 27, 2023

Can you please fix the spelling errors reported from the CI tests ?

@github-actions github-actions bot removed the stale Uh oh! Seems this work is abandoned, and the PR is about to close. label Jan 27, 2023
@elpaso elpaso enabled auto-merge January 29, 2023 11:33
@elpaso
Copy link
Contributor

elpaso commented Jan 30, 2023

@kikitte you need to replace QgsRaster::IdentifyFormat with Qgis::RasterIdentifyFormat from <qgis.h>

auto-merge was automatically disabled January 30, 2023 14:22

Head branch was pushed to by a user without write access

@kikitte
Copy link
Author

kikitte commented Jan 30, 2023

Oh, I am forgot to sync the branch to the newest, now it should be fixed.


// Checking the value returned by QgsRasterDataProvider::identify
// If it is NoData value, replace it with QVariant()
QMap<int, QVariant> bandValues = result.results();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I missed this: did you check if is there a possibility that bandValues is empty or it does not have an item with index 1 ?

If the answer is yes you'll need to check it before calling bandValues[1] or it will crash.

The same goes for mSrcNoDataValue[0] (just check if mSrcNoDataValue can be empty).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The QgsVirtualRasterProvider supports IdentifyValue only, and I think any other code that want to call QgsVirtualRasterProvider::identify should check the provider's capability before calling and that code should pass Qgis::RasterIdentifyFormat::Value as the format.

int QgsVirtualRasterProvider::capabilities() const
{
const int capability = QgsRasterDataProvider::Identify
| QgsRasterDataProvider::IdentifyValue
| QgsRasterDataProvider::Size

When we look into the implementation of QgsRasterDataProvider::identify, the only case that bandValues will be empty is the format not equals to Qgis::RasterIdentifyFormat::Value(line 276). However, that should not be happend acoording to the above explaination. Note that the band number of QgsVirtualRasterProvider is always 1, so the execution of QgsRasterDataProvider::identify ensures that bandValues has only one item. .

QgsRasterIdentifyResult QgsRasterDataProvider::identify( const QgsPointXY &point, Qgis::RasterIdentifyFormat format, const QgsRectangle &boundingBox, int width, int height, int /*dpi*/ )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
QMap<int, QVariant> results;
if ( format != Qgis::RasterIdentifyFormat::Value || !( capabilities() & IdentifyValue ) )
{
QgsDebugMsg( QStringLiteral( "Format not supported" ) );
return QgsRasterIdentifyResult( ERR( tr( "Format not supported" ) ) );
}
if ( !extent().contains( point ) )
{
// Outside the raster
for ( int bandNo = 1; bandNo <= bandCount(); bandNo++ )
{
results.insert( bandNo, QVariant() );
}
return QgsRasterIdentifyResult( Qgis::RasterIdentifyFormat::Value, results );
}
QgsRectangle finalExtent = boundingBox;
if ( finalExtent.isEmpty() )
finalExtent = extent();
if ( width == 0 )
{
width = capabilities() & Size ? xSize() : 1000;
}
if ( height == 0 )
{
height = capabilities() & Size ? ySize() : 1000;
}
// Calculate the row / column where the point falls
const double xres = ( finalExtent.width() ) / width;
const double yres = ( finalExtent.height() ) / height;
const int col = static_cast< int >( std::floor( ( point.x() - finalExtent.xMinimum() ) / xres ) );
const int row = static_cast< int >( std::floor( ( finalExtent.yMaximum() - point.y() ) / yres ) );
const double xMin = finalExtent.xMinimum() + col * xres;
const double xMax = xMin + xres;
const double yMax = finalExtent.yMaximum() - row * yres;
const double yMin = yMax - yres;
const QgsRectangle pixelExtent( xMin, yMin, xMax, yMax );
for ( int bandNumber = 1; bandNumber <= bandCount(); bandNumber++ )
{
std::unique_ptr< QgsRasterBlock > bandBlock( block( bandNumber, pixelExtent, 1, 1 ) );
if ( bandBlock )
{
const double value = bandBlock->value( 0 );
results.insert( bandNumber, value );
}
else
{
results.insert( bandNumber, QVariant() );
}
}
return QgsRasterIdentifyResult( Qgis::RasterIdentifyFormat::Value, results );
}

Since QgsVirtualRasterProvider has only one band, mSrcNoDataValue is initialized with one value in the constructor, it can't be empty.

May be we should check if the bandValues is empty, and no need to check mSrcNoDataValue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This patch has committed.

@github-actions
Copy link

The QGIS project highly values your contribution and would love to see this work merged! Unfortunately this PR has not had any activity in the last 14 days and is being automatically marked as "stale". If you think this pull request should be merged, please check

  • that all unit tests are passing

  • that all comments by reviewers have been addressed

  • that there is enough information for reviewers, in particular

    • link to any issues which this pull request fixes

    • add a description of workflows which this pull request fixes

    • add screenshots if applicable

  • that you have written unit tests where possible
    In case you should have any uncertainty, please leave a comment and we will be happy to help you proceed with this pull request.
    If there is no further activity on this pull request, it will be closed in a week.

@github-actions github-actions bot added the stale Uh oh! Seems this work is abandoned, and the PR is about to close. label Feb 14, 2023
@github-actions
Copy link

While we hate to see this happen, this PR has been automatically closed because it has not had any activity in the last 21 days. If this pull request should be reconsidered, please follow the guidelines in the previous comment and reopen this pull request. Or, if you have any further questions, just ask! We love to help, and if there's anything the QGIS project can do to help push this PR forward please let us know how we can assist.

@github-actions github-actions bot closed this Feb 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stale Uh oh! Seems this work is abandoned, and the PR is about to close.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

On-the-fly raster provider does not have nodata support
3 participants