2

I have a vector image that is set with a single symbology. I would like to update the file to categorical symbology based on either 1 or 0.

Based on the documentation: https://docs.qgis.org/3.28/en/docs/pyqgis_developer_cookbook/vector.html#categorized-symbol-renderer

from qgis._core import QgsApplication
import threading
import sys
from osgeo import gdal, ogr, osr
from qgis.analysis import QgsNativeAlgorithms
from qgis.core import *
from qgis.core import (
    QgsApplication,
    QgsProcessingFeedback,
    QgsVectorLayer
)
from qgis.core import QgsVectorFileWriter

src_path = 'src_files'
res_path = 'res_files'


def fail_with_grace(func):
    def wrapper(*args, **kwargs):
        try:
            func(*args, **kwargs)
        except Exception as e:
            print(e)

    return wrapper


@fail_with_grace
def rename(folder_path, new_name):
    file_num = 0
    for f in os.listdir(folder_path):
        # rename all files in folder
        os.rename(os.path.join(folder_path, f), os.path.join(folder_path, f'{new_name}_{file_num}.tif'))
        file_num += 1


def polygonize(in_path, out_path):
    gdal.UseExceptions()
    gdal.AllRegister()

    # get raster datasource
    src_ds = gdal.Open(in_path)
    if src_ds is None:
        print('Unable to open %s' % in_path)
        sys.exit(1)

    #
    src_band = src_ds.GetRasterBand(1)
    dst_layername = "POLYGONIZED_STUFF"
    drv = ogr.GetDriverByName("ESRI Shapefile")
    dst_ds = drv.CreateDataSource(out_path)

    sp_ref = osr.SpatialReference()
    sp_ref.SetFromUserInput("EPSG:4326")

    dst_layer = dst_ds.CreateLayer(dst_layername, srs=sp_ref)
    fd = ogr.FieldDefn("canopy", ogr.OFTInteger)
    dst_layer.CreateField(fd)
    dst_field = dst_layer.GetLayerDefn().GetFieldIndex("canopy")

    gdal.Polygonize(src_band, None, dst_layer, dst_field, [], callback=None)


# START HEADLESS QGIS
QgsApplication.setPrefixPath('/Applications/QGIS.app/Contents', True)
qgs = QgsApplication([], False)
qgs.initQgis()
print("QGIS initialized")

# rename(src_path, 'canopy_quad')

# merge all tif files in folder
gdal.BuildVRT(os.path.join(src_path, 'canopy_2022.vrt'),
              [os.path.join(src_path, f) for f in os.listdir(src_path) if f.endswith('.tif')])
# load virtual raster into qgis
layer = QgsRasterLayer(os.path.join(src_path, 'canopy_2022.vrt'), 'canopy_2022.vrt')

# polygonize raster
polygonize(os.path.join(src_path, 'canopy_2022.vrt'), os.path.join(res_path, 'canopy_2022.shp'))

# load polygonized vector in qgis
layer = QgsVectorLayer(os.path.join(res_path, 'canopy_2022.shp'), 'canopy_2022.shp', 'ogr')
categorized = QgsCategorizedSymbolRenderer()
# categorized.setClassAttribute('canopy')
canopy = QgsRendererCategory('1', QgsFillSymbol.createSimple({'color': '#ff0000'}), '1')
no_canopy = QgsRendererCategory('0', QgsFillSymbol.createSimple({'color': '#000000'}), '0')
layer.setRenderer(categorized)
layer.triggerRepaint()

qgs.exitQgis()

I believe my code is correct, however, it fails to update when I load it into QGIS. I'm new to PyQGIS and GIS in general.

4
  • It should be as simple as attaching layer.triggerRepaint() to the end of your code, to prompt the symbology to actually update from what it previously was. Does that do the trick?
    – Noxy
    Aug 14, 2023 at 13:24
  • I added layer.triggerRepaint() and it had no effect. Do I need to resave the file back to the dir?
    – Christof
    Aug 14, 2023 at 14:27
  • How do you load the layer into QGIS anyway? Your current code doesn't show that part.
    – Noxy
    Aug 14, 2023 at 14:44
  • @Noxy I updated the OP with my Complete Script.
    – Christof
    Aug 14, 2023 at 15:49

1 Answer 1

1

You never added the categories to your renderer nor did you specify the field to let the renderer match on.

Try this:

categorized = QgsCategorizedSymbolRenderer()
categorized.setClassAttribute("canopy")
canopy = QgsRendererCategory('1', QgsFillSymbol.createSimple({'color': '#ff0000'}), '1')
no_canopy = QgsRendererCategory('0', QgsFillSymbol.createSimple({'color': '#000000'}), '0')
categorized.addCategory(canopy)
categorized.addCategory(no_canopy)

Or this:

canopy = QgsRendererCategory('1', QgsFillSymbol.createSimple({'color': '#ff0000'}), '1')
no_canopy = QgsRendererCategory('0', QgsFillSymbol.createSimple({'color': '#000000'}), '0')
categorized = QgsCategorizedSymbolRenderer(attrName="canopy", categories=[canopy, no_canopy])

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.