Beispiel #1
0
    def styleMap(self):
        categories = []
        for cat in range(0, self.districts + 1):
            symbol = QgsSymbol.defaultSymbol(self.activeLayer.geometryType())
            layer_style = {}
            layer_style['color'] = '%d, %d, %d' % (randrange(
                0, 256), randrange(0, 256), randrange(0, 256))
            layer_style['outline'] = '#000000'
            symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style)

            # replace default symbol layer with the configured one
            if symbol_layer is not None:
                symbol.changeSymbolLayer(0, symbol_layer)

            # create renderer object
            if cat == 0:
                category = QgsRendererCategory("", symbol, "")
            else:
                category = QgsRendererCategory(districtName[cat], symbol,
                                               str(districtName[cat]))
            # entry for the list of category items
            categories.append(category)
        renderer = QgsCategorizedSymbolRenderer(self.distfield, categories)
        # assign the created renderer to the layer
        if renderer is not None:
            self.activeLayer.setRenderer(renderer)

        self.activeLayer.triggerRepaint()
Beispiel #2
0
    def set_style_mapzones(self):

        extras = f'"mapzones":""'
        body = self.create_body(extras=extras)

        # self.controller.log_info(f"SELECT gw_fct_getstylemapzones ({body})")
        json_return = self.controller.get_json('gw_fct_getstylemapzones', body)
        if not json_return:
            return False

        for mapzone in json_return['body']['data']['mapzones']:

            # self.controller.log_info(f"Mapzone: ({mapzone})")
            # Loop for each mapzone returned on json
            lyr = self.controller.get_layer_by_tablename(mapzone['layer'])
            categories = []
            status = mapzone['status']
            if status == 'Disable':
                pass

            if lyr:
                # Loop for each id returned on json
                for id in mapzone['values']:
                    # initialize the default symbol for this geometry type
                    symbol = QgsSymbol.defaultSymbol(lyr.geometryType())
                    symbol.setOpacity(float(mapzone['opacity']))

                    # Setting simp
                    R = random.randint(0, 255)
                    G = random.randint(0, 255)
                    B = random.randint(0, 255)
                    if status == 'Stylesheet':
                        try:
                            R = id['stylesheet']['color'][0]
                            G = id['stylesheet']['color'][1]
                            B = id['stylesheet']['color'][2]
                        except TypeError:
                            R = random.randint(0, 255)
                            G = random.randint(0, 255)
                            B = random.randint(0, 255)

                    elif status == 'Random':
                        R = random.randint(0, 255)
                        G = random.randint(0, 255)
                        B = random.randint(0, 255)

                    # Setting sytle
                    layer_style = {'color': '{}, {}, {}'.format(int(R), int(G), int(B))}
                    symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style)

                    if symbol_layer is not None:
                        symbol.changeSymbolLayer(0, symbol_layer)
                    category = QgsRendererCategory(id['id'], symbol, str(id['id']))
                    categories.append(category)

                    # apply symbol to layer renderer
                    lyr.setRenderer(QgsCategorizedSymbolRenderer(mapzone['idname'], categories))

                    # repaint layer
                    lyr.triggerRepaint()
Beispiel #3
0
def set_category_renderer(layer: QgsVectorLayer, column: str,
                          start_color: Tuple[int, int, int],
                          end_color: Tuple[int, int, int], unit: str = ''):
    '''
    apply a category renderer to a layer. The categories will be the unique
    values of the given column with interpolated colors between given start
    and end color

    Parameters
    ----------
    layer : QgsVectorLayer
        layer to apply the renderer to
    column : str
        name of the column containing the categories
    start_color : tuple
        rgb values of the first color in the color range
    end_color : tuple
        rgb values of the last color in the color range
    unit : str
        a unit to be added to the category label, defaults to no unit
    '''
    idx = layer.fields().indexOf(column)
    unique_values = list(layer.uniqueValues(idx))
    unique_values.sort()
    categories = []
    geometry_type = layer.geometryType()
    for i, value in enumerate(unique_values):
        # initialize the default symbol for this geometry type
        symbol = QgsSymbol.defaultSymbol(geometry_type)

        # configure a symbol layer
        layer_style = {}
        rgb = []
        for c in range(3):
            rgb.append(str(int(interpolate(start_color[c], end_color[c],
                                           i, len(unique_values)))))
        layer_style['color'] = ','.join(rgb)
        #layer_style['outline'] = '#000000'
        symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style)

        # replace default symbol layer with the configured one
        if symbol_layer is not None:
            symbol.changeSymbolLayer(0, symbol_layer)

        # create renderer object
        category = QgsRendererCategory(value, symbol, f'{value} {unit}')
        # entry for the list of category items
        categories.append(category)

    # create renderer object
    renderer = QgsCategorizedSymbolRenderer(column, categories)
    layer.setRenderer(renderer)
Beispiel #4
0
def vector_apply_unique_value_renderer(vector_layer, column):
    """Apply colours to each unique value for a vector layer column.

    source: https://gis.stackexchange.com/a/175114

    Args:
        vector_layer (QgsVectorLayer): A vector layer to apply unique symbology to.
        column (str): The column containing the unique values

    """

    categories = []
    uniq_vals = vector_layer.dataProvider().uniqueValues(
        vector_layer.fields().lookupField(column))
    randcolors = random_colours(len(uniq_vals))

    for i, ea_value in enumerate(sorted(uniq_vals)):
        # initialize the default symbol for this geometry type
        symbol = QgsSymbol.defaultSymbol(vector_layer.geometryType())

        # configure a symbol layer
        layer_style = {
            'color': '{}, {}, {}'.format(*randcolors[i]),
            'outline': '#000000'
        }

        symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style)

        # replace default symbol layer with the configured one
        if symbol_layer is not None:
            symbol.changeSymbolLayer(0, symbol_layer)

        # create renderer object
        category = QgsRendererCategory(ea_value, symbol, str(ea_value))

        # entry for the list of category items
        categories.append(category)

    # create renderer object
    renderer = QgsCategorizedSymbolRenderer(column, categories)

    # assign the created renderer to the layer
    if renderer is not None:
        vector_layer.setRenderer(renderer)

    # refresh
    vector_layer.triggerRepaint()
Beispiel #5
0
    def categorize_layer(self, layer=None, attr='class', layer_name='Cyano_merged'):
        """
        :param attr:
        :return:
        """
        if not layer:
            layer = self.get_layer_by_name(layer_name)

        try:
            attr_idx = layer.fields().indexFromName(attr)
        except:
            attr_idx = 0

        unique_values = layer.uniqueValues(attr_idx)
        categories = []

        color_map = {0: '0, 0, 0', 1: '187, 187, 187', 2: '255, 255, 26', 3: '247, 126, 60', 4: '0, 0, 0'}
        color_map_hex = {0: '#000000', 1: '#bbbbbb', 2: '#ffff1a', 3: '#f77e3c', 4: '#000000'}
        for value in unique_values:
            symbol = QgsSymbol.defaultSymbol(layer.geometryType())

            # configure a symbol layer
            layer_style = {'color': color_map[value],
                           'outline': color_map_hex[value]}
            symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style)

            # replace default symbol layer with the configured one
            if symbol_layer is not None:
                symbol.changeSymbolLayer(0, symbol_layer)

            # create renderer object
            category = QgsRendererCategory(value, symbol, str(value))
            # entry for the list of category items
            categories.append(category)

        # create renderer object
        renderer = QgsCategorizedSymbolRenderer(attr, categories)

        # assign the created renderer to the layer
        if renderer is not None:
            layer.setRenderer(renderer)

        layer.triggerRepaint()
    def style_categorized(self, layer=None, style_by=None):
        if layer is None:
            layer = self.layer
        if style_by is None:
            style_by = self.default_field_name
        # get unique values
        fni = layer.fields().indexOf(style_by)
        unique_values = layer.dataProvider().uniqueValues(fni)
        # define categories
        categories = []
        for unique_value in unique_values:
            # initialize the default symbol for this geometry type
            symbol = QgsSymbol.defaultSymbol(layer.geometryType())
            # configure a symbol layer
            layer_style = {}
            layer_style['color'] = '%d, %d, %d' % (randrange(
                0, 256), randrange(0, 256), randrange(0, 256))
            layer_style['outline'] = '#000000'
            symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style)
            # replace default symbol layer with the configured one
            if symbol_layer is not None:
                symbol.changeSymbolLayer(0, symbol_layer)
            # create renderer object
            category = QgsRendererCategory(unique_value, symbol,
                                           str(unique_value))
            # entry for the list of category items
            categories.append(category)
        # create renderer object
        renderer = QgsCategorizedSymbolRenderer(style_by, categories)
        # assign the created renderer to the layer
        if renderer is not None:
            layer.setRenderer(renderer)
        layer.triggerRepaint()

        # NOTE QGIS3: probably not needed
        # self.iface.layerTreeView().refreshLayerSymbology(layer.id())

        self.iface.mapCanvas().refresh()
Beispiel #7
0
categories = []
count = 0
iteration = 1

for unique_value in unique_values:
    count += 1

for unique_value in unique_values:
    symbol = QgsSymbol.defaultSymbol(layer.geometryType())
    layer_style = {}
    increase = 256 / count
    layer_style['color'] = '%d, %d, %d' % (0, (256 - (iteration *
                                                      (increase / 2))), 0)
    """layer_style['color'] = '%d, %d, %d' % (randrange(0,256), randrange(0,256), randrange(0,256))"""
    layer_style['outline'] = '#000000'
    symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style)

    if symbol_layer is not None:
        symbol.changeSymbolLayer(0, symbol_layer)

    category = QgsRendererCategory(unique_value, symbol, str(unique_value))
    categories.append(category)

    iteration += 1

renderer = QgsCategorizedSymbolRenderer('ffh_typ_text', categories)

if renderer is not None:
    valuableIntersected.setRenderer(renderer)
valuableIntersected.triggerRepaint()
"""We do  the same for the non-valuable habitats"""
Beispiel #8
0
    def unionespacial(self, event):
        # Datos de viviendas de catastro
        bu = self.input_gml.filePath()

        # Datos de perímetros de núcleos de población EIEL
        nucleo = self.input_shp.filePath()

        # Unir aributos por localización: Viviendas y núcleos
        bu_output = 'C:/V_EIEL/viviendasclasificadas.shp'
        processing.run(
            "qgis:joinattributesbylocation", {
                'INPUT': bu,
                'JOIN': nucleo,
                'PREDICATE': 0,
                'JOIN_FIELDS': ['CODIGO', 'DENOMINACI'],
                'METHOD': 0,
                'PREFIX': 'NU_',
                'OUTPUT': bu_output
            })

        joinat = QgsVectorLayer(bu_output, "Viviendas clasificadas nucleo",
                                "ogr")
        QgsProject.instance().addMapLayers([joinat])

        # Selecciono registros sin clasificar de la capa de viviendas clasificadas utilizando la capa núcleos

        expresion = "NU_CODIGO is NULL"
        joinat.selectByExpression(expresion, QgsVectorLayer.SetSelection)

        #Genero el buffer de la capa núcleos

        nucleo = self.input_shp.filePath()
        file_output = 'C:/V_EIEL/buffernucleo.shp'

        processing.run(
            "native:buffer", {
                'INPUT': nucleo,
                'DISTANCE': 200,
                'SEGMENTS': 10,
                'DISSOLVE': False,
                'END_CAP_STYLE': 0,
                'JOIN_STYLE': 0,
                'MITER_LIMIT': 1,
                'OUTPUT': file_output
            })

        lyrBuffer = QgsVectorLayer(file_output, "Buffer nucleo", "ogr")
        QgsProject.instance().addMapLayers([lyrBuffer])

        # Unión espacial de los registros seleccionados de joinat con buffer
        bu_output_2 = 'C:/V_EIEL/viviendasclasificadas_2.shp'
        processing.run(
            "qgis:joinattributesbylocation", {
                'INPUT':
                QgsProcessingFeatureSourceDefinition(
                    'Viviendas clasificadas nucleo', True),
                'JOIN':
                lyrBuffer,
                'PREDICATE':
                0,
                'JOIN_FIELDS': ['CODIGO', 'DENOMINACI'],
                'METHOD':
                0,
                'PREFIX':
                'NU_',
                'OUTPUT':
                bu_output_2
            })

        joinat_2 = QgsVectorLayer(bu_output_2, "Viviendas clasificadas buffer",
                                  "ogr")
        QgsProject.instance().addMapLayers([joinat_2])

        joinat.removeSelection()
        joinat.commitChanges()

        # El resto de viviendas no clasificadas mediante la union con capa buffer pasan a estar en diseminado
        joinat_2 = iface.activeLayer()

        expresion_2 = "NU_CODIGO_ is NULL"
        joinat_2.selectByExpression(expresion_2, QgsVectorLayer.SetSelection)
        joinat_2.startEditing()
        n = joinat_2.selectedFeatureCount()

        for i in range(0, n):
            diseminado = joinat_2.selectedFeatures()
            viv_diseminado = diseminado[i]
            viv_diseminado.setAttribute("NU_CODIGO_", "99")
            viv_diseminado["NU_CODIGO_"] = "99"
            joinat_2.updateFeature(viv_diseminado)
            viv_diseminado.setAttribute("NU_DENOM_1", "DISEMINADO")
            viv_diseminado["NU_DENOM_1"] = "DISEMINADO"
            joinat_2.updateFeature(viv_diseminado)

        joinat_2.commitChanges()
        joinat_2.removeSelection()

        joinat_2.startEditing()
        features = joinat_2.getFeatures()
        for feature in features:
            feature.setAttribute(feature.fieldNameIndex('NU_CODIGO'),
                                 feature['NU_CODIGO_'])
            feature.setAttribute(feature.fieldNameIndex('NU_DENOMIN'),
                                 feature['NU_DENOM_1'])
            joinat_2.updateFeature(feature)

        joinat_2.commitChanges()
        joinat_2.removeSelection()

        # Elimino los campos NU_CODIGO_ y NU_DENOM_1 para conservar la misma estructura en las dos capas joint attributes
        joinat_2.startEditing()
        joinat_2.deleteAttributes([27, 28])
        joinat_2.updateFields()
        joinat_2.commitChanges()

        # Creo la capa union de Viviendas clasificadas nucleo(solo la selección) y viviendas clasificadas buffer
        # En primer lugar extraigo las viviendas clasificadas en la union con la capa nucleos
        expresion_3 = "NU_CODIGO is not NULL"
        joinat.selectByExpression(expresion_3, QgsVectorLayer.SetSelection)
        joinat.startEditing()

        seleccion = 'C:/V_EIEL/viviendasclasificadas_seleccion.shp'
        processing.run("native:saveselectedfeatures", {
            'INPUT': joinat,
            'OUTPUT': seleccion
        })
        nucleo_seleccion = QgsVectorLayer(
            seleccion, "Viviendas clasificadas nucleo seleccion", "ogr")
        QgsProject.instance().addMapLayers([nucleo_seleccion])

        joinat.removeSelection()

        resultado = 'C:/V_EIEL/viviendasclasificadas_resultado.shp'
        processing.run("native:mergevectorlayers", {
            'LAYERS': [nucleo_seleccion, joinat_2],
            'OUTPUT': resultado
        })

        resultado_merge = QgsVectorLayer(resultado, "Viviendas clasificadas",
                                         "ogr")
        QgsProject.instance().addMapLayers([resultado_merge])

        # Suprimo del proyecto todas las capas intermedias generadas en el proceso
        QgsProject.instance().removeMapLayer(nucleo_seleccion)
        QgsProject.instance().removeMapLayer(joinat_2)
        QgsProject.instance().removeMapLayer(joinat)
        QgsProject.instance().removeMapLayer(lyrBuffer)

        #Representación categorizada de la capa resultado
        #Valores únicos
        resultado_merge = iface.activeLayer()

        valoresnucleo = []
        unico = resultado_merge.dataProvider()
        campos = unico.fields()
        id = campos.indexFromName('NU_DENOMIN')
        valoresnucleo = unico.uniqueValues(id)

        #Creación de categorías
        categorias = []
        for valornucleo in valoresnucleo:

            # inicio el valor de símbolo por defecto para la geometría tipo
            symbol = QgsSymbol.defaultSymbol(resultado_merge.geometryType())

            # configuración de capa de simbología
            layer_style = {}
            layer_style['color'] = '%d, %d, %d' % (random.randint(
                0, 256), random.randint(0, 256), random.randint(0, 256))
            layer_style['outline'] = '#000000'
            symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style)

            # sustitución de simbología por defecto por simbología configurada
            if symbol_layer is not None:
                symbol.changeSymbolLayer(0, symbol_layer)

            # creación de objeto renderer
            categoria = QgsRendererCategory(valornucleo, symbol,
                                            str(valornucleo))

            # generación de entrada para la lista de categorías
            categorias.append(categoria)

        renderer = QgsCategorizedSymbolRenderer('NU_DENOMIN', categorias)
        # asignación del renderer a la capa
        if renderer is not None:
            resultado_merge.setRenderer(renderer)

        resultado_merge.triggerRepaint()

        # Cálculo de estadísticas

        resultado = iface.activeLayer()
        estadisticas = 'C:/V_EIEL/estadisticas.csv'
        processing.run(
            "qgis:statisticsbycategories", {
                'CATEGORIES_FIELD_NAME': ['NU_DENOMIN', 'NU_CODIGO'],
                'INPUT': 'Viviendas clasificadas',
                'OUTPUT': 'C:/V_EIEL/estadisticas.csv',
                'VALUES_FIELD_NAME': 'numberOfDw',
            })

        # Cargo datos calculados de estadísticas de distribución de viviendas en QTableWidget tbl_resultados
        with open(estadisticas, 'r') as leer_estadisticas:
            registros = leer_estadisticas.read().splitlines()
            contar = 0  #Descarto la primera linea del archivo por contener las cabeceras de los campos
            for registro in registros:
                r = 0
                if contar > 0:
                    campos = registro.split(',')
                    #Puesto que el campo codigo se almacena con "" las elimino para que no aparezcan en la tabla
                    sc = campos[1].lstrip('"').rstrip('"')

                    #Cargo datos del csv en Qtable widget
                    self.tbl_resultados.insertRow(r)
                    self.tbl_resultados.setItem(r, 0,
                                                QTableWidgetItem(str(sc)))
                    self.tbl_resultados.setItem(
                        r, 1, QTableWidgetItem(str(campos[0])))
                    self.tbl_resultados.setItem(
                        r, 2, QTableWidgetItem(str(campos[7])))
                    r = r + 1
                contar = contar + 1

        # Rastreo de registros duplicados en capa resultado por intersectar con dos buffer o dos núcleos
        features = resultado_merge.getFeatures()
        referencias = []
        referencias_dup = []
        for f in features:
            idr = f.fieldNameIndex('reference')
            referencia = f.attribute(idr)
            if referencia not in referencias:
                referencias.append(referencia)
            else:
                referencias_dup.append(referencia)

        self.lst_duplicados.addItems(referencias_dup)
        total_duplicados = self.lst_duplicados.count()
        self.text_duplicados.append(str(total_duplicados))
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """

        # Retrieve the feature source and sink. The 'dest_id' variable is used
        # to uniquely identify the feature sink, and must be included in the
        # dictionary returned by the processAlgorithm function.
        source = self.parameterAsSource(parameters, self.INPUT_SHP, context)

        sourcecsv = self.parameterAsSource(parameters, self.INPUT_CSV, context)

        # If source was not found, throw an exception to indicate that the algorithm
        # encountered a fatal error. The exception text can be any string, but in this
        # case we use the pre-built invalidSourceError method to return a standard
        # helper text for when a source cannot be evaluated
        if source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, source.fields(),
                                               source.wkbType(),
                                               source.sourceCrs())

        # Send some information to the user
        # feedback.pushInfo('CRS is {}'.format(source.sourceCrs().authid()))
        # chosenFile = self.parameterDefinition('OUTPUT').valueAsPythonString(parameters['OUTPUT'], context)
        #outfilepath = os.path.dirname
        #feedback.pushInfo(outfilepath)
        chosenFile = self.parameterDefinition('OUTPUT').valueAsPythonString(
            parameters['OUTPUT'], context)
        filepath = os.path.dirname(chosenFile[1:]) + '/'
        feedback.pushInfo(filepath)

        joindict = {
            'INPUT': parameters[self.INPUT_SHP],
            'FIELD': 'ABSLGACODE',
            'INPUT_2': parameters[self.INPUT_CSV],
            'FIELD_2': 'LGAcode',
            'METHOD': 0,
            'DISCARD_NONMATCHING': True,
            'PREFIX': 'JOINED',
            'OUTPUT': 'memory'
        }
        join_layer = processing.run('qgis:joinattributestable', joindict)

        layers = self.QgsMapLayerRegistry.instance().mapLayers().values()
        allFeatures = []
        for l in layers:
            for f in l.getFeatures():
                allFeatures.append(f.geometry())

        features = join_layer.getFeatures()
        for feature in features:
            new_feature = QgsFeature()
            # Set geometry to dissolved geometry
            new_feature.setGeometry(f.geometry())
            # Set attributes from sum_unique_values dictionary that we had computed
            new_feature.setAttributes(
                [f[ABSLGACODE], sum_unique_values[f[ABSLGACODE]]])
            sink.addFeature(new_feature, QgsFeatureSink.FastInsert)

        self.OUTPUT.dataProvider().addAttributes(
            [QgsField('65+', QVariant.String)])

        self.OUTPUT.updateFields()
        self.OUTPUT.commitChanges()

        features = self.OUTPUT.getFeatures()

        for feature in features:
            lgastats.startEditing()
            elderly = feature['JOINED65+%']
            old = feature['65+']
            if float(elderly) > 35:
                old = 'VERY HIGH'
            elif float(elderly) > 25:
                old = 'HIGH'
            elif float(elderly) > 20:
                old = 'MEDIUM'
            elif float(elderly) > 15:
                old = 'LOW'
            else:
                old = 'VERY LOW'
        feature['65+'] = old
        self.OUTPUT.updateFeature(feature)
        self.OUTPUT.commitChanges()

        fni = self.OUTPUT.fields().indexFromName('65+')
        unique_ids = self.OUTPUT.dataProvider().uniqueValues(fni)
        QgsMessageLog.logMessage("sstyle for run layer..." + str(unique_ids))

        categories = []
        for unique_id in unique_ids:
            # initialize the default symbol for this geometry type
            symbol = QgsSymbol.defaultSymbol(lgastats.geometryType())
            symbol.setOpacity(0.5)

            layer_style = {}
            layer_style['color'] = '%d, %d, %d' % (random.randrange(
                0, 256), random.randrange(0, 256), random.randrange(0, 256))
            layer_style['outline'] = '#000000'
            symbolLayer = QgsSimpleFillSymbolLayer.create(layer_style)

            if symbolLayer is not None:
                symbol.changeSymbolLayer(0, symbolLayer)
            category = QgsRendererCategory(unique_id, symbol, str(unique_id))
            categories.append(category)

        renderer = QgsCategorizedSymbolRenderer('65+', categories)
        # assign the created renderer to the layer
        if renderer is not None:
            self.OUTPUT.setRenderer(renderer)
            self.OUTPUT.triggerRepaint()

        return {self.OUTPUT: dest_id}
Beispiel #10
0
def stylize_map(layer: QgsVectorLayer) -> [List[str], List[str]]:
    """Stylize the layer with unique colors per timezone

    Args:
        layer (QgsVectorLayer): The layer to stylize

    Returns:
        [List[str], List[str]]: A list with all timezone ids and one with the respective color
    """

    print("Reading timezones from file")
    timezones = layer.uniqueValues(layer.fields().indexOf("tzid"))
    timezones = list(timezones)
    timezones.sort()

    categorized_renderer = QgsCategorizedSymbolRenderer()

    print("Stylizing map")

    timezone_ids = []
    timezone_colors = []
    features = layer.getFeatures()
    categories = []
    currentColor = 0

    for tz in timezones:
        # Modify the Etc timezones to match the Qt format

        qt_tz = tz

        # There are a few exceptions where the Qt timezone ids differ from the dataset ids:
        match = re.match(r"Etc/GMT([+-])(\d+)", tz)
        if match:
            qt_tz = f"UTC{match.group(1)}{match.group(2):0>2}:00"
        elif tz == "Etc/UTC":
            qt_tz = "UTC"
        elif tz == "Etc/GMT":
            qt_tz = "UTC+00:00"

        # Generate a consecutive color for each timezone
        currentColor += 25000
        r = (currentColor >> 16) & 255
        g = (currentColor >> 8 ) & 255
        b = (currentColor      ) & 255

        # Add it to the mapping
        rh = hex(r)[2:]
        gh = hex(g)[2:]
        bh = hex(b)[2:]
        timezone_ids.append(qt_tz)
        timezone_colors.append(f"#{rh:0>2}{gh:0>2}{bh:0>2}")

        symbol = QgsSymbol.defaultSymbol(layer.geometryType())
        symbol_layer = QgsSimpleFillSymbolLayer.create({"color": f"{r}, {g}, {b}"})
        symbol_layer.setStrokeWidth(0.0)
        symbol_layer.setStrokeStyle(Qt.PenStyle.NoPen)
        symbol.changeSymbolLayer(0, symbol_layer)

        category = QgsRendererCategory(tz, symbol, tz)
        categories.append(category)

    renderer = QgsCategorizedSymbolRenderer("tzid", categories)
    layer.setRenderer(renderer)
    layer.triggerRepaint()

    return timezone_ids, timezone_colors