[FOSSGIS-Talk] kleine Auffälligkeiten beim featureCount()

bmarcus at giswana.de bmarcus at giswana.de
Mo Mai 11 09:39:09 CEST 2026


Hallo Thomas,

 

da zieh ich aber meinen Hut. Besten Dank für die ausführliche Erklärung und die Bereitstellung von Lösungsansätzen.

 

Dir einen guten Start in die Woche.

 

Schöne Grüße

Bernd

 

Von: Thomas Wölk <thomas.woelk at gmail.com> 
Gesendet: Montag, 11. Mai 2026 08:56
An: bmarcus at giswana.de
Cc: fossgis-talk-liste at fossgis.de
Betreff: Re: [FOSSGIS-Talk] kleine Auffälligkeiten beim featureCount()

 

Drei Phänomene, drei verschiedene Ursachen.

 

Warum featureCount() der proc_child_lyr -2 liefert:

 

Das ist kein mysteriöser Wert, sondern ein Sentinel aus dem Qgis.FeatureCountState-Enum:

 

# Aus qgis.h (QGIS 3.20+)

enum class FeatureCountState : int {

    Uncounted = -2,       # Feature count noch nicht berechnet

    UnknownCount = -1,    # Provider kennt den Count nicht

};

 

Dein Layer ist also nicht etwa „negativ befüllt“, sondern ungültig – und der OGR-Provider hat seinen Counter noch im Initialzustand. Die Ursache steckt in dieser Zeile:

 

proc_child_lyr = QgsVectorLayer('proc_child', '', 'ogr')

 

Du übergibst den String-Literal 'proc_child' als URI, nicht die Variable proc_child mit dem Pfad zum Temp-Output. Ein proc_child_lyr.isValid() würde hier False liefern. Richtig wäre:

 

proc_child_lyr = QgsVectorLayer(proc_child, '', 'ogr')   # ohne Quotes

 

Warum Punkt 2 und 3 dann immer noch unterschiedlich sind:

 

Selbst nach dem Quote-Fix bleiben die beiden Aufrufe konzeptionell verschieden, und das ist gewollt:

 

processing.run(..., is_child_algorithm=True) 

 

gibt im OUTPUT-Dict nur einen URI/Pfad-String zurück. Du bist selbst dafür verantwortlich, daraus einen Layer zu bauen. Dieser Modus ist für die Verkettung innerhalb von Modellen gedacht, wo nicht jeder Zwischenschritt als vollwertiges Layer-Objekt instanziiert werden soll.

 

processing.run(..., is_child_algorithm=False) 

(Default) liefert dir direkt ein lebendes QgsMapLayer-Objekt zurück, das intern bereits über einen QgsProcessingContext aus dem URI gehoben wurde. Daher funktioniert proc_lyr.featureCount() sofort.

 

Wenn du Variante 2 sauber haben willst, geh über die offizielle Helper-Funktion, statt selbst zu basteln:

 

from qgis.core import QgsProcessingContext, QgsProcessingUtils

 

context = QgsProcessingContext()

proc_child = processing.run(

    'native:extractbyattribute', alg_params,

    context=context, is_child_algorithm=True

)['OUTPUT']

proc_child_lyr = QgsProcessingUtils.mapLayerFromString(proc_child, context)

 

Damit klappt Memory-Output genauso wie File-Output, ohne dass du den Provider raten musst.

 

Aktuellen Zustand der GeoPackage-Tabelle abrufen:

 

Du hast den Grund schon richtig diagnostiziert: Der OGR-Provider cached den Feature-Count beim Öffnen, und Trigger-Inserts auf SQLite-Ebene gehen daran vorbei. 

 

Drei Wege, sauber sortiert nach Aufwand:

 

Variante A – Layer im Speicher refreshen:

 

log_tbl.dataProvider().reloadData()   # ab QGIS 3.20

log_tbl.updateExtents()                # optional, für räumliche Indizes

print(log_tbl.featureCount())

 

reloadData() zwingt den Provider, seinen internen Cache zu verwerfen. In den meisten Fällen reicht das. Vorsicht: Wenn dein Modell den auslösenden Layer per QGIS-Edit-Session ändert, feuern die Trigger erst bei commitChanges(). Vorher ist in der DB nichts zu sehen.

 

Variante B – über die Provider-Connection direkt SQL absetzen:

 

from qgis.core import QgsProviderRegistry

 

md = QgsProviderRegistry.instance().providerMetadata('ogr')

conn = md.createConnection('/pfad/zum/file.gpkg', {})

result = conn.executeSql(

    "SELECT COUNT(*) FROM t_sys_log_leitbild WHERE user = 'Horst'"

)

count = result[0][0]

 

Das ist der providerunabhängige PyQGIS-Weg und umgeht jeden Caching-Layer. Funktioniert für GPKG, PostGIS, SpatiaLite gleichermassen.

 

Variante C – sqlite3 direkt:

 

import sqlite3

with sqlite3.connect('/pfad/zum/file.gpkg') as con:

    count = con.execute(

        "SELECT COUNT(*) FROM t_sys_log_leitbild WHERE user = ?",

        ('Horst',)

    ).fetchone()[0]

 

Kürzer und für GeoPackage absolut legitim, aber bindet dich an den konkreten Treiber. Für reine Lese-Counts unproblematisch, fürs Schreiben würde ich es vermeiden, weil du das GeoPackage-Metadaten-Management umgehst.

 

Tradeoff: Variante A ist am idiomatischsten, wenn der Log-Layer ohnehin geladen ist. Variante B ist die robusteste, wenn das Skript auch außerhalb eines geladenen Projekts läuft (Standalone-Tool, Headless-Processing). Variante C nur, wenn du sicher bist, dass es immer ein GeoPackage bleibt.

 

— Thomas

 

Am 11.05.2026, 08:11:40 schrieb bmarcus via FOSSGIS-Talk-Liste <fossgis-talk-liste at fossgis.de <mailto:fossgis-talk-liste at fossgis.de> >:

Hallo Listenmitglieder,



ich bin zwar kein blutiger Python Anfänger, aber mit der PyGIS-API tu ich
mich immer wieder gerne schwer.

Um den Standart-Wert von Eingabeparameter eines QGIS-Modells etwas
dynamischer zu gestalten, habe ich das Modell als Python-Skript exportiert
und entsprechend angepasst. Um die Lauffähigkeit des Skripts zu
gewährleisten, stehen natürlich noch weitere Änderungen an, bei denen ich
auf ein interessantes, mir unerklärliches Phänomen bei der Bestimmung der
Objektanzahl gestoßen bin. Nachfolgendes Skriptschnippsel  



log_tbl = QgsProject.instance().mapLayer(

   't_sys_log_leitbild_24ced76d_bd40_43af_a214_35d4ad8b545f'

   )

print(f'1. Anzahl Features log: {log_tbl.featureCount()}')



alg_params = {

   'FIELD': 'user',

   'INPUT': 't_sys_log_leitbild_24ced76d_bd40_43af_a214_35d4ad8b545f', 

   'OPERATOR': 0,

   'VALUE': 'Horst'

   'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT

}

proc_child = processing.run(

   'native:extractbyattribute', alg_params, is_child_algorithm=True

   )['OUTPUT']

proc_child_lyr = QgsVectorLayer('proc_child', '', 'ogr')

print(f'2. Anzahl Features proc_child_lyr: {proc_child_lyr.featureCount()}')



proc_lyr = processing.run(

   'native:extractbyattribute', alg_params

   )['OUTPUT']

print(f'3. Anzahl Features proc_lyr: {proc_lyr.featureCount()}')



führt zu folgender Ausgabe:



1. Anzahl Features log: 0

2. Anzahl Features proc_child_lyr: -2

3. Anzahl Features proc_lyr: 4



Die Tabelle "t_sys_log_leitbild" nimmt über Trigger gesteuert, Änderungen
eines Layers innerhalb eines GeoPackages entgegen. Horst hat 4 Änderungen an
dem Layer durchgeführt. Die Ausgabe von 3. ist somit korrekt. 

Da QGIS von den Änderungen innerhalb der Datenbank nichts mitbekommen hat,
wird für 1. eine Objektanzahl von 0 ausgegeben. Ist somit nachvollziehbar.

Warum sich allerdings die Ergebnisse von 2. und 3. unterscheiden und wieso
die Anzahl von Tabelleneinträgen negativ sein kann, bleibt mir schleierhaft.
Kann hier jemand Licht ins Dunkel meiner Gehirnwindungen bringen?

Zudem würde mich ebenfalls interessieren, ob es eine Methode gibt, die das
Abrufen des aktuellen Zustands einer Datenbanktabelle ermöglicht oder ob
eine Verbindung zur Datenbank mit SQL-Abfrage den einzig sicheren Weg
darstellt.



Mein Dank im Voraus 

und schöne Grüße

Bernd





-- 
....................................................................
FOSSGIS-Konferenz 2027 mit OpenStreetMap-Event in Heidelberg!
09.-13. März 2027                 https://www.fossgis-konferenz.de/

FOSSGIS Vereinstermine:
https://fossgis.de/aktivit%C3%A4ten/termine/

FOSSGIS e.V, der Verein zur Förderung von Freier Software aus dem
GIS-Bereich und Freier Geodaten!
https://www.fossgis.de/          https://mastodon.online/@FOSSGISeV
____________________________________________________________________
FOSSGIS-Talk-Liste mailing list
FOSSGIS-Talk-Liste at fossgis.de <mailto:FOSSGIS-Talk-Liste at fossgis.de> 
https://lists.fossgis.de/mailman/listinfo/fossgis-talk-liste



Mehr Informationen über die Mailingliste FOSSGIS-Talk-Liste