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

adding layer with custom QgsRasterDataProvider crashes QGIS #59284

Open
2 tasks done
janzandr opened this issue Oct 31, 2024 · 0 comments
Open
2 tasks done

adding layer with custom QgsRasterDataProvider crashes QGIS #59284

janzandr opened this issue Oct 31, 2024 · 0 comments
Labels
Bug Either a bug report, or a bug fix. Let's hope for the latter! Crash/Data Corruption PyQGIS Related to the PyQGIS API

Comments

@janzandr
Copy link

What is the bug or the crash?

When adding a layer with a custom QgsRasterDataProvider to the QGIS map canvas, QGIS crashes.
As a minimal example to reproduce this behaviour, I implemented a data provider that simply wraps the GDAL provider.
All the basic data reading methods (block(), sample() and identify()) work as expected, but when adding the layer to the QGIS map canvas (via iface.addRasterLayer()), it crashes QGIS with the following error.

QGIS ended unexpectedly
Report details
Python Stack Trace
Windows fatal exception: access violation

Current thread 0x00003fac (most recent call first):

Stack Trace

QgsRasterPipe::setResamplingStage :
QgsRasterPipe::QgsRasterPipe :
QgsRasterLayerRenderer::QgsRasterLayerRenderer :
QgsRasterLayer::createMapRenderer :
QgsMapRendererJob::prepareJobs :
QgsMapRendererCustomPainterJob::startPrivate :
QgsMapRendererSequentialJob::startPrivate :
QgsMapCanvas::refreshMap :
QObject::qt_static_metacall :
QTimer::timerEvent :
QObject::event :
QApplicationPrivate::notify_helper :
QApplication::notify :
QgsApplication::notify :
QCoreApplication::notifyInternal2 :
QEventDispatcherWin32Private::sendTimerEvent :
QEventDispatcherWin32::event :
QApplicationPrivate::notify_helper :
QApplication::notify :
QgsApplication::notify :
QCoreApplication::notifyInternal2 :
QCoreApplicationPrivate::sendPostedEvents :
qt_plugin_query_metadata :
QEventDispatcherWin32::processEvents :
qt_plugin_query_metadata :
QEventLoop::exec :
QCoreApplication::exec :
main :
BaseThreadInitThunk :
RtlUserThreadStart :

QGIS Info
QGIS Version: 3.34.11-Prizren
QGIS code revision: 2904bce
Compiled against Qt: 5.15.13
Running against Qt: 5.15.13
Compiled against GDAL: 3.9.2
Running against GDAL: 3.9.2

System Info
CPU Type: x86_64
Kernel Type: winnt
Kernel Version: 10.0.19045

Steps to reproduce the issue

Run this minimalistic code example in the QGIS Python Console.

from os.path import join, basename
from typing import Optional

import processing


from qgis.core import QgsCoordinateReferenceSystem, QgsProcessingUtils, QgsPointXY, \
    QgsRasterLayer, QgsRectangle, QgsRasterBlockFeedback, QgsProviderMetadata, QgsProviderRegistry, \
    QgsRasterDataProvider, QgsRasterBlock, Qgis

# custom raster provider (minimalistic example that simply wraps the GDAL provider)
class MyProvider(QgsRasterDataProvider):
    NAME = 'MY_GDAL'
    DESCRIPTION = 'My GDAL Wrapper'

    def __init__(self, uri):
        super().__init__()
        self.uri = uri
        self.layer = QgsRasterLayer(uri, basename(uri), 'gdal')
        self.provider = self.layer.dataProvider()
        assert self.provider.name() == 'gdal'

    @classmethod
    def createProvider(cls, uri, providerOptions, *args, **kwargs):
        return MyProvider(uri)

    def description(self):
        return self.DESCRIPTION

    def name(self):
        return self.NAME

    def capabilities(self):
        return self.provider.capabilities()

    def bandCount(self):
        return self.provider.bandCount()

    def extent(self):
        return self.provider.extent()

    def crs(self):
        return self.provider.crs()

    def sourceDataType(self, bandNo):
        return self.provider.sourceDataType(bandNo)

    def dataType(self, bandNo):
        return self.provider.dataType(bandNo)

    def xSize(self):
        return self.provider.xSize()

    def ySize(self):
        return self.provider.ySize()

    def isValid(self) -> bool:
        return self.provider.isValid()

    def generateBandName(self, bandNumber: int):
        return self.provider.generateBandName(bandNumber)

    def sample(self, *args, **kwargs):
        return self.provider.sample(*args, **kwargs)

    def identify(self, *args, **kwargs):
        return self.provider.identify(*args, **kwargs)

    def block(
            self, bandNo: int, boundingBox: QgsRectangle, width: int, height: int,
            feedback: QgsRasterBlockFeedback = None
    ) -> Optional[QgsRasterBlock]:
        return self.provider.block(bandNo, boundingBox, width, height, feedback)

    def clone(self):
        provider = MyProvider(self.uri)
        return provider

# register custom provider
metadata = QgsProviderMetadata(
    MyProvider.NAME,
    MyProvider.DESCRIPTION,
    MyProvider.createProvider
)
registry = QgsProviderRegistry.instance()
registry.registerProvider(metadata)

# create single pixel, single band raster with data value 42
filename = join(QgsProcessingUtils.tempFolder(), 'test.tif')
processing.run(
    "native:createconstantrasterlayer",
    {
        'EXTENT': '0,0.99,0,0.99 [EPSG:4326]',
        'TARGET_CRS': QgsCoordinateReferenceSystem('EPSG:4326'),
        'PIXEL_SIZE': 1,
        'NUMBER': 42,
        'OUTPUT_TYPE': 5,
        'OUTPUT': filename
    }
)

# test custom provider (works fine!)
layer = QgsRasterLayer(filename, basename(filename), MyProvider.NAME)
assert layer.isValid()
assert layer.dataProvider().name() == MyProvider.NAME
block = layer.dataProvider().block(1, layer.extent(), layer.width(), layer.height())
assert (block.width(), block.height()) == (1, 1)
assert block.value(0, 0) == 42
value, success = layer.dataProvider().sample(QgsPointXY(0.5, 0.5), 1)
assert success
assert value == 42
value = layer.dataProvider().identify(QgsPointXY(0.5, 0.5), Qgis.RasterIdentifyFormat.Value)
assert value.results()[1] == 42
print('Reading raster data successfully!')

# this will crash QGIS
iface.addRasterLayer(filename, 'basename', MyProvider.NAME)

Versions

QGIS-Version
3.34.11-Prizren
QGIS-Codeversion
2904bce
Qt-Version
5.15.13
Python-Version
3.12.6
GDAL-Version
3.9.2
PROJ-Version
9.4.0
EPSG-Registraturdatenbankversion
v11.004 (2024-02-24)
GEOS-Version
3.12.2-CAPI-1.18.2
SQLite-Version
3.45.1
PDAL-Version
2.6.3
PostgreSQL-Client-Version
16.2
SpatiaLite-Version
5.1.0
QWT-Version
6.2.0
QScintilla2-Version
2.14.1
BS-Version
Windows 10 Version 2009

Aktive Python-Erweiterungen
db_manager
0.1.20
grassprovider
2.12.99
MetaSearch
0.3.6
processing
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 Oct 31, 2024
@elpaso elpaso added the PyQGIS Related to the PyQGIS API label Oct 31, 2024
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! Crash/Data Corruption PyQGIS Related to the PyQGIS API
Projects
None yet
Development

No branches or pull requests

3 participants