Exemple #1
0
 def expression_iterator(self, layer, expression, geometryStorage):
     featReq = QgsFeatureRequest()
     expression = QgsExpression(expression)
     context = QgsExpressionContext()
     self.stopLoop = False
     i = 0
     for f in layer.getFeatures(featReq):
         QCoreApplication.processEvents()
         if self.stopLoop:
             break
         self.recordingSearchProgress.emit(i)
         i += 1
         context.setFeature(f)
         evaluated = unicode(expression.evaluate(context))
         if expression.hasEvalError():
             continue
         if f.geometry() is None or f.geometry().centroid() is None:
             continue
         centroid = f.geometry().centroid().asPoint()
         if geometryStorage == 'wkb':
             geom = binascii.b2a_hex(f.geometry().asWkb())
         elif geometryStorage == 'wkt':
             geom = f.geometry().exportToWkt()
         elif geometryStorage == 'extent':
             geom = f.geometry().boundingBox().asWktPolygon()
         yield ( evaluated, centroid.x(), centroid.y(), geom )
Exemple #2
0
 def update_variables_gui(self):
     variables_scope = QgsExpressionContextScope(self.tr('Model Variables'))
     for k, v in self.model.variables().items():
         variables_scope.setVariable(k, v)
     variables_context = QgsExpressionContext()
     variables_context.appendScope(variables_scope)
     self.variables_editor.setContext(variables_context)
     self.variables_editor.setEditableScopeIndex(0)
    def processAlgorithm(self, feedback):
        layer = dataobjects.getLayerFromString(
            self.getParameterValue(self.INPUT_LAYER))

        geometry_type = self.getParameterValue(self.OUTPUT_GEOMETRY)
        wkb_type = None
        if geometry_type == 0:
            wkb_type = QgsWkbTypes.Polygon
        elif geometry_type == 1:
            wkb_type = QgsWkbTypes.LineString
        else:
            wkb_type = QgsWkbTypes.Point
        if self.getParameterValue(self.WITH_Z):
            wkb_type = QgsWkbTypes.addZ(wkb_type)
        if self.getParameterValue(self.WITH_M):
            wkb_type = QgsWkbTypes.addM(wkb_type)

        writer = self.getOutputFromName(
            self.OUTPUT_LAYER).getVectorWriter(
                layer.fields(),
                wkb_type,
                layer.crs())

        expression = QgsExpression(self.getParameterValue(self.EXPRESSION))
        if expression.hasParserError():
            raise GeoAlgorithmExecutionException(expression.parserErrorString())

        exp_context = QgsExpressionContext(QgsExpressionContextUtils.globalProjectLayerScopes(layer))

        if not expression.prepare(exp_context):
            raise GeoAlgorithmExecutionException(
                self.tr('Evaluation error: {0}').format(expression.evalErrorString()))

        features = vector.features(layer)
        total = 100.0 / len(features)
        for current, input_feature in enumerate(features):
            output_feature = input_feature

            exp_context.setFeature(input_feature)
            value = expression.evaluate(exp_context)
            if expression.hasEvalError():
                raise GeoAlgorithmExecutionException(
                    self.tr('Evaluation error: {0}').format(expression.evalErrorString()))

            if not value:
                output_feature.setGeometry(QgsGeometry())
            else:
                if not isinstance(value, QgsGeometry):
                    raise GeoAlgorithmExecutionException(
                        self.tr('{} is not a geometry').format(value))
                output_feature.setGeometry(value)

            writer.addFeature(output_feature)
            feedback.setProgress(int(current * total))

        del writer
Exemple #4
0
    def check_for_update_events(self, widget, value):
        if not self.feature:
            return

        from qgis.core import QgsExpression, QgsExpressionContext, QgsExpressionContextScope
        # If we don't have any events for this widgets just get out now
        if not widget.id in self.events:
            return

        events = self.events[widget.id]
        events = [event for event in events if event['event'].lower() == 'update']
        if not events:
            return

        feature = self.to_feature(no_defaults=True)

        for event in events:
            action = event['action'].lower()
            targetid = event['target']
            if targetid == widget.id:
                utils.log("Can't connect events to the same widget. ID {}".format(targetid))
                continue

            widget = self.get_widget_from_id(targetid)

            if not widget:
                utils.log("Can't find widget for id {} in form".format(targetid))
                continue

            condition = event['condition']
            expression = event['value']

            context = QgsExpressionContext()
            scope = QgsExpressionContextScope()
            scope.setVariable("value", value)
            scope.setVariable("field", widget.field)
            context.setFeature(feature)
            context.appendScope(scope)

            conditionexp = QgsExpression(condition)
            exp = QgsExpression(expression)

            if action.lower() == "show":
                widget.hidden = not conditionexp.evaluate(context)
            if action.lower() == "hide":
                widget.hidden = conditionexp.evaluate(context)
            if action == 'widget expression':
                if conditionexp.evaluate(context):
                    newvalue = self.widget_default(field, feature=feature)
                    widget.setvalue(newvalue)
            if action == 'set value':
                if conditionexp.evaluate(context):
                    newvalue = exp.evaluate(context)
                    widget.setvalue(newvalue)
Exemple #5
0
 def evaluateExpression(self, text):
     context = QgsExpressionContext()
     context.appendScope(QgsExpressionContextUtils.globalScope())
     context.appendScope(QgsExpressionContextUtils.projectScope())
     exp = QgsExpression(text)
     if exp.hasParserError():
         raise Exception(exp.parserErrorString())
     result = exp.evaluate(context)
     if exp.hasEvalError():
         raise ValueError(exp.evalErrorString())
     return result
def sum_fields(layer, output_field_key, input_fields):
    """Sum the value of input_fields and put it as output_field.

    :param layer: The vector layer.
    :type layer: QgsVectorLayer

    :param output_field_key: The output field definition key.
    :type output_field_key: basestring

    :param input_fields: List of input fields' name.
    :type input_fields: list
    """
    field_definition = definition(output_field_key)
    output_field_name = field_definition['field_name']
    # If the fields only has one element
    if len(input_fields) == 1:
        # Name is different, copy it
        if input_fields[0] != output_field_name:
            to_rename = {input_fields[0]: output_field_name}
            # We copy only, it will be deleted later.
            # We can't rename the field, we need to copy it as the same
            # field might be used many times in the FMT tool.
            copy_fields(layer, to_rename)
        else:
            # Name is same, do nothing
            return
    else:
        # Creating expression
        # Put field name in a double quote. See #4248
        input_fields = ['"%s"' % f for f in input_fields]
        string_expression = ' + '.join(input_fields)
        sum_expression = QgsExpression(string_expression)
        context = QgsExpressionContext()
        context.setFields(layer.fields())
        sum_expression.prepare(context)

        # Get the output field index
        output_idx = layer.fields().lookupField(output_field_name)
        # Output index is not found
        layer.startEditing()
        if output_idx == -1:
            output_field = create_field_from_definition(field_definition)
            layer.addAttribute(output_field)
            output_idx = layer.fields().lookupField(output_field_name)

        # Iterate to all features
        for feature in layer.getFeatures():
            context.setFeature(feature)
            result = sum_expression.evaluate(context)
            feature[output_idx] = result
            layer.updateFeature(feature)

        layer.commitChanges()
def sum_fields(layer, output_field_key, input_fields):
    """Sum the value of input_fields and put it as output_field.

    :param layer: The vector layer.
    :type layer: QgsVectorLayer

    :param output_field_key: The output field definition key.
    :type output_field_key: basestring

    :param input_fields: List of input fields' name.
    :type input_fields: list
    """
    field_definition = definition(output_field_key)
    output_field_name = field_definition['field_name']
    # If the fields only has one element
    if len(input_fields) == 1:
        # Name is different, copy it
        if input_fields[0] != output_field_name:
            copy_fields(layer, {
                input_fields[0]: output_field_name})
        # Name is same, do nothing
        else:
            return
    else:
        # Creating expression
        # Put field name in a double quote. See #4248
        input_fields = ['"%s"' % f for f in input_fields]
        string_expression = ' + '.join(input_fields)
        sum_expression = QgsExpression(string_expression)
        context = QgsExpressionContext()
        context.setFields(layer.pendingFields())
        sum_expression.prepare(context)

        # Get the output field index
        output_idx = layer.fieldNameIndex(output_field_name)
        # Output index is not found
        if output_idx == -1:
            output_field = create_field_from_definition(field_definition)
            layer.startEditing()
            layer.addAttribute(output_field)
            layer.commitChanges()
            output_idx = layer.fieldNameIndex(output_field_name)

        layer.startEditing()
        # Iterate to all features
        for feature in layer.getFeatures():
            context.setFeature(feature)
            result = sum_expression.evaluate(context)
            feature[output_idx] = result
            layer.updateFeature(feature)

        layer.commitChanges()
    def add_flooded_field(self, shapefile_path):
        """Create the layer from the local shp adding the flooded field.

        .. versionadded:: 3.3

        Use this method to add a calculated field to a shapefile. The shapefile
        should have a field called 'count' containing the number of flood
        reports for the field. The field values will be set to 0 if the count
        field is < 1, otherwise it will be set to 1.

        :param shapefile_path: Path to the shapefile that will have the flooded
            field added.
        :type shapefile_path: basestring

        :return: A vector layer with the flooded field added.
        :rtype: QgsVectorLayer
        """
        layer = QgsVectorLayer(
            shapefile_path, self.tr('Jakarta Floods'), 'ogr')
        # Add a calculated field indicating if a poly is flooded or not
        # from qgis.PyQt.QtCore import QVariant
        layer.startEditing()
        # Add field with integer from 0 to 4 which represents the flood
        # class. Its the same as 'state' field except that is being treated
        # as a string.
        # This is used for cartography
        flood_class_field = QgsField('floodclass', QVariant.Int)
        layer.addAttribute(flood_class_field)
        layer.commitChanges()
        layer.startEditing()
        flood_class_idx = layer.fields().lookupField('floodclass')
        flood_class_expression = QgsExpression('to_int(state)')
        context = QgsExpressionContext()
        context.setFields(layer.fields())
        flood_class_expression.prepare(context)

        # Add field with boolean flag to say if the area is flooded
        # This is used by the impact function
        flooded_field = QgsField('flooded', QVariant.Int)
        layer.dataProvider().addAttributes([flooded_field])
        layer.commitChanges()
        layer.startEditing()
        flooded_idx = layer.fields().lookupField('flooded')
        flood_flag_expression = QgsExpression('state > 0')
        flood_flag_expression.prepare(context)
        for feature in layer.getFeatures():
            context.setFeature(feature)
            feature[flood_class_idx] = flood_class_expression.evaluate(context)
            feature[flooded_idx] = flood_flag_expression.evaluate(context)
            layer.updateFeature(feature)
        layer.commitChanges()
        return layer
    def test_length_expression(self):
        # compare length using the ellipsoid in kms and the planimetric distance in meters
        self.lyr.fieldName = "round($length,5) || ' - ' || round(length($geometry),2)"
        self.lyr.isExpression = True

        QgsProject.instance().setCrs(QgsCoordinateReferenceSystem("EPSG:32613"))
        QgsProject.instance().setEllipsoid("WGS84")
        QgsProject.instance().setDistanceUnits(QgsUnitTypes.DistanceKilometers)

        ctxt = QgsExpressionContext()
        ctxt.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance()))
        ctxt.appendScope(QgsExpressionContextUtils.layerScope(self.layer))
        self._TestMapSettings.setExpressionContext(ctxt)

        self.lyr.placement = QgsPalLayerSettings.Curved
        self.lyr.placementFlags = QgsPalLayerSettings.AboveLine | QgsPalLayerSettings.MapOrientation
        self.checkTest()
Exemple #10
0
def add25dAttributes(cleanLayer, layer, canvas):
    provider = cleanLayer.dataProvider()
    provider.addAttributes([QgsField("height", QVariant.Double),
                            QgsField("wallColor", QVariant.String),
                            QgsField("roofColor", QVariant.String)])
    cleanLayer.updateFields()
    fields = cleanLayer.fields()
    renderer = layer.renderer()
    renderContext = QgsRenderContext.fromMapSettings(canvas.mapSettings())
    feats = layer.getFeatures()
    context = QgsExpressionContext()
    context.appendScope(QgsExpressionContextUtils.layerScope(layer))
    expression = QgsExpression('eval(@qgis_25d_height)')
    heightField = fields.indexFromName("height")
    wallField = fields.indexFromName("wallColor")
    roofField = fields.indexFromName("roofColor")
    renderer.startRender(renderContext, fields)
    cleanLayer.startEditing()
    for feat in feats:
        context.setFeature(feat)
        height = expression.evaluate(context)
        if isinstance(renderer, QgsCategorizedSymbolRenderer):
            classAttribute = renderer.classAttribute()
            attrValue = feat.attribute(classAttribute)
            catIndex = renderer.categoryIndexForValue(attrValue)
            categories = renderer.categories()
            symbol = categories[catIndex].symbol()
        elif isinstance(renderer, QgsGraduatedSymbolRenderer):
            classAttribute = renderer.classAttribute()
            attrValue = feat.attribute(classAttribute)
            ranges = renderer.ranges()
            for range in ranges:
                if (attrValue >= range.lowerValue() and
                        attrValue <= range.upperValue()):
                    symbol = range.symbol().clone()
        else:
            symbol = renderer.symbolForFeature(feat, renderContext)
        sl1 = symbol.symbolLayer(1)
        sl2 = symbol.symbolLayer(2)
        wallColor = sl1.subSymbol().color().name()
        roofColor = sl2.subSymbol().color().name()
        provider.changeAttributeValues({feat.id() + 1: {heightField: height,
                                                        wallField: wallColor,
                                                        roofField: roofColor}})
    cleanLayer.commitChanges()
    renderer.stopRender(renderContext)
Exemple #11
0
    def runGetFeatureTests(self, source):
        FeatureSourceTestCase.runGetFeatureTests(self, source)

        # combination of an uncompilable expression and limit
        feature = next(self.vl.getFeatures('pk=4'))
        context = QgsExpressionContext()
        scope = QgsExpressionContextScope()
        scope.setVariable('parent', feature)
        context.appendScope(scope)

        request = QgsFeatureRequest()
        request.setExpressionContext(context)
        request.setFilterExpression('"pk" = attribute(@parent, \'pk\')')
        request.setLimit(1)

        values = [f['pk'] for f in self.vl.getFeatures(request)]
        self.assertEqual(values, [4])
Exemple #12
0
    def processAlgorithm(self, feedback):
        ns = {}
        ns['feedback'] = feedback
        ns['scriptDescriptionFile'] = self.descriptionFile

        for param in self.parameters:
            ns[param.name] = param.value

        for out in self.outputs:
            ns[out.name] = out.value

        variables = re.findall('@[a-zA-Z0-9_]*', self.script)
        script = 'import processing\n'
        script += self.script

        context = QgsExpressionContext()
        context.appendScope(QgsExpressionContextUtils.globalScope())
        context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance()))
        for var in variables:
            varname = var[1:]
            if context.hasVariable(varname):
                script = script.replace(var, context.variable(varname))
            else:
                ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Cannot find variable: %s' % varname)

        exec((script), ns)
        for out in self.outputs:
            out.setValue(ns[out.name])
    def processAlgorithm(self, progress):
        layer = layer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT))

        expression = self.getParameterValue(self.EXPRESSION)
        qExp = QgsExpression(expression)
        if qExp.hasParserError():
            raise GeoAlgorithmExecutionException(qExp.parserErrorString())

        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
            layer.fields(), layer.wkbType(), layer.crs())

        context = QgsExpressionContext()
        context.appendScope(QgsExpressionContextUtils.globalScope())
        context.appendScope(QgsExpressionContextUtils.projectScope())
        context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        count = layer.featureCount()
        step = 100.0 / count if count else 1

        request = QgsFeatureRequest(qExp, context)

        for current, f in enumerate(layer.getFeatures(request)):
            writer.addFeature(f)
            progress.setPercentage(int(current * step))

        del writer
Exemple #14
0
    def processAlgorithm(self, parameters, context, feedback):
        ns = {}
        ns['feedback'] = feedback
        ns['scriptDescriptionFile'] = self.descriptionFile
        ns['context'] = context

        for param in self.parameterDefinitions():
            ns[param.name] = parameters[param.name()]

        for out in self.outputs:
            ns[out.name] = out.value

        variables = re.findall('@[a-zA-Z0-9_]*', self.script)
        script = 'import processing\n'
        script += self.script

        context = QgsExpressionContext()
        context.appendScope(QgsExpressionContextUtils.globalScope())
        context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance()))
        for var in variables:
            varname = var[1:]
            if context.hasVariable(varname):
                script = script.replace(var, context.variable(varname))
            else:
                QgsMessageLog.logMessage(self.tr('Cannot find variable: {0}').format(varname), self.tr('Processing'), QgsMessageLog.WARNING)

        exec((script), ns)
        for out in self.outputs:
            out.setValue(ns[out.name])
Exemple #15
0
    def processAlgorithm(self, progress):
        ns = {}
        ns["progress"] = progress
        ns["scriptDescriptionFile"] = self.descriptionFile

        for param in self.parameters:
            ns[param.name] = param.value

        for out in self.outputs:
            ns[out.name] = out.value

        variables = re.findall("@[a-zA-Z0-9_]*", self.script)
        script = "import processing\n"
        script += self.script

        context = QgsExpressionContext()
        context.appendScope(QgsExpressionContextUtils.globalScope())
        context.appendScope(QgsExpressionContextUtils.projectScope())
        for var in variables:
            varname = var[1:]
            if context.hasVariable(varname):
                script = script.replace(var, context.variable(varname))
            else:
                ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, "Cannot find variable: %s" % varname)

        exec((script), ns)
        for out in self.outputs:
            out.setValue(ns[out.name])
Exemple #16
0
 def expressionContext(self):
     context = QgsExpressionContext()
     context.appendScope(QgsExpressionContextUtils.globalScope())
     context.appendScope(QgsExpressionContextUtils.projectScope())
     processingScope = QgsExpressionContextScope()
     for param in self.alg.parameters:
         processingScope.setVariable("%s_value" % param.name, "")
     context.appendScope(processingScope)
     return context
Exemple #17
0
def _expressionContext(alg):
    context = QgsExpressionContext()
    context.appendScope(QgsExpressionContextUtils.globalScope())
    context.appendScope(QgsExpressionContextUtils.projectScope())
    processingScope = QgsExpressionContextScope()
    for param in alg.parameters:
        processingScope.setVariable('%s_value' % param.name, '')
    context.appendScope(processingScope)
    return context
Exemple #18
0
def createExpressionContext():
    context = QgsExpressionContext()
    context.appendScope(QgsExpressionContextUtils.globalScope())
    context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance()))

    if iface.mapCanvas():
        context.appendScope(QgsExpressionContextUtils.mapSettingsScope(iface.mapCanvas().mapSettings()))

    processingScope = QgsExpressionContextScope()

    extent = iface.mapCanvas().fullExtent()
    processingScope.setVariable('fullextent_minx', extent.xMinimum())
    processingScope.setVariable('fullextent_miny', extent.yMinimum())
    processingScope.setVariable('fullextent_maxx', extent.xMaximum())
    processingScope.setVariable('fullextent_maxy', extent.yMaximum())
    context.appendScope(processingScope)
    return context
Exemple #19
0
    def testConcurrency(self):
        """
        The connection pool has a maximum of 4 connections defined (+2 spare connections)
        Make sure that if we exhaust those 4 connections and force another connection
        it is actually using the spare connections and does not freeze.
        This situation normally happens when (at least) 4 rendering threads are active
        in parallel and one requires an expression to be evaluated.
        """
        # Acquire the maximum amount of concurrent connections
        iterators = list()
        for i in range(QgsApplication.instance().maxConcurrentConnectionsPerPool()):
            iterators.append(self.vl.getFeatures())

        # Run an expression that will also do a request and should use a spare
        # connection. It just should not deadlock here.

        feat = next(iterators[0])
        context = QgsExpressionContext()
        context.setFeature(feat)
        exp = QgsExpression('get_feature(\'{layer}\', \'pk\', 5)'.format(layer=self.vl.id()))
        exp.evaluate(context)
Exemple #20
0
    def pointPicked(self, event):
        for b in self.rubberBands:
            del b
        self.rubberBands[:] = []

        fieldX = self.cmbXField.currentField()
        fieldY = self.cmbYField.currentField()

        artist = event.artist
        indices = event.ind
        for i in indices:
            x = self.xData[artist.name][i]
            y = self.yData[artist.name][i]

            if isinstance(x, int):
                expr = '"{}" = {} AND '.format(fieldX, x)
            elif isinstance(x, float):
                expr = 'abs("{}" - {}) <= 0.0000001 AND '.format(fieldX, x)
            elif isinstance(x, (str, unicode)):
                expr = """"{}" = '{}' AND """.format(fieldX, x)
            else:
                expr = """"{}" = '{}' AND """.format(fieldX, x.toString('yyyy-MM-dd'))

            if isinstance(y, float):
                expr += 'abs("{}" - {}) <= 0.0000001'.format(fieldY, y)
            elif isinstance(y, (str, unicode)):
                expr += """"{}" = '{}'""".format(fieldY, y)
            else:
                expr += '"{}" = {}'.format(fieldY, y)

            layer = self.cmbLayer.currentLayer()
            expression = QgsExpression(expr)
            context = QgsExpressionContext()
            context.appendScope(QgsExpressionContextUtils.globalScope())
            context.appendScope(QgsExpressionContextUtils.projectScope())
            context.appendScope(QgsExpressionContextUtils.mapSettingsScope(self.canvas.mapSettings()))
            context.appendScope(QgsExpressionContextUtils.layerScope(layer))

            request = QgsFeatureRequest(expression, context)
            for f in layer.getFeatures(request):
                hl = QgsHighlight(self.canvas, f.geometry(), layer)
                hl.setColor(QColor(255, 0, 0))
                hl.setWidth(2)
                self.rubberBands.append(hl)
Exemple #21
0
    def __init__(self, provider):
        super(PyFeatureSource, self).__init__()
        self._provider = provider
        self._features = provider._features

        self._expression_context = QgsExpressionContext()
        self._expression_context.appendScope(QgsExpressionContextUtils.globalScope())
        self._expression_context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance()))
        self._expression_context.setFields(self._provider.fields())
        if self._provider.subsetString():
            self._subset_expression = QgsExpression(self._provider.subsetString())
            self._subset_expression.prepare(self._expression_context)
        else:
            self._subset_expression = None
    def testVariables(self):
        """ check through widget model to ensure it is populated with variables """
        w = QgsExpressionBuilderWidget()
        m = w.model()

        s = QgsExpressionContextScope()
        s.setVariable('my_var1', 'x')
        s.setVariable('my_var2', 'y')
        c = QgsExpressionContext()
        c.appendScope(s)

        # check that variables are added when setting context
        w.setExpressionContext(c)
        items = m.findItems('my_var1', Qt.MatchRecursive)
        self.assertEqual(len(items), 1)
        items = m.findItems('my_var2', Qt.MatchRecursive)
        self.assertEqual(len(items), 1)
        items = m.findItems('not_my_var', Qt.MatchRecursive)
        self.assertEqual(len(items), 0)
        # double check that functions are still only there once
        items = m.findItems('lower', Qt.MatchRecursive)
        self.assertEqual(len(items), 1)
        items = m.findItems('upper', Qt.MatchRecursive)
        self.assertEqual(len(items), 1)
    def doAction(self, layer, uid, feature):
        if layer.actions().action(uid).name() == 'openFeatureForm':
            self.plugin.iface.openFeatureForm(layer, feature)
        else :
            ctxt = QgsExpressionContext()
            ctxt.appendScope(QgsExpressionContextUtils.globalScope())
            ctxt.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance()))
            ctxt.appendScope(QgsExpressionContextUtils.mapSettingsScope(self.canvas.mapSettings()))

            # Add click_x and click_y to context
            p = self.toLayerCoordinates(layer, self.pos())            
            QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), 'click_x', p.x())
            QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), 'click_y', p.y())

            layer.actions().doAction(uid, feature, ctxt)
Exemple #24
0
    def expressionContext(self):
        context = QgsExpressionContext()
        context.appendScope(QgsExpressionContextUtils.globalScope())
        context.appendScope(QgsExpressionContextUtils.projectScope())
        processingScope = QgsExpressionContextScope()
        layers = dataobjects.getAllLayers()
        for layer in layers:
            name = layer.name()
            processingScope.setVariable('%s_minx' % name, layer.extent().xMinimum())
            processingScope.setVariable('%s_miny' % name, layer.extent().yMinimum())
            processingScope.setVariable('%s_maxx' % name, layer.extent().xMaximum())
            processingScope.setVariable('%s_maxy' % name, layer.extent().yMaximum())
            if isinstance(layer, QgsRasterLayer):
                cellsize = (layer.extent().xMaximum()
                            - layer.extent().xMinimum()) / layer.width()
                processingScope.setVariable('%s_cellsize' % name, cellsize)

        layers = dataobjects.getRasterLayers()
        for layer in layers:
            for i in range(layer.bandCount()):
                stats = layer.dataProvider().bandStatistics(i + 1)
                processingScope.setVariable('%s_band%i_avg' % (name, i + 1), stats.mean)
                processingScope.setVariable('%s_band%i_stddev' % (name, i + 1), stats.stdDev)
                processingScope.setVariable('%s_band%i_min' % (name, i + 1), stats.minimumValue)
                processingScope.setVariable('%s_band%i_max' % (name, i + 1), stats.maximumValue)

        extent = iface.mapCanvas().extent()
        processingScope.setVariable('canvasextent_minx', extent.xMinimum())
        processingScope.setVariable('canvasextent_miny', extent.yMinimum())
        processingScope.setVariable('canvasextent_maxx', extent.xMaximum())
        processingScope.setVariable('canvasextent_maxy', extent.yMaximum())

        extent = iface.mapCanvas().fullExtent()
        processingScope.setVariable('fullextent_minx', extent.xMinimum())
        processingScope.setVariable('fullextent_miny', extent.yMinimum())
        processingScope.setVariable('fullextent_maxx', extent.xMaximum())
        processingScope.setVariable('fullextent_maxy', extent.yMaximum())
        context.appendScope(processingScope)
        return context
    def setCustomExpression( self ):
        """ Initialize and show the expression builder dialog """
        layer = None
        if len( self.tblLayers.selectedItems() ) / 3 == 1: # Single layer selected?
            for item in self.tblLayers.selectedItems():
                if item.column() == 1: # It's the layer name item
                    layer = QgsMapLayerRegistry.instance().mapLayer( item.data( Qt.UserRole ) )

        if not self.expressionDlg:
            self.expressionDlg = ExpressionBuilderDialog( self.iface.mainWindow() )
            context = QgsExpressionContext()
            context.appendScope( QgsExpressionContextUtils.globalScope() )
            context.appendScope( QgsExpressionContextUtils.projectScope() )

            # Initialize dialog with layer-based names and variables if single layer selected
            if len( self.tblLayers.selectedItems() ) / 3 == 1:
                context.appendScope( QgsExpressionContextUtils.layerScope( layer ) )
                self.expressionDlg.expressionBuilderWidget.setLayer( layer )
                self.expressionDlg.expressionBuilderWidget.loadFieldNames()

                # This block was borrowed from QGIS/python/plugins/processing/algs/qgis/FieldsCalculator.py
                da = QgsDistanceArea()
                da.setSourceCrs( layer.crs().srsid() )
                da.setEllipsoidalMode( self.iface.mapCanvas().mapSettings().hasCrsTransformEnabled() )
                da.setEllipsoid( QgsProject.instance().readEntry( 'Measure', '/Ellipsoid', GEO_NONE )[0] )
                self.expressionDlg.expressionBuilderWidget.setGeomCalculator( da )

                # If this layer-field is an AutoField, get its expression
                if self.optExistingField.isChecked():
                    fieldName = self.cboField.currentText()
                    expression = self.autoFieldManager.getFieldExpression( layer, fieldName )
                    self.expressionDlg.expressionBuilderWidget.setExpressionText( expression )
                    self.expressionDlg.expression = expression # To remember it when closing/opening

            self.expressionDlg.expressionBuilderWidget.setExpressionContext( context )

        self.expressionDlg.show()
Exemple #26
0
class PyFeatureSource(QgsAbstractFeatureSource):
    def __init__(self, provider):
        super(PyFeatureSource, self).__init__()
        self._provider = provider
        self._features = provider._features

        self._expression_context = QgsExpressionContext()
        self._expression_context.appendScope(
            QgsExpressionContextUtils.globalScope())
        self._expression_context.appendScope(
            QgsExpressionContextUtils.projectScope(QgsProject.instance()))
        self._expression_context.setFields(self._provider.fields())
        if self._provider.subsetString():
            self._subset_expression = QgsExpression(
                self._provider.subsetString())
            self._subset_expression.prepare(self._expression_context)
        else:
            self._subset_expression = None

    def getFeatures(self, request):
        return QgsFeatureIterator(PyFeatureIterator(self, request))
Exemple #27
0
def expression_eval(expression_text,
                    project_id=None,
                    qgs_layer_id=None,
                    form_data=None,
                    formatter=0):
    """Evaluates a QgsExpression and returns the result

    :param expression_text: The QgsExpression text
    :type expression_text: str
    :param project_id: ID of the qdjango project, defaults to None
    :type project_id: int, optional
    :param qgs_layer_id: ID of the QGIS Layer, defaults to None
    :type qgslayer_id: str, optional
    :param form_data: A dictionary that maps to a GeoJSON representation of the feature currently edited in the form
    :type form_data: dict, optional
    :param formatter: Indicate if form_data values contains formatter values or original features value.
    :type formatter: int, optional
    """

    expression = QgsExpression(expression_text)
    expression_context = QgsExpressionContext()

    layer = None

    for func_name in expression.referencedFunctions():
        if func_name in FORBIDDEN_FUNCTIONS:
            raise ExpressionForbiddenError(
                _('Function "{}" is not allowed for security reasons!').format(
                    func_name))

    for var_name in expression.referencedVariables():
        if var_name in FORBIDDEN_VARIABLES:
            raise ExpressionForbiddenError(
                _('Variable "{}" is not allowed for security reasons!').format(
                    var_name))

    if project_id is not None:

        try:
            project = Project.objects.get(pk=project_id)

            if qgs_layer_id is not None:
                try:
                    layer = project.layer_set.get(qgs_layer_id=qgs_layer_id)
                except Layer.DoesNotExist:
                    raise ExpressionLayerError(
                        _('QGIS layer with id "{}" could not be found!').
                        format(qgs_layer_id))

                expression_contex = QgsExpressionContextUtils.globalProjectLayerScopes(
                    layer.qgis_layer)

            else:
                expression_contex = QgsExpressionContextUtils.globalScope()
                expression_context.appendScope(
                    QgsExpressionContextUtils.projectScope(
                        project.qgis_project))

        except Project.DoesNotExist:
            raise ExpressionProjectError(
                _('QDjango project with id "{}" could not be found!').format(
                    project_id))

    else:
        expression_contex = QgsExpressionContextUtils.globalScope()

    if form_data is not None:

        if layer is None:
            raise ExpressionLayerError(
                _('A valid QGIS layer is required to process form data!'))

        try:
            # Case by formatter
            # formatter == 1 : get featureid from layer, usually must be used with formatter form_data
            # formatter == 0 : default behavior
            if formatter == 0:
                fields = layer.qgis_layer.fields()
                form_feature = QgsJsonUtils.stringToFeatureList(
                    json.dumps(form_data), fields, None)[0]

                # Set attributes manually because QgsJsonUtils does not respect order
                for k, v in form_data['properties'].items():
                    form_feature.setAttribute(k, v)
            else:
                qgis_feature_request = QgsFeatureRequest()
                exp = expression_from_server_fids(
                    [form_data['id']], layer.qgis_layer.dataProvider())
                qgis_feature_request.combineFilterExpression(exp)
                form_feature = get_qgis_features(layer.qgis_layer,
                                                 qgis_feature_request)[0]

            expression_context.appendScope(
                QgsExpressionContextUtils.formScope(form_feature))
            expression_context.setFeature(form_feature)
        except:
            raise ExpressionFormDataError()

    valid, errors = expression.checkExpression(expression_text,
                                               expression_context)

    if not valid:
        raise ExpressionParseError(errors)

    result = expression.evaluate(expression_context)

    if expression.hasEvalError():
        raise ExpressionEvalError(expression.evalErrorString())

    return result
Exemple #28
0
def raster_to_polygon(raster_path, out_gpkg, out_layer_name, raster_value, surface_name='valley bottom'):

    # raster_layer = QgsRasterLayer(raster_path, 'in_raster')
    out_polygon_path = os.path.join(out_gpkg, out_layer_name)

    # --------- PROCESSING -------------

    # -- DEM --
    tempdir = tempfile.TemporaryDirectory()
    temp_raster = os.path.join(tempdir.name, "less_than.tif")

    gp_calc = processing.run('gdal:rastercalculator', {'INPUT_A': raster_path,
                                                       'BAND_A': 1,
                                                       'FORMULA': f'(A <= {raster_value})',
                                                       'OUTPUT': temp_raster})  # 'raster'})

    # raster_less_than = gp_calc['OUTPUT']

    raster_less_than = QgsRasterLayer(gp_calc['OUTPUT'])

    # -- DEM to VECTOR --
    gp_raw = processing.run("gdal:polygonize",
                            {'INPUT': raster_less_than,
                             'BAND': 1,
                             'FIELD': 'DN',
                             'EIGHT_CONNECTEDNESS': False,
                             'EXTRA': '',
                             'OUTPUT': 'TEMPORARY_OUTPUT'})

    raw_vector = QgsVectorLayer(
        gp_raw['OUTPUT'], "raw_vectors", "ogr")

    # TODO remove when done
    # QgsProject.instance().addMapLayer(raw_vector)

    # -- CALCULATE AREA --
    # create a provider
    pv = raw_vector.dataProvider()

    # add the attribute and update
    pv.addAttributes([QgsField('raw_area_m', QVariant.Int), QgsField(
        'max_elev_m', QVariant.Double), QgsField('surface_name', QVariant.String)])
    raw_vector.updateFields()

    # Create a context and scope
    context = QgsExpressionContext()
    context.appendScopes(
        QgsExpressionContextUtils.globalProjectLayerScopes(raw_vector))

    # Loop through and add the areas
    delete_features = []
    with edit(raw_vector):
        # loop them
        for feature in raw_vector.getFeatures():
            if feature['DN'] != 1:
                delete_features.append(feature.id())
            else:
                context.setFeature(feature)
                feature['raw_area_m'] = QgsExpression('$area').evaluate(context)
                feature['max_elev_m'] = raster_value
                feature['surface_name'] = surface_name
                raw_vector.updateFeature(feature)
        raw_vector.dataProvider().deleteFeatures(delete_features)

    # -- BUFFER POLYGONS --
    gp_buffered = processing.run("native:buffer",
                                 {'INPUT': raw_vector,
                                  'DISTANCE': 0.000001,
                                  'SEGMENTS': 5,
                                  'END_CAP_STYLE': 0,
                                  'JOIN_STYLE': 0,
                                  'MITER_LIMIT': 2,
                                  'DISSOLVE': False,
                                  'OUTPUT': 'TEMPORARY_OUTPUT'})

    buffered_vector = gp_buffered['OUTPUT']

    # TODO remove when final
    # QgsProject.instance().addMapLayer(buffered_vector)

    # -- Simplify Polygons --
    gp_simple = processing.run("native:simplifygeometries",
                               {'INPUT': buffered_vector,
                                'METHOD': 0,
                                'TOLERANCE': simplify_tolerance,
                                'OUTPUT': 'TEMPORARY_OUTPUT'})

    simple_vector = gp_simple['OUTPUT']  # QgsVectorLayer(
    # gp_simplify['OUTPUT'], "simplified_polygons", 'ogr')

    # -- Smooth the polygons --
    gp_smooth = processing.run("native:smoothgeometry",
                               {'INPUT': simple_vector,
                                'ITERATIONS': 1,
                                'OFFSET': smoothing_offset,
                                'MAX_ANGLE': 180,
                                'OUTPUT': 'TEMPORARY_OUTPUT'})

    smooth_vector = gp_smooth['OUTPUT']  # QgsVectorLayer(
    # , "smoothed_polygons", 'ogr')

    gp_multi = processing.run("native:multiparttosingleparts",
                              {'INPUT': smooth_vector,
                               'OUTPUT': 'TEMPORARY_OUTPUT'})

    multi_vector = gp_multi['OUTPUT']

    # Fix any crossed geometry as final vector
    gp_fix = processing.run("native:fixgeometries",
                            {'INPUT': multi_vector,
                             'OUTPUT': 'TEMPORARY_OUTPUT'})

    final_vector = gp_fix['OUTPUT']

    # Create a context and scope
    # Understand WTF this is??
    context = QgsExpressionContext()
    context.appendScopes(
        QgsExpressionContextUtils.globalProjectLayerScopes(final_vector))

    # add an area attribute
    # create a provider
    pv = final_vector.dataProvider()

    # add the attribute and update
    pv.addAttributes([QgsField('area_m', QVariant.Int)])
    final_vector.updateFields()

    # Loop through and add the areas
    with edit(final_vector):
        # loop them
        for feature in final_vector.getFeatures():
            context.setFeature(feature)
            feature['area_m'] = QgsExpression('$area').evaluate(context)
            final_vector.updateFeature(feature)

    # -- Delete Unneeded Fields --
    pv = final_vector.dataProvider()
    pv.deleteAttributes([1, 2])
    final_vector.updateFields()

    # -- Delete small polygons --
    # data provider capabilities
    caps = final_vector.dataProvider().capabilities()

    # features and empty list of features to delete
    features = final_vector.getFeatures()
    delete_features = []

    # if the layer can have deleted features
    if caps & QgsVectorDataProvider.DeleteFeatures:
        for feature in features:
            if feature['area_m'] <= polygon_min_size:
                delete_features.append(feature.id())
        final_vector.dataProvider().deleteFeatures(delete_features)

    # TODO fix final data export
    options = QgsVectorFileWriter.SaveVectorOptions()
    options.layerName = out_layer_name
    options.driverName = 'GPKG'
    if os.path.exists(out_gpkg):
        options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
    QgsVectorFileWriter.writeAsVectorFormat(
        final_vector, out_gpkg, options)

    # open the output layer
    QgsVectorLayer(out_polygon_path, out_layer_name, 'ogr')
    def updateLayer(self):
        self.layer = dataobjects.getObject(self.cmbInputLayer.currentText())

        self.builder.setLayer(self.layer)
        self.builder.loadFieldNames()

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope())
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(self.layer))
        exp_context.lastScope().setVariable("row_number", 1)
        exp_context.setHighlightedVariables(["row_number"])
        self.builder.setExpressionContext(exp_context)

        self.populateFields()
Exemple #30
0
def handleAlgorithmResults(alg,
                           context,
                           feedback=None,
                           showResults=True,
                           parameters={}):
    wrongLayers = []
    if feedback is None:
        feedback = QgsProcessingFeedback()
    feedback.setProgressText(
        QCoreApplication.translate('Postprocessing',
                                   'Loading resulting layers'))
    i = 0

    for l, details in context.layersToLoadOnCompletion().items():
        if feedback.isCanceled():
            return False

        if len(context.layersToLoadOnCompletion()) > 2:
            # only show progress feedback if we're loading a bunch of layers
            feedback.setProgress(
                100 * i / float(len(context.layersToLoadOnCompletion())))
        try:
            layer = QgsProcessingUtils.mapLayerFromString(
                l, context, typeHint=details.layerTypeHint)
            if layer is not None:
                set_layer_name(layer, details)
                '''If running a model, the execution will arrive here when an algorithm that is part of
                that model is executed. We check if its output is a final otuput of the model, and
                adapt the output name accordingly'''
                outputName = details.outputName
                expcontext = QgsExpressionContext()
                scope = QgsExpressionContextScope()
                expcontext.appendScope(scope)
                for out in alg.outputDefinitions():
                    if out.name() not in parameters:
                        continue
                    outValue = parameters[out.name()]
                    if hasattr(outValue, "sink"):
                        outValue = outValue.sink.valueAsString(expcontext)[0]
                    else:
                        outValue = str(outValue)
                    if outValue == l:
                        outputName = out.name()
                        break
                style = None
                if outputName:
                    style = RenderingStyles.getStyle(alg.id(), outputName)
                if style is None:
                    if layer.type() == QgsMapLayerType.RasterLayer:
                        style = ProcessingConfig.getSetting(
                            ProcessingConfig.RASTER_STYLE)
                    else:
                        if layer.geometryType() == QgsWkbTypes.PointGeometry:
                            style = ProcessingConfig.getSetting(
                                ProcessingConfig.VECTOR_POINT_STYLE)
                        elif layer.geometryType() == QgsWkbTypes.LineGeometry:
                            style = ProcessingConfig.getSetting(
                                ProcessingConfig.VECTOR_LINE_STYLE)
                        else:
                            style = ProcessingConfig.getSetting(
                                ProcessingConfig.VECTOR_POLYGON_STYLE)
                if style:
                    layer.loadNamedStyle(style)

                details.project.addMapLayer(
                    context.temporaryLayerStore().takeMapLayer(layer))

                if details.postProcessor():
                    details.postProcessor().postProcessLayer(
                        layer, context, feedback)

            else:
                wrongLayers.append(str(l))
        except Exception:
            QgsMessageLog.logMessage(
                QCoreApplication.translate(
                    'Postprocessing', "Error loading result layer:") + "\n" +
                traceback.format_exc(), 'Processing', Qgis.Critical)
            wrongLayers.append(str(l))
        i += 1

    feedback.setProgress(100)

    if wrongLayers:
        msg = QCoreApplication.translate(
            'Postprocessing',
            "The following layers were not correctly generated.")
        msg += "<ul>" + "".join(["<li>%s</li>" % lay
                                 for lay in wrongLayers]) + "</ul>"
        msg += QCoreApplication.translate(
            'Postprocessing',
            "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm."
        )
        feedback.reportError(msg)

    return len(wrongLayers) == 0
Exemple #31
0
    def processAlgorithm(self, progress):
        layer = self.getParameterValue(self.INPUT_LAYER)
        mapping = self.getParameterValue(self.FIELDS_MAPPING)
        output = self.getOutputFromName(self.OUTPUT_LAYER)

        layer = dataobjects.getObjectFromUri(layer)
        fields = []
        expressions = []

        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs().srsid())
        da.setEllipsoidalMode(
            iface.mapCanvas().mapSettings().hasCrsTransformEnabled())
        da.setEllipsoid(QgsProject.instance().readEntry(
            'Measure', '/Ellipsoid', GEO_NONE)[0])

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope())
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        for field_def in mapping:
            fields.append(
                QgsField(name=field_def['name'],
                         type=field_def['type'],
                         len=field_def['length'],
                         prec=field_def['precision']))

            expression = QgsExpression(field_def['expression'])
            expression.setGeomCalculator(da)
            expression.setDistanceUnits(QgsProject.instance().distanceUnits())
            expression.setAreaUnits(QgsProject.instance().areaUnits())

            if expression.hasParserError():
                raise GeoAlgorithmExecutionException(
                    self.tr(u'Parser error in expression "{}": {}').format(
                        str(field_def['expression']),
                        str(expression.parserErrorString())))
            expression.prepare(exp_context)
            if expression.hasEvalError():
                raise GeoAlgorithmExecutionException(
                    self.tr(u'Evaluation error in expression "{}": {}').format(
                        str(field_def['expression']),
                        str(expression.evalErrorString())))
            expressions.append(expression)

        writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs())

        # Create output vector layer with new attributes
        error = ''
        calculationSuccess = True
        inFeat = QgsFeature()
        outFeat = QgsFeature()
        features = vector.features(layer)
        total = 100.0 / len(features)
        for current, inFeat in enumerate(features):
            rownum = current + 1

            geometry = inFeat.geometry()
            outFeat.setGeometry(geometry)

            attrs = []
            for i in range(0, len(mapping)):
                field_def = mapping[i]
                expression = expressions[i]
                exp_context.setFeature(inFeat)
                exp_context.lastScope().setVariable("row_number", rownum)
                value = expression.evaluate(exp_context)
                if expression.hasEvalError():
                    calculationSuccess = False
                    error = expression.evalErrorString()
                    break

                attrs.append(value)
            outFeat.setAttributes(attrs)

            writer.addFeature(outFeat)

            progress.setPercentage(int(current * total))

        del writer

        if not calculationSuccess:
            raise GeoAlgorithmExecutionException(
                self.tr('An error occurred while evaluating the calculation'
                        ' string:\n') + error)
    def processAlgorithm(self, parameters, context, feedback):
        if DEBUG_MODE:
            logMessage(
                "processAlgorithm(): {}".format(self.__class__.__name__),
                False)

        clayer = self.parameterAsLayer(parameters, self.INPUT, context)
        title_field = self.parameterAsString(parameters, self.TITLE_FIELD,
                                             context)
        cf_filter = self.parameterAsBool(parameters, self.CF_FILTER, context)
        fixed_scale = self.parameterAsEnum(parameters, self.SCALE,
                                           context)  # == 1
        buf = self.parameterAsDouble(parameters, self.BUFFER, context)
        tex_width = self.parameterAsInt(parameters, self.TEX_WIDTH, context)
        orig_tex_height = self.parameterAsInt(parameters, self.TEX_HEIGHT,
                                              context)

        header_exp = QgsExpression(
            self.parameterAsExpression(parameters, self.HEADER, context))
        footer_exp = QgsExpression(
            self.parameterAsExpression(parameters, self.FOOTER, context))

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(clayer))

        out_dir = self.parameterAsString(parameters, self.OUTPUT, context)
        if not QDir(out_dir).exists():
            QDir().mkpath(out_dir)

        if DEBUG_MODE:
            openDirectory(out_dir)

        mapSettings = self.controller.settings.mapSettings
        baseExtent = self.controller.settings.baseExtent
        rotation = mapSettings.rotation()
        orig_size = mapSettings.outputSize()

        if cf_filter:
            cf_layer = QgsMemoryProviderUtils.createMemoryLayer(
                "current feature", clayer.fields(), clayer.wkbType(),
                clayer.crs())
            layers = [
                cf_layer if lyr == clayer else lyr
                for lyr in mapSettings.layers()
            ]
            mapSettings.setLayers(layers)

            doc = QDomDocument("qgis")
            clayer.exportNamedStyle(doc)
            cf_layer.importNamedStyle(doc)

        total = clayer.featureCount()
        for current, feature in enumerate(clayer.getFeatures()):
            if feedback.isCanceled():
                break

            if cf_filter:
                cf_layer.startEditing()
                cf_layer.deleteFeatures(
                    [f.id() for f in cf_layer.getFeatures()])
                cf_layer.addFeature(feature)
                cf_layer.commitChanges()

            title = feature.attribute(title_field)
            feedback.setProgressText("({}/{}) Exporting {}...".format(
                current + 1, total, title))
            logMessage("Exporting {}...".format(title), False)

            # extent
            geometry = QgsGeometry(feature.geometry())
            geometry.transform(self.transform)
            center = geometry.centroid().asPoint()

            if fixed_scale or geometry.type() == QgsWkbTypes.PointGeometry:
                tex_height = orig_tex_height or int(
                    tex_width * orig_size.height() / orig_size.width())
                rect = RotatedRect(center, baseExtent.width(),
                                   baseExtent.width() * tex_height / tex_width,
                                   rotation).scale(1 + buf / 100)
            else:
                geometry.rotate(rotation, center)
                rect = geometry.boundingBox().scaled(1 + buf / 100)
                center = RotatedRect.rotatePoint(rect.center(), rotation,
                                                 center)
                if orig_tex_height:
                    tex_height = orig_tex_height
                    tex_ratio = tex_width / tex_height
                    rect_ratio = rect.width() / rect.height()
                    if tex_ratio > rect_ratio:
                        rect = RotatedRect(center,
                                           rect.height() * tex_ratio,
                                           rect.height(), rotation)
                    else:
                        rect = RotatedRect(center, rect.width(),
                                           rect.width() / tex_ratio, rotation)
                else:
                    # fit to buffered geometry bounding box
                    rect = RotatedRect(center, rect.width(), rect.height(),
                                       rotation)
                    tex_height = tex_width * rect.height() / rect.width()

            rect.toMapSettings(mapSettings)
            mapSettings.setOutputSize(QSize(tex_width, tex_height))

            self.controller.settings.setMapSettings(mapSettings)

            # labels
            exp_context.setFeature(feature)
            self.controller.settings.setHeaderLabel(
                header_exp.evaluate(exp_context))
            self.controller.settings.setFooterLabel(
                footer_exp.evaluate(exp_context))

            self.export(title, out_dir, feedback)

            feedback.setProgress(int(current / total * 100))

        if P_OPEN_DIRECTORY and not DEBUG_MODE:
            openDirectory(out_dir)

        return {}
 def createExpressionContext(self) -> QgsExpressionContext:  # pylint: disable=missing-docstring, no-self-use
     context = QgsExpressionContext()
     scope = QgsExpressionContextScope()
     scope.setVariable('some_var', 10)
     context.appendScope(scope)
     return context
    def processAlgorithm(self, parameters, context, feedback):
        self.feedback = feedback
        """
        Here is where the processing itself takes place.
        """
        baseUrl = self.parameterAsString(parameters, self.BASE_URL, context)
        layers = self.parameterAsString(parameters, self.LAYERS, context)
        style_name = self.parameterAsString(parameters, self.STYLE_NAME, context)
        wmsVersion = self.parameterAsString(parameters, self.WMS_VERSION, context)
        imageFormatIdx = self.parameterAsEnum(parameters, self.IMAGE_FORMAT, context)
        pixelSize = self.parameterAsDouble(parameters, self.PIXEL_SIZE, context)
        
        exprFileNameString = self.parameterAsString(parameters, self.FILENAME_FIELD, context)
        vectorLayer= self.parameterAsVectorLayer(parameters, self.INPUT_LAYER, context)
        self.path = self.parameterAsFile(parameters, self.DOWNLOAD_DIR, context)

        # fileNameFileIndex=-1
        # if not fileNameField=="":
            # fileNameFileIndex = vectorLayer.fields().lookupField(fileNameField)
        exprFileName=QgsExpression(exprFileNameString)
        if exprFileName.hasParserError():
            raise QgsProcessingException("Invalid Filename Expression: " + exprFileName.parserErrorString())

        exprContext = QgsExpressionContext()
            
        if baseUrl=="":
            raise ###

        fields = vectorLayer.fields()
        fields.append( QgsField( self.fieldNameBBOX, QVariant.String ) )
        fields.append( QgsField( self.fieldNameUrl, QVariant.String ) )
        fields.append( QgsField( self.fieldNamePath, QVariant.String ) )
        fields.append( QgsField( self.fieldNameFile, QVariant.String ) )
        fields.append( QgsField( self.fieldNameResponseType, QVariant.String ) )
        fields.append( QgsField( self.fieldNameHttpStatusCode, QVariant.Int) )
        #take CRS from Project
        crsProject=QgsProject.instance().crs()
        feedback.pushInfo("CRS " + str(crsProject))

        
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                context, fields, vectorLayer.wkbType(), crsProject)

        total=1
               
        #Check if any features are selected
        if vectorLayer.selectedFeatureCount() > 0:
            # Take only the selected features
            iter=vectorLayer.selectedFeatures()
            total = 100.0 / vectorLayer.selectedFeatureCount()
        else:

            iter = vectorLayer.getFeatures()
            try:
                total = 100.0 / vectorLayer.featureCount() #Division durch 0
            except:
                msg = self.tr("no Features to process")
                feedback.reportError(msg)
                raise QgsProcessingException(msg)
        feedback.pushInfo("Pixel size: " + str(pixelSize))

        countDownload=0 
        for i, feature in enumerate(iter):
        
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():

                msg = self.tr("Process was canceled!")

                raise QgsProcessingException(msg)
                
            hasError=False
            loopText=''
            attrs=feature.attributes()
            geom = feature.geometry()
            bbox = geom.boundingBox()
            xMin = round( bbox.xMinimum(), 2)
            xMax = round( bbox.xMaximum(), 2)
            yMin = round( bbox.yMinimum(), 2)
            yMax = round( bbox.yMaximum(), 2)
            
            bboxText ='{0},{1},{2},{3}'.format( xMin, yMin, xMax, yMax )
            pixelsX = int( (xMax - xMin) / pixelSize )
            pixelsY = int( (yMax - yMin) / pixelSize )
            feedback.pushInfo("BBox: " + str(i) + bboxText + " --> Tile width: " + str(pixelsX) + " height: " + str(pixelsY) )
            
            epsg=vectorLayer.crs().authid()
            url = unicode( baseUrl + 'request=GetMap&Service=WMS&Version=' + wmsVersion + '&SRS=' + str(epsg) + '&LAYERS=' + layers + '&STYLES=' + style_name + '&BBOX=' + bboxText + '&FORMAT=image/' + self.imageFormats[imageFormatIdx].lower() +'&WIDTH=' + str(pixelsX)+ '&HEIGHT=' + str(pixelsY) )
            #qUrl=QUrl(url)
            
            #get File type combo item
            fileExt = self.imageFormats[imageFormatIdx]
            
            
            exprContext.setFeature(feature)
            file_name = exprFileName.evaluate(exprContext)
            if file_name is None or file_name == '':
                file_name = "WMS_Tile_"+ str( i+1 )
                feedback.pushInfo("Invalid Filename Expression on Feature: " + str(i) + " saved as " + file_name)

            contentType=None
            httpStatusCode=None
            writeModus = 'wb' # binary
            numTrials = 5 # 'Number of trial (Anzahl der Versuche)
            if type(file_name) == int or type(file_name) == float:
                # if filename is numeric
                file_name = str(file_name)
            else:
                #Replace Special Characters
                file_name=file_name.replace(":","_")
                file_name=file_name.replace("*","_")
                file_name=file_name.replace("\\","_")
                file_name=file_name.replace("/","_")


            if not str(url) == "":
            
                try:
           
                    feedback.pushInfo( "----------------------------\n Start Download for feature " + str(i)+ " ("+ file_name +")" )
                    feedback.pushInfo( "\nGetMap-URL: "+ unicode(url) + "\n"  ) #+ url" \n" + str(url.encode("utf-8"))+

                    result = self.getResponse(feedback, url, numTrials, 0) # % Versuche
                    if result is not None:
                        feedback.pushInfo(".. trials executed. ")
                        # To Do: es gibt result is not null und trotzdem wird keine Looptext-Augabe oder Datei erzeugt
                        try:
                            httpStatusCode = result.status_code

                        
                            if not result.headers is None and not result.status_code is None:
                            
                                contentType = result.headers.get('Content-Type')
                                contentType = contentType.lower()

                            else:
                                feedback.pushInfo("No Header and Status_Code" + str(i) +' '+ file_name + ' ('+str(httpStatusCode)+') ' + url)
                                fileExt='xml'
                                writeModus = 'w'


                        except Exception as resultErr:  #Result has No Header or status_code
                            fileExt='xml'
                            writeModus = 'w'
                            loopText = loopText + "\n\t Download Result has No Header or status_code " + str(i) + "(" + file_name + "): " + " URL: " + unicode(url) + " " + str(resultErr.args) + ";" + str(repr(resultErr) + "\n  HTTP Status Code: " + str( httpStatusCode ) + '\n Content-Type: ' + str(contentType) )
                            
                  
                        # change file type if not a image
                        if contentType.find('image') == -1 and contentType.find('jpeg') == -1 and  contentType.find('jpg') == -1 and  contentType.find('png') == -1 and  contentType.find('gif') == -1 and contentType.find('tif') == -1:
                            isImage = False
                            feedback.pushInfo("!!! Result is no valid WMS-image !!!!")
                            writeModus = 'w'
                            fileExt='xml'
                        else:
                            isImage=True

                        feedback.pushInfo("contentType: " + str(contentType))
                        feedback.pushInfo('httpStatusCode: '+str( httpStatusCode ))
                        feedback.pushInfo('Filetype: ' + str( fileExt ) )


                        filePath=self.path + "\\" + file_name + '.' + fileExt.lower()

                        with open(filePath, writeModus) as fd:
                        
                            #contentType='application/application/vnd.ogc.se_xml'
                            if writeModus== 'wb': #binary
                                fd.write( result.content ) 
                            else: # string
                                fd.write( result.text ) 
                            

                        fd.close()
                        feedback.pushInfo('File saved: ' + str( filePath ) )
                        
                        if httpStatusCode == 200 and isImage==True:
                                                
                           
                            #create World File
                            #first and last Character of image extension
                            wldExt=''
                            try:
                                wldExt = fileExt[0] + fileExt[-1] + 'w'
                            except:
                                wldExt='wld'
                            filePathWld=self.path + "\\" + file_name + '.' + wldExt.lower()
                            outWld = open( filePathWld , 'w')
                            outWld.write( str( pixelSize )  )
                            outWld.write( '\n' + str(0) )
                            outWld.write( '\n' + str(0) )
                            outWld.write( '\n' + str( - pixelSize ) )
                            outWld.write( '\n' + str( xMin + pixelSize/2 ) ) #Nimm Zentrum des linken oberen Pixels 
                            outWld.write( '\n' + str( yMax - pixelSize/2) )
                            outWld.close()
                            feedback.pushInfo('WorldFile saved: ' + str( filePathWld ) )

                        else:
                            hasError=True
                            feedback.pushInfo("Problem at Feature " + str(i) + "(" + file_name + "): " + unicode(url) + "  HTTP Status Code:" + str(httpStatusCode) )
                    

                    else:
                        # Result is empty
                        feedback.pushInfo(".. trials without Result: " + str(result))

                        loopText = loopText + " Result is empty for (" + file_name + "): " + unicode(url) + "\n WMS-Server delivers no data to create a file. " + str(numTrials) + ' trials were excecuted.'

                    
                except Exception as err:
                    hasError=True
                    loopText = loopText + "\n\t Download error: On Feature " + str(i) + "(" + file_name + "): " + " URL: " + unicode(url) + " " + str(err.args) + ";" + str(repr(err) + "\n  HTTP Status Code: " + str( httpStatusCode ) + '\n Content-Type: ' + str(contentType) )


            attrs.append( bboxText )
            attrs.append( url )
            attrs.append( self.path + "\\")
            attrs.append( file_name + '.' + fileExt.lower() )
            attrs.append( contentType )
            attrs.append( httpStatusCode )
           

            try:
                out_feat = QgsFeature()
                #check if source is a spatial layer
                if vectorLayer.wkbType() != 100:
                    out_feat.setGeometry(geom)
                out_feat.setAttributes(attrs)
                # Add a feature in the sink
                sink.addFeature(out_feat, QgsFeatureSink.FastInsert)
            except IOError as e:
                hasError=True
                loopText = loopText + "\n\t" + "DP I/O error({0}): {1}".format(e.errno, e.strerror)
            except ValueError:
                hasError=True
                loopText = loopText + "\n\t" + "Value-Error"
            except QgsError as qGisErr:
                hasError=True
                loopText = loopText + "\n\t" +  " QgsError: " + qGisErr.message()
                loopText = loopText + "\n\t" +  qGisErr.summary()
            except Exception as err:
                hasError=True
                loopText = loopText + "\n\t Error while taking over the feature " + str(i) + "(" + file_name + "): " +" "+ str(err.args) + ";" + str(repr(err))
                pass

            feedback.setProgress( int( i * total ) )
            if hasError==True:
                feedback.pushInfo(loopText)
            else:
                countDownload=countDownload+1
            


        msgInfo=self.tr( str( countDownload ) + " Downloads from " + str(i+1) + " Features to Directory " + self.path)
        feedback.pushInfo(msgInfo)
        # Return the results of the algorithm. In this case our only result is
        return {self.OUTPUT: dest_id}
Exemple #35
0
    def processAlgorithm(self, parameters, context, feedback):

        t_regard = self.parameterAsSource(parameters, self.MANHOLES_TABLE,
                                          context)
        g_regard = self.parameterAsSource(parameters, self.GEOM_MANHOLES,
                                          context)
        t_troncon = self.parameterAsVectorLayer(parameters,
                                                self.SEGMENTS_TABLE, context)
        g_troncon = self.parameterAsVectorLayer(parameters, self.GEOM_SEGMENTS,
                                                context)

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project()))
        exp_context.appendScope(
            QgsExpressionContextUtils.layerScope(t_troncon))

        exp_str = '"id_geom_troncon" IS NULL'
        exp = QgsExpression(exp_str)

        exp.prepare(exp_context)
        if exp.hasEvalError():
            raise QgsProcessingException(
                tr('* ERROR: Expression {} has eval error: {}').format(
                    exp.expression(), exp.evalErrorString()))

        request = QgsFeatureRequest(exp, exp_context)
        r_ids = []  # identifiant des regards
        l_t_f = {}  # lien troncon fichier
        l_f_t = {}  # lien fichier troncons
        troncons = {}
        sorties = {}
        entrees = {}
        for tro in t_troncon.getFeatures(request):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}

            segment_number = tro['id']
            r1 = tro['id_regard1']
            r2 = tro['id_regard2']

            if r1 not in r_ids:
                r_ids.append(r1)
            if r2 not in r_ids:
                r_ids.append(r2)

            fid = tro['id_file']
            l_t_f[segment_number] = fid
            if fid in l_f_t:
                l_f_t[fid] = l_f_t[fid] + [segment_number]
            else:
                l_f_t[fid] = [segment_number]

            troncons[segment_number] = (r1, r2)
            if r1 in sorties:
                sorties[r1] = sorties[r1] + [segment_number]
            else:
                sorties[r1] = [segment_number]
            if r2 in entrees:
                entrees[r2] = entrees[r2] + [segment_number]
            else:
                entrees[r2] = [segment_number]

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project()))
        exp_context.appendScope(t_regard.createExpressionContextScope())

        exp_str = ('"id_geom_regard" IS NOT NULL '
                   'AND '
                   '"id" IN ({})').format(','.join([str(i)
                                                    for i in r_ids] + ['-1']))
        exp = QgsExpression(exp_str)

        exp.prepare(exp_context)
        if exp.hasEvalError():
            raise QgsProcessingException(
                tr('* ERROR: Expression {} has eval error: {}').format(
                    exp.expression(), exp.evalErrorString()))

        request = QgsFeatureRequest(exp, exp_context)
        g_ids = []  # identifiants des géométrie de regards
        l_r_g = {}  # lien regard geometrie
        l_g_r = {}  # lien geometrie regards
        for reg in t_regard.getFeatures(request):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}

            segment_number = reg['id']
            fid = reg['id_file']

            if segment_number in sorties:
                sorties[segment_number] = [
                    trid for trid in sorties[segment_number]
                    if l_t_f[trid] == fid
                ]
            if segment_number in entrees:
                entrees[segment_number] = [
                    trid for trid in entrees[segment_number]
                    if l_t_f[trid] == fid
                ]

            gid = reg['id_geom_regard']
            l_r_g[segment_number] = gid
            if gid not in g_ids:
                g_ids.append(gid)
            if gid in l_g_r:
                l_g_r[gid] = l_g_r[gid] + [segment_number]
            else:
                l_g_r[gid] = [segment_number]

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project()))
        exp_context.appendScope(g_regard.createExpressionContextScope())

        exp_str = '"id" IN ({})'.format(','.join([str(i)
                                                  for i in g_ids] + ['-1']))
        exp = QgsExpression(exp_str)

        exp.prepare(exp_context)
        if exp.hasEvalError():
            raise QgsProcessingException(
                tr('* ERROR: Expression {} has eval error: {}').format(
                    exp.expression(), exp.evalErrorString()))

        request = QgsFeatureRequest(exp, exp_context)
        points = {}
        pt_labels = {}
        for reg in g_regard.getFeatures(request):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}

            segment_number = reg['id']
            points[segment_number] = reg.geometry().asPoint()
            pt_labels[segment_number] = reg['label']

        lines = {}
        for gid in points:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}

            if gid not in l_g_r:
                continue

            for rid in l_g_r[gid]:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SEGMENT_CREATED: None}

                if rid in sorties:
                    for tid in sorties[rid]:
                        # Stop the algorithm if cancel button has been clicked
                        if feedback.isCanceled():
                            return {self.SEGMENT_CREATED: None}

                        if tid in lines:
                            continue
                        if tid not in troncons:
                            continue
                        r2 = troncons[tid][1]
                        if r2 not in l_r_g:
                            continue
                        g2 = l_r_g[r2]
                        if g2 not in points:
                            continue
                        lines[tid] = (gid, g2)

                if rid in entrees:
                    for tid in entrees[rid]:
                        # Stop the algorithm if cancel button has been clicked
                        if feedback.isCanceled():
                            return {self.SEGMENT_CREATED: None}

                        if tid in lines:
                            continue
                        if tid not in troncons:
                            continue
                        r1 = troncons[tid][0]
                        if r1 not in l_r_g:
                            continue
                        g1 = l_r_g[r1]
                        if g1 not in points:
                            continue
                        lines[tid] = (g1, gid)

        # Creation des troncons
        l_t_g = {}  # lien troncon et geometrie troncon
        l_pts_t = {}  # lien points troncons
        features = []  # les objets de geometrie de troncon
        geom_point_keys = []  # liste des clés des points troncons
        for tid, pts in lines.items():
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}

            exp_context = QgsExpressionContext()
            exp_context.appendScope(QgsExpressionContextUtils.globalScope())
            exp_context.appendScope(
                QgsExpressionContextUtils.projectScope(context.project()))
            exp_context.appendScope(
                QgsExpressionContextUtils.layerScope(g_troncon))

            exp_str = ('"id_geom_regard_amont" = {} AND '
                       '"id_geom_regard_aval" = {}').format(pts[0], pts[1])
            exp = QgsExpression(exp_str)

            exp.prepare(exp_context)
            if exp.hasEvalError():
                raise QgsProcessingException(
                    tr('* ERROR: Expression {} has eval error: {}').format(
                        exp.expression(), exp.evalErrorString()))

            request = QgsFeatureRequest(exp, exp_context)
            request.setLimit(1)
            for tro in g_troncon.getFeatures(request):
                l_t_g[tid] = tro['id']
                continue

            if (pts[0], pts[1]) in geom_point_keys:
                l_pts_t[(pts[0], pts[1])].append(tid)
                continue
            else:
                l_pts_t[(pts[0], pts[1])] = [tid]
                geom_point_keys.append((pts[0], pts[1]))

            feat_t = QgsVectorLayerUtils.createFeature(g_troncon)
            feat_t.setAttribute(
                'label', '{}-{}'.format(pt_labels[pts[0]], pt_labels[pts[1]]))
            feat_t.setAttribute('id_geom_regard_amont', pts[0])
            feat_t.setAttribute('id_geom_regard_aval', pts[1])
            feat_t.setGeometry(
                QgsGeometry.fromPolylineXY([points[pts[0]], points[pts[1]]]))
            features.append(feat_t)

        # Ajout des objets troncons
        if features:
            g_troncon.startEditing()
            (res, outFeats) = g_troncon.dataProvider().addFeatures(features)
            if not res or not outFeats:
                raise QgsProcessingException(
                    tr('* ERREUR: lors de l\'enregistrement '
                       'des regards {}').format(', '.join(
                           g_troncon.dataProvider().errors())))
            if not g_troncon.commitChanges():
                raise QgsProcessingException(
                    tr('* ERROR: Commit {}.').format(g_troncon.commitErrors()))

            for tro in outFeats:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SEGMENT_CREATED: None}

                if not tro['id']:
                    continue
                key = (tro['id_geom_regard_amont'], tro['id_geom_regard_aval'])
                if key not in geom_point_keys:
                    continue
                for tid in l_pts_t[(pts[0], pts[1])]:
                    l_t_g[tid] = tro['id']
                    # Stop the algorithm if cancel button has been clicked
                    if feedback.isCanceled():
                        return {self.SEGMENT_CREATED: None}

        for tid, pts in lines.items():
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}

            if tid in l_t_g:
                continue
            exp_context = QgsExpressionContext()
            exp_context.appendScope(QgsExpressionContextUtils.globalScope())
            exp_context.appendScope(
                QgsExpressionContextUtils.projectScope(context.project()))
            exp_context.appendScope(
                QgsExpressionContextUtils.layerScope(g_troncon))

            exp_str = ('"id_geom_regard_amont" = {} AND '
                       '"id_geom_regard_aval" = {}').format(pts[0], pts[1])
            exp = QgsExpression(exp_str)

            exp.prepare(exp_context)
            if exp.hasEvalError():
                raise QgsProcessingException(
                    tr('* ERROR: Expression {} has eval error: {}').format(
                        exp.expression(), exp.evalErrorString()))

            request = QgsFeatureRequest(exp, exp_context)
            request.setLimit(1)
            for tro in g_troncon.getFeatures(request):
                l_t_g[tid] = tro['id']
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SEGMENT_CREATED: None}

        if not l_t_g.keys():
            raise QgsProcessingException(
                tr('* ERREUR: Aucune géométrie de tronçon'))

        # Mise a jour de la table troncon
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project()))
        exp_context.appendScope(
            QgsExpressionContextUtils.layerScope(t_troncon))

        exp_str = ('"id_geom_troncon" IS NULL AND '
                   'id IN ({})').format(','.join(
                       [str(i) for i in l_t_g.keys()]))
        exp = QgsExpression(exp_str)

        exp.prepare(exp_context)
        if exp.hasEvalError():
            raise QgsProcessingException(
                tr('* ERROR: Expression %s has eval error: %s').format(
                    exp.expression(), exp.evalErrorString()))

        # Mise a jour de la table de tronçon
        request = QgsFeatureRequest(exp, exp_context)
        t_troncon.startEditing()
        segment_number = 0
        for tro in t_troncon.getFeatures(request):
            tro.setAttribute('id_geom_troncon', l_t_g[tro['id']])
            t_troncon.updateFeature(tro)
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}
            segment_number += 1

        if not t_troncon.commitChanges():
            raise QgsProcessingException(
                tr('* ERROR: Commit %s.') % t_troncon.commitErrors())

        # Returns empty dict if no outputs
        return {self.SEGMENT_CREATED: segment_number}
Exemple #36
0
    def fetch_values_from_layer(self):  # pylint: disable=too-many-locals, too-many-branches, too-many-statements
        """
        (Re)fetches plot values from the source layer.
        """

        # Note: we keep things nice and efficient and only iterate a single time over the layer!

        if not self.context_generator:
            context = QgsExpressionContext()
            context.appendScopes(
                QgsExpressionContextUtils.globalProjectLayerScopes(
                    self.source_layer))
        else:
            context = self.context_generator.createExpressionContext()

        self.settings.data_defined_properties.prepare(context)

        def add_source_field_or_expression(field_or_expression):
            field_index = self.source_layer.fields().lookupField(
                field_or_expression)
            if field_index == -1:
                expression = QgsExpression(field_or_expression)
                if not expression.hasParserError():
                    expression.prepare(context)
                return expression, expression.needsGeometry(
                ), expression.referencedColumns()

            return None, False, {field_or_expression}

        x_expression, x_needs_geom, x_attrs = add_source_field_or_expression(self.settings.properties['x_name']) if \
            self.settings.properties[
                'x_name'] else (None, False, set())
        y_expression, y_needs_geom, y_attrs = add_source_field_or_expression(self.settings.properties['y_name']) if \
            self.settings.properties[
                'y_name'] else (None, False, set())
        z_expression, z_needs_geom, z_attrs = add_source_field_or_expression(self.settings.properties['z_name']) if \
            self.settings.properties[
                'z_name'] else (None, False, set())
        additional_info_expression, additional_needs_geom, additional_attrs = add_source_field_or_expression(
            self.settings.layout['additional_info_expression']
        ) if self.settings.layout['additional_info_expression'] else (None,
                                                                      False,
                                                                      set())

        attrs = set().union(
            self.settings.data_defined_properties.referencedFields(), x_attrs,
            y_attrs, z_attrs, additional_attrs)

        request = QgsFeatureRequest()

        if self.settings.data_defined_properties.property(
                PlotSettings.PROPERTY_FILTER).isActive():
            expression = self.settings.data_defined_properties.property(
                PlotSettings.PROPERTY_FILTER).asExpression()
            request.setFilterExpression(expression)
            request.setExpressionContext(context)

        request.setSubsetOfAttributes(attrs, self.source_layer.fields())

        if not x_needs_geom and not y_needs_geom and not z_needs_geom and not additional_needs_geom and not self.settings.data_defined_properties.hasActiveProperties(
        ):
            request.setFlags(QgsFeatureRequest.NoGeometry)

        visible_geom_engine = None
        if self.visible_features_only and self.visible_region is not None:
            ct = QgsCoordinateTransform(
                self.visible_region.crs(), self.source_layer.crs(),
                QgsProject.instance().transformContext())
            try:
                rect = ct.transformBoundingBox(self.visible_region)
                request.setFilterRect(rect)
            except QgsCsException:
                pass
        elif self.visible_features_only and self.polygon_filter is not None:
            ct = QgsCoordinateTransform(
                self.polygon_filter.crs(), self.source_layer.crs(),
                QgsProject.instance().transformContext())
            try:
                rect = ct.transformBoundingBox(
                    self.polygon_filter.geometry.boundingBox())
                request.setFilterRect(rect)
                g = self.polygon_filter.geometry
                g.transform(ct)

                visible_geom_engine = QgsGeometry.createGeometryEngine(
                    g.constGet())
                visible_geom_engine.prepareGeometry()
            except QgsCsException:
                pass

        if self.selected_features_only:
            it = self.source_layer.getSelectedFeatures(request)
        else:
            it = self.source_layer.getFeatures(request)

        xx = []
        yy = []
        zz = []
        additional_hover_text = []
        marker_sizes = []
        colors = []
        stroke_colors = []
        stroke_widths = []
        for f in it:
            if visible_geom_engine and not visible_geom_engine.intersects(
                    f.geometry().constGet()):
                continue

            self.settings.feature_ids.append(f.id())
            context.setFeature(f)

            x = None
            if x_expression:
                x = x_expression.evaluate(context)
                if x == NULL or x is None:
                    continue
            elif self.settings.properties['x_name']:
                x = f[self.settings.properties['x_name']]
                if x == NULL or x is None:
                    continue

            y = None
            if y_expression:
                y = y_expression.evaluate(context)
                if y == NULL or y is None:
                    continue
            elif self.settings.properties['y_name']:
                y = f[self.settings.properties['y_name']]
                if y == NULL or y is None:
                    continue

            z = None
            if z_expression:
                z = z_expression.evaluate(context)
                if z == NULL or z is None:
                    continue
            elif self.settings.properties['z_name']:
                z = f[self.settings.properties['z_name']]
                if z == NULL or z is None:
                    continue

            if additional_info_expression:
                additional_hover_text.append(
                    additional_info_expression.evaluate(context))
            elif self.settings.layout['additional_info_expression']:
                additional_hover_text.append(
                    f[self.settings.layout['additional_info_expression']])

            if x is not None:
                xx.append(x)
            if y is not None:
                yy.append(y)
            if z is not None:
                zz.append(z)

            if self.settings.data_defined_properties.isActive(
                    PlotSettings.PROPERTY_MARKER_SIZE):
                default_value = self.settings.properties['marker_size']
                context.setOriginalValueVariable(default_value)
                value, _ = self.settings.data_defined_properties.valueAsDouble(
                    PlotSettings.PROPERTY_MARKER_SIZE, context, default_value)
                marker_sizes.append(value)
            if self.settings.data_defined_properties.isActive(
                    PlotSettings.PROPERTY_STROKE_WIDTH):
                default_value = self.settings.properties['marker_width']
                context.setOriginalValueVariable(default_value)
                value, _ = self.settings.data_defined_properties.valueAsDouble(
                    PlotSettings.PROPERTY_STROKE_WIDTH, context, default_value)
                stroke_widths.append(value)
            if self.settings.data_defined_properties.isActive(
                    PlotSettings.PROPERTY_COLOR):
                default_value = QColor(self.settings.properties['in_color'])
                value, _ = self.settings.data_defined_properties.valueAsColor(
                    PlotSettings.PROPERTY_COLOR, context, default_value)
                colors.append(value.name())
            if self.settings.data_defined_properties.isActive(
                    PlotSettings.PROPERTY_STROKE_COLOR):
                default_value = QColor(self.settings.properties['out_color'])
                value, _ = self.settings.data_defined_properties.valueAsColor(
                    PlotSettings.PROPERTY_STROKE_COLOR, context, default_value)
                stroke_colors.append(value.name())

        self.settings.additional_hover_text = additional_hover_text
        self.settings.x = xx
        self.settings.y = yy
        self.settings.z = zz
        if marker_sizes:
            self.settings.data_defined_marker_sizes = marker_sizes
        if colors:
            self.settings.data_defined_colors = colors
        if stroke_colors:
            self.settings.data_defined_stroke_colors = stroke_colors
        if stroke_widths:
            self.settings.data_defined_stroke_widths = stroke_widths
Exemple #37
0
import time

from qgis.core import QgsExpression, QgsExpressionContext, QgsProject

POLY_LYR = "test_poly"

EXPRESSION = """
collect_geometries(
    array_foreach(
        generate_series(1, num_points($geometry)),
        make_line(
            centroid($geometry),
            point_n($geometry, @element)
        )
    )
)
"""

lyr_polys = QgsProject.instance().mapLayersByName(POLY_LYR)[0]

t0 = time.time()
for counter, poly in enumerate(lyr_polys.getFeatures()):
    exp = QgsExpression(EXPRESSION)
    context = QgsExpressionContext()
    context.setFeature(poly)
    star = exp.evaluate(context)

t1 = time.time()
print(f"EXPRESSIONS: run {counter + 1} times for {t1-t0:.2f} secs")
    def updateLayer(self):
        self.layer = dataobjects.getObject(self.cmbInputLayer.currentText())

        self.builder.setLayer(self.layer)
        self.builder.loadFieldNames()

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope())
        exp_context.appendScope(
            QgsExpressionContextUtils.layerScope(self.layer))
        exp_context.lastScope().setVariable("row_number", 1)
        exp_context.setHighlightedVariables(["row_number"])
        self.builder.setExpressionContext(exp_context)

        self.populateFields()
Exemple #39
0
    def processAlgorithm(self, progress):
        layer = dataobjects.getObjectFromUri(
            self.getParameterValue(self.INPUT_LAYER))
        fieldName = self.getParameterValue(self.FIELD_NAME)
        fieldType = self.TYPES[self.getParameterValue(self.FIELD_TYPE)]
        width = self.getParameterValue(self.FIELD_LENGTH)
        precision = self.getParameterValue(self.FIELD_PRECISION)
        newField = self.getParameterValue(self.NEW_FIELD)
        formula = self.getParameterValue(self.FORMULA)

        output = self.getOutputFromName(self.OUTPUT_LAYER)

        fields = layer.fields()
        if newField:
            fields.append(QgsField(fieldName, fieldType, '', width, precision))

        writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs())

        exp = QgsExpression(formula)

        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs().srsid())
        da.setEllipsoidalMode(
            iface.mapCanvas().mapSettings().hasCrsTransformEnabled())
        da.setEllipsoid(QgsProject.instance().readEntry(
            'Measure', '/Ellipsoid', GEO_NONE)[0])
        exp.setGeomCalculator(da)
        exp.setDistanceUnits(QgsProject.instance().distanceUnits())
        exp.setAreaUnits(QgsProject.instance().areaUnits())

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope())
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        if not exp.prepare(exp_context):
            raise GeoAlgorithmExecutionException(
                self.tr('Evaluation error: %s' % exp.evalErrorString()))

        outFeature = QgsFeature()
        outFeature.initAttributes(len(fields))
        outFeature.setFields(fields)

        error = ''
        calculationSuccess = True

        features = vector.features(layer)
        total = 100.0 / len(features)

        rownum = 1
        for current, f in enumerate(features):
            rownum = current + 1
            exp_context.setFeature(f)
            exp_context.lastScope().setVariable("row_number", rownum)
            value = exp.evaluate(exp_context)
            if exp.hasEvalError():
                calculationSuccess = False
                error = exp.evalErrorString()
                break
            else:
                outFeature.setGeometry(f.geometry())
                for fld in f.fields():
                    outFeature[fld.name()] = f[fld.name()]
                outFeature[fieldName] = value
                writer.addFeature(outFeature)

            progress.setPercentage(int(current * total))
        del writer

        if not calculationSuccess:
            raise GeoAlgorithmExecutionException(
                self.tr('An error occurred while evaluating the calculation '
                        'string:\n%s' % error))
    def testExpression(self):
        """ test aggregate calculation using an expression """

        # numeric
        layer = QgsVectorLayer("Point?field=fldint:integer", "layer", "memory")
        pr = layer.dataProvider()

        int_values = [4, 2, 3, 2, 5, None, 8]

        features = []
        for v in int_values:
            f = QgsFeature()
            f.setFields(layer.fields())
            f.setAttributes([v])
            features.append(f)
        assert pr.addFeatures(features)

        #int
        agg = QgsAggregateCalculator(layer)
        val, ok = agg.calculate(QgsAggregateCalculator.Sum, 'fldint * 2')
        self.assertTrue(ok)
        self.assertEqual(val, 48)

        # double
        val, ok = agg.calculate(QgsAggregateCalculator.Sum, 'fldint * 1.5')
        self.assertTrue(ok)
        self.assertEqual(val, 36)

        # datetime
        val, ok = agg.calculate(QgsAggregateCalculator.Max, "to_date('2012-05-04') + to_interval( fldint || ' day' )")
        self.assertTrue(ok)
        self.assertEqual(val, QDateTime(QDate(2012, 5, 12), QTime(0, 0, 0)))

        # date
        val, ok = agg.calculate(QgsAggregateCalculator.Min, "to_date(to_date('2012-05-04') + to_interval( fldint || ' day' ))")
        self.assertTrue(ok)
        self.assertEqual(val, QDateTime(QDate(2012, 5, 6), QTime(0, 0, 0)))

        # string
        val, ok = agg.calculate(QgsAggregateCalculator.Max, "fldint || ' oranges'")
        self.assertTrue(ok)
        self.assertEqual(val, '8 oranges')

        # geometry
        val, ok = agg.calculate(QgsAggregateCalculator.GeometryCollect, "make_point( coalesce(fldint,0), 2 )")
        self.assertTrue(ok)
        self.assertTrue(val.exportToWkt(), 'MultiPoint((4 2, 2 2, 3 2, 2 2,5 2, 0 2,8 2))')

        # try a bad expression
        val, ok = agg.calculate(QgsAggregateCalculator.Max, "not_a_field || ' oranges'")
        self.assertFalse(ok)
        val, ok = agg.calculate(QgsAggregateCalculator.Max, "5+")
        self.assertFalse(ok)

        # test expression context

        # check default context first
        # should have layer variables:
        val, ok = agg.calculate(QgsAggregateCalculator.Min, "@layer_name")
        self.assertTrue(ok)
        self.assertEqual(val, 'layer')
        # but not custom variables:
        val, ok = agg.calculate(QgsAggregateCalculator.Min, "@my_var")
        self.assertTrue(ok)
        self.assertEqual(val, NULL)

        # test with manual expression context
        scope = QgsExpressionContextScope()
        scope.setVariable('my_var', 5)
        context = QgsExpressionContext()
        context.appendScope(scope)
        val, ok = agg.calculate(QgsAggregateCalculator.Min, "@my_var", context)
        self.assertTrue(ok)
        self.assertEqual(val, 5)
    def get_feature_with_form_scope(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None:
        """ Get filtered features with a form scope
        In parameters:
            LAYER=wms-layer-name
            FILTER=An expression to filter layer
            FORM_FEATURE={"type": "Feature", "geometry": {}, "properties": {}}
            // optionals
            FIELDS=list of requested field separated by comma
            WITH_GEOMETRY=False
        """
        layer_name = params.get('LAYER', '')
        if not layer_name:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'GetFeatureWithFormScope' REQUEST: LAYER parameter is mandatory",
                400)

        # get layer
        layer = find_vector_layer(layer_name, project)
        # layer not found
        if not layer:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid LAYER parameter for 'VirtualField': {} provided".format(layer_name),
                400)

        # get filter
        exp_filter = params.get('FILTER', '')
        if not exp_filter:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'GetFeatureWithFormScope' REQUEST: FILTER parameter is mandatory",
                400)

        # get form feature
        form_feature = params.get('FORM_FEATURE', '')
        if not form_feature:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE parameter is mandatory",
                400)

        # Check features
        try:
            geojson = json.loads(form_feature)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads form feature '{}' exception:\n{}".format(form_feature, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE '{}' are not well formed".format(form_feature),
                400)

        if not geojson or not isinstance(geojson, dict):
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE '{}' are not well formed".format(form_feature),
                400)

        if ('type' not in geojson) or geojson['type'] != 'Feature':
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE '{}' are not well formed: type not defined "
                 "or not Feature.").format(form_feature),
                400)

        # try to load form feature
        # read fields
        form_feature_fields = QgsJsonUtils.stringToFields(
            form_feature,
            QTextCodec.codecForName("UTF-8"))
        # read features
        form_feature_list = QgsJsonUtils.stringToFeatureList(
            form_feature,
            form_feature_fields,
            QTextCodec.codecForName("UTF-8"))

        # features not well formed
        if not form_feature_list:
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid FORM_FEATURE for 'GetFeatureWithFormScope': not GeoJSON feature provided\n"
                 "{}").format(form_feature),
                400)

        if len(form_feature_list) != 1:
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid FORM_FEATURE for 'GetFeatureWithFormScope': not GeoJSON feature provided\n"
                 "{}").format(form_feature),
                400)

        # Get the form feature
        form_feat = form_feature_list[0]

        # create expression context
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope(project))
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))
        exp_context.appendScope(QgsExpressionContextUtils.formScope(form_feat))

        # create distance area context
        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs(), project.transformContext())
        da.setEllipsoid(project.ellipsoid())

        # Get filter expression
        exp_f = QgsExpression(exp_filter)
        exp_f.setGeomCalculator(da)
        exp_f.setDistanceUnits(project.distanceUnits())
        exp_f.setAreaUnits(project.areaUnits())

        if exp_f.hasParserError():
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid FILTER for 'GetFeatureWithFormScope': Error \"{}\": {}".format(
                    exp_filter, exp_f.parserErrorString()),
                400)

        if not exp_f.isValid():
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid FILTER for 'GetFeatureWithFormScope': Expression not valid \"{}\"".format(exp_filter),
                400)

        exp_f.prepare(exp_context)

        req = QgsFeatureRequest(exp_f, exp_context)

        # With geometry
        with_geom = params.get('WITH_GEOMETRY', '').lower() in ['true', '1', 't']
        if not with_geom:
            req.setFlags(QgsFeatureRequest.NoGeometry)

        # Fields
        pk_attributes = layer.primaryKeyAttributes()
        attribute_list = [i for i in pk_attributes]
        fields = layer.fields()
        r_fields = [f.strip() for f in params.get('FIELDS', '').split(',') if f]
        for f in r_fields:
            attribute_list.append(fields.indexOf(f))

        # response
        response.setStatusCode(200)
        response.setHeader("Content-Type", "application/json")
        response.write('{ "type": "FeatureCollection","features":[')
        response.flush()

        json_exporter = QgsJsonExporter(layer)
        if attribute_list:
            json_exporter.setAttributes(attribute_list)

        separator = ''
        for feat in layer.getFeatures(req):
            fid = layer_name + '.' + get_server_fid(feat, pk_attributes)
            response.write(separator + json_exporter.exportFeature(feat, {}, fid))
            response.flush()
            separator = ',\n'
        response.write(']}')
        return
Exemple #42
0
def handleAlgorithmResults(alg, context, feedback=None, showResults=True, parameters={}):
    wrongLayers = []
    if feedback is None:
        feedback = QgsProcessingFeedback()
    feedback.setProgressText(QCoreApplication.translate('Postprocessing', 'Loading resulting layers'))
    i = 0

    for l, details in context.layersToLoadOnCompletion().items():
        if feedback.isCanceled():
            return False

        if len(context.layersToLoadOnCompletion()) > 2:
            # only show progress feedback if we're loading a bunch of layers
            feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion())))
        try:
            layer = QgsProcessingUtils.mapLayerFromString(l, context, typeHint=details.layerTypeHint)
            if layer is not None:
                set_layer_name(layer, details)

                '''If running a model, the execution will arrive here when an algorithm that is part of
                that model is executed. We check if its output is a final otuput of the model, and
                adapt the output name accordingly'''
                outputName = details.outputName
                expcontext = QgsExpressionContext()
                scope = QgsExpressionContextScope()
                expcontext.appendScope(scope)
                for out in alg.outputDefinitions():
                    if out.name() not in parameters:
                        continue
                    outValue = parameters[out.name()]
                    if hasattr(outValue, "sink"):
                        outValue = outValue.sink.valueAsString(expcontext)[0]
                    else:
                        outValue = str(outValue)
                    if outValue == l:
                        outputName = out.name()
                        break
                style = None
                if outputName:
                    style = RenderingStyles.getStyle(alg.id(), outputName)
                if style is None:
                    if layer.type() == QgsMapLayer.RasterLayer:
                        style = ProcessingConfig.getSetting(ProcessingConfig.RASTER_STYLE)
                    else:
                        if layer.geometryType() == QgsWkbTypes.PointGeometry:
                            style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POINT_STYLE)
                        elif layer.geometryType() == QgsWkbTypes.LineGeometry:
                            style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_LINE_STYLE)
                        else:
                            style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POLYGON_STYLE)
                if style:
                    layer.loadNamedStyle(style)

                details.project.addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))

                if details.postProcessor():
                    details.postProcessor().postProcessLayer(layer, context, feedback)

            else:
                wrongLayers.append(str(l))
        except Exception:
            QgsMessageLog.logMessage(QCoreApplication.translate('Postprocessing', "Error loading result layer:") + "\n" + traceback.format_exc(), 'Processing', Qgis.Critical)
            wrongLayers.append(str(l))
        i += 1

    feedback.setProgress(100)

    if wrongLayers:
        msg = QCoreApplication.translate('Postprocessing', "The following layers were not correctly generated.")
        msg += "<ul>" + "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>"
        msg += QCoreApplication.translate('Postprocessing', "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm.")
        feedback.reportError(msg)

    return len(wrongLayers) == 0
    def virtualFields(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None:
        """ Get virtual fields for features
        In parameters:
            LAYER=wms-layer-name
            VIRTUALS={"key1": "first expression", "key2": "second expression"}
            // optionals
            FILTER=An expression to filter layer
            FIELDS=list of requested field separated by comma
            WITH_GEOMETRY=False
        """
        layer_name = params.get('LAYER', '')
        if not layer_name:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'VirtualFields' REQUEST: LAYER parameter is mandatory",
                400)

        # get layer
        layer = find_vector_layer(layer_name, project)
        # layer not found
        if not layer:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid LAYER parameter for 'VirtualFields': {} provided".format(layer_name),
                400)

        # get virtuals
        virtuals = params.get('VIRTUALS', '')
        if not virtuals:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'VirtualFields' REQUEST: VIRTUALS parameter is mandatory",
                400)

        # try to load virtuals dict
        try:
            vir_json = json.loads(virtuals)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads virtuals '{}' exception:\n{}".format(virtuals, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'VirtualFields' REQUEST: VIRTUALS '{}' are not well formed".format(virtuals),
                400)

        if not isinstance(vir_json, dict):
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'VirtualFields' REQUEST: VIRTUALS '{}' are not well formed".format(virtuals),
                400)

        # create expression context
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope(project))
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        # create distance area context
        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs(), project.transformContext())
        da.setEllipsoid(project.ellipsoid())

        # parse virtuals
        exp_map = {}
        exp_parser_errors = []
        for k, e in vir_json.items():
            exp = QgsExpression(e)
            exp.setGeomCalculator(da)
            exp.setDistanceUnits(project.distanceUnits())
            exp.setAreaUnits(project.areaUnits())

            if exp.hasParserError():
                exp_parser_errors.append('Error "{}": {}'.format(e, exp.parserErrorString()))
                continue

            if not exp.isValid():
                exp_parser_errors.append('Expression not valid "{}"'.format(e))
                continue

            exp.prepare(exp_context)
            exp_map[k] = exp

        # expression parser errors found
        if exp_parser_errors:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid VIRTUALS for 'VirtualFields':\n{}".format('\n'.join(exp_parser_errors)),
                400)

        req = QgsFeatureRequest()

        # get filter
        req_filter = params.get('FILTER', '')
        if req_filter:
            req_exp = QgsExpression(req_filter)
            req_exp.setGeomCalculator(da)
            req_exp.setDistanceUnits(project.distanceUnits())
            req_exp.setAreaUnits(project.areaUnits())

            if req_exp.hasParserError():
                raise ExpressionServiceError(
                    "Bad request error",
                    "Invalid FILTER for 'VirtualFields' Error \"{}\": {}".format(
                        req_filter, req_exp.parserErrorString()),
                    400)

            if not req_exp.isValid():
                raise ExpressionServiceError(
                    "Bad request error",
                    "Invalid FILTER for 'VirtualFields' Expression not valid \"{}\"".format(req_filter),
                    400)

            req_exp.prepare(exp_context)
            req = QgsFeatureRequest(req_exp, exp_context)

        # With geometry
        with_geom = params.get('WITH_GEOMETRY', '').lower() in ['true', '1', 't']
        if not with_geom:
            req.setFlags(QgsFeatureRequest.NoGeometry)

        # Fields
        pk_attributes = layer.primaryKeyAttributes()
        attribute_list = [i for i in pk_attributes]
        fields = layer.fields()
        r_fields = [f.strip() for f in params.get('FIELDS', '').split(',') if f]
        for f in r_fields:
            attribute_list.append(fields.indexOf(f))

        # response
        response.setStatusCode(200)
        response.setHeader("Content-Type", "application/json")
        response.write('{ "type": "FeatureCollection","features":[')
        response.flush()

        json_exporter = QgsJsonExporter(layer)
        if attribute_list:
            json_exporter.setAttributes(attribute_list)

        separator = ''
        for feat in layer.getFeatures(req):
            fid = layer_name + '.' + get_server_fid(feat, pk_attributes)

            extra = {}

            # Update context
            exp_context.setFeature(feat)
            exp_context.setFields(feat.fields())

            # Evaluate expressions for virtual fields
            errors = {}
            for k, exp in exp_map.items():
                value = exp.evaluate(exp_context)
                if exp.hasEvalError():
                    extra[k] = None
                    errors[k] = exp.evalErrorString()
                else:
                    extra[k] = json.loads(QgsJsonUtils.encodeValue(value))
                    errors[k] = exp.expression()

            response.write(separator + json_exporter.exportFeature(feat, extra, fid))
            response.flush()
            separator = ',\n'
        response.write(']}')
        return
class ExportKmzAlgorithm(QgsProcessingAlgorithm):
    """
    Algorithm to import KML and KMZ files.
    """
    PrmInputLayer = 'InputLayer'
    PrmOutputKmz = 'OutputKmz'
    PrmNameField = 'NameField'
    PrmDescriptionField = 'DescriptionField'
    PrmExportStyle = 'ExportStyle'
    PrmUseGoogleIcon = 'UseGoogleIcon'
    PrmLineWidthFactor = 'LineWidthFactor'
    PrmAltitudeInterpretation = 'AltitudeInterpretation'
    PrmAltitudeMode = 'AltitudeMode'
    PrmAltitudeModeField = 'AltitudeModeField'
    PrmAltitudeField = 'AltitudeField'
    PrmAltitudeAddend = 'AltitudeAddend'
    PrmDateTimeStampField = 'DateTimeStampField'
    PrmDateStampField = 'DateStampField'
    PrmTimeStampField = 'TimeStampField'
    PrmDateTimeBeginField = 'DateTimeBeginField'
    PrmDateBeginField = 'DateBeginField'
    PrmTimeBeginField = 'TimeBeginField'
    PrmDateTimeEndField = 'DateTimeEndField'
    PrmDateEndField = 'DateEndField'
    PrmTimeEndField = 'TimeEndField'
    PrmPhotoField = 'PhotoField'
    PrmPhotoDir = 'PhotoDir'
    epsg4326 = QgsCoordinateReferenceSystem("EPSG:4326")
    temp_dir = tempfile.gettempdir()

    def initAlgorithm(self, config):
        self.addParameter(
            QgsProcessingParameterFeatureSource(self.PrmInputLayer,
                                                'Input layer',
                                                [QgsProcessing.TypeVector]))
        self.addParameter(
            QgsProcessingParameterField(
                self.PrmNameField,
                'Name/Label field',
                parentLayerParameterName=self.PrmInputLayer,
                type=QgsProcessingParameterField.Any,
                defaultValue='name',
                optional=True))
        if Qgis.QGIS_VERSION_INT >= 31200:
            self.addParameter(
                QgsProcessingParameterField(
                    self.PrmDescriptionField,
                    'Description fields',
                    parentLayerParameterName=self.PrmInputLayer,
                    type=QgsProcessingParameterField.Any,
                    optional=True,
                    allowMultiple=True,
                    defaultToAllFields=True))
        else:
            self.addParameter(
                QgsProcessingParameterField(
                    self.PrmDescriptionField,
                    'Description fields',
                    parentLayerParameterName=self.PrmInputLayer,
                    type=QgsProcessingParameterField.Any,
                    optional=True,
                    allowMultiple=True))
        self.addParameter(
            QgsProcessingParameterBoolean(
                self.PrmExportStyle,
                'Export style for single, categorized, and graduated symbols',
                True,
                optional=True))
        self.google_icons = list(GOOGLE_ICONS.keys())
        self.addParameter(
            QgsProcessingParameterEnum(
                self.PrmUseGoogleIcon,
                #'Use QGIS point color &amp; size, but use one of these Google icons.',
                'Point Layers: Use the following Google icon but use QGIS icon color and size',
                options=self.google_icons,
                optional=True))
        self.addParameter(
            QgsProcessingParameterEnum(
                self.PrmAltitudeInterpretation,
                'Specify whether to include altitude in the KMZ (must be in meters)',
                options=[
                    'Don\'t use altitude',
                    'Use QGIS geometry Z value if present',
                    'Use altitude from one of the feature\'s attributes'
                ],
                defaultValue=1,
                optional=True))
        self.addParameter(
            QgsProcessingParameterEnum(
                self.PrmAltitudeMode,
                'Default altitude mode when not obtained from the attribute table',
                options=ALTITUDE_MODES,
                defaultValue=0,
                optional=True))
        self.addParameter(
            QgsProcessingParameterField(
                self.PrmAltitudeModeField,
                'Altitude mode field',
                parentLayerParameterName=self.PrmInputLayer,
                type=QgsProcessingParameterField.String,
                defaultValue='alt_mode',
                optional=True))
        self.addParameter(
            QgsProcessingParameterField(
                self.PrmAltitudeField,
                'Altitude field (value must be in meters)',
                parentLayerParameterName=self.PrmInputLayer,
                type=QgsProcessingParameterField.Any,
                defaultValue='altitude',
                optional=True))
        self.addParameter(
            QgsProcessingParameterNumber(
                self.PrmAltitudeAddend,
                'Altitude addend (value must be in meters)',
                type=QgsProcessingParameterNumber.Double,
                defaultValue=0,
                optional=True))
        self.addParameter(
            QgsProcessingParameterField(
                self.PrmDateTimeStampField,
                'Date/Time stamp field (see advanced parameters)',
                parentLayerParameterName=self.PrmInputLayer,
                type=QgsProcessingParameterField.Any,
                defaultValue='time_when',
                optional=True))
        self.addParameter(
            QgsProcessingParameterField(
                self.PrmDateTimeBeginField,
                'Date/Time span begin field (see advanced parameters)',
                parentLayerParameterName=self.PrmInputLayer,
                type=QgsProcessingParameterField.Any,
                defaultValue='time_begin',
                optional=True))
        self.addParameter(
            QgsProcessingParameterField(
                self.PrmDateTimeEndField,
                'Date/Time span end field (see advanced parameters)',
                parentLayerParameterName=self.PrmInputLayer,
                type=QgsProcessingParameterField.Any,
                defaultValue='time_end',
                optional=True))
        self.addParameter(
            QgsProcessingParameterField(
                self.PrmPhotoField,
                'Image path/name field',
                parentLayerParameterName=self.PrmInputLayer,
                type=QgsProcessingParameterField.String,
                optional=True))
        self.addParameter(
            QgsProcessingParameterFileDestination(self.PrmOutputKmz,
                                                  'Output KMZ file',
                                                  fileFilter='*.kmz'))
        # Set up Advanced Parameters
        param = QgsProcessingParameterNumber(
            self.PrmLineWidthFactor,
            'Line width multiplication factor (widths appear smaller in Google Earth)',
            QgsProcessingParameterNumber.Double,
            defaultValue=2,
            minValue=0,
            optional=True)
        param.setFlags(param.flags()
                       | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(param)
        param = QgsProcessingParameterField(
            self.PrmDateStampField,
            'Date stamp field',
            parentLayerParameterName=self.PrmInputLayer,
            type=QgsProcessingParameterField.Any,
            optional=True)
        param.setFlags(param.flags()
                       | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(param)
        param = QgsProcessingParameterField(
            self.PrmTimeStampField,
            'Time stamp field',
            parentLayerParameterName=self.PrmInputLayer,
            type=QgsProcessingParameterField.Any,
            optional=True)
        param.setFlags(param.flags()
                       | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(param)
        param = QgsProcessingParameterField(
            self.PrmDateBeginField,
            'Date span begin field',
            parentLayerParameterName=self.PrmInputLayer,
            type=QgsProcessingParameterField.Any,
            optional=True)
        param.setFlags(param.flags()
                       | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(param)
        param = QgsProcessingParameterField(
            self.PrmTimeBeginField,
            'Time span begin field',
            parentLayerParameterName=self.PrmInputLayer,
            type=QgsProcessingParameterField.Any,
            optional=True)
        param.setFlags(param.flags()
                       | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(param)
        param = QgsProcessingParameterField(
            self.PrmDateEndField,
            'Date span end field',
            parentLayerParameterName=self.PrmInputLayer,
            type=QgsProcessingParameterField.Any,
            optional=True)
        param.setFlags(param.flags()
                       | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(param)
        param = QgsProcessingParameterField(
            self.PrmTimeEndField,
            'Time span end field',
            parentLayerParameterName=self.PrmInputLayer,
            type=QgsProcessingParameterField.Any,
            optional=True)
        param.setFlags(param.flags()
                       | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(param)

    def processAlgorithm(self, parameters, context, feedback):
        self.parameters = parameters
        self.context = context
        self.feedback = feedback
        filename = self.parameterAsFileOutput(parameters, self.PrmOutputKmz,
                                              context)
        source = self.parameterAsSource(parameters, self.PrmInputLayer,
                                        context)

        # Before we go further check to make sure we have a valid vector layer
        wkbtype = source.wkbType()
        geomtype = QgsWkbTypes.geometryType(wkbtype)
        if geomtype == QgsWkbTypes.UnknownGeometry or geomtype == QgsWkbTypes.NullGeometry:
            raise QgsProcessingException(
                'Algorithm input is not a valid point, line, or polygon layer.'
            )
        layer = self.parameterAsLayer(parameters, self.PrmInputLayer, context)
        if self.PrmNameField not in parameters or parameters[
                self.PrmNameField] is None:
            name_field = None
        else:
            name_field = self.parameterAsString(parameters, self.PrmNameField,
                                                context)
        desc_fields = self.parameterAsFields(parameters,
                                             self.PrmDescriptionField, context)
        desc_cnt = len(desc_fields)
        export_style = self.parameterAsInt(parameters, self.PrmExportStyle,
                                           context)
        if self.PrmUseGoogleIcon not in parameters or parameters[
                self.PrmUseGoogleIcon] is None:
            google_icon = None
        else:
            google_icon = self.parameterAsEnum(parameters,
                                               self.PrmUseGoogleIcon, context)
        self.line_width_factor = self.parameterAsDouble(
            parameters, self.PrmLineWidthFactor, context)
        alt_interpret = self.parameterAsEnum(parameters,
                                             self.PrmAltitudeInterpretation,
                                             context)
        if self.PrmAltitudeMode not in parameters or parameters[
                self.PrmAltitudeMode] is None:
            default_alt_mode = None
        else:
            default_alt_mode = ALTITUDE_MODES[self.parameterAsEnum(
                parameters, self.PrmAltitudeMode, context)]
        alt_mode_field = self.parameterAsString(parameters,
                                                self.PrmAltitudeModeField,
                                                context)
        altitude_field = self.parameterAsString(parameters,
                                                self.PrmAltitudeField, context)
        altitude_addend = self.parameterAsDouble(parameters,
                                                 self.PrmAltitudeAddend,
                                                 context)
        date_time_stamp_field = self.parameterAsString(
            parameters, self.PrmDateTimeStampField, context)
        date_stamp_field = self.parameterAsString(parameters,
                                                  self.PrmDateStampField,
                                                  context)
        time_stamp_field = self.parameterAsString(parameters,
                                                  self.PrmTimeStampField,
                                                  context)
        date_time_begin_field = self.parameterAsString(
            parameters, self.PrmDateTimeBeginField, context)
        date_begin_field = self.parameterAsString(parameters,
                                                  self.PrmDateBeginField,
                                                  context)
        time_begin_field = self.parameterAsString(parameters,
                                                  self.PrmTimeBeginField,
                                                  context)
        date_time_end_field = self.parameterAsString(parameters,
                                                     self.PrmDateTimeEndField,
                                                     context)
        date_end_field = self.parameterAsString(parameters,
                                                self.PrmDateEndField, context)
        time_end_field = self.parameterAsString(parameters,
                                                self.PrmTimeEndField, context)
        if self.PrmPhotoField not in parameters or parameters[
                self.PrmPhotoField] is None:
            photo_path_field = None
        else:
            photo_path_field = self.parameterAsString(parameters,
                                                      self.PrmPhotoField,
                                                      context)
        self.photos = {}

        hasz = QgsWkbTypes.hasZ(wkbtype)
        if alt_interpret == 0:
            hasz = False
            default_alt_mode = None
            alt_mode_field = None
            altitude_field = None
        elif alt_interpret == 2:
            hasz = False
        src_crs = source.sourceCrs()
        if src_crs != self.epsg4326:
            geomTo4326 = QgsCoordinateTransform(src_crs, self.epsg4326,
                                                QgsProject.instance())

        self.symcontext = QgsRenderContext.fromMapSettings(
            settings.canvas.mapSettings())
        self.png_icons = []
        self.cat_styles = {}
        kml = simplekml.Kml()
        kml.resetidcounter()
        if layer:
            try:
                self.render = layer.renderer()
                self.exp_context = QgsExpressionContext()
                self.exp_context.appendScopes(
                    QgsExpressionContextUtils.globalProjectLayerScopes(layer))
            except Exception:
                if export_style:
                    export_style = 0
                    feedback.reportError(
                        'Layer style cannot be determined. Processing will continue without symbol style export.'
                    )
        else:
            if export_style:
                feedback.reportError(
                    'There appears to be a valid source, but not a valid layer style. Processing will continue without symbol style export.'
                )
                export_style = 0
        if export_style:
            render_type = self.render.type()
            if render_type == 'singleSymbol':
                export_style = 1
            elif render_type == 'categorizedSymbol':
                style_field = self.render.classAttribute()
                self.field_exp = QgsExpression(style_field)
                export_style = 2
            elif render_type == 'graduatedSymbol':
                style_field = self.render.classAttribute()
                self.field_exp = QgsExpression(style_field)
                export_style = 3
            else:
                feedback.reportError(
                    'Only single, categorized, and graduated symbol styles can be exported. Processing will continue without symbol style export.'
                )
                export_style = 0
            if export_style:
                self.initStyles(export_style, google_icon, name_field,
                                geomtype, kml)

        folder = kml.newfolder(name=source.sourceName())
        altitude = 0
        featureCount = source.featureCount()
        total = 100.0 / featureCount if featureCount else 0
        num_features = 0
        iterator = source.getFeatures()
        for cnt, feature in enumerate(iterator):
            if feedback.isCanceled():
                break
            num_features += 1
            if altitude_field:
                try:
                    altitude = float(feature[altitude_field])
                except Exception:
                    altitude = 0
            geom = feature.geometry()
            if src_crs != self.epsg4326:
                geom.transform(geomTo4326)
            if geom.isMultipart() or (name_field and geomtype
                                      == QgsWkbTypes.PolygonGeometry):
                kmlgeom = folder.newmultigeometry()
                kml_item = kmlgeom
            else:
                kmlgeom = folder
                kml_item = None
            if geomtype == QgsWkbTypes.PointGeometry:  # POINTS
                for pt in geom.parts():
                    kmlpart = kmlgeom.newpoint()
                    self.setAltitudeMode(kmlpart, feature, default_alt_mode,
                                         alt_mode_field)
                    if kml_item is None:
                        kml_item = kmlpart
                    if hasz:
                        kmlpart.coords = [(pt.x(), pt.y(),
                                           pt.z() + altitude_addend)]
                    else:
                        kmlpart.coords = [(pt.x(), pt.y(),
                                           altitude + altitude_addend)]
            elif geomtype == QgsWkbTypes.LineGeometry:  # LINES
                for part in geom.parts():
                    kmlpart = kmlgeom.newlinestring()
                    self.setAltitudeMode(kmlpart, feature, default_alt_mode,
                                         alt_mode_field)
                    if kml_item is None:
                        kml_item = kmlpart
                    if hasz:
                        kmlpart.coords = [(pt.x(), pt.y(),
                                           pt.z() + altitude_addend)
                                          for pt in part]
                    else:
                        kmlpart.coords = [(pt.x(), pt.y(),
                                           altitude + altitude_addend)
                                          for pt in part]
            elif geomtype == QgsWkbTypes.PolygonGeometry:  # POLYGONS
                if name_field:
                    centroid = geom.centroid().asPoint()
                    name = '{}'.format(feature[name_field])
                    labelpart = kmlgeom.newpoint(coords=[(centroid.x(),
                                                          centroid.y())],
                                                 name=name)

                for part in geom.parts():
                    kmlpart = kmlgeom.newpolygon()
                    self.setAltitudeMode(kmlpart, feature, default_alt_mode,
                                         alt_mode_field)
                    if kml_item is None:
                        kml_item = kmlpart
                    num_interior_rings = part.numInteriorRings()
                    ext_ring = part.exteriorRing()
                    if hasz:
                        kmlpart.outerboundaryis = [(pt.x(), pt.y(),
                                                    pt.z() + altitude_addend)
                                                   for pt in ext_ring]
                    else:
                        kmlpart.outerboundaryis = [(pt.x(), pt.y(),
                                                    altitude + altitude_addend)
                                                   for pt in ext_ring]
                    if num_interior_rings:
                        ib = []
                        for i in range(num_interior_rings):
                            if hasz:
                                ib.append([(pt.x(), pt.y(),
                                            pt.z() + altitude_addend)
                                           for pt in part.interiorRing(i)])
                            else:
                                ib.append([(pt.x(), pt.y(),
                                            altitude + altitude_addend)
                                           for pt in part.interiorRing(i)])
                        kmlpart.innerboundaryis = ib

            self.exportStyle(kml_item, feature, export_style, geomtype)
            if name_field:
                self.exportName(kml_item, feature[name_field])

            if photo_path_field:
                photo_path = feature[photo_path_field].strip()
                if os.path.exists(photo_path):
                    if not (photo_path in self.photos):
                        local_path = kml.addfile(photo_path)
                        self.photos[photo_path] = local_path
                else:
                    photo_path = None
            else:
                photo_path = None

            if desc_cnt == 1:
                self.exportDescription(kml_item, feature[desc_fields[0]],
                                       photo_path)
            elif desc_cnt > 1:
                self.exportFields(kml_item, desc_fields, feature, photo_path)

            # Process the first date / time fields
            date_time_str = self.parseDateTimeValues(feature,
                                                     date_time_stamp_field,
                                                     date_stamp_field,
                                                     time_stamp_field)
            if date_time_str:
                kml_item.timestamp.when = date_time_str
            date_time_str = self.parseDateTimeValues(feature,
                                                     date_time_begin_field,
                                                     date_begin_field,
                                                     time_begin_field)
            if date_time_str:
                kml_item.timespan.begin = date_time_str
            date_time_str = self.parseDateTimeValues(feature,
                                                     date_time_end_field,
                                                     date_end_field,
                                                     time_end_field)
            if date_time_str:
                kml_item.timespan.end = date_time_str

            if cnt % 100 == 0:
                feedback.setProgress(int(cnt * total))
        if num_features == 0:
            feedback.pushInfo('No features processed')
        else:
            kml.savekmz(filename)

        self.cleanup()

        return ({})

    def exportStyle(self, kml_item, feature, export_style, geomtype):
        # self.feedback.pushInfo('exportStyle')
        if export_style == 1:
            kml_item.style = self.simple_style
        elif export_style == 2:
            # Determine the category expression value
            self.exp_context.setFeature(feature)
            try:
                value = self.field_exp.evaluate(self.exp_context)
                # Which category does feature value fall in
                catindex = self.render.categoryIndexForValue(value)
            except Exception:
                return
            # If it is outside the category ranges assign it to the 0 index
            if catindex not in self.cat_styles:
                catindex = 0
            if catindex in self.cat_styles:
                kml_item.style = self.cat_styles[catindex]
        elif export_style == 3:
            # Determine the gradient expression value
            self.exp_context.setFeature(feature)
            try:
                value = self.field_exp.evaluate(self.exp_context)
                # Which range of the gradient does this value fall in
                range = self.render.rangeForValue(value)
                if range is None:
                    minimum = 1e16
                    maximum = -1e16
                    for ran in self.render.ranges():
                        if ran.lowerValue() < minimum:
                            minimum = ran.lowerValue()
                        if ran.upperValue() > maximum:
                            maximum = ran.upperValue()
                    if value > maximum:
                        value = maximum
                    if value < minimum:
                        value = minimum
                    range = self.render.rangeForValue(value)
                    if range is None:
                        self.feedback.pushInfo(
                            'An error occured in defining the range object')
                        return
            except Exception:
                '''s = traceback.format_exc()
                self.feedback.pushInfo(s)'''
                return
            # Get the symbol related to the specified gradient range
            # For lines and polygons we would use the color and line sizes
            symbol = range.symbol()
            opacity = symbol.opacity()
            if geomtype == QgsWkbTypes.PointGeometry:
                sym_size = symbol.size(self.symcontext)
                color = qcolor2kmlcolor(symbol.color())
            elif geomtype == QgsWkbTypes.LineGeometry:
                sym_size = symbol.width()
                if sym_size == 0:
                    sym_size = 0.5
                color = qcolor2kmlcolor(symbol.color())
                key = (sym_size, color)
            else:
                symbol_layer = symbol.symbolLayer(0)
                stroke_style = symbol_layer.strokeStyle()
                if stroke_style == 0:
                    sym_size = 0
                else:
                    sym_size = symbol_layer.strokeWidth()
                color = qcolor2kmlcolor(symbol_layer.color(), opacity)
            key = (sym_size, color)
            if key in self.cat_styles:
                # self.feedback.pushInfo('  catindex in cat_styles')
                kml_item.style = self.cat_styles[key]
                # self.feedback.pushInfo('  style {}'.format(kml_item.style))

    def initStyles(self, symtype, google_icon, name_field, geomtype, kml):
        # self.feedback.pushInfo('initStyles type: {}'.format(symtype))
        if symtype == 1:  # Single Symbol
            symbol = self.render.symbol()
            opacity = symbol.opacity()
            self.simple_style = simplekml.Style()
            if geomtype == QgsWkbTypes.PointGeometry:
                sym_size = symbol.size(self.symcontext)
                if google_icon is None:
                    bounds = symbol.bounds(QPointF(0, 0), self.symcontext)
                    size = bounds.width()
                    if bounds.height() > size:
                        size = bounds.height()
                    size = math.ceil(size * 1.1)
                    path = os.path.join(self.temp_dir, 'icon.png')
                    self.png_icons.append(path)
                    symbol.exportImage(path, "png", QSize(size, size))
                    kml.addfile(path)
                    self.simple_style.iconstyle.scale = sym_size / 15
                    self.simple_style.iconstyle.icon.href = 'files/icon.png'
                else:
                    self.simple_style.iconstyle.scale = sym_size / 10
                    self.simple_style.iconstyle.icon.href = GOOGLE_ICONS[
                        self.google_icons[google_icon]]
                    self.simple_style.iconstyle.color = qcolor2kmlcolor(
                        symbol.color())
            elif geomtype == QgsWkbTypes.LineGeometry:
                symbol_width = symbol.width()
                if symbol_width == 0:
                    symbol_width = 0.5
                self.simple_style.linestyle.color = qcolor2kmlcolor(
                    symbol.color(), opacity)
                self.simple_style.linestyle.width = symbol_width * self.line_width_factor
                if name_field:
                    self.simple_style.linestyle.gxlabelvisibility = True
            else:
                symbol_layer = symbol.symbolLayer(0)
                stroke_style = symbol_layer.strokeStyle()
                if stroke_style == 0:
                    stroke_width = 0
                else:
                    stroke_width = symbol_layer.strokeWidth()
                self.simple_style.linestyle.color = qcolor2kmlcolor(
                    symbol_layer.strokeColor(), opacity)
                self.simple_style.linestyle.width = stroke_width * self.line_width_factor
                self.simple_style.polystyle.color = qcolor2kmlcolor(
                    symbol_layer.color(), opacity)
                if name_field:
                    self.simple_style.iconstyle.scale = 0
        elif symtype == 2:  # Categorized Symbols
            for idx, category in enumerate(self.render.categories()):
                cat_style = simplekml.Style()
                symbol = category.symbol()
                opacity = symbol.opacity()
                # self.feedback.pushInfo(' categories idx: {}'.format(idx))
                if geomtype == QgsWkbTypes.PointGeometry:
                    # self.feedback.pushInfo('  PointGeometry')
                    sym_size = symbol.size(self.symcontext)
                    # self.feedback.pushInfo('sym_size: {}'.format(sym_size))
                    if google_icon is None:
                        bounds = symbol.bounds(QPointF(0, 0), self.symcontext)
                        size = bounds.width()
                        if bounds.height() > size:
                            size = bounds.height()
                        size = math.ceil(size * 1.1)
                        name = 'icon{}.png'.format(idx)
                        path = os.path.join(self.temp_dir, name)
                        self.png_icons.append(path)
                        symbol.exportImage(path, "png", QSize(size, size))
                        kml.addfile(path)
                        cat_style.iconstyle.scale = sym_size / 15
                        cat_style.iconstyle.icon.href = 'files/' + name
                    else:
                        cat_style.iconstyle.scale = sym_size / 10
                        cat_style.iconstyle.icon.href = GOOGLE_ICONS[
                            self.google_icons[google_icon]]
                        cat_style.iconstyle.color = qcolor2kmlcolor(
                            symbol.color())
                elif geomtype == QgsWkbTypes.LineGeometry:
                    # self.feedback.pushInfo('  LineGeometry')
                    symbol_width = symbol.width()
                    if symbol_width == 0:
                        symbol_width = 0.5
                    cat_style.linestyle.color = qcolor2kmlcolor(
                        symbol.color(), opacity)
                    cat_style.linestyle.width = symbol_width * self.line_width_factor
                    if name_field:
                        cat_style.linestyle.gxlabelvisibility = True
                else:
                    # self.feedback.pushInfo('  PolygonGeometry')
                    symbol_layer = symbol.symbolLayer(0)
                    stroke_style = symbol_layer.strokeStyle()
                    if stroke_style == 0:
                        stroke_width = 0
                    else:
                        stroke_width = symbol_layer.strokeWidth()
                    cat_style.linestyle.color = qcolor2kmlcolor(
                        symbol_layer.strokeColor(), opacity)
                    cat_style.linestyle.width = stroke_width * self.line_width_factor
                    cat_style.polystyle.color = qcolor2kmlcolor(
                        symbol_layer.color(), opacity)
                    if name_field:
                        cat_style.iconstyle.scale = 0
                self.cat_styles[idx] = cat_style
        else:  # Graduated Symbols
            for idx, range in enumerate(self.render.ranges()):
                cat_style = simplekml.Style()
                symbol = range.symbol()
                opacity = symbol.opacity()
                # self.feedback.pushInfo(' categories idx: {}'.format(idx))
                if geomtype == QgsWkbTypes.PointGeometry:
                    # self.feedback.pushInfo('  PointGeometry')
                    sym_size = symbol.size(self.symcontext)
                    color = qcolor2kmlcolor(symbol.color(), opacity)
                    # self.feedback.pushInfo('sym_size: {}'.format(sym_size))
                    if google_icon is None:
                        bounds = symbol.bounds(QPointF(0, 0), self.symcontext)
                        size = bounds.width()
                        if bounds.height() > size:
                            size = bounds.height()
                        size = math.ceil(size * 1.1)
                        name = 'icon{}.png'.format(idx)
                        path = os.path.join(self.temp_dir, name)
                        self.png_icons.append(path)
                        symbol.exportImage(path, "png", QSize(size, size))
                        kml.addfile(path)
                        cat_style.iconstyle.scale = sym_size / 15
                        cat_style.iconstyle.icon.href = 'files/' + name
                    else:
                        cat_style.iconstyle.scale = sym_size / 10
                        cat_style.iconstyle.icon.href = GOOGLE_ICONS[
                            self.google_icons[google_icon]]
                        cat_style.iconstyle.color = color
                elif geomtype == QgsWkbTypes.LineGeometry:
                    # self.feedback.pushInfo('  LineGeometry')
                    color = qcolor2kmlcolor(symbol.color(), opacity)
                    cat_style.linestyle.color = color
                    symbol_width = symbol.width()
                    if symbol_width == 0:
                        symbol_width = 0.5
                    cat_style.linestyle.width = symbol_width * self.line_width_factor
                    if name_field:
                        cat_style.linestyle.gxlabelvisibility = True
                else:
                    # self.feedback.pushInfo('  PolygonGeometry')
                    symbol_layer = symbol.symbolLayer(0)
                    stroke_style = symbol_layer.strokeStyle()
                    if stroke_style == 0:
                        stroke_width = 0
                    else:
                        stroke_width = symbol_layer.strokeWidth()
                    color = qcolor2kmlcolor(symbol_layer.color(), opacity)
                    cat_style.linestyle.color = qcolor2kmlcolor(
                        symbol_layer.strokeColor(), opacity)
                    cat_style.linestyle.width = stroke_width * self.line_width_factor
                    cat_style.polystyle.color = color
                    if name_field:
                        cat_style.iconstyle.scale = 0
                self.cat_styles[(stroke_width, color)] = cat_style

    def cleanup(self):
        for icon in self.png_icons:
            if os.path.exists(icon):
                os.remove(icon)

    def get_attribute_str(self, attr):
        if not attr:
            return (attr)
        if isinstance(attr, QDateTime):
            attr = attr.toString(Qt.ISODate)
        elif isinstance(attr, QDate):
            attr = attr.toString(Qt.ISODate)
        elif isinstance(attr, QTime):
            attr = attr.toString(Qt.ISODate)
        attr = escape('{}'.format(attr).strip())
        return (attr)

    def exportName(self, kml_item, fname):
        name = self.get_attribute_str(fname)
        name = name.strip()
        kml_item.name = name

    def exportDescription(self, kml_item, desc, photo_path):
        desc = self.get_attribute_str(desc)
        if photo_path:
            desc = '<img src="{}" style="max-width:300"/><br/><br/>{}'.format(
                self.photos[photo_path], desc)
        else:
            desc = '{}'.format(desc).strip()
        if desc:
            kml_item.description = desc

    def exportFields(self, kml_item, fields, f, photo_path):
        strs = ['<![CDATA[']
        if photo_path:
            strs.append(
                '<img src="{}" style="max-width:300"/><br/><br/>'.format(
                    self.photos[photo_path]))
        strs.append('<table>')
        for row, field in enumerate(fields):
            v = self.get_attribute_str(f[field])
            kml_item.extendeddata.newdata(name=field,
                                          value=v,
                                          displayname=field)
            if row & 1:
                strs.append('<tr><td>{}</td><td>$[{}]</td></tr>'.format(
                    field, field))
            else:
                strs.append(
                    '<tr style="background-color:#DDDDFF"><td>{}</td><td>$[{}]</td></tr>'
                    .format(field, field))
        strs.append('</table>\n]]>')
        str = '\n'.join(strs)
        kml_item.description = str

    def setAltitudeMode(self, kml_item, f, alt_mode, mode_field):
        try:
            mode = None
            if mode_field:
                mode = f[mode_field]
            if mode not in ALTITUDE_MODES and alt_mode:
                kml_item.altitudemode = alt_mode
                return
            if mode in ALTITUDE_MODES:
                kml_item.altitudemode = mode
        except Exception:
            return

    def parseDateTimeValues(self, feature, dt_field, date_field, time_field):
        if dt_field is None and date_field is None:
            return (None)
        try:
            dt = None
            date = None
            time = None
            if dt_field:
                dt = feature[dt_field]
            else:
                if date_field:
                    date = feature[date_field]
                if time_field:
                    time = feature[time_field]
            if dt:
                if isinstance(dt, QDateTime):
                    year = dt.date().year()
                    month = dt.date().month()
                    day = dt.date().day()
                    hour = dt.time().hour()
                    minute = dt.time().minute()
                    second = dt.time().second()
                    msec = dt.time().msec()
                    if msec == 0:
                        str = '{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}'.format(
                            year, month, day, hour, minute, second)
                    else:
                        str = '{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}.{:03d}'.format(
                            year, month, day, hour, minute, second, msec)
                    return (str)
                elif isinstance(dt, QDate):
                    year = dt.year()
                    month = dt.month()
                    day = dt.day()
                    str = '{:04d}-{:02d}-{:02d}'.format(year, month, day)
                    return (str)
                elif isinstance(dt, float) or isinstance(dt, int):
                    str = self.prepareEpochTimeString(dt)
                    return (str)
                else:
                    s = '{}'.format(dt).strip()
                    if not s:
                        return (None)
                    try:  # Check for EPOCH Time
                        str = self.prepareEpochTimeString(float(s))
                        return (str)
                    except ValueError:
                        pass
                    d1 = dateutil.parser.parse(s,
                                               default=datetime.datetime(
                                                   datetime.MINYEAR,
                                                   1,
                                                   1,
                                                   hour=0,
                                                   minute=0,
                                                   second=0,
                                                   microsecond=0,
                                                   tzinfo=None))
                    d2 = dateutil.parser.parse(s,
                                               default=datetime.datetime(
                                                   datetime.MINYEAR,
                                                   2,
                                                   2,
                                                   hour=1,
                                                   minute=1,
                                                   second=1,
                                                   microsecond=1,
                                                   tzinfo=None))
                    str = self.prepareDateString(d1, d2)
                    return (str)
            else:
                # First format the date portion of the string
                if not date:
                    return (None)
                # If we have a date string that only has partial values items
                # will be stored here. We will use it at the end if there were not
                # time values.
                date_str_partial = None
                if isinstance(date, QDateTime):
                    year = date.date().year()
                    month = date.date().month()
                    day = date.date().day()
                    date_str = '{:04d}-{:02d}-{:02d}'.format(year, month, day)
                elif isinstance(date, QDate):
                    year = date.year()
                    month = date.month()
                    day = date.day()
                    date_str = '{:04d}-{:02d}-{:02d}'.format(year, month, day)
                else:
                    s = '{}'.format(date).strip()
                    if not s:
                        return (None)
                    d1 = dateutil.parser.parse(s,
                                               default=datetime.datetime(
                                                   datetime.MINYEAR, 1, 1))
                    if not time:
                        d2 = dateutil.parser.parse(s,
                                                   default=datetime.datetime(
                                                       datetime.MINYEAR, 2, 2))
                        date_str_partial = '{:04d}'.format(d1.year)
                        if d1.month == d2.month:
                            date_str_partial = date_str_partial + '-{:02d}'.format(
                                d1.month)
                        if d1.day == d2.day:
                            date_str_partial = date_str_partial + '-{:02d}'.format(
                                d1.day)
                    date_str = '{:04d}-{:02d}-{:02d}'.format(
                        d1.year, d1.month, d1.day)

                # Format the time portion string
                time_str = None
                if time:
                    if isinstance(time, QDateTime):
                        hour = time.time().hour()
                        minute = time.time().minute()
                        second = time.time().second()
                        msec = time.time().msec()
                        if msec == 0:
                            time_str = '{:02d}:{:02d}:{:02d}'.format(
                                hour, minute, second)
                        else:
                            time_str = '{:02d}:{:02d}:{:02d}.{:03d}'.format(
                                hour, minute, second, msec)
                    elif isinstance(time, QTime):
                        hour = time.hour()
                        minute = time.minute()
                        second = time.second()
                        msec = time.msec()
                        if msec == 0:
                            time_str = '{:02d}:{:02d}:{:02d}'.format(
                                hour, minute, second)
                        else:
                            time_str = '{:02d}:{:02d}:{:02d}.{:03d}'.format(
                                hour, minute, second, msec)
                    else:
                        s = '{}'.format(time).strip()
                        if not s:
                            return (None)
                        d = dateutil.parser.parse(s,
                                                  default=datetime.datetime(
                                                      datetime.MINYEAR,
                                                      1,
                                                      1,
                                                      hour=0,
                                                      minute=0,
                                                      second=0,
                                                      microsecond=0,
                                                      tzinfo=None))
                        time_str = '{:02d}:{:02d}:{:02d}'.format(
                            d.hour, d.minute, d.second)
                if time_str:
                    return (date_str + 'T' + time_str)
                else:
                    if date_str_partial:
                        return (date_str_partial)
                    return (date_str)
        except Exception:
            '''s = traceback.format_exc()
            self.feedback.pushInfo(s)'''
            return (None)

    def prepareEpochTimeString(self, dt):
        edt = datetime.datetime.fromtimestamp(dt)
        year = edt.year
        month = edt.month
        day = edt.day
        hour = edt.hour
        minute = edt.minute
        second = edt.second
        microsec = edt.microsecond
        if microsec == 0:
            str = '{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}'.format(
                year, month, day, hour, minute, second)
        else:
            str = '{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}.{:03d}'.format(
                year, month, day, hour, minute, second, int(microsec / 1000))
        return (str)

    def prepareDateString(self, d1, d2):
        # if only parts of the date are valid then just return those portions
        # otherwise return a fully formatted iso string.
        # self.feedback.pushInfo('{} {} {} {} {} {}'.format(d1.year, d1.month, d1.day, d2.year, d2.month, d2.day))
        if d1.year == datetime.MINYEAR:
            return (None)
        str = '{:04d}'.format(d1.year)
        if d1.month != d2.month:
            return (str)
        str = str + '-{:02d}'.format(d1.month)
        if d1.day != d2.day:
            return (str)
        str = str + '-{:02d}'.format(d1.day)
        if d1.hour != d2.hour:
            return (str)
        # We have a valid date and time string so just return the iso formated string
        str = d1.isoformat()
        return (str)

    def name(self):
        return 'exportkmz'

    def icon(self):
        return QIcon(os.path.dirname(__file__) + '/icons/export.svg')

    def displayName(self):
        return 'Export KMZ'

    def group(self):
        return 'Vector conversion'

    def groupId(self):
        return 'vectorconversion'

    def helpUrl(self):
        file = os.path.dirname(__file__) + '/index.html'
        if not os.path.exists(file):
            return ''
        return QUrl.fromLocalFile(file).toString(QUrl.FullyEncoded)

    def createInstance(self):
        return ExportKmzAlgorithm()
 def createExpressionContext(self) -> QgsExpressionContext:  # pylint: disable=missing-docstring, no-self-use
     context = QgsExpressionContext()
     scope = QgsExpressionContextScope()
     context.appendScope(scope)
     context.appendScope(vl1.createExpressionContextScope())
     return context
def addRowInLayer(row, errTable, table_codif):
    """
    Rows will be converted in a geometry thanks to codification.
    All attributes will be added thanks to QgsExpressionContext.

    Parameters
    ----------
    row: list of list
        A row contains one or many list of points.
    errTable: list of list
        Contains points in error.
        Some points can be added after the end of this function.
    table_codif: dictionnary
        The codification file. See the information about qlsc format.
    """

    #  TODO: assert?
    code = row[CODE_POSITION][0]
    parameters = row[PARAM_POSITION]

    codif = table_codif['Codification'][code]
    layerName = codif['Layer']

    layer = QgsVectorLayer(layerName)

    dim = 4 if QgsWkbTypes.hasZ(layer.dataProvider().wkbType()) else 3
    geom = geomFromType(list(zip(*row[1:dim])), parameters,
                        codif['GeometryType'], layer.geometryType())

    if geom:
        layer.startEditing()

        fields = layer.fields()
        newFeature = QgsFeature(fields)

        newFeature.setGeometry(geom)

        for e in codif['Attributes']:
            # print(e, e[1], e[1].startswith('_att'))
            if e[1].startswith('_att'):
                # len('_att') == 4
                try:
                    nb = int(e[1][4:]) - 1
                    assert(nb >= 0)
                    val = row[ATTRS_POSITION + nb][0]
                    newFeature[e[0]] = val
                except:
                    # print("attributes error")
                    pass
            else:
                context = QgsExpressionContext()
                scope = QgsExpressionContextScope()
                try:
                    exp = QgsExpression(e[1])
                    scope.setFeature(newFeature)
                    context.appendScope(scope)
                    newFeature[e[0]] = exp.evaluate(context)
                except:
                    # print('key error')
                    pass

        ret = layer.addFeature(newFeature)
        # if not ret:
         #   print(ret)

        layer.commitChanges()
        layer.updateExtents()
    else:
        # can it happen?
        errTable.append(row)
Exemple #47
0
    def testRemappingSink(self):
        """
        Test remapping features
        """
        fields = QgsFields()
        fields.append(QgsField('fldtxt', QVariant.String))
        fields.append(QgsField('fldint', QVariant.Int))
        fields.append(QgsField('fldtxt2', QVariant.String))

        store = QgsFeatureStore(fields,
                                QgsCoordinateReferenceSystem('EPSG:3857'))

        mapping_def = QgsRemappingSinkDefinition()
        mapping_def.setDestinationWkbType(QgsWkbTypes.Point)
        mapping_def.setSourceCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
        mapping_def.setDestinationCrs(
            QgsCoordinateReferenceSystem('EPSG:3857'))
        mapping_def.setDestinationFields(fields)
        mapping_def.addMappedField('fldtxt2', QgsProperty.fromField('fld1'))
        mapping_def.addMappedField(
            'fldint', QgsProperty.fromExpression('@myval * fldint'))

        proxy = QgsRemappingProxyFeatureSink(mapping_def, store)
        self.assertEqual(proxy.destinationSink(), store)

        self.assertEqual(len(store), 0)

        incoming_fields = QgsFields()
        incoming_fields.append(QgsField('fld1', QVariant.String))
        incoming_fields.append(QgsField('fldint', QVariant.Int))

        context = QgsExpressionContext()
        scope = QgsExpressionContextScope()
        scope.setVariable('myval', 2)
        context.appendScope(scope)
        context.setFields(incoming_fields)
        proxy.setExpressionContext(context)
        proxy.setTransformContext(QgsProject.instance().transformContext())

        f = QgsFeature()
        f.setFields(incoming_fields)
        f.setAttributes(["test", 123])
        f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(1, 2)))
        self.assertTrue(proxy.addFeature(f))
        self.assertEqual(len(store), 1)
        self.assertEqual(store.features()[0].geometry().asWkt(1),
                         'Point (111319.5 222684.2)')
        self.assertEqual(store.features()[0].attributes(), [None, 246, 'test'])

        f2 = QgsFeature()
        f2.setAttributes(["test2", 457])
        f2.setGeometry(QgsGeometry.fromWkt('LineString( 1 1, 2 2)'))
        f3 = QgsFeature()
        f3.setAttributes(["test3", 888])
        f3.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(3, 4)))
        self.assertTrue(proxy.addFeatures([f2, f3]))
        self.assertEqual(len(store), 4)
        self.assertEqual(store.features()[1].attributes(),
                         [None, 914, 'test2'])
        self.assertEqual(store.features()[2].attributes(),
                         [None, 914, 'test2'])
        self.assertEqual(store.features()[3].attributes(),
                         [None, 1776, 'test3'])
        self.assertEqual(store.features()[1].geometry().asWkt(1),
                         'Point (111319.5 111325.1)')
        self.assertEqual(store.features()[2].geometry().asWkt(1),
                         'Point (222639 222684.2)')
        self.assertEqual(store.features()[3].geometry().asWkt(1),
                         'Point (333958.5 445640.1)')
Exemple #48
0
def print_atlas(project, layout_name, feature_filter, scales=None, scale=None):
    """Generate an atlas.

    :param project: The project to render as atlas.
    :type project: QgsProject

    :param layout_name: Name of the layout.
    :type layout_name: basestring

    :param feature_filter: QGIS Expression to use to select the feature.
    It can return many features, a multiple pages PDF will be returned.
    :type feature_filter: basestring

    :param scale: A scale to force in the atlas context. Default to None.
    :type scale: int

    :param scales: A list of predefined list of scales to force in the atlas context.
    Default to None.
    :type scales: list

    :return: Path to the PDF.
    :rtype: basestring
    """
    canvas = QgsMapCanvas()
    bridge = QgsLayerTreeMapCanvasBridge(project.layerTreeRoot(), canvas)
    bridge.setCanvasLayers()
    manager = project.layoutManager()
    master_layout = manager.layoutByName(layout_name)

    if not master_layout:
        raise AtlasPrintException('Layout not found')

    if master_layout.layoutType() != QgsMasterLayoutInterface.PrintLayout:
        raise AtlasPrintException('The layout is not a print layout')

    for l in manager.printLayouts():
        if l.name() == layout_name:
            layout = l
            break
    else:
        raise AtlasPrintException('The layout is not found')

    atlas = layout.atlas()

    if not atlas.enabled():
        raise AtlasPrintException('The layout is not enabled for an atlas')

    settings = QgsLayoutExporter.PdfExportSettings()

    if scale:
        layout.referenceMap().setAtlasScalingMode(QgsLayoutItemMap.Fixed)
        layout.referenceMap().setScale(scale)

    if scales:
        layout.referenceMap().setAtlasScalingMode(QgsLayoutItemMap.Predefined)
        if Qgis.QGIS_VERSION_INT >= 30900:
            settings.predefinedMapScales = scales
        else:
            layout.reportContext().setPredefinedScales(scales)

    layer = atlas.coverageLayer()
    feature_filter = optimize_expression(layer, feature_filter)

    expression = QgsExpression(feature_filter)
    if expression.hasParserError():
        raise AtlasPrintException(
            'Expression is invalid, parser error: {}'.format(
                expression.parserErrorString()))

    context = QgsExpressionContext()
    context.appendScope(QgsExpressionContextUtils.globalScope())
    context.appendScope(QgsExpressionContextUtils.projectScope(project))
    context.appendScope(QgsExpressionContextUtils.layoutScope(layout))
    context.appendScope(QgsExpressionContextUtils.atlasScope(atlas))
    context.appendScope(QgsExpressionContextUtils.layerScope(layer))

    expression.prepare(context)
    if expression.hasEvalError():
        raise AtlasPrintException(
            'Expression is invalid, eval error: {}'.format(
                expression.evalErrorString()))

    atlas.setFilterFeatures(True)
    atlas.setFilterExpression(feature_filter)

    if not scales and layout.referenceMap().atlasScalingMode(
    ) == QgsLayoutItemMap.Predefined:
        if Qgis.QGIS_VERSION_INT >= 30900:
            use_project = project.useProjectScales()
            map_scales = project.mapScales()
        else:
            map_scales = project_scales(project)
            use_project = len(map_scales) == 0

        if not use_project or len(map_scales) == 0:
            QgsMessageLog.logMessage(
                'Map scales not found in project, fetching predefined map scales in global config',
                'atlasprint', Qgis.Info)
            map_scales = global_scales()

        if Qgis.QGIS_VERSION_INT >= 30900:
            settings.predefinedMapScales = map_scales
        else:
            layout.reportContext().setPredefinedScales(map_scales)

    export_path = os.path.join(tempfile.gettempdir(),
                               '{}_{}.pdf'.format(layout_name, uuid4()))
    exporter = QgsLayoutExporter(layout)
    result = exporter.exportToPdf(atlas, export_path, settings)

    if result[0] != QgsLayoutExporter.Success and not os.path.isfile(
            export_path):
        raise Exception('export not generated {}'.format(export_path))

    return export_path
Exemple #49
0
    def processAlgorithm(self, progress):
        layer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT_LAYER))
        fieldName = self.getParameterValue(self.FIELD_NAME)
        fieldType = self.TYPES[self.getParameterValue(self.FIELD_TYPE)]
        width = self.getParameterValue(self.FIELD_LENGTH)
        precision = self.getParameterValue(self.FIELD_PRECISION)
        newField = self.getParameterValue(self.NEW_FIELD)
        formula = self.getParameterValue(self.FORMULA)

        output = self.getOutputFromName(self.OUTPUT_LAYER)

        fields = layer.fields()
        if newField:
            fields.append(QgsField(fieldName, fieldType, '', width, precision))

        writer = output.getVectorWriter(fields, layer.wkbType(),
                                        layer.crs())

        exp = QgsExpression(formula)

        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs().srsid())
        da.setEllipsoidalMode(
            iface.mapCanvas().mapSettings().hasCrsTransformEnabled())
        da.setEllipsoid(QgsProject.instance().readEntry(
            'Measure', '/Ellipsoid', GEO_NONE)[0])
        exp.setGeomCalculator(da)
        exp.setDistanceUnits(QgsProject.instance().distanceUnits())
        exp.setAreaUnits(QgsProject.instance().areaUnits())

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope())
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        if not exp.prepare(exp_context):
            raise GeoAlgorithmExecutionException(
                self.tr('Evaluation error: %s' % exp.evalErrorString()))

        outFeature = QgsFeature()
        outFeature.initAttributes(len(fields))
        outFeature.setFields(fields)

        error = ''
        calculationSuccess = True

        features = vector.features(layer)
        total = 100.0 / len(features)

        rownum = 1
        for current, f in enumerate(features):
            rownum = current + 1
            exp_context.setFeature(f)
            exp_context.lastScope().setVariable("row_number", rownum)
            value = exp.evaluate(exp_context)
            if exp.hasEvalError():
                calculationSuccess = False
                error = exp.evalErrorString()
                break
            else:
                outFeature.setGeometry(f.geometry())
                for fld in f.fields():
                    outFeature[fld.name()] = f[fld.name()]
                outFeature[fieldName] = value
                writer.addFeature(outFeature)

            progress.setPercentage(int(current * total))
        del writer

        if not calculationSuccess:
            raise GeoAlgorithmExecutionException(
                self.tr('An error occurred while evaluating the calculation '
                        'string:\n%s' % error))
    def processAlgorithm(self, parameters, context, feedback):
        self.parameters = parameters
        self.context = context
        self.feedback = feedback
        filename = self.parameterAsFileOutput(parameters, self.PrmOutputKmz,
                                              context)
        source = self.parameterAsSource(parameters, self.PrmInputLayer,
                                        context)

        # Before we go further check to make sure we have a valid vector layer
        wkbtype = source.wkbType()
        geomtype = QgsWkbTypes.geometryType(wkbtype)
        if geomtype == QgsWkbTypes.UnknownGeometry or geomtype == QgsWkbTypes.NullGeometry:
            raise QgsProcessingException(
                'Algorithm input is not a valid point, line, or polygon layer.'
            )
        layer = self.parameterAsLayer(parameters, self.PrmInputLayer, context)
        if self.PrmNameField not in parameters or parameters[
                self.PrmNameField] is None:
            name_field = None
        else:
            name_field = self.parameterAsString(parameters, self.PrmNameField,
                                                context)
        desc_fields = self.parameterAsFields(parameters,
                                             self.PrmDescriptionField, context)
        desc_cnt = len(desc_fields)
        export_style = self.parameterAsInt(parameters, self.PrmExportStyle,
                                           context)
        if self.PrmUseGoogleIcon not in parameters or parameters[
                self.PrmUseGoogleIcon] is None:
            google_icon = None
        else:
            google_icon = self.parameterAsEnum(parameters,
                                               self.PrmUseGoogleIcon, context)
        self.line_width_factor = self.parameterAsDouble(
            parameters, self.PrmLineWidthFactor, context)
        alt_interpret = self.parameterAsEnum(parameters,
                                             self.PrmAltitudeInterpretation,
                                             context)
        if self.PrmAltitudeMode not in parameters or parameters[
                self.PrmAltitudeMode] is None:
            default_alt_mode = None
        else:
            default_alt_mode = ALTITUDE_MODES[self.parameterAsEnum(
                parameters, self.PrmAltitudeMode, context)]
        alt_mode_field = self.parameterAsString(parameters,
                                                self.PrmAltitudeModeField,
                                                context)
        altitude_field = self.parameterAsString(parameters,
                                                self.PrmAltitudeField, context)
        altitude_addend = self.parameterAsDouble(parameters,
                                                 self.PrmAltitudeAddend,
                                                 context)
        date_time_stamp_field = self.parameterAsString(
            parameters, self.PrmDateTimeStampField, context)
        date_stamp_field = self.parameterAsString(parameters,
                                                  self.PrmDateStampField,
                                                  context)
        time_stamp_field = self.parameterAsString(parameters,
                                                  self.PrmTimeStampField,
                                                  context)
        date_time_begin_field = self.parameterAsString(
            parameters, self.PrmDateTimeBeginField, context)
        date_begin_field = self.parameterAsString(parameters,
                                                  self.PrmDateBeginField,
                                                  context)
        time_begin_field = self.parameterAsString(parameters,
                                                  self.PrmTimeBeginField,
                                                  context)
        date_time_end_field = self.parameterAsString(parameters,
                                                     self.PrmDateTimeEndField,
                                                     context)
        date_end_field = self.parameterAsString(parameters,
                                                self.PrmDateEndField, context)
        time_end_field = self.parameterAsString(parameters,
                                                self.PrmTimeEndField, context)
        if self.PrmPhotoField not in parameters or parameters[
                self.PrmPhotoField] is None:
            photo_path_field = None
        else:
            photo_path_field = self.parameterAsString(parameters,
                                                      self.PrmPhotoField,
                                                      context)
        self.photos = {}

        hasz = QgsWkbTypes.hasZ(wkbtype)
        if alt_interpret == 0:
            hasz = False
            default_alt_mode = None
            alt_mode_field = None
            altitude_field = None
        elif alt_interpret == 2:
            hasz = False
        src_crs = source.sourceCrs()
        if src_crs != self.epsg4326:
            geomTo4326 = QgsCoordinateTransform(src_crs, self.epsg4326,
                                                QgsProject.instance())

        self.symcontext = QgsRenderContext.fromMapSettings(
            settings.canvas.mapSettings())
        self.png_icons = []
        self.cat_styles = {}
        kml = simplekml.Kml()
        kml.resetidcounter()
        if layer:
            try:
                self.render = layer.renderer()
                self.exp_context = QgsExpressionContext()
                self.exp_context.appendScopes(
                    QgsExpressionContextUtils.globalProjectLayerScopes(layer))
            except Exception:
                if export_style:
                    export_style = 0
                    feedback.reportError(
                        'Layer style cannot be determined. Processing will continue without symbol style export.'
                    )
        else:
            if export_style:
                feedback.reportError(
                    'There appears to be a valid source, but not a valid layer style. Processing will continue without symbol style export.'
                )
                export_style = 0
        if export_style:
            render_type = self.render.type()
            if render_type == 'singleSymbol':
                export_style = 1
            elif render_type == 'categorizedSymbol':
                style_field = self.render.classAttribute()
                self.field_exp = QgsExpression(style_field)
                export_style = 2
            elif render_type == 'graduatedSymbol':
                style_field = self.render.classAttribute()
                self.field_exp = QgsExpression(style_field)
                export_style = 3
            else:
                feedback.reportError(
                    'Only single, categorized, and graduated symbol styles can be exported. Processing will continue without symbol style export.'
                )
                export_style = 0
            if export_style:
                self.initStyles(export_style, google_icon, name_field,
                                geomtype, kml)

        folder = kml.newfolder(name=source.sourceName())
        altitude = 0
        featureCount = source.featureCount()
        total = 100.0 / featureCount if featureCount else 0
        num_features = 0
        iterator = source.getFeatures()
        for cnt, feature in enumerate(iterator):
            if feedback.isCanceled():
                break
            num_features += 1
            if altitude_field:
                try:
                    altitude = float(feature[altitude_field])
                except Exception:
                    altitude = 0
            geom = feature.geometry()
            if src_crs != self.epsg4326:
                geom.transform(geomTo4326)
            if geom.isMultipart() or (name_field and geomtype
                                      == QgsWkbTypes.PolygonGeometry):
                kmlgeom = folder.newmultigeometry()
                kml_item = kmlgeom
            else:
                kmlgeom = folder
                kml_item = None
            if geomtype == QgsWkbTypes.PointGeometry:  # POINTS
                for pt in geom.parts():
                    kmlpart = kmlgeom.newpoint()
                    self.setAltitudeMode(kmlpart, feature, default_alt_mode,
                                         alt_mode_field)
                    if kml_item is None:
                        kml_item = kmlpart
                    if hasz:
                        kmlpart.coords = [(pt.x(), pt.y(),
                                           pt.z() + altitude_addend)]
                    else:
                        kmlpart.coords = [(pt.x(), pt.y(),
                                           altitude + altitude_addend)]
            elif geomtype == QgsWkbTypes.LineGeometry:  # LINES
                for part in geom.parts():
                    kmlpart = kmlgeom.newlinestring()
                    self.setAltitudeMode(kmlpart, feature, default_alt_mode,
                                         alt_mode_field)
                    if kml_item is None:
                        kml_item = kmlpart
                    if hasz:
                        kmlpart.coords = [(pt.x(), pt.y(),
                                           pt.z() + altitude_addend)
                                          for pt in part]
                    else:
                        kmlpart.coords = [(pt.x(), pt.y(),
                                           altitude + altitude_addend)
                                          for pt in part]
            elif geomtype == QgsWkbTypes.PolygonGeometry:  # POLYGONS
                if name_field:
                    centroid = geom.centroid().asPoint()
                    name = '{}'.format(feature[name_field])
                    labelpart = kmlgeom.newpoint(coords=[(centroid.x(),
                                                          centroid.y())],
                                                 name=name)

                for part in geom.parts():
                    kmlpart = kmlgeom.newpolygon()
                    self.setAltitudeMode(kmlpart, feature, default_alt_mode,
                                         alt_mode_field)
                    if kml_item is None:
                        kml_item = kmlpart
                    num_interior_rings = part.numInteriorRings()
                    ext_ring = part.exteriorRing()
                    if hasz:
                        kmlpart.outerboundaryis = [(pt.x(), pt.y(),
                                                    pt.z() + altitude_addend)
                                                   for pt in ext_ring]
                    else:
                        kmlpart.outerboundaryis = [(pt.x(), pt.y(),
                                                    altitude + altitude_addend)
                                                   for pt in ext_ring]
                    if num_interior_rings:
                        ib = []
                        for i in range(num_interior_rings):
                            if hasz:
                                ib.append([(pt.x(), pt.y(),
                                            pt.z() + altitude_addend)
                                           for pt in part.interiorRing(i)])
                            else:
                                ib.append([(pt.x(), pt.y(),
                                            altitude + altitude_addend)
                                           for pt in part.interiorRing(i)])
                        kmlpart.innerboundaryis = ib

            self.exportStyle(kml_item, feature, export_style, geomtype)
            if name_field:
                self.exportName(kml_item, feature[name_field])

            if photo_path_field:
                photo_path = feature[photo_path_field].strip()
                if os.path.exists(photo_path):
                    if not (photo_path in self.photos):
                        local_path = kml.addfile(photo_path)
                        self.photos[photo_path] = local_path
                else:
                    photo_path = None
            else:
                photo_path = None

            if desc_cnt == 1:
                self.exportDescription(kml_item, feature[desc_fields[0]],
                                       photo_path)
            elif desc_cnt > 1:
                self.exportFields(kml_item, desc_fields, feature, photo_path)

            # Process the first date / time fields
            date_time_str = self.parseDateTimeValues(feature,
                                                     date_time_stamp_field,
                                                     date_stamp_field,
                                                     time_stamp_field)
            if date_time_str:
                kml_item.timestamp.when = date_time_str
            date_time_str = self.parseDateTimeValues(feature,
                                                     date_time_begin_field,
                                                     date_begin_field,
                                                     time_begin_field)
            if date_time_str:
                kml_item.timespan.begin = date_time_str
            date_time_str = self.parseDateTimeValues(feature,
                                                     date_time_end_field,
                                                     date_end_field,
                                                     time_end_field)
            if date_time_str:
                kml_item.timespan.end = date_time_str

            if cnt % 100 == 0:
                feedback.setProgress(int(cnt * total))
        if num_features == 0:
            feedback.pushInfo('No features processed')
        else:
            kml.savekmz(filename)

        self.cleanup()

        return ({})
Exemple #51
0
def layer_value(feature, layer, defaultconfig):
    if not canvas:
        roam.utils.warning(
            "No canvas set for using layer_values default function")
        return None
    layers = []
    # layer name can also be a list of layers to search
    layername = defaultconfig['layer']
    if isinstance(layername, basestring):
        layers.append(layername)
    else:
        layers = layername

    expression = defaultconfig['expression']
    field = defaultconfig['field']

    for searchlayer in layers:
        try:
            searchlayer = QgsMapLayerRegistry.instance().mapLayersByName(
                searchlayer)[0]
        except IndexError:
            RoamEvents.raisemessage(
                "Missing layer",
                "Unable to find layer used in widget expression {}".format(
                    searchlayer),
                level=1)
            roam.utils.warning(
                "Unable to find expression layer {}".format(searchlayer))
            return
        if feature.geometry():
            rect = feature.geometry().boundingBox()
            if layer.geometryType() == QGis.Point:
                point = feature.geometry().asPoint()
                rect = QgsRectangle(point.x(), point.y(),
                                    point.x() + 10,
                                    point.y() + 10)

            rect.scale(20)

            rect = canvas.mapRenderer().mapToLayerCoordinates(layer, rect)
            rq = QgsFeatureRequest().setFilterRect(rect) \
                .setFlags(QgsFeatureRequest.ExactIntersect)
            features = searchlayer.getFeatures(rq)
        else:
            features = searchlayer.getFeatures()

        expression = expression.replace("$roamgeometry", "@roamgeometry")

        exp = QgsExpression(expression)
        exp.prepare(searchlayer.pendingFields())
        if exp.hasParserError():
            error = exp.parserErrorString()
            roam.utils.warning(error)

        context = QgsExpressionContext()
        scope = QgsExpressionContextScope()
        context.appendScope(scope)
        scope.setVariable("roamgeometry", feature.geometry())

        for f in features:
            context.setFeature(f)
            value = exp.evaluate(context)
            if exp.hasEvalError():
                error = exp.evalErrorString()
                roam.utils.warning(error)
            if value:
                return f[field]

    raise DefaultError('No features found')
    def evaluate(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None:
        """ Evaluate expressions against layer or features
        In parameters:
            LAYER=wms-layer-name
            EXPRESSION=An expression to evaluate
            or
            EXPRESSIONS=["first expression", "second expression"]
            or
            EXPRESSIONS={"key1": "first expression", "key2": "second expression"}
            // optionals
            FEATURE={"type": "Feature", "geometry": {}, "properties": {}}
            or
            FEATURES=[{"type": "Feature", "geometry": {}, "properties": {}}, {"type": "Feature", "geometry": {},
            "properties": {}}]
            FORM_SCOPE=boolean to add formScope based on provided features
        """

        layername = params.get('LAYER', '')
        if not layername:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: LAYER parameter is mandatory",
                400)

        # get layer
        layer = find_vector_layer(layername, project)
        # layer not found
        if not layer:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid LAYER parameter for 'Evaluate': {} provided".format(layername),
                400)

        # get expressions
        expressions = params.get('EXPRESSIONS', '')
        if not expressions:
            expression = params.get('EXPRESSION', '')
            if not expression:
                raise ExpressionServiceError(
                    "Bad request error",
                    "Invalid 'Evaluate' REQUEST: EXPRESSION or EXPRESSIONS parameter is mandatory",
                    400)
            expressions = '["{}"]'.format(expression)

        # try to load expressions list or dict
        try:
            exp_json = json.loads(expressions)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads expressions '{}' exception:\n{}".format(expressions, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: EXPRESSIONS '{}' are not well formed".format(expressions),
                400)

        # create expression context
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope(project))
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        # create distance area context
        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs(), project.transformContext())
        da.setEllipsoid(project.ellipsoid())

        # parse expressions
        exp_map = {}
        exp_parser_errors = []
        exp_items = []
        if isinstance(exp_json, list):
            exp_items = enumerate(exp_json)
        elif isinstance(exp_json, dict):
            exp_items = exp_json.items()
        for k, e in exp_items:
            exp = QgsExpression(e)
            exp.setGeomCalculator(da)
            exp.setDistanceUnits(project.distanceUnits())
            exp.setAreaUnits(project.areaUnits())

            if exp.hasParserError():
                exp_parser_errors.append('Error "{}": {}'.format(e, exp.parserErrorString()))
                continue

            if not exp.isValid():
                exp_parser_errors.append('Expression not valid "{}"'.format(e))
                continue

            exp.prepare(exp_context)
            exp_map[k] = exp

        # expression parser errors found
        if exp_parser_errors:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid EXPRESSIONS for 'Evaluate':\n{}".format('\n'.join(exp_parser_errors)),
                400)

        # get features
        features = params.get('FEATURES', '')
        if not features:
            feature = params.get('FEATURE', '')
            if feature:
                features = '[' + feature + ']'

        # create the body
        body = {
            'status': 'success',
            'results': [],
            'errors': [],
            'features': 0
        }

        # without features just evaluate expression with layer context
        if not features:
            result = {}
            error = {}
            for k, exp in exp_map.items():
                value = exp.evaluate(exp_context)
                if exp.hasEvalError():
                    result[k] = None
                    error[k] = exp.evalErrorString()
                else:
                    result[k] = json.loads(QgsJsonUtils.encodeValue(value))
            body['results'].append(result)
            body['errors'].append(error)
            write_json_response(body, response)
            return

        # Check features
        try:
            geojson = json.loads(features)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads features '{}' exception:\n{}".format(features, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features),
                400)

        if not geojson or not isinstance(geojson, list) or len(geojson) == 0:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features),
                400)

        if 'type' not in geojson[0] or geojson[0]['type'] != 'Feature':
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed: type not defined or not "
                 "Feature.").format(features),
                400)

        # try to load features
        # read fields
        feature_fields = QgsJsonUtils.stringToFields(
            '{ "type": "FeatureCollection","features":' + features + '}',
            QTextCodec.codecForName("UTF-8"))
        # read features
        feature_list = QgsJsonUtils.stringToFeatureList(
            '{ "type": "FeatureCollection","features":' + features + '}',
            feature_fields,
            QTextCodec.codecForName("UTF-8"))

        # features not well formed
        if not feature_list:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid FEATURES for 'Evaluate': not GeoJSON features array provided\n{}".format(features),
                400)

        # Extend layer fields with this provided in GeoJSON Features
        feat_fields = QgsFields(layer.fields())
        feat_fields.extend(feature_fields)

        # form scope
        add_form_scope = params.get('FORM_SCOPE', '').lower() in ['true', '1', 't']

        # loop through provided features to evaluate expressions
        for f in feature_list:
            # clone the features with all attributes
            # those defined in layer + fields from GeoJSON Features
            feat = QgsFeature(feat_fields)
            feat.setGeometry(f.geometry())
            for field in f.fields():
                fname = field.name()
                if feat_fields.indexOf(fname) != -1:
                    feat.setAttribute(fname, f[fname])

            # Add form scope to expression context
            if add_form_scope:
                exp_context.appendScope(QgsExpressionContextUtils.formScope(feat))

            exp_context.setFeature(feat)
            exp_context.setFields(feat.fields())

            # Evaluate expressions with the new feature
            result = {}
            error = {}
            for k, exp in exp_map.items():
                if add_form_scope:
                    # need to prepare the expression because the context has been updated with a new scope
                    exp.prepare(exp_context)
                value = exp.evaluate(exp_context)
                if exp.hasEvalError():
                    result[k] = None
                    error[k] = exp.evalErrorString()
                else:
                    result[k] = json.loads(QgsJsonUtils.encodeValue(value))
                    error[k] = exp.expression()
            body['results'].append(result)
            body['errors'].append(error)

        write_json_response(body, response)
        return
Exemple #53
0
def print_layout(project,
                 layout_name,
                 feature_filter: str = None,
                 scales=None,
                 scale=None,
                 **kwargs):
    """Generate a PDF for an atlas or a report.

    :param project: The QGIS project.
    :type project: QgsProject

    :param layout_name: Name of the layout of the atlas or report.
    :type layout_name: basestring

    :param feature_filter: QGIS Expression to use to select the feature.
    It can return many features, a multiple pages PDF will be returned.
    This is required to print atlas, not report
    :type feature_filter: basestring

    :param scale: A scale to force in the atlas context. Default to None.
    :type scale: int

    :param scales: A list of predefined list of scales to force in the atlas context.
    Default to None.
    :type scales: list

    :return: Path to the PDF.
    :rtype: basestring
    """
    canvas = QgsMapCanvas()
    bridge = QgsLayerTreeMapCanvasBridge(project.layerTreeRoot(), canvas)
    bridge.setCanvasLayers()
    manager = project.layoutManager()
    master_layout = manager.layoutByName(layout_name)
    settings = QgsLayoutExporter.PdfExportSettings()

    atlas = None
    atlas_layout = None
    report_layout = None

    logger = Logger()

    if not master_layout:
        raise AtlasPrintException('Layout `{}` not found'.format(layout_name))

    if master_layout.layoutType() == QgsMasterLayoutInterface.PrintLayout:
        for _print_layout in manager.printLayouts():
            if _print_layout.name() == layout_name:
                atlas_layout = _print_layout
                break

        atlas = atlas_layout.atlas()
        if not atlas.enabled():
            raise AtlasPrintException('The layout is not enabled for an atlas')

        layer = atlas.coverageLayer()

        if feature_filter is None:
            raise AtlasPrintException(
                'EXP_FILTER is mandatory to print an atlas layout')

        feature_filter = optimize_expression(layer, feature_filter)

        expression = QgsExpression(feature_filter)
        if expression.hasParserError():
            raise AtlasPrintException(
                'Expression is invalid, parser error: {}'.format(
                    expression.parserErrorString()))

        context = QgsExpressionContext()
        context.appendScope(QgsExpressionContextUtils.globalScope())
        context.appendScope(QgsExpressionContextUtils.projectScope(project))
        context.appendScope(
            QgsExpressionContextUtils.layoutScope(atlas_layout))
        context.appendScope(QgsExpressionContextUtils.atlasScope(atlas))
        context.appendScope(QgsExpressionContextUtils.layerScope(layer))
        expression.prepare(context)
        if expression.hasEvalError():
            raise AtlasPrintException(
                'Expression is invalid, eval error: {}'.format(
                    expression.evalErrorString()))

        atlas.setFilterFeatures(True)
        atlas.setFilterExpression(feature_filter)

        if scale:
            atlas_layout.referenceMap().setAtlasScalingMode(
                QgsLayoutItemMap.Fixed)
            atlas_layout.referenceMap().setScale(scale)

        if scales:
            atlas_layout.referenceMap().setAtlasScalingMode(
                QgsLayoutItemMap.Predefined)
            if Qgis.QGIS_VERSION_INT >= 30900:
                settings.predefinedMapScales = scales
            else:
                atlas_layout.reportContext().setPredefinedScales(scales)

        if not scales and atlas_layout.referenceMap().atlasScalingMode(
        ) == QgsLayoutItemMap.Predefined:
            if Qgis.QGIS_VERSION_INT >= 30900:
                use_project = project.useProjectScales()
                map_scales = project.mapScales()
            else:
                map_scales = project_scales(project)
                use_project = len(map_scales) == 0

            if not use_project or len(map_scales) == 0:
                logger.info(
                    'Map scales not found in project, fetching predefined map scales in global config'
                )
                map_scales = global_scales()

            if Qgis.QGIS_VERSION_INT >= 30900:
                settings.predefinedMapScales = map_scales
            else:
                atlas_layout.reportContext().setPredefinedScales(map_scales)

    elif master_layout.layoutType() == QgsMasterLayoutInterface.Report:
        report_layout = master_layout

    else:
        raise AtlasPrintException('The layout is not supported by the plugin')

    for key, value in kwargs.items():
        found = False
        if atlas_layout:
            item = atlas_layout.itemById(key.lower())
            if isinstance(item, QgsLayoutItemLabel):
                item.setText(value)
                found = True
        logger.info(
            'Additional parameters: {} found in layout {}, value {}'.format(
                key, found, value))

    export_path = os.path.join(tempfile.gettempdir(),
                               '{}_{}.pdf'.format(layout_name, uuid4()))
    result, error = QgsLayoutExporter.exportToPdf(atlas or report_layout,
                                                  export_path, settings)

    if result != QgsLayoutExporter.Success and not os.path.isfile(export_path):
        raise Exception('export not generated {} ({})'.format(
            export_path, error))

    return export_path
    def replace_expression_text(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None:
        """ Replace expression texts against layer or features
        In parameters:
            LAYER=wms-layer-name
            STRING=A string with expression between [% and %]
            or
            STRINGS=["first string with expression", "second string with expression"]
            or
            STRINGS={"key1": "first string with expression", "key2": "second string with expression"}
            // optionals
            FEATURE={"type": "Feature", "geometry": {}, "properties": {}}
            or
            FEATURES=[{"type": "Feature", "geometry": {}, "properties": {}}, {"type": "Feature", "geometry": {},
            "properties": {}}]
            FORM_SCOPE=boolean to add formScope based on provided features
        """
        layername = params.get('LAYER', '')
        if not layername:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'ReplaceExpressionText' REQUEST: LAYER parameter is mandatory",
                400)

        # get layer
        layer = find_vector_layer(layername, project)
        # layer not found
        if not layer:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid LAYER parameter for 'ReplaceExpressionText': {} provided".format(layername),
                400)

        # get strings
        strings = params.get('STRINGS', '')
        if not strings:
            the_string = params.get('STRING', '')
            if not the_string:
                raise ExpressionServiceError(
                    "Bad request error",
                    "Invalid 'ReplaceExpressionText' REQUEST: STRING or STRINGS parameter is mandatory",
                    400)
            strings = '["{}"]'.format(the_string)

        # try to load expressions list or dict
        try:
            str_json = json.loads(strings)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads strings '{}' exception:\n{}".format(strings, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'ReplaceExpressionText' REQUEST: STRINGS '{}' are not well formed".format(strings),
                400)

        # get features
        features = params.get('FEATURES', '')
        if not features:
            feature = params.get('FEATURE', '')
            if feature:
                features = '[' + feature + ']'

        # create expression context
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope(project))
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        # create distance area context
        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs(), project.transformContext())
        da.setEllipsoid(project.ellipsoid())

        # organized strings
        str_map = {}
        str_items = []
        if isinstance(str_json, list):
            str_items = enumerate(str_json)
        elif isinstance(str_json, dict):
            str_items = str_json.items()
        for k, s in str_items:
            str_map[k] = s

        # create the body
        body = {
            'status': 'success',
            'results': [],
            'errors': [],
            'features': 0
        }

        # without features just replace expression string with layer context
        if not features:
            result = {}
            for k, s in str_map.items():
                value = QgsExpression.replaceExpressionText(s, exp_context, da)
                result[k] = json.loads(QgsJsonUtils.encodeValue(value))
            body['results'].append(result)
            write_json_response(body, response)
            return

        # Check features
        try:
            geojson = json.loads(features)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads features '{}' exception:\n{}".format(features, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features),
                400)

        if not geojson or not isinstance(geojson, list) or len(geojson) == 0:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features),
                400)

        if ('type' not in geojson[0]) or geojson[0]['type'] != 'Feature':
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed: type not defined or not "
                 "Feature.").format(features),
                400)

        # try to load features
        # read fields
        feature_fields = QgsJsonUtils.stringToFields(
            '{ "type": "FeatureCollection","features":' + features + '}',
            QTextCodec.codecForName("UTF-8"))
        # read features
        feature_list = QgsJsonUtils.stringToFeatureList(
            '{ "type": "FeatureCollection","features":' + features + '}',
            feature_fields,
            QTextCodec.codecForName("UTF-8"))

        # features not well formed
        if not feature_list:
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid FEATURES for 'ReplaceExpressionText': not GeoJSON features array "
                 "provided\n{}").format(features),
                400)

        # Extend layer fields with this provided in GeoJSON Features
        feat_fields = QgsFields(layer.fields())
        feat_fields.extend(feature_fields)

        # form scope
        add_form_scope = params.get('FORM_SCOPE', '').lower() in ['true', '1', 't']

        # loop through provided features to replace expression strings
        for f in feature_list:
            # clone the features with all attributes
            # those defined in layer + fields from GeoJSON Features
            feat = QgsFeature(feat_fields)
            feat.setGeometry(f.geometry())
            for field in f.fields():
                field_name = field.name()
                if feat_fields.indexOf(field_name) != -1:
                    feat.setAttribute(field_name, f[field_name])

            # Add form scope to expression context
            if add_form_scope:
                exp_context.appendScope(QgsExpressionContextUtils.formScope(feat))

            exp_context.setFeature(feat)
            exp_context.setFields(feat.fields())

            # replace expression strings with the new feature
            result = {}
            for k, s in str_map.items():
                value = QgsExpression.replaceExpressionText(s, exp_context, da)
                result[k] = json.loads(QgsJsonUtils.encodeValue(value))
            body['results'].append(result)

        write_json_response(body, response)
        return
Exemple #55
0
    def processAlgorithm(self, context, feedback):
        layer = QgsProcessingUtils.mapLayerFromString(
            self.getParameterValue(self.INPUT_LAYER), context)
        fieldName = self.getParameterValue(self.FIELD_NAME)
        fieldType = self.TYPES[self.getParameterValue(self.FIELD_TYPE)]
        width = self.getParameterValue(self.FIELD_LENGTH)
        precision = self.getParameterValue(self.FIELD_PRECISION)
        newField = self.getParameterValue(self.NEW_FIELD)
        formula = self.getParameterValue(self.FORMULA)

        output = self.getOutputFromName(self.OUTPUT_LAYER)

        fields = layer.fields()
        if newField:
            fields.append(QgsField(fieldName, fieldType, '', width, precision))

        writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs(),
                                        context)

        exp = QgsExpression(formula)

        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs())
        da.setEllipsoid(QgsProject.instance().ellipsoid())
        exp.setGeomCalculator(da)
        exp.setDistanceUnits(QgsProject.instance().distanceUnits())
        exp.setAreaUnits(QgsProject.instance().areaUnits())

        exp_context = QgsExpressionContext(
            QgsExpressionContextUtils.globalProjectLayerScopes(layer))

        if not exp.prepare(exp_context):
            raise GeoAlgorithmExecutionException(
                self.tr('Evaluation error: {0}').format(exp.evalErrorString()))

        outFeature = QgsFeature()
        outFeature.initAttributes(len(fields))
        outFeature.setFields(fields)

        error = ''
        calculationSuccess = True

        features = QgsProcessingUtils.getFeatures(layer, context)
        total = 100.0 / QgsProcessingUtils.featureCount(layer, context)

        rownum = 1
        for current, f in enumerate(features):
            rownum = current + 1
            exp_context.setFeature(f)
            exp_context.lastScope().setVariable("row_number", rownum)
            value = exp.evaluate(exp_context)
            if exp.hasEvalError():
                calculationSuccess = False
                error = exp.evalErrorString()
                break
            else:
                outFeature.setGeometry(f.geometry())
                for fld in f.fields():
                    outFeature[fld.name()] = f[fld.name()]
                outFeature[fieldName] = value
                writer.addFeature(outFeature)

            feedback.setProgress(int(current * total))
        del writer

        if not calculationSuccess:
            raise GeoAlgorithmExecutionException(
                self.tr('An error occurred while evaluating the calculation '
                        'string:\n{0}').format(error))
    def runGetFeatureTests(self, provider):
        assert len([f for f in provider.getFeatures()]) == 5
        self.assert_query(provider, 'name ILIKE \'QGIS\'', [])
        self.assert_query(provider, '"name" IS NULL', [5])
        self.assert_query(provider, '"name" IS NOT NULL', [1, 2, 3, 4])
        self.assert_query(provider, '"name" NOT LIKE \'Ap%\'', [1, 3, 4])
        self.assert_query(provider, '"name" NOT ILIKE \'QGIS\'', [1, 2, 3, 4])
        self.assert_query(provider, '"name" NOT ILIKE \'pEAR\'', [1, 2, 4])
        self.assert_query(provider, 'name = \'Apple\'', [2])
        self.assert_query(provider, 'name <> \'Apple\'', [1, 3, 4])
        self.assert_query(provider, 'name = \'apple\'', [])
        self.assert_query(provider, '"name" <> \'apple\'', [1, 2, 3, 4])
        self.assert_query(provider, '(name = \'Apple\') is not null',
                          [1, 2, 3, 4])
        self.assert_query(provider, 'name LIKE \'Apple\'', [2])
        self.assert_query(provider, 'name LIKE \'aPple\'', [])
        self.assert_query(provider, 'name ILIKE \'aPple\'', [2])
        self.assert_query(provider, 'name ILIKE \'%pp%\'', [2])
        self.assert_query(provider, 'cnt > 0', [1, 2, 3, 4])
        self.assert_query(provider, '-cnt > 0', [5])
        self.assert_query(provider, 'cnt < 0', [5])
        self.assert_query(provider, '-cnt < 0', [1, 2, 3, 4])
        self.assert_query(provider, 'cnt >= 100', [1, 2, 3, 4])
        self.assert_query(provider, 'cnt <= 100', [1, 5])
        self.assert_query(provider, 'pk IN (1, 2, 4, 8)', [1, 2, 4])
        self.assert_query(provider, 'cnt = 50 * 2', [1])
        self.assert_query(provider, 'cnt = 99 + 1', [1])
        self.assert_query(provider, 'cnt = 101 - 1', [1])
        self.assert_query(provider, 'cnt - 1 = 99', [1])
        self.assert_query(provider, '-cnt - 1 = -101', [1])
        self.assert_query(provider, '-(-cnt) = 100', [1])
        self.assert_query(provider, '-(cnt) = -(100)', [1])
        self.assert_query(provider, 'cnt + 1 = 101', [1])
        self.assert_query(provider, 'cnt = 1100 % 1000', [1])
        self.assert_query(provider,
                          '"name" || \' \' || "name" = \'Orange Orange\'', [1])
        self.assert_query(provider,
                          '"name" || \' \' || "cnt" = \'Orange 100\'', [1])
        self.assert_query(provider, '\'x\' || "name" IS NOT NULL',
                          [1, 2, 3, 4])
        self.assert_query(provider, '\'x\' || "name" IS NULL', [5])
        self.assert_query(provider, 'cnt = 10 ^ 2', [1])
        self.assert_query(provider, '"name" ~ \'[OP]ra[gne]+\'', [1])
        self.assert_query(
            provider, '"name"="name2"',
            [2, 4])  # mix of matched and non-matched case sensitive names
        self.assert_query(provider, 'true', [1, 2, 3, 4, 5])
        self.assert_query(provider, 'false', [])

        # Three value logic
        self.assert_query(provider, 'false and false', [])
        self.assert_query(provider, 'false and true', [])
        self.assert_query(provider, 'false and NULL', [])
        self.assert_query(provider, 'true and false', [])
        self.assert_query(provider, 'true and true', [1, 2, 3, 4, 5])
        self.assert_query(provider, 'true and NULL', [])
        self.assert_query(provider, 'NULL and false', [])
        self.assert_query(provider, 'NULL and true', [])
        self.assert_query(provider, 'NULL and NULL', [])
        self.assert_query(provider, 'false or false', [])
        self.assert_query(provider, 'false or true', [1, 2, 3, 4, 5])
        self.assert_query(provider, 'false or NULL', [])
        self.assert_query(provider, 'true or false', [1, 2, 3, 4, 5])
        self.assert_query(provider, 'true or true', [1, 2, 3, 4, 5])
        self.assert_query(provider, 'true or NULL', [1, 2, 3, 4, 5])
        self.assert_query(provider, 'NULL or false', [])
        self.assert_query(provider, 'NULL or true', [1, 2, 3, 4, 5])
        self.assert_query(provider, 'NULL or NULL', [])
        self.assert_query(provider, 'not true', [])
        self.assert_query(provider, 'not false', [1, 2, 3, 4, 5])
        self.assert_query(provider, 'not null', [])

        # not
        self.assert_query(provider, 'not name = \'Apple\'', [1, 3, 4])
        self.assert_query(provider, 'not name IS NULL', [1, 2, 3, 4])
        self.assert_query(provider, 'not name = \'Apple\' or name = \'Apple\'',
                          [1, 2, 3, 4])
        self.assert_query(provider,
                          'not name = \'Apple\' or not name = \'Apple\'',
                          [1, 3, 4])
        self.assert_query(provider, 'not name = \'Apple\' and pk = 4', [4])
        self.assert_query(provider, 'not name = \'Apple\' and not pk = 4',
                          [1, 3])
        self.assert_query(provider, 'not pk IN (1, 2, 4, 8)', [3, 5])

        # type conversion - QGIS expressions do not mind that we are comparing a string
        # against numeric literals
        self.assert_query(provider, 'num_char IN (2, 4, 5)', [2, 4, 5])

        # geometry
        self.assert_query(
            provider,
            'intersects($geometry,geom_from_wkt( \'Polygon ((-72.2 66.1, -65.2 66.1, -65.2 72.0, -72.2 72.0, -72.2 66.1))\'))',
            [1, 2])

        # combination of an uncompilable expression and limit
        feature = next(self.vl.getFeatures('pk=4'))
        context = QgsExpressionContext()
        scope = QgsExpressionContextScope()
        scope.setVariable('parent', feature)
        context.appendScope(scope)

        request = QgsFeatureRequest()
        request.setExpressionContext(context)
        request.setFilterExpression('"pk" = attribute(@parent, \'pk\')')
        request.setLimit(1)

        values = [f['pk'] for f in self.vl.getFeatures(request)]
        self.assertEqual(values, [4])
    def testExpression(self):
        """ test aggregate calculation using an expression """

        # numeric
        layer = QgsVectorLayer("Point?field=fldint:integer", "layer", "memory")
        pr = layer.dataProvider()

        int_values = [4, 2, 3, 2, 5, None, 8]

        features = []
        for v in int_values:
            f = QgsFeature()
            f.setFields(layer.fields())
            f.setAttributes([v])
            features.append(f)
        assert pr.addFeatures(features)

        #int
        agg = QgsAggregateCalculator(layer)
        val, ok = agg.calculate(QgsAggregateCalculator.Sum, 'fldint * 2')
        self.assertTrue(ok)
        self.assertEqual(val, 48)

        # double
        val, ok = agg.calculate(QgsAggregateCalculator.Sum, 'fldint * 1.5')
        self.assertTrue(ok)
        self.assertEqual(val, 36)

        # datetime
        val, ok = agg.calculate(
            QgsAggregateCalculator.Max,
            "to_date('2012-05-04') + to_interval( fldint || ' day' )")
        self.assertTrue(ok)
        self.assertEqual(val, QDateTime(QDate(2012, 5, 12), QTime(0, 0, 0)))

        # date
        val, ok = agg.calculate(
            QgsAggregateCalculator.Min,
            "to_date(to_date('2012-05-04') + to_interval( fldint || ' day' ))")
        self.assertTrue(ok)
        self.assertEqual(val, QDateTime(QDate(2012, 5, 6), QTime(0, 0, 0)))

        # string
        val, ok = agg.calculate(QgsAggregateCalculator.Max,
                                "fldint || ' oranges'")
        self.assertTrue(ok)
        self.assertEqual(val, '8 oranges')

        # geometry
        val, ok = agg.calculate(QgsAggregateCalculator.GeometryCollect,
                                "make_point( coalesce(fldint,0), 2 )")
        self.assertTrue(ok)
        self.assertTrue(val.exportToWkt(),
                        'MultiPoint((4 2, 2 2, 3 2, 2 2,5 2, 0 2,8 2))')

        # try a bad expression
        val, ok = agg.calculate(QgsAggregateCalculator.Max,
                                "not_a_field || ' oranges'")
        self.assertFalse(ok)
        val, ok = agg.calculate(QgsAggregateCalculator.Max, "5+")
        self.assertFalse(ok)

        # test expression context

        # check default context first
        # should have layer variables:
        val, ok = agg.calculate(QgsAggregateCalculator.Min, "@layer_name")
        self.assertTrue(ok)
        self.assertEqual(val, 'layer')
        # but not custom variables:
        val, ok = agg.calculate(QgsAggregateCalculator.Min, "@my_var")
        self.assertTrue(ok)
        self.assertEqual(val, NULL)

        # test with manual expression context
        scope = QgsExpressionContextScope()
        scope.setVariable('my_var', 5)
        context = QgsExpressionContext()
        context.appendScope(scope)
        val, ok = agg.calculate(QgsAggregateCalculator.Min, "@my_var", context)
        self.assertTrue(ok)
        self.assertEqual(val, 5)
Exemple #58
0
    def processAlgorithm(self, parameters, context, feedback):
        # Get input vector layer
        vlayer = self.parameterAsVectorLayer(parameters, self.INPUT_LAYER,
                                             context)
        self.layer = vlayer
        symbol_level = parameters[self.SYMBOL_LEVEL]
        self.symbol_level = symbol_level
        self.feedback = feedback

        color_field = parameters[self.COLOR_FIELD]
        virtual_color_field = parameters[self.VIRTUAL_COLOR_FIELD]
        label_field = parameters[self.LABEL_FIELD]
        virtual_label_field = parameters[self.VIRTUAL_LABEL_FIELD]

        self.feedback.pushInfo('Layer is {}'.format(vlayer.name()))

        # Compute symbol based expressions
        color_expression = self.getColorExpressionFromSymbology()
        label_expression = self.getLabelExpressionFromSymbology()

        if not color_expression or not label_expression:
            raise QgsProcessingException(
                self.
                tr('Color expression or label expression cannot be generated'))

        # Modify features
        # Start an undo block
        if color_field and label_field:

            # Compute the number of steps to display within the progress bar and
            # get features from source
            total = 100.0 / vlayer.featureCount() if vlayer.featureCount(
            ) else 0

            # prepare expression
            exp_context = QgsExpressionContext()
            exp_context.appendScopes(
                QgsExpressionContextUtils.globalProjectLayerScopes(vlayer))

            # color_expression = "concat('hop ', pcolor)"
            color_exp = QgsExpression(color_expression)

            color_idx = vlayer.fields().indexFromName(color_field)
            label_exp = QgsExpression(label_expression)
            label_idx = vlayer.fields().indexFromName(label_field)

            vlayer.beginEditCommand('Translating all features')
            features = vlayer.getFeatures(QgsFeatureRequest().setFlags(
                QgsFeatureRequest.NoGeometry))
            # .setSubsetOfAttributes([color_idx, label_idx]) )
            for current, feature in enumerate(features):
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    break

                # Edit feature
                exp_context.setFeature(feature)

                # color
                color_val = color_exp.evaluate(exp_context)
                self.feedback.pushInfo(color_val)
                vlayer.changeAttributeValue(feature.id(), color_idx, color_val)

                # label
                label_val = label_exp.evaluate(exp_context)
                self.feedback.pushInfo(label_val)
                vlayer.changeAttributeValue(feature.id(), label_idx, label_val)

                # Update the progress bar
                feedback.setProgress(int(current * total))

        if virtual_color_field:
            self.createOrUpdateLayerExpressionField(virtual_color_field,
                                                    color_expression)
        if virtual_label_field:
            self.createOrUpdateLayerExpressionField(virtual_label_field,
                                                    label_expression)

        # End the undo block
        vlayer.endEditCommand()

        return {self.OUTPUT: 'ok'}
Exemple #59
0
    def fetch_values_from_layer(self):  # pylint: disable=too-many-locals, too-many-branches, too-many-statements
        """
        (Re)fetches plot values from the source layer.
        """

        # Note: we keep things nice and efficient and only iterate a single time over the layer!

        if not self.context_generator:
            context = QgsExpressionContext()
            context.appendScopes(
                QgsExpressionContextUtils.globalProjectLayerScopes(
                    self.source_layer))
        else:
            context = self.context_generator.createExpressionContext()
            # add a new scope corresponding to the source layer -- this will potentially overwrite any other
            # layer scopes which may be present in the context (e.g. from atlas layers), but we need to ensure
            # that source layer fields and attributes are present in the context
            context.appendScope(
                self.source_layer.createExpressionContextScope())

        self.settings.data_defined_properties.prepare(context)

        self.fetch_layout_properties(context)

        def add_source_field_or_expression(field_or_expression):
            field_index = self.source_layer.fields().lookupField(
                field_or_expression)
            if field_index == -1:
                expression = QgsExpression(field_or_expression)
                if not expression.hasParserError():
                    expression.prepare(context)
                return expression, expression.needsGeometry(
                ), expression.referencedColumns()

            return None, False, {field_or_expression}

        x_expression, x_needs_geom, x_attrs = add_source_field_or_expression(self.settings.properties['x_name']) if \
            self.settings.properties[
                'x_name'] else (None, False, set())
        y_expression, y_needs_geom, y_attrs = add_source_field_or_expression(self.settings.properties['y_name']) if \
            self.settings.properties[
                'y_name'] else (None, False, set())
        z_expression, z_needs_geom, z_attrs = add_source_field_or_expression(self.settings.properties['z_name']) if \
            self.settings.properties[
                'z_name'] else (None, False, set())
        additional_info_expression, additional_needs_geom, additional_attrs = add_source_field_or_expression(
            self.settings.layout['additional_info_expression']
        ) if self.settings.layout['additional_info_expression'] else (None,
                                                                      False,
                                                                      set())

        attrs = set().union(
            self.settings.data_defined_properties.referencedFields(), x_attrs,
            y_attrs, z_attrs, additional_attrs)

        request = QgsFeatureRequest()

        if self.settings.data_defined_properties.property(
                PlotSettings.PROPERTY_FILTER).isActive():
            expression = self.settings.data_defined_properties.property(
                PlotSettings.PROPERTY_FILTER).asExpression()
            request.setFilterExpression(expression)
            request.setExpressionContext(context)

        request.setSubsetOfAttributes(attrs, self.source_layer.fields())

        if not x_needs_geom and not y_needs_geom and not z_needs_geom and not additional_needs_geom and not self.settings.data_defined_properties.hasActiveProperties(
        ):
            request.setFlags(QgsFeatureRequest.NoGeometry)

        visible_geom_engine = None
        if self.visible_features_only and self.visible_region is not None:
            ct = QgsCoordinateTransform(
                self.visible_region.crs(), self.source_layer.crs(),
                QgsProject.instance().transformContext())
            try:
                rect = ct.transformBoundingBox(self.visible_region)
                request.setFilterRect(rect)
            except QgsCsException:
                pass
        elif self.visible_features_only and self.polygon_filter is not None:
            ct = QgsCoordinateTransform(
                self.polygon_filter.crs(), self.source_layer.crs(),
                QgsProject.instance().transformContext())
            try:
                rect = ct.transformBoundingBox(
                    self.polygon_filter.geometry.boundingBox())
                request.setFilterRect(rect)
                g = self.polygon_filter.geometry
                g.transform(ct)

                visible_geom_engine = QgsGeometry.createGeometryEngine(
                    g.constGet())
                visible_geom_engine.prepareGeometry()
            except QgsCsException:
                pass

        if self.selected_features_only:
            it = self.source_layer.getSelectedFeatures(request)
        else:
            it = self.source_layer.getFeatures(request)

        # Some plot types don't draw individual glyphs for each feature, but aggregate them instead.
        # In that case it doesn't make sense to evaluate expressions for settings like marker size or color for each
        # feature. Instead, the evaluation should be executed only once for these settings.
        aggregating = self.settings.plot_type in ['box', 'histogram']
        executed = False

        xx = []
        yy = []
        zz = []
        additional_hover_text = []
        marker_sizes = []
        colors = []
        stroke_colors = []
        stroke_widths = []
        for f in it:
            if visible_geom_engine and not visible_geom_engine.intersects(
                    f.geometry().constGet()):
                continue

            self.settings.feature_ids.append(f.id())
            context.setFeature(f)

            x = None
            if x_expression:
                x = x_expression.evaluate(context)
                if x == NULL or x is None:
                    continue
            elif self.settings.properties['x_name']:
                x = f[self.settings.properties['x_name']]
                if x == NULL or x is None:
                    continue

            y = None
            if y_expression:
                y = y_expression.evaluate(context)
                if y == NULL or y is None:
                    continue
            elif self.settings.properties['y_name']:
                y = f[self.settings.properties['y_name']]
                if y == NULL or y is None:
                    continue

            z = None
            if z_expression:
                z = z_expression.evaluate(context)
                if z == NULL or z is None:
                    continue
            elif self.settings.properties['z_name']:
                z = f[self.settings.properties['z_name']]
                if z == NULL or z is None:
                    continue

            if additional_info_expression:
                additional_hover_text.append(
                    additional_info_expression.evaluate(context))
            elif self.settings.layout['additional_info_expression']:
                additional_hover_text.append(
                    f[self.settings.layout['additional_info_expression']])

            if x is not None:
                xx.append(x)
            if y is not None:
                yy.append(y)
            if z is not None:
                zz.append(z)

            if self.settings.data_defined_properties.isActive(
                    PlotSettings.PROPERTY_MARKER_SIZE):
                default_value = self.settings.properties['marker_size']
                context.setOriginalValueVariable(default_value)
                value, _ = self.settings.data_defined_properties.valueAsDouble(
                    PlotSettings.PROPERTY_MARKER_SIZE, context, default_value)
                marker_sizes.append(value)
            if self.settings.data_defined_properties.isActive(
                    PlotSettings.PROPERTY_STROKE_WIDTH):
                default_value = self.settings.properties['marker_width']
                context.setOriginalValueVariable(default_value)
                value, _ = self.settings.data_defined_properties.valueAsDouble(
                    PlotSettings.PROPERTY_STROKE_WIDTH, context, default_value)
                stroke_widths.append(value)

            if self.settings.data_defined_properties.isActive(
                    PlotSettings.PROPERTY_COLOR) and (not aggregating
                                                      or not executed):
                default_value = QColor(self.settings.properties['in_color'])
                value, conversion_success = self.settings.data_defined_properties.valueAsColor(
                    PlotSettings.PROPERTY_COLOR, context, default_value)
                if conversion_success:
                    # We were given a valid color specification, use that color
                    colors.append(value.name())
                else:
                    try:
                        # Attempt to interpret the value as a list of color specifications
                        value_list = self.settings.data_defined_properties.value(
                            PlotSettings.PROPERTY_COLOR, context)
                        color_list = [
                            QgsSymbolLayerUtils.decodeColor(item).name()
                            for item in value_list
                        ]
                        colors.extend(color_list)
                    except TypeError:
                        # Not a list of color specifications, use the default color instead
                        colors.append(default_value.name())

            if self.settings.data_defined_properties.isActive(
                    PlotSettings.PROPERTY_STROKE_COLOR) and (not aggregating
                                                             or not executed):
                default_value = QColor(self.settings.properties['out_color'])
                value, conversion_success = self.settings.data_defined_properties.valueAsColor(
                    PlotSettings.PROPERTY_STROKE_COLOR, context, default_value)
                if conversion_success:
                    # We were given a valid color specification, use that color
                    stroke_colors.append(value.name())
                else:
                    try:
                        # Attempt to interpret the value as a list of color specifications
                        value_list = self.settings.data_defined_properties.value(
                            PlotSettings.PROPERTY_STROKE_COLOR, context)
                        color_list = [
                            QgsSymbolLayerUtils.decodeColor(item).name()
                            for item in value_list
                        ]
                        stroke_colors.extend(color_list)
                    except TypeError:
                        # Not a list of color specifications, use the default color instead
                        stroke_colors.append(default_value.name())

            executed = True

        self.settings.additional_hover_text = additional_hover_text
        self.settings.x = xx
        self.settings.y = yy
        self.settings.z = zz
        if marker_sizes:
            self.settings.data_defined_marker_sizes = marker_sizes
        if colors:
            self.settings.data_defined_colors = colors
        if stroke_colors:
            self.settings.data_defined_stroke_colors = stroke_colors
        if stroke_widths:
            self.settings.data_defined_stroke_widths = stroke_widths
Exemple #60
0
    def processAlgorithm(self, progress):
        layer = self.getParameterValue(self.INPUT_LAYER)
        mapping = self.getParameterValue(self.FIELDS_MAPPING)
        output = self.getOutputFromName(self.OUTPUT_LAYER)

        layer = dataobjects.getObjectFromUri(layer)
        fields = []
        expressions = []

        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs().srsid())
        da.setEllipsoidalMode(
            iface.mapCanvas().mapSettings().hasCrsTransformEnabled())
        da.setEllipsoid(QgsProject.instance().readEntry(
            'Measure', '/Ellipsoid', GEO_NONE)[0])

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope())
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        for field_def in mapping:
            fields.append(QgsField(name=field_def['name'],
                                   type=field_def['type'],
                                   len=field_def['length'],
                                   prec=field_def['precision']))

            expression = QgsExpression(field_def['expression'])
            expression.setGeomCalculator(da)
            expression.setDistanceUnits(QgsProject.instance().distanceUnits())
            expression.setAreaUnits(QgsProject.instance().areaUnits())

            if expression.hasParserError():
                raise GeoAlgorithmExecutionException(
                    self.tr(u'Parser error in expression "{}": {}')
                    .format(str(field_def['expression']),
                            str(expression.parserErrorString())))
            expression.prepare(exp_context)
            if expression.hasEvalError():
                raise GeoAlgorithmExecutionException(
                    self.tr(u'Evaluation error in expression "{}": {}')
                    .format(str(field_def['expression']),
                            str(expression.evalErrorString())))
            expressions.append(expression)

        writer = output.getVectorWriter(fields,
                                        layer.wkbType(),
                                        layer.crs())

        # Create output vector layer with new attributes
        error = ''
        calculationSuccess = True
        inFeat = QgsFeature()
        outFeat = QgsFeature()
        features = vector.features(layer)
        total = 100.0 / len(features)
        for current, inFeat in enumerate(features):
            rownum = current + 1

            geometry = inFeat.geometry()
            outFeat.setGeometry(geometry)

            attrs = []
            for i in range(0, len(mapping)):
                field_def = mapping[i]
                expression = expressions[i]
                exp_context.setFeature(inFeat)
                exp_context.lastScope().setVariable("row_number", rownum)
                value = expression.evaluate(exp_context)
                if expression.hasEvalError():
                    calculationSuccess = False
                    error = expression.evalErrorString()
                    break

                attrs.append(value)
            outFeat.setAttributes(attrs)

            writer.addFeature(outFeat)

            progress.setPercentage(int(current * total))

        del writer

        if not calculationSuccess:
            raise GeoAlgorithmExecutionException(
                self.tr('An error occurred while evaluating the calculation'
                        ' string:\n') + error)