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

noDataValues aren't correctly rendered when using a custom QgsRasterDataProvider #53454

Open
1 of 2 tasks
janzandr opened this issue Jun 13, 2023 · 0 comments
Open
1 of 2 tasks
Labels
Bug Either a bug report, or a bug fix. Let's hope for the latter! PyQGIS Related to the PyQGIS API Rasters Related to general raster layer handling (not specific data formats)

Comments

@janzandr
Copy link

What is the bug or the crash?

I want to implement a custom QgsRasterDataProvider to do some on-the-fly raster processing/manipulation. It seams that the noDataValues aren't rendered correctly. NoDataValues should be transparent, instead they are rendered as normal values.

I prepared a minimalistic example reproducing the bug. The provider simply delivers data with values 0, 1, 2, and the value 1 is set as the noDataValue.

In QGIS the layer is wrongly rendered. The gray area in the middle should be transparent:
image

In the Layer Properties, the noDataValue is correctly displayed:
image

When using the Custom Transparency Options to manually mask out values of 1, it looks correct:
image

Steps to reproduce the issue

The minimalistic example can be executed in the QGIS Python Console:

import numpy as np

from qgis.core import QgsRasterDataProvider, QgsProviderRegistry, QgsProviderMetadata, QgsMessageLog, Qgis, \
    QgsDataProvider, QgsRasterLayer, QgsRectangle, QgsRasterBlock, QgsCoordinateReferenceSystem


class MyRasterDataProvider(QgsRasterDataProvider):
    REFS = list()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    @classmethod
    def description(cls):
        return 'My raster data provider'

    @classmethod
    def providerKey(cls):
        return 'MyRasterDataProvider'

    @classmethod
    def createProvider(cls, uri, providerOptions, *args):
        flags = QgsDataProvider.ReadFlags()
        return MyRasterDataProvider(uri, providerOptions, flags)

    def bandCount(self):
        return 1

    def block(self, bandNo, boundingBox, width, height, feedback=None):
        block = QgsRasterBlock(self.dataType(bandNo), width, height)
        array = np.zeros((width, height), np.uint8)
        for x in range(width):
            for y in range(height):
                array[x, y] = int(x / width * 3)
        block.setData(array.tobytes())
        return block

    def clone(self):
        layer = QgsRasterLayer(self.dataSourceUri(), self.name(), self.providerKey())
        provider = layer.dataProvider()
        self.REFS.append((layer, provider))
        return provider

    def crs(self):
        return QgsCoordinateReferenceSystem.fromEpsgId(3857)

    def dataType(self, bandNo):
        return Qgis.DataType.Byte

    def extent(self):
        return QgsRectangle(700000, 6000000, 1700000, 7300000)  # roughly the extent of Germany

    def generateBandName(self, bandNo):
        return 'Band ' + str(bandNo)

    def isValid(self):
        return True

    def name(self):
        return self.providerKey()

    def xSize(self):
        return 100

    def ySize(self):
        return 130

    def sourceHasNoDataValue(self, bandNo):
        return True

    def sourceNoDataValue(self, bandNo):
        return 1

    def useSourceNoDataValue(self, bandNo):
        return True

    def htmlMetadata(self) -> str:
        return ''

    def sourceDataType(self, bandNo: int):
        return self.dataType(bandNo)


def register_data_provider():
    metadata = QgsProviderMetadata(
        MyRasterDataProvider.providerKey(),
        MyRasterDataProvider.description(),
        MyRasterDataProvider.createProvider)
    registry = QgsProviderRegistry.instance()
    registry.registerProvider(metadata)
    QgsMessageLog.logMessage(
        f'{MyRasterDataProvider.providerKey()} raster data provider registered', level=Qgis.MessageLevel.Info
    )

register_data_provider()
layer = QgsRasterLayer('dummy', 'my layer', MyRasterDataProvider.providerKey())
QgsProject.instance().addMapLayer(layer)

Versions

QGIS version
3.28.7-Firenze
QGIS code revision
fedae0e
Qt version
5.15.3
Python version
3.9.5
GDAL/OGR version
3.7.0
PROJ version
9.2.0
EPSG Registry database version
v10.082 (2023-02-06)
GEOS version
3.11.2-CAPI-1.17.2
SQLite version
3.41.1
PDAL version
2.5.3
PostgreSQL client version
unknown
SpatiaLite version
5.0.1
QWT version
6.1.6
QScintilla2 version
2.13.1
OS version
Windows 10 Version 2009

Active Python plugins
Coregistration
22.5.19
DataPlotly
3.9.2
ee_plugin
0.0.6
enmapboxplugin
3.13.0-alpha-0
IPyConsole
version 2.0
magnetic_bearing_measurement
0.5
nominatim_locator_filter
0.2.4
openlayers_plugin
2.0.0
qgis_stac
1.1.1
quick_map_services
0.19.33
rasterdataplotting
1.7
rastertimeseriesmanager
1.8
timeseriesviewerplugin
1.17.20210312T143004.master
vrtbuilderplugin
0.9.20210222T174536.master
zoomview
1.1.2
db_manager
0.1.20
grassprovider
2.12.99
MetaSearch
0.3.6
processing
2.12.99
sagaprovider
2.12.99

Supported QGIS version

  • I'm running a supported QGIS version according to the roadmap.

New profile

Additional context

No response

@janzandr janzandr added the Bug Either a bug report, or a bug fix. Let's hope for the latter! label Jun 13, 2023
@agiudiceandrea agiudiceandrea added Rasters Related to general raster layer handling (not specific data formats) PyQGIS Related to the PyQGIS API labels Jun 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Either a bug report, or a bug fix. Let's hope for the latter! PyQGIS Related to the PyQGIS API Rasters Related to general raster layer handling (not specific data formats)
Projects
None yet
Development

No branches or pull requests

2 participants