예제 #1
0
class Style:
    def __init__(self):
        self._styles = []

    def __getitem__(self, index):
        return self._styles[index]

    def __len__(self):
        return len(self._styles)

    def __iter__(self):
        return iter(self._styles)

    def _load_color_ramps(self, filename, attribute, classes=10):
        self._styleFactory = QgsStyle()
        self._styleFactory.importXml(filename)
        for styleName in self._styleFactory.colorRampNames():
            self._styles.append({
                'name':
                styleName,
                'colorramp':
                self._styleFactory.colorRamp(styleName),
                'classes':
                classes,
                'attribute':
                attribute
            })
예제 #2
0
def getInterpolatedColors(nn, colorName):
    myStyle = QgsStyle().defaultStyle()
    ramp = myStyle.colorRamp(colorName)
    c1 = ramp.color1()
    c2 = ramp.color2()
    t_list = getIntervalInterpolation(nn)
    colors = []
    for i in t_list:
        colors.append(linear_color_interpolation(c1, c2, i))
    return colors
예제 #3
0
def raster_apply_unique_value_renderer(raster_layer,
                                       band_num=1,
                                       n_decimals=0,
                                       color_ramp='',
                                       invert=False):
    """
    Apply a random colour to each each unique value for a raster band.

    In some case the unique values are floating, n_decimals allows these to be rounded for display

    Args:
        raster_layer (QgsRasterLayer): input raster layer
        band_num (int):    the band number used to determine unique values
        n_decimals (int):  number of decimals to round values to
        invert (bool) : Invert the color ramp before applying - Not implemented
    """
    qgsStyles = QgsStyle().defaultStyle()
    # check to see if the colour ramp is installed
    if color_ramp != '' and color_ramp not in qgsStyles.colorRampNames():
        raise ValueError(
            'PAT symbology does not exist. See user manual for install instructions'
        )

    if color_ramp == '':
        ramp = QgsRandomColorRamp()
    else:
        # get an existing color ramp
        ramp = qgsStyles.colorRamp(color_ramp)

    # generate a list of unique values and their colours.
    uniq_classes = QgsPalettedRasterRenderer.classDataFromRaster(
        raster_layer.dataProvider(), band_num, ramp)

    # Create the renderer
    renderer = QgsPalettedRasterRenderer(raster_layer.dataProvider(), band_num,
                                         uniq_classes)

    # assign the renderer to the raster layer:
    raster_layer.setRenderer(renderer)

    # refresh
    raster_layer.triggerRepaint()
예제 #4
0
def layerFromPath(lineFilePath: str, rootGroup: QgsLayerTreeGroup,
                  project: QgsLayerTreeGroup) -> None:
    lineFileBasename = os.path.splitext(os.path.basename(lineFilePath))[0]
    lineLayer = QgsVectorLayer(lineFilePath, lineFileBasename, 'ogr')

    # Get number of features (range of Sequence#, number of renderer color classes)
    driver = ogr.GetDriverByName('ESRI Shapefile')
    dataSource = driver.Open(lineFilePath,
                             0)  # 0 means read-only. 1 means writeable.
    layer = dataSource.GetLayer()
    dataSource = None

    #Setup graduated color renderer based on year
    targetField = 'Year'
    renderer = QgsGraduatedSymbolRenderer('', [QgsRendererRange()])
    renderer.setClassAttribute(targetField)
    lineLayer.setRenderer(renderer)

    #Get viridis color ramp
    style = QgsStyle().defaultStyle()
    defaultColorRampNames = style.colorRampNames()
    viridisIndex = defaultColorRampNames.index('Viridis')
    viridisColorRamp = style.colorRamp(
        defaultColorRampNames[viridisIndex])  #Spectral color ramp

    #Dynamically recalculate number of classes and colors
    renderer.updateColorRamp(viridisColorRamp)
    yearsRange = list(range(1972, 2020))
    classCount = len(yearsRange)
    renderer.updateClasses(lineLayer, QgsGraduatedSymbolRenderer.EqualInterval,
                           classCount)

    #Set graduated color renderer based on Sequence#
    for i in range(classCount):  #[1972-2019], 2020 not included
        targetField = 'DateUnix'
        year = yearsRange[i]
        renderer.updateRangeLowerValue(i, year)
        renderer.updateRangeUpperValue(i, year)

    project.addMapLayer(lineLayer, False)
    rootGroup.insertLayer(0, lineLayer)
    def style_maps(layer,
                   style_by,
                   iface,
                   output_type='damages-rlzs',
                   perils=None,
                   add_null_class=False,
                   render_higher_on_top=False,
                   repaint=True,
                   use_sgc_style=False):
        symbol = QgsSymbol.defaultSymbol(layer.geometryType())
        # see properties at:
        # https://qgis.org/api/qgsmarkersymbollayerv2_8cpp_source.html#l01073
        symbol.setOpacity(1)
        if isinstance(symbol, QgsMarkerSymbol):
            # do it only for the layer with points
            symbol.symbolLayer(0).setStrokeStyle(Qt.PenStyle(Qt.NoPen))

        style = get_style(layer, iface.messageBar())

        # this is the default, as specified in the user settings
        ramp = QgsGradientColorRamp(style['color_from'], style['color_to'])
        style_mode = style['style_mode']

        # in most cases, we override the user-specified setting, and use
        # instead a setting that was required by scientists
        if output_type in OQ_TO_LAYER_TYPES:
            default_qgs_style = QgsStyle().defaultStyle()
            default_color_ramp_names = default_qgs_style.colorRampNames()
            if output_type in (
                    'damages-rlzs',
                    'avg_losses-rlzs',
                    'avg_losses-stats',
            ):
                # options are EqualInterval, Quantile, Jenks, StdDev, Pretty
                # jenks = natural breaks
                if Qgis.QGIS_VERSION_INT < 31000:
                    style_mode = QgsGraduatedSymbolRenderer.Jenks
                else:
                    style_mode = 'Jenks'
                ramp_type_idx = default_color_ramp_names.index('Reds')
                symbol.setColor(QColor(RAMP_EXTREME_COLORS['Reds']['top']))
                inverted = False
            elif (output_type in ('gmf_data', 'ruptures')
                  or (output_type == 'hmaps' and not use_sgc_style)):
                # options are EqualInterval, Quantile, Jenks, StdDev, Pretty
                # jenks = natural breaks
                if output_type == 'ruptures':
                    if Qgis.QGIS_VERSION_INT < 31000:
                        style_mode = QgsGraduatedSymbolRenderer.Pretty
                    else:
                        style_mode = 'PrettyBreaks'
                else:
                    if Qgis.QGIS_VERSION_INT < 31000:
                        style_mode = QgsGraduatedSymbolRenderer.EqualInterval
                    else:
                        style_mode = 'EqualInterval'
                ramp_type_idx = default_color_ramp_names.index('Spectral')
                inverted = True
                symbol.setColor(QColor(RAMP_EXTREME_COLORS['Reds']['top']))
            elif output_type == 'hmaps' and use_sgc_style:
                # FIXME: for SGC they were using size 10000 map units

                # options are EqualInterval, Quantile, Jenks, StdDev, Pretty
                # jenks = natural breaks
                if Qgis.QGIS_VERSION_INT < 31000:
                    style_mode = QgsGraduatedSymbolRenderer.Pretty
                else:
                    style_mode = 'PrettyBreaks'
                try:
                    ramp_type_idx = default_color_ramp_names.index(
                        'SGC_Green2Red_Hmap_Color_Ramp')
                except ValueError:
                    raise ValueError(
                        'Color ramp SGC_Green2Red_Hmap_Color_Ramp was '
                        'not found. Please import it from '
                        'Settings -> Style Manager, loading '
                        'svir/resources/sgc_green2red_hmap_color_ramp.xml')
                inverted = False
                registry = QgsApplication.symbolLayerRegistry()
                symbol_props = {
                    'name': 'square',
                    'color': '0,0,0',
                    'color_border': '0,0,0',
                    'offset': '0,0',
                    'size': '1.5',  # FIXME
                    'angle': '0',
                }
                square = registry.symbolLayerMetadata(
                    "SimpleMarker").createSymbolLayer(symbol_props)
                symbol = QgsSymbol.defaultSymbol(layer.geometryType()).clone()
                symbol.deleteSymbolLayer(0)
                symbol.appendSymbolLayer(square)
                symbol.symbolLayer(0).setStrokeStyle(Qt.PenStyle(Qt.NoPen))
            elif output_type in ['asset_risk', 'input']:
                # options are EqualInterval, Quantile, Jenks, StdDev, Pretty
                # jenks = natural breaks
                if Qgis.QGIS_VERSION_INT < 31000:
                    style_mode = QgsGraduatedSymbolRenderer.EqualInterval
                else:
                    style_mode = 'EqualInterval'
                # exposure_strings = ['number', 'occupants', 'value']
                # setting exposure colors by default
                colors = {
                    'single': RAMP_EXTREME_COLORS['Blues']['top'],
                    'ramp_name': 'Blues'
                }
                inverted = False
                if output_type == 'asset_risk':
                    damage_strings = perils
                    for damage_string in damage_strings:
                        if damage_string in style_by:
                            colors = {
                                'single':
                                RAMP_EXTREME_COLORS['Spectral']['top'],
                                'ramp_name': 'Spectral'
                            }
                            inverted = True
                            break
                else:  # 'input'
                    colors = {
                        'single': RAMP_EXTREME_COLORS['Greens']['top'],
                        'ramp_name': 'Greens'
                    }
                    symbol.symbolLayer(0).setShape(
                        QgsSimpleMarkerSymbolLayerBase.Square)
                single_color = colors['single']
                ramp_name = colors['ramp_name']
                ramp_type_idx = default_color_ramp_names.index(ramp_name)
                symbol.setColor(QColor(single_color))
            else:
                raise NotImplementedError(
                    'Undefined color ramp for output type %s' % output_type)
            ramp = default_qgs_style.colorRamp(
                default_color_ramp_names[ramp_type_idx])
            if inverted:
                ramp.invert()
        # get unique values
        fni = layer.fields().indexOf(style_by)
        unique_values = layer.dataProvider().uniqueValues(fni)
        num_unique_values = len(unique_values - {NULL})
        if num_unique_values > 2:
            if Qgis.QGIS_VERSION_INT < 31000:
                renderer = QgsGraduatedSymbolRenderer.createRenderer(
                    layer, style_by, min(num_unique_values, style['classes']),
                    style_mode, symbol.clone(), ramp)
            else:
                renderer = QgsGraduatedSymbolRenderer(style_by, [])
                # NOTE: the following returns an instance of one of the
                #       subclasses of QgsClassificationMethod
                classification_method = \
                    QgsApplication.classificationMethodRegistry().method(
                        style_mode)
                renderer.setClassificationMethod(classification_method)
                renderer.updateColorRamp(ramp)
                renderer.updateSymbols(symbol.clone())
                renderer.updateClasses(
                    layer, min(num_unique_values, style['classes']))
            if not use_sgc_style:
                if Qgis.QGIS_VERSION_INT < 31000:
                    label_format = renderer.labelFormat()
                    # NOTE: the following line might be useful
                    # label_format.setTrimTrailingZeroes(True)
                    label_format.setPrecision(2)
                    renderer.setLabelFormat(label_format, updateRanges=True)
                else:
                    renderer.classificationMethod().setLabelPrecision(2)
                    renderer.calculateLabelPrecision()
        elif num_unique_values == 2:
            categories = []
            for unique_value in unique_values:
                symbol = symbol.clone()
                try:
                    symbol.setColor(
                        QColor(RAMP_EXTREME_COLORS[ramp_name]
                               ['bottom' if unique_value ==
                                min(unique_values) else 'top']))
                except Exception:
                    symbol.setColor(
                        QColor(style['color_from'] if unique_value ==
                               min(unique_values) else style['color_to']))
                category = QgsRendererCategory(unique_value, symbol,
                                               str(unique_value))
                # entry for the list of category items
                categories.append(category)
            renderer = QgsCategorizedSymbolRenderer(style_by, categories)
        else:
            renderer = QgsSingleSymbolRenderer(symbol.clone())
        if add_null_class and NULL in unique_values:
            # add a class for NULL values
            rule_renderer = QgsRuleBasedRenderer(symbol.clone())
            root_rule = rule_renderer.rootRule()
            not_null_rule = root_rule.children()[0].clone()
            # strip parentheses from stringified color HSL
            not_null_rule.setFilterExpression(
                '%s IS NOT NULL' % QgsExpression.quotedColumnRef(style_by))
            not_null_rule.setLabel('%s:' % style_by)
            root_rule.appendChild(not_null_rule)
            null_rule = root_rule.children()[0].clone()
            null_rule.setSymbol(
                QgsFillSymbol.createSimple({
                    'color': '160,160,160',
                    'style': 'diagonal_x'
                }))
            null_rule.setFilterExpression(
                '%s IS NULL' % QgsExpression.quotedColumnRef(style_by))
            null_rule.setLabel(tr('No points'))
            root_rule.appendChild(null_rule)
            if isinstance(renderer, QgsGraduatedSymbolRenderer):
                # create value ranges
                rule_renderer.refineRuleRanges(not_null_rule, renderer)
                # remove default rule
            elif isinstance(renderer, QgsCategorizedSymbolRenderer):
                rule_renderer.refineRuleCategoris(not_null_rule, renderer)
            for rule in rule_renderer.rootRule().children()[1].children():
                label = rule.label()
                # by default, labels are like:
                # ('"collapse-structural-ASH_DRY_sum" >= 0.0000 AND
                # "collapse-structural-ASH_DRY_sum" <= 2.3949')
                first, second = label.split(" AND ")
                bottom = first.rsplit(" ", 1)[1]
                top = second.rsplit(" ", 1)[1]
                simplified = "%s - %s" % (bottom, top)
                rule.setLabel(simplified)
            root_rule.removeChildAt(0)
            renderer = rule_renderer
        if render_higher_on_top:
            renderer.setUsingSymbolLevels(True)
            symbol_items = [item for item in renderer.legendSymbolItems()]
            for i in range(len(symbol_items)):
                sym = symbol_items[i].symbol().clone()
                key = symbol_items[i].ruleKey()
                for lay in range(sym.symbolLayerCount()):
                    sym.symbolLayer(lay).setRenderingPass(i)
                renderer.setLegendSymbolItem(key, sym)
        layer.setRenderer(renderer)
        if not use_sgc_style:
            layer.setOpacity(0.7)
        if repaint:
            layer.triggerRepaint()
            iface.setActiveLayer(layer)
            iface.zoomToActiveLayer()
            # NOTE QGIS3: probably not needed
            # iface.layerTreeView().refreshLayerSymbology(layer.id())
            iface.mapCanvas().refresh()
예제 #6
0
파일: Network.py 프로젝트: R4GIS/QTRANUS
    def editNetworkLayer(self, progressBar, layerName, scenariosExpression,
                         networkExpression, variable, level, projectPath,
                         group, networkLinkShapePath, method, layerId,
                         expressionNetworkText, color):
        """
            @summary: Get operators dictionary
            @param layerName: Layer name
            @type layerName: String
            @param scenariosExpression: Scenarios expression
            @type scenariosExpression: Stack object
            @param networkExpression: Network expression
            @type networkExpression: Stack object
            @param variable: Variable to evaluate
            @type variable: String
            @param level: Level to evaluate (Total, Routes, Operators)
            @type level: Level object
            @param projectPath: Project path
            @type projectPath: String
            @param group: Project group
            @type group: Layer group
            @param networkLinkShapePath: Network link shape path
            @type networkLinkShapePath: String
            @return: Result of the layer creation
        """

        if scenariosExpression is None:
            QMessageBox.warning(None, "Network expression",
                                "There is not scenarios information.")
            print("There is not scenarios information.")
            return False

        registry = QgsProject.instance()
        layersCount = len(registry.mapLayers())
        result, resultData, minValue, maxValue = self.network_data_access.create_network_memory(
            layerName, scenariosExpression, networkExpression, variable, level,
            projectPath)
        progressBar.setValue(15)

        if result:
            # Source shape, name of the new shape, providerLib
            layer = QgsVectorLayer(networkLinkShapePath,
                                   layerName + "_network", 'ogr')
            epsg = layer.crs().postgisSrid()
            intMethod = 0 if method == "Color" else 1
            rowCounter = len(resultData)

            if not layer.isValid():
                return False

            feats = [feat for feat in layer.getFeatures()]

            # Create a vector layer with data on Memory
            memoryLayer = registry.mapLayer(layerId)

            memory_data = memoryLayer.dataProvider()
            joinedFieldName = "Result"
            shpField = "Id"
            attr = layer.dataProvider().fields().toList()
            attr += [QgsField(joinedFieldName, QVariant.Double)]
            progressBar.setValue(25)

            memory_data.addAttributes(attr)
            memory_data.addFeatures(feats)

            num = 30
            progressBar.setValue(num)
            progressInterval = 70 / len(resultData)

            memoryLayer.startEditing()
            for rowItem in np.nditer(resultData):
                value = 0
                num += progressInterval
                progressBar.setValue(num)

                it = memoryLayer.getFeatures("LINKID  = '{0}'".format(
                    str(rowItem['Id']).replace("b", "").replace("'", "")))
                for id_feature in it:
                    memoryLayer.changeAttributeValue(
                        id_feature.id(),
                        memory_data.fieldNameIndex(joinedFieldName),
                        QVariant(round(float(rowItem['Result']), 2)))

            memoryLayer.commitChanges()

            myStyle = QgsStyle().defaultStyle()
            defaultColorRampNames = myStyle.colorRampNames()
            ramp = myStyle.colorRamp(defaultColorRampNames[0])
            ranges = []
            nCats = ramp.count()
            rng = maxValue - minValue
            nCats = 8
            scale = QgsMapUnitScale(minValue, maxValue)

            if method == "Color":
                color1 = list(
                    map(lambda x: int(x), color['color1'].split(",")[0:3]))
                color2 = list(
                    map(lambda x: int(x), color['color2'].split(",")[0:3]))
                interpolatedColors = HP.linear_gradient(color1, color2, nCats)

            for i in range(0, nCats):
                v0 = minValue + rng / float(nCats) * i
                v1 = minValue + rng / float(nCats) * (i + 1)
                if method == "Color":
                    line = QgsSimpleLineSymbolLayer(
                        QColor(interpolatedColors['r'][i],
                               interpolatedColors['g'][i],
                               interpolatedColors['b'][i]))
                    line.setOffsetUnit(2)
                    line.setOffset(2)
                    line.setWidth(0.8)
                    symbol = QgsLineSymbol()
                    symbol.changeSymbolLayer(0, line)
                    myRange = QgsRendererRange(v0, v1, symbol, "")

                elif method == "Size":
                    qcolor = QColor()
                    qcolor.setRgb(color)
                    line = QgsSimpleLineSymbolLayer(qcolor)
                    line.setOffsetUnit(2)
                    line.setOffset(0.7)
                    # Symbol
                    # symbolLine = QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayerBase.ArrowHead)
                    # Mark line
                    # markLine = QgsMarkerLineSymbolLayer()
                    # markLine.setPlacement(4)
                    symbolo = QgsLineSymbol()
                    symbolo.changeSymbolLayer(0, line)
                    # symbolo.appendSymbolLayer(line)
                    myRange = QgsRendererRange(v0, v1, symbolo, "")
                ranges.append(myRange)

            # The first parameter refers to the name of the field that contains the calculated value (expression)
            modeRender = QgsGraduatedSymbolRenderer.Mode(2)
            renderer = QgsGraduatedSymbolRenderer(joinedFieldName, ranges)
            renderer.setMode(modeRender)
            renderer.setGraduatedMethod(intMethod)

            if method == "Size":
                renderer.setSymbolSizes(0.200000, 2.60000)

            renderer.setSourceColorRamp(ramp)
            memoryLayer.setRenderer(renderer)

            typeLayer = "network"
            fieldName = "LINKID"
            networkExpressionText = str(scenariosExpression)

            # Create XML File ".qtranus" with the parameters of the executions
            if FileMXML.if_exist_xml_layers(projectPath):
                if FileMXML.if_exist_layer(projectPath, memoryLayer.id()):
                    FileMXML.update_xml_file(memoryLayer.name(),
                                             memoryLayer.id(),
                                             scenariosExpression, variable,
                                             networkExpression, projectPath,
                                             expressionNetworkText, method,
                                             level, color)
                else:
                    FileMXML.add_layer_xml_file(memoryLayer.name(),
                                                memoryLayer.id(),
                                                scenariosExpression, variable,
                                                networkExpression, projectPath,
                                                expressionNetworkText,
                                                shpField, typeLayer, method,
                                                level, color)
            else:
                FileMXML.create_xml_file(memoryLayer.name(), memoryLayer.id(),
                                         scenariosExpression, variable,
                                         networkExpression, projectPath,
                                         expressionNetworkText, shpField,
                                         typeLayer, method, level, color)

            #group.insertLayer((layersCount+1), memoryLayer)
            progressBar.setValue(100)

        return True
예제 #7
0
def raster_apply_classified_renderer(raster_layer,
                                     rend_type,
                                     num_classes,
                                     color_ramp,
                                     invert=False,
                                     band_num=1,
                                     n_decimals=1):
    """
    Applies quantile or equal intervals render to a raster layer. It also allows for the rounding of the values and
    legend labels.

    Args:
        raster_layer (QgsRasterLayer): The rasterlayer to apply classes to
        rend_type (str): The type of renderer to apply ('quantile' or 'equal interval')
        num_classes (int): The number of classes to create
        color_ramp (str): The colour ramp used to display the data
        band_num(int): The band number to use in the renderer
        invert (bool): invert the colour ramp
        n_decimals (int): the number of decimal places to round the values and labels


    Returns:

    """
    # use an existing color ramp
    qgsStyles = QgsStyle().defaultStyle()

    # check to see if the colour ramp is installed
    if color_ramp != '' and color_ramp not in qgsStyles.colorRampNames():
        raise ValueError(
            'PAT symbology does not exist. See user manual for install instructions'
        )

    ramp = qgsStyles.colorRamp(color_ramp)

    # get band statistics
    cbStats = raster_layer.dataProvider().bandStatistics(
        band_num, QgsRasterBandStats.All, raster_layer.extent(), 0)

    # create the renderer
    renderer = QgsSingleBandPseudoColorRenderer(raster_layer.dataProvider(),
                                                band_num)

    # set the max and min heights we found earlier
    renderer.setClassificationMin(cbStats.minimumValue)
    renderer.setClassificationMax(cbStats.maximumValue)

    if rend_type.lower() == 'quantile':
        renderer.createShader(ramp, QgsColorRampShader.Discrete,
                              QgsColorRampShader.Quantile, num_classes)

    elif rend_type.lower() == 'equal interval':
        renderer.createShader(ramp, QgsColorRampShader.Discrete,
                              QgsColorRampShader.EqualInterval, num_classes)

    # Round values off to the nearest decimal place and construct the label
    # get the newly created values and classes
    color_shader = renderer.shader().rasterShaderFunction()

    # iterate the values rounding and creating a range label.
    new_lst = []
    for i, (value, color) in enumerate(color_shader.legendSymbologyItems(),
                                       start=1):
        value = float('{:.3g}'.format(float(value)))
        if i == 1:
            label = "<= {}".format(value)
        elif i == len(color_shader.legendSymbologyItems()):
            label = "> {}".format(last)
        else:
            label = "{} - {}".format(last, value)
        last = value

        new_lst.append(QgsColorRampShader.ColorRampItem(value, color, label))

    # apply back to the shader then the layer
    color_shader.setColorRampItemList(new_lst)

    raster_layer.setRenderer(renderer)
    raster_layer.triggerRepaint()
예제 #8
0
    def processAlgorithm(self, parameters, context, feedback):
        project = QgsProject()
        project.setFileName(
            os.path.join(parameters[self.FOLDER], "all-outputs-qgis.qgs"))
        project.setCrs(QgsCoordinateReferenceSystem('EPSG:27700'))

        def getMaxValue(layer, fieldname):
            maxfound = float("-inf")
            for f in layer.getFeatures():
                attr = f.attribute(fieldname)
                assert attr >= 0
                if attr > maxfound:
                    maxfound = attr
            return maxfound

        with open(
                os.path.join(parameters[self.FOLDER],
                             "all-town-metadata.json")) as f:
            metadata = json.load(f)

        classmethods = {
            'quantile': QgsClassificationQuantile,
            'jenks': QgsClassificationJenks,
            'equal': QgsClassificationEqualInterval
        }

        html = ""
        output = []
        views_sorted_by_mode = sorted(metadata["views"],
                                      key=lambda v: v["mode"])
        for view in views_sorted_by_mode:
            keysymbol = u'🔑'
            viewname = view["label"]
            keysign = ""
            if viewname.find(keysymbol) != -1:
                viewname = viewname.replace(keysymbol, '', 1)
                keysign = "*** "
            viewname = keysign + view["mode"] + " " + viewname

            html += f"""
                    <h2>{viewname}</h2>
                    {view["description"]}
                    <ul>
                    """
            for layer in view["layers"]:
                layername = viewname + " - " + layer["scalar_field_units"]

                layerpath = os.path.join(parameters[self.FOLDER],
                                         layer["file"])
                vlayer = QgsVectorLayer(layerpath, layername, "ogr")
                if not vlayer.isValid():
                    feedback.pushInfo("Layer failed to load: " + layerpath)
                else:
                    context.temporaryLayerStore().addMapLayer(vlayer)
                    html += f"""<li><b>file:</b> {layer["file"]}"""
                    if "symbol_field" in layer:
                        html += f"""<ul>
                                    <li><b>symbol field:</b> {layer["symbol_field"]}
                                  </ul>
                                """
                        categories = []
                        scalar_fieldname = layer["scalar_field"]
                        maxvalue = getMaxValue(vlayer, scalar_fieldname)
                        feedback.pushInfo("Max value for %s is %f" %
                                          (scalar_fieldname, maxvalue))
                        for formality in ["I", "F"]:
                            for severity, colour in [(3, 'red'), (2, 'yellow'),
                                                     (1, 'green')]:
                                colour = {
                                    ("I", "red"): "#FF0000",
                                    ("I", "yellow"): "#FFFF00",
                                    ("I", "green"): "#00FF00",
                                    ("F", "red"): "#FF9999",
                                    ("F", "yellow"): "#FFFF66",
                                    ("F", "green"): "#99FF99",
                                }[(formality, colour)]
                                symbol_code = "%s%d" % (formality, severity)
                                if formality == "F":
                                    symbol = QgsMarkerSymbol.createSimple({
                                        'color':
                                        colour,
                                        'size':
                                        '3',
                                        'outline_color':
                                        '#888888'
                                    })
                                else:
                                    assert (formality == "I")
                                    symbol = QgsMarkerSymbol.createSimple({
                                        'color':
                                        colour,
                                        'size':
                                        '3',
                                        'outline_color':
                                        '#000000',
                                        'name':
                                        'star'
                                    })

                                objTransf = QgsSizeScaleTransformer(
                                    QgsSizeScaleTransformer.Flannery,
                                    0,  #minvalue
                                    maxvalue,  #maxvalue
                                    3,  #minsize
                                    10,  #maxsize
                                    0,  #nullsize
                                    1)  #exponent
                                objProp = QgsProperty()
                                objProp.setField(scalar_fieldname)
                                objProp.setTransformer(objTransf)
                                symbol.setDataDefinedSize(objProp)
                                label = {
                                    "F": "Formal",
                                    "I": "Informal"
                                }[formality] + " " + {
                                    3: "Major",
                                    2: "Secondary",
                                    1: "Tertiary"
                                }[severity]
                                cat = QgsRendererCategory(
                                    symbol_code, symbol, label, True)
                                categories.append(cat)
                        renderer = QgsCategorizedSymbolRenderer(
                            "Crossings", categories)
                        renderer.setClassAttribute(layer["symbol_field"])
                        vlayer.setRenderer(renderer)
                    else:
                        html += f"""<ul>
                                    <li><b>field:</b> {layer["scalar_field"]}
                                    <li><b>units:</b> {layer["scalar_field_units"]}
                                    <li><b>recommended classification:</b> {layer["classes"]}
                                  </ul>
                                """
                        default_style = QgsStyle().defaultStyle()
                        color_ramp = default_style.colorRamp('bt')
                        renderer = QgsGraduatedSymbolRenderer()
                        renderer.setClassAttribute(layer["scalar_field"])
                        classmethod = classmethods[layer["classes"]]()
                        renderer.setClassificationMethod(classmethod)
                        renderer.updateClasses(vlayer, 5)
                        renderer.updateColorRamp(color_ramp)
                        vlayer.setRenderer(renderer)

                    project.addMapLayer(vlayer)
                    feedback.pushInfo("Loaded " + layerpath)
            html += "</ul>"

        project.write()
        town = views_sorted_by_mode[0]["town"]
        with open(os.path.join(parameters[self.FOLDER], "metadata.html"),
                  "w") as htmlfile:
            htmlfile.write(
                f"<html><head><title>{town} metadata</title></head><body><h1>{town}</h1>{html}</body></html>"
            )
        return {self.OUTPUT: output}