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
Beispiel #2
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        layer = self.parameterAsVectorLayer(parameters, self.INPUT, context)
        field_name = self.parameterAsString(parameters, self.FIELD_NAME, context)
        field_type = self.TYPES[self.parameterAsEnum(parameters, self.FIELD_TYPE, context)]
        width = self.parameterAsInt(parameters, self.FIELD_LENGTH, context)
        precision = self.parameterAsInt(parameters, self.FIELD_PRECISION, context)
        new_field = self.parameterAsBool(parameters, self.NEW_FIELD, context)
        formula = self.parameterAsString(parameters, self.FORMULA, context)

        expression = QgsExpression(formula)
        da = QgsDistanceArea()
        da.setSourceCrs(source.sourceCrs())
        da.setEllipsoid(context.project().ellipsoid())
        expression.setGeomCalculator(da)

        expression.setDistanceUnits(context.project().distanceUnits())
        expression.setAreaUnits(context.project().areaUnits())

        fields = source.fields()
        field_index = fields.lookupField(field_name)
        if new_field or field_index < 0:
            fields.append(QgsField(field_name, field_type, '', width, precision))

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

        exp_context = self.createExpressionContext(parameters, context)
        if layer is not None:
            exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

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

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0

        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            rownum = current + 1
            exp_context.setFeature(f)
            exp_context.lastScope().setVariable("row_number", rownum)
            value = expression.evaluate(exp_context)
            if expression.hasEvalError():
                feedback.reportError(expression.evalErrorString())
            else:
                attrs = f.attributes()
                if new_field or field_index < 0:
                    attrs.append(value)
                else:
                    attrs[field_index] = value
                f.setAttributes(attrs)
                sink.addFeature(f, QgsFeatureSink.FastInsert)
            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
Beispiel #3
0
    def processAlgorithm(self, progress):
        layer = dataobjects.getObjectFromUri(
            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()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope())
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        if not expression.prepare(exp_context):
            raise GeoAlgorithmExecutionException(
                self.tr('Evaluation error: %s' % 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: %s' % 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)
            progress.setPercentage(int(current * total))

        del writer
 def initContext(self):
     exp_context = self.builder.expressionContext()
     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)
    def calculate( self, layer, fieldName, expression ):
        if ( layer.featureCount() == 0 ):
            self.msg.show( "[Info] * No existing features on layer " + layer.name() + " to calculate expression.", 'info', True )
            return

        expression = QgsExpression( expression )
        if expression.hasParserError():
            self.msg.show( QApplication.translate( "AutoFields-FieldCalculator", "[Error] (Parsing) " ) + \
                expression.parserErrorString(), 'critical' )
            return
        
        context = QgsExpressionContext()
        context.appendScope( QgsExpressionContextUtils.globalScope() )
        context.appendScope( QgsExpressionContextUtils.projectScope() )
        context.appendScope( QgsExpressionContextUtils.layerScope( layer ) )
        context.setFields( layer.fields() )

        if expression.needsGeometry():
            if self.iface:
                # 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] )
                expression.setGeomCalculator( da )
                if QGis.QGIS_VERSION_INT >= 21400: # Methods added in QGIS 2.14
                    expression.setDistanceUnits( QgsProject.instance().distanceUnits() ) 
                    expression.setAreaUnits( QgsProject.instance().areaUnits() )
        
        expression.prepare( context )

        fieldIndex = layer.fieldNameIndex( fieldName )
        if fieldIndex == -1:
            return           
        field = layer.fields()[fieldIndex]
        
        dictResults = {}
        for feature in layer.getFeatures():
            context.setFeature( feature )
            result = expression.evaluate( context )
            if expression.hasEvalError():
                self.msg.show( QApplication.translate( "AutoFields-FieldCalculator", "[Error] (Evaluating) " ) + \
                    expression.evalErrorString(), 'critical' )
                return
                
            dictResults[feature.id()] = { fieldIndex: field.convertCompatible( result ) }
            

        layer.dataProvider().changeAttributeValues( dictResults )
        
        self.msg.show( "[Info] * An expression was calculated on existing features of layer " + layer.name() + ", field " + fieldName + ".", 'info', True )
    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()
    def load_shapefile(self, feature_type, base_path):
        """Load downloaded shape file to QGIS Main Window.

        :param feature_type: What kind of features should be downloaded.
            Currently 'buildings', 'building-points' or 'roads' are supported.
        :type feature_type: str

        :param base_path: The base path of the shape file (without extension).
        :type base_path: str

        :raises: FileMissingError - when buildings.shp not exist
        """

        path = '%s.shp' % base_path

        if not os.path.exists(path):
            message = self.tr(
                '%s does not exist. The server does not have any data for '
                'this extent.' % path)
            raise FileMissingError(message)

        layer = self.iface.addVectorLayer(path, feature_type, 'ogr')

        # Check if it's a building layer and if it's QGIS 2.14 about the 2.5D
        if qgis_version() >= 21400 and feature_type == 'buildings':
            layer_scope = QgsExpressionContextUtils.layerScope(layer)
            if not layer_scope.variable('qgis_25d_height'):
                QgsExpressionContextUtils.setLayerVariable(
                    layer, 'qgis_25d_height', 0.0002)
            if not layer_scope.variable('qgis_25d_angle'):
                QgsExpressionContextUtils.setLayerVariable(
                    layer, 'qgis_25d_angle', 70)

        canvas_srid = self.canvas.mapSettings().destinationCrs().srsid()
        on_the_fly_projection = self.canvas.hasCrsTransformEnabled()
        if canvas_srid != 4326 and not on_the_fly_projection:
            if QGis.QGIS_VERSION_INT >= 20400:
                self.canvas.setCrsTransformEnabled(True)
            else:
                display_warning_message_bar(
                    self.iface,
                    self.tr('Enable \'on the fly\''),
                    self.tr(
                        'Your current projection is different than EPSG:4326. '
                        'You should enable \'on the fly\' to display '
                        'correctly your layers')
                )
    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()
Beispiel #9
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)
Beispiel #10
0
 def _layer_tooltip(self, layer, feat):
     try:
         df = layer.displayField()
         if df:
             return str(feat.attribute(df))
         else:
             context = QgsExpressionContext()
             context.appendScope(QgsExpressionContextUtils.globalScope())
             context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance()))
             context.appendScope(QgsExpressionContextUtils.layerScope(layer))
             context.appendScope(QgsExpressionContextUtils.mapSettingsScope( self.canvas.mapSettings() ) )
             context.setFeature( feat )
             
             x = QgsExpression(layer.displayExpression())
             x.prepare(context)
             return x.evaluate(context).replace('\n', "<br/>")
     except:
         return ""
Beispiel #11
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)
    def __init__(self, objectTypeManager, renderContext, layer, properties):
        assert (properties is not None)
        self.renderContext = renderContext
        self.expressionContext = QgsExpressionContext()
        self.expressionContext.appendScope(
            QgsExpressionContextUtils.layerScope(layer))
        self.layer = layer
        properties = properties or {}
        self.properties = properties

        if properties:
            self.objType = objectTypeManager.objectType(
                layer.geometryType(), properties["comboBox_ObjectType"])
            self.visible = properties.get("visible", True)
        else:
            self.visible = False

        self._exprs = {}
        self.exprAlt = QgsExpression(
            properties.get("fieldExpressionWidget_altitude") or "0")
        self.exprLabel = QgsExpression(
            properties.get("labelHeightWidget", {}).get("editText") or "0")
Beispiel #13
0
    def _layer_tooltip(self, layer, feat):
        try:
            df = layer.displayField()
            if df:
                return str(feat.attribute(df))
            else:
                context = QgsExpressionContext()
                context.appendScope(QgsExpressionContextUtils.globalScope())
                context.appendScope(
                    QgsExpressionContextUtils.projectScope(
                        QgsProject.instance()))
                context.appendScope(
                    QgsExpressionContextUtils.layerScope(layer))
                context.appendScope(
                    QgsExpressionContextUtils.mapSettingsScope(
                        self.canvas.mapSettings()))
                context.setFeature(feat)

                x = QgsExpression(layer.displayExpression())
                x.prepare(context)
                return x.evaluate(context).replace("\n", "<br/>")
        except:
            return ""
Beispiel #14
0
    def load_shapefile(self, feature_type, base_path):
        """Load downloaded shape file to QGIS Main Window.

        :param feature_type: What kind of features should be downloaded.
            Currently 'buildings', 'building-points' or 'roads' are supported.
        :type feature_type: str

        :param base_path: The base path of the shape file (without extension).
        :type base_path: str

        :raises: FileMissingError - when buildings.shp not exist
        """

        path = '%s.shp' % base_path

        if not os.path.exists(path):
            message = self.tr(
                '%s does not exist. The server does not have any data for '
                'this extent.' % path)
            raise FileMissingError(message)

        layer = self.iface.addVectorLayer(path, feature_type, 'ogr')

        # Check if it's a building layer about the 2.5D
        if feature_type == 'buildings':
            layer_scope = QgsExpressionContextUtils.layerScope(layer)
            if not layer_scope.variable('qgis_25d_height'):
                QgsExpressionContextUtils.setLayerVariable(
                    layer, 'qgis_25d_height', 0.0002)
            if not layer_scope.variable('qgis_25d_angle'):
                QgsExpressionContextUtils.setLayerVariable(
                    layer, 'qgis_25d_angle', 70)

        canvas_srid = self.canvas.mapSettings().destinationCrs().srsid()
        on_the_fly_projection = self.canvas.hasCrsTransformEnabled()
        if canvas_srid != 4326 and not on_the_fly_projection:
            self.canvas.setCrsTransformEnabled(True)
    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()
Beispiel #16
0
def print_layout(
    project: QgsProject,
    layout_name: str,
    output_format: OutputFormat,
    feature_filter: str = None,
    scales: list = None,
    scale: int = 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

    :param output_format: The output format, default to PDF if not provided.

    :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 output_format == OutputFormat.Svg:
        settings = QgsLayoutExporter.SvgExportSettings()
    elif output_format in (OutputFormat.Png, OutputFormat.Jpeg):
        settings = QgsLayoutExporter.ImageExportSettings()
    else:
        # PDF by default
        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)
        atlas.updateFeatures()

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

        if scales:
            atlas_layout.referenceMap().setAtlasScalingMode(
                QgsLayoutItemMap.Predefined)
            settings.predefinedMapScales = scales

        if (not scales and atlas_layout.referenceMap().atlasScalingMode()
                == QgsLayoutItemMap.Predefined):
            use_project = project.useProjectScales()
            map_scales = project.mapScales()
            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()
            settings.predefinedMapScales = 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 "{key}" {found} in layout, value "{value}"'.
            format(key=key,
                   found="found" if found else "not found",
                   value=value))

    file_name = "{}_{}.{}".format(clean_string(layout_name), uuid4(),
                                  output_format.name.lower())
    export_path = Path(tempfile.gettempdir()).joinpath(file_name)

    Logger().info("Exporting the request in {} using {}".format(
        export_path, output_format.value))

    if output_format in (OutputFormat.Png, OutputFormat.Jpeg):
        exporter = QgsLayoutExporter(atlas_layout or report_layout)
        result = exporter.exportToImage(str(export_path), settings)
        error = result_message(result)
    elif output_format in (OutputFormat.Svg, ):
        exporter = QgsLayoutExporter(atlas_layout or report_layout)
        result = exporter.exportToSvg(str(export_path), settings)
        error = result_message(result)
    else:
        # Default to PDF
        result, error = QgsLayoutExporter.exportToPdf(atlas or report_layout,
                                                      str(export_path),
                                                      settings)
        # Let's override error message
        _ = error
        error = result_message(result)

    if result != QgsLayoutExporter.Success:
        raise Exception("Export not generated in QGIS exporter {} : {}".format(
            export_path, error))

    if not export_path.is_file():
        logger.warning(
            "No error from QGIS Exporter, but the file does not exist.\n"
            "Message from QGIS exporter : {}\n"
            "File path : {}\n".format(error, export_path))
        raise Exception(
            "Export OK from QGIS, but file not found on the file system : {}".
            format(export_path))

    return export_path
Beispiel #17
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))

        layer = self.parameterAsVectorLayer(parameters, self.INPUT, context)
        field_name = self.parameterAsString(parameters, self.FIELD_NAME,
                                            context)
        field_type = self.TYPES[self.parameterAsEnum(parameters,
                                                     self.FIELD_TYPE, context)]
        width = self.parameterAsInt(parameters, self.FIELD_LENGTH, context)
        precision = self.parameterAsInt(parameters, self.FIELD_PRECISION,
                                        context)
        new_field = self.parameterAsBoolean(parameters, self.NEW_FIELD,
                                            context)
        formula = self.parameterAsString(parameters, self.FORMULA, context)

        expression = QgsExpression(formula)
        da = QgsDistanceArea()
        da.setSourceCrs(source.sourceCrs(), context.transformContext())
        da.setEllipsoid(context.project().ellipsoid())
        expression.setGeomCalculator(da)

        expression.setDistanceUnits(context.project().distanceUnits())
        expression.setAreaUnits(context.project().areaUnits())

        fields = source.fields()
        field_index = fields.lookupField(field_name)
        if new_field or field_index < 0:
            fields.append(
                QgsField(field_name, field_type, '', width, precision))

        (sink, dest_id) = self.parameterAsSink(parameters,
                                               self.OUTPUT, context, fields,
                                               source.wkbType(),
                                               source.sourceCrs())
        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        exp_context = self.createExpressionContext(parameters, context)
        if layer is not None:
            exp_context.appendScope(
                QgsExpressionContextUtils.layerScope(layer))

        expression.prepare(exp_context)

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0

        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            rownum = current + 1
            exp_context.setFeature(f)
            exp_context.lastScope().setVariable("row_number", rownum)
            value = expression.evaluate(exp_context)
            if expression.hasEvalError():
                feedback.reportError(expression.evalErrorString())
            else:
                attrs = f.attributes()
                if new_field or field_index < 0:
                    attrs.append(value)
                else:
                    attrs[field_index] = value
                f.setAttributes(attrs)
                sink.addFeature(f, QgsFeatureSink.FastInsert)
            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
Beispiel #18
0
    def getMatchingFeatures(self, geometry, contains, singleSelect):
        newFeatures = []
        if geometry.type() != QgsWkbTypes.PolygonGeometry:
            return newFeatures

        layer = self.canvas.currentLayer()
        if layer is None:
            return newFeatures

        selectGeomTrans = QgsGeometry(geometry)
        try:
            ct = QgsCoordinateTransform(self.canvas.mapSettings().destinationCrs(), layer.crs(), QgsProject.instance())
            if not ct.isShortCircuited() and selectGeomTrans.type() == QgsWkbTypes.PolygonGeometry:
                poly = selectGeomTrans.asPolygon()
                if len(poly) == 1 and len(poly[0]) == 5:
                    ringIn = poly[0]
                    ringOut = []
                    ringOut.append(ringIn[0])
                    i = 1
                    for j in range(1, 5):
                        v = QgsVector((ringIn[j] - ringIn[j - 1]) / 10.0)
                        for k in range(9):
                            ringOut.append(ringOut[i - 1] + v)
                            i += 1
                        ringOut.append(ringIn[j])
                        i += 1
                    selectGeomTrans = QgsGeometry.fromPolygonXY([ringOut])

            selectGeomTrans.transform(ct)
        except QgsCsException as e:
            QgsMessageLog.logMessage("Selection extends beyond layer's coordinate system")
            return newFeatures

        context = QgsRenderContext.fromMapSettings(self.canvas.mapSettings())
        context.expressionContext().appendScope(QgsExpressionContextUtils.layerScope(layer))

        r = None
        if layer.renderer():
            r = layer.renderer().clone()
            r.startRender(context, layer.fields())

        request = QgsFeatureRequest()
        request.setFilterRect(selectGeomTrans.boundingBox())
        request.setFlags(QgsFeatureRequest.ExactIntersect)

        if r:
            request.setSubsetOfAttributes(r.usedAttributes(context), layer.fields())
        else:
            request.setSubsetOfAttributes([])

        closestFeatureId = 0
        foundSingleFeature = False
        closestFeatureDist = sys.float_info.max
        for f in layer.getFeatures(request):
            context.expressionContext().setFeature(f)
            if r and  not r.willRenderFeature(f, context):
                continue

            g = f.geometry()
            if contains:
                if not selectGeomTrans.contains(g):
                    continue

            else:
                if not selectGeomTrans.intersects(g):
                    continue

            if singleSelect:
                foundSingleFeature = True
                distance = g.distance(selectGeomTrans)
                if distance <= closestFeatureDist:
                    closestFeatureDist = distance
                    closestFeatureId = f.id()
            else:
                newFeatures.append(f.id())

        if singleSelect and foundSingleFeature:
            newFeatures.append(closestFeatureId)

        if r:
            r.stopRender(context)

        return newFeatures
Beispiel #19
0
    def processAlgorithm(self, parameters, context, feedback):
        expr_context = self.createExpressionContext(parameters, context, self.source)
        self.group_by_expr.prepare(expr_context)

        # Group features in memory layers
        source = self.source
        count = self.source.featureCount()
        if count:
            progress_step = 50.0 / count
        current = 0
        groups = {}
        keys = []  # We need deterministic order for the tests
        feature = QgsFeature()
        for feature in self.source.getFeatures():
            expr_context.setFeature(feature)
            group_by_value = self.evaluateExpression(self.group_by_expr, expr_context)

            # Get an hashable key for the dict
            key = group_by_value
            if isinstance(key, list):
                key = tuple(key)

            group = groups.get(key, None)
            if group is None:
                sink, id = QgsProcessingUtils.createFeatureSink(
                    'memory:',
                    context,
                    source.fields(),
                    source.wkbType(),
                    source.sourceCrs())
                layer = QgsProcessingUtils.mapLayerFromString(id, context)
                group = {
                    'sink': sink,
                    'layer': layer,
                    'feature': feature
                }
                groups[key] = group
                keys.append(key)

            group['sink'].addFeature(feature, QgsFeatureSink.FastInsert)

            current += 1
            feedback.setProgress(int(current * progress_step))
            if feedback.isCanceled():
                return

        (sink, dest_id) = self.parameterAsSink(parameters,
                                               self.OUTPUT,
                                               context,
                                               self.fields,
                                               QgsWkbTypes.multiType(source.wkbType()),
                                               source.sourceCrs())
        if sink is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

        # Calculate aggregates on memory layers
        if len(keys):
            progress_step = 50.0 / len(keys)
        for current, key in enumerate(keys):
            group = groups[key]
            expr_context = self.createExpressionContext(parameters, context)
            expr_context.appendScope(QgsExpressionContextUtils.layerScope(group['layer']))
            expr_context.setFeature(group['feature'])

            geometry = self.evaluateExpression(self.geometry_expr, expr_context)
            if geometry is not None and not geometry.isEmpty():
                geometry = QgsGeometry.unaryUnion(geometry.asGeometryCollection())
                if geometry.isEmpty():
                    raise QgsProcessingException(
                        'Impossible to combine geometries for {} = {}'
                        .format(self.group_by, group_by_value))

            attrs = []
            for fields_expr in self.fields_expr:
                attrs.append(self.evaluateExpression(fields_expr, expr_context))

            # Write output feature
            outFeat = QgsFeature()
            if geometry is not None:
                outFeat.setGeometry(geometry)
            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(50 + int(current * progress_step))
            if feedback.isCanceled():
                return

        return {self.OUTPUT: dest_id}
    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
    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
Beispiel #22
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
Beispiel #23
0
    def processAlgorithm(self, parameters, context, feedback):
        expr_context = self.createExpressionContext(parameters, context)
        self.group_by_expr.prepare(expr_context)

        # Group features in memory layers
        source = self.source
        count = self.source.featureCount()
        if count:
            progress_step = 50.0 / count
        current = 0
        groups = {}
        keys = []  # We need deterministic order for the tests
        feature = QgsFeature()
        for feature in self.source.getFeatures():
            expr_context.setFeature(feature)
            group_by_value = self.evaluateExpression(self.group_by_expr,
                                                     expr_context)

            # Get an hashable key for the dict
            key = group_by_value
            if isinstance(key, list):
                key = tuple(key)

            group = groups.get(key, None)
            if group is None:
                sink, id = QgsProcessingUtils.createFeatureSink(
                    'memory:', context, source.fields(), source.wkbType(),
                    source.sourceCrs())
                layer = QgsProcessingUtils.mapLayerFromString(id, context)
                group = {'sink': sink, 'layer': layer, 'feature': feature}
                groups[key] = group
                keys.append(key)

            group['sink'].addFeature(feature, QgsFeatureSink.FastInsert)

            current += 1
            feedback.setProgress(int(current * progress_step))
            if feedback.isCanceled():
                return

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

        # Calculate aggregates on memory layers
        if len(keys):
            progress_step = 50.0 / len(keys)
        for current, key in enumerate(keys):
            group = groups[key]
            expr_context = self.createExpressionContext(parameters, context)
            expr_context.appendScope(
                QgsExpressionContextUtils.layerScope(group['layer']))
            expr_context.setFeature(group['feature'])

            geometry = self.evaluateExpression(self.geometry_expr,
                                               expr_context)
            if geometry is not None and not geometry.isEmpty():
                geometry = QgsGeometry.unaryUnion(
                    geometry.asGeometryCollection())
                if geometry.isEmpty():
                    raise QgsProcessingException(
                        'Impossible to combine geometries for {} = {}'.format(
                            self.group_by, group_by_value))

            attrs = []
            for fields_expr in self.fields_expr:
                attrs.append(self.evaluateExpression(fields_expr,
                                                     expr_context))

            # Write output feature
            outFeat = QgsFeature()
            if geometry is not None:
                outFeat.setGeometry(geometry)
            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(50 + int(current * progress_step))
            if feedback.isCanceled():
                return

        return {self.OUTPUT: dest_id}
    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)

        if output.value == '':
            ext = output.getDefaultFileExtension(self)
            output.value = system.getTempFilenameInTempFolder(
                output.name + '.' + ext)

        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()))

        # add layer to registry to fix https://issues.qgis.org/issues/17300
        # it is necessary only for aggregate expressions that verify that layer
        # is registered
        removeRegistryAfterEvaluation = False
        if not QgsMapLayerRegistry.instance().mapLayer(layer.id()):
            removeRegistryAfterEvaluation = True
            QgsMapLayerRegistry.instance().addMapLayer(layer, addToLegend=False)

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

        error = ''
        calculationSuccess = True

        features = vector.features(layer)
        total = 100.0 / len(features) if len(features) > 0 else 1

        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

        # remove from registry if added for expression requirement
        # see above comment about fix #17300
        if removeRegistryAfterEvaluation:
            QgsMapLayerRegistry.instance().removeMapLayer(layer)

        if not calculationSuccess:
            raise GeoAlgorithmExecutionException(
                self.tr('An error occurred while evaluating the calculation '
                        'string:\n%s' % error))
    def expressionBasedUpdate(self,
                              layer,
                              dictProperties,
                              featureId,
                              index=None,
                              value=None):
        """ Defines the logic of the expression-based update to be applied.
            This SLOT listens to featureAdded, geometryChanged, and attributeValueChanged SIGNALS.
        """
        # Check if AutoField is there, otherwise return
        fieldIndex = layer.fieldNameIndex(dictProperties['field'])
        if fieldIndex == -1:
            self.msg.show(
                QApplication.translate( "EventManager", "[Error] Updating AutoField " ) + \
                dictProperties['field'] + \
                QApplication.translate( "EventManager", " in layer " ) + \
                layer.name() + QApplication.translate( "EventManager", " was NOT possible." ) + \
                QApplication.translate( "EventManager", " Perhaps you just removed it but haven't saved the changes yet?" ),
                'warning' )
            return

        event = ""
        result = None

        expression = QgsExpression(dictProperties['expression'])
        if expression.hasParserError():
            self.msg.show( QApplication.translate( "EventManager", "[Error] (Parsing) " ) + \
                expression.parserErrorString(), 'critical' )
            result = NULL

        # Avoid infinite recursion (changing the same attribute value infinitely).
        if not index is None:  # Filters out the featureAdded SIGNAL
            if type(index) == int:  # Filters out the geometryChanged SIGNAL

                if index == fieldIndex:  # This call comes from the same AutoField, so return
                    return

                if self.afm.isFieldAnAutoField(
                        layer,
                        layer.fields()
                    [index].name()):  # Call from AutoField, don't listen
                    # This is to prevent corrupting the layerEditBuffer and being bitten by:
                    #   Fatal: ASSERT: "mChangedAttributeValues.isEmpty()" in file /tmp/buildd/qgis-2.14.2+20trusty/src/core/qgsvectorlayereditbuffer.cpp, line 585
                    return

                #if type(value)==QPyNullVariant:
                # Vector layers with numeric field whose value for 1st feature is NULL
                #   trigger an attributeValueChanged SIGNAL when start editing from the
                #   attribute table window. We use this conditional to avoid such SIGNAL.
                #   The ideal case is that such NULL valued SIGNAL shouldn't be emitted by QGIS.
                #    return
                # While the previous block reduces the number of times attributeValueChanged
                #   is called from the attribute table, it leads to a QGIS bug:
                #   Fatal: ASSERT: "mChangedAttributeValues.isEmpty()" in file /tmp/buildd/qgis-2.14.2+20trusty/src/core/qgsvectorlayereditbuffer.cpp, line 585
                #   I prefer the attributeValueChanged to be called multiple
                #   times (inefficient) than to open the possibility to a bug.
                # As soon as QGIS bug #15272 is solved, the number of calls will be reduced!

                event = "attributeValueChanged"
            else:
                event = "geometryChanged"
        else:
            event = "featureAdded"

        feature = layer.getFeatures(QgsFeatureRequest(featureId)).next()

        if result is None:
            context = QgsExpressionContext()
            context.appendScope(QgsExpressionContextUtils.globalScope())
            context.appendScope(QgsExpressionContextUtils.projectScope())
            context.appendScope(QgsExpressionContextUtils.layerScope(layer))
            context.setFields(feature.fields())
            context.setFeature(feature)

            if expression.needsGeometry():
                if self.iface:
                    # 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])
                    expression.setGeomCalculator(da)
                    if QGis.QGIS_VERSION_INT >= 21400:  # Methods added in QGIS 2.14
                        expression.setDistanceUnits(
                            QgsProject.instance().distanceUnits())
                        expression.setAreaUnits(
                            QgsProject.instance().areaUnits())

            expression.prepare(context)
            result = expression.evaluate(context)

            if expression.hasEvalError():
                self.msg.show( QApplication.translate( "EventManager", "[Error] (Evaluating) " ) + \
                    expression.evalErrorString(), 'critical' )
                result = NULL

        field = layer.fields()[fieldIndex]
        res = field.convertCompatible(result)
        # If result is None, res will be None, but even in that case, QGIS knows
        #   what to do with it while saving, it seems it's treated as NULL.

        # TODO when bug #15311 is fixed, this block should work better
        #if dictProperties['expression'] in self.listProviderExpressions:
        #    # Save directly to provider
        #    layer.dataProvider().changeAttributeValues( { featureId : { fieldIndex : res } } )
        #else: # Save to layer
        #    layer.changeAttributeValue( featureId, fieldIndex, res )

        # Workaround
        if event == 'featureAdded':  # Save directly to the provider
            layer.dataProvider().changeAttributeValues(
                {featureId: {
                    fieldIndex: res
                }})
        else:  # Save to layer
            layer.changeAttributeValue(featureId, fieldIndex, res)

        self.msg.show( "[Info] * AutoField's value updated to " + unicode(res) + \
            ", (" + layer.name() + "." + dictProperties['field'] + ") by " + event +".", 'info', True )
Beispiel #26
0
 def layer_context(self, layer: QgsVectorLayer) -> QgsRenderContext:
     context = QgsRenderContext.fromMapSettings(
         self.iface.mapCanvas().mapSettings())
     context.expressionContext().appendScope(
         QgsExpressionContextUtils.layerScope(layer))
     return context
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))

        layer = self.parameterAsVectorLayer(parameters, self.INPUT, context)
        field_name = self.parameterAsString(parameters, self.FIELD_NAME, context)
        field_type = self.TYPES[self.parameterAsEnum(parameters, self.FIELD_TYPE, context)]
        width = self.parameterAsInt(parameters, self.FIELD_LENGTH, context)
        precision = self.parameterAsInt(parameters, self.FIELD_PRECISION, context)
        new_field = self.parameterAsBool(parameters, self.NEW_FIELD, context)
        formula = self.parameterAsString(parameters, self.FORMULA, context)

        expression = QgsExpression(formula)
        da = QgsDistanceArea()
        da.setSourceCrs(source.sourceCrs(), context.transformContext())
        da.setEllipsoid(context.project().ellipsoid())
        expression.setGeomCalculator(da)

        expression.setDistanceUnits(context.project().distanceUnits())
        expression.setAreaUnits(context.project().areaUnits())

        field_index = layer.fields().lookupField(field_name)
        if new_field or field_index < 0:
            res = layer.dataProvider().addAttributes([QgsField(field_name, field_type, '', width, precision)])
            if not res:
                raise QgsProcessingException("The new field couldn't be created! We suggest you to create it using QGIS tools and then update its values with this algorithm.")

            layer.updateFields()
            field_index = layer.fields().lookupField(field_name)

        field = layer.fields()[field_index]

        exp_context = self.createExpressionContext(parameters, context)
        if layer is not None:
            exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        expression.prepare(exp_context)

        features = layer.getFeatures()
        total = 100.0 / layer.featureCount() if layer.featureCount() else 0

        dict_results = dict()

        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            rownum = current + 1
            exp_context.setFeature(f)
            exp_context.lastScope().setVariable("row_number", rownum)
            value = expression.evaluate(exp_context)
            if expression.hasEvalError():
                feedback.reportError(expression.evalErrorString())
            else:
                dict_results[f.id()] = {field_index: field.convertCompatible(value)}

            feedback.setProgress(int(current * total))

        layer.dataProvider().changeAttributeValues(dict_results)

        return {self.OUTPUT: layer}
Beispiel #28
0
def calculate_node(
        node, node_attr_name, node_attr_id, layer, discarded_feats):
    operator = node.get('operator', DEFAULT_OPERATOR)
    # for backwards compatibility, we treat the old
    # 'Use a custom field (no recalculation) as the new one with no parentheses
    if operator in (OPERATORS_DICT['CUSTOM'],
                    'Use a custom field (no recalculation)'):
        customFormula = node.get('customFormula', '')
        expression = QgsExpression(customFormula)
        valid, err_msg = QgsExpression.checkExpression(customFormula, None)
        if not valid:
            raise InvalidFormula(
                'Invalid formula "%s": %s' % (customFormula, err_msg))
        if customFormula == '':
            # use the custom field values instead of recalculating them
            request = QgsFeatureRequest().setFlags(
                QgsFeatureRequest.NoGeometry).setSubsetOfAttributes(
                    [node['field']], layer.fields())
            for feat in layer.getFeatures(request):
                if feat[node['field']] == NULL:
                    discard_feat = True
                    discarded_feat = DiscardedFeature(
                        feat.id(), 'Missing value')
                    discarded_feats.add(discarded_feat)
            return discarded_feats
        else:
            # attempt to retrieve a formula from the description and to
            # calculate the field values based on that formula
            context = QgsExpressionContext()
            context.appendScope(QgsExpressionContextUtils.layerScope(layer))
            expression.prepare(context)
            request = QgsFeatureRequest().setFlags(
                QgsFeatureRequest.NoGeometry)
            with edit(layer):
                for feat in layer.getFeatures(request):
                    context.setFeature(feat)
                    value = expression.evaluate(context)
                    if expression.hasEvalError():
                        raise ValueError(expression.evalErrorString())
                    if value == NULL:
                        discard_feat = True
                        discarded_feat = DiscardedFeature(
                            feat.id(), 'Missing value')
                        discarded_feats.add(discarded_feat)
                    layer.changeAttributeValue(feat.id(), node_attr_id, value)
            return discarded_feats
    # the existance of children should already be checked
    children = node['children']
    with edit(layer):
        request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry)
        for feat in layer.getFeatures(request):
            # If a feature contains any NULL value, discard_feat will
            # be set to True and the corresponding node value will be
            # set to NULL
            discard_feat = False
            feat_id = feat.id()

            # init node_value to the correct value depending on the
            # node's operator
            if operator in SUM_BASED_OPERATORS:
                node_value = 0
            elif operator in MUL_BASED_OPERATORS:
                node_value = 1
            else:
                raise InvalidOperator('Invalid operator: %s' % operator)
            for child in children:
                if 'field' not in child:
                    raise InvalidChild()
                    # for instance, if the RI can't be calculated, then
                    # also the IRI can't be calculated
                    # But it shouldn't happen, because all the children
                    # should be previously linked to corresponding fields
                if feat[child['field']] == NULL:
                    discard_feat = True
                    discarded_feat = DiscardedFeature(feat_id, 'Missing value')
                    discarded_feats.add(discarded_feat)
                    break  # proceed to the next feature
                # multiply a variable by -1 if it isInverted
                try:
                    inversion_factor = -1 if child['isInverted'] else 1
                except KeyError:  # child is not inverted
                    inversion_factor = 1
                if operator in IGNORING_WEIGHT_OPERATORS:
                    # although these operators ignore weights, they
                    # take into account the inversion
                    child_weighted = \
                        feat[child['field']] * inversion_factor
                else:  # also multiply by the weight
                    child_weighted = (
                        child['weight'] *
                        feat[child['field']] * inversion_factor)

                if operator in SUM_BASED_OPERATORS:
                    node_value += child_weighted
                elif operator in MUL_BASED_OPERATORS:
                    node_value *= child_weighted
                else:
                    error_message = 'Invalid operator: %s' % operator
                    raise RuntimeError(error_message)
            if discard_feat:
                node_value = NULL
            elif operator == OPERATORS_DICT['AVG']:
                # it is equivalent to do a weighted sum with equal weights, or
                # to do the simple sum (ignoring weights) and dividing by the
                # number of children (we use the latter solution)
                node_value /= len(children)  # for sure, len(children)!=0
            elif operator == OPERATORS_DICT['GEOM_MEAN']:
                # the geometric mean
                # (see http://en.wikipedia.org/wiki/Geometric_mean)
                # is the product of the N combined items, elevated by 1/N
                try:
                    # NOTE: in python2 this check was the default. In python3
                    # it would produce a complex number without raising any
                    # error
                    if (node_value < 0
                            and not (1. / len(children)).is_integer()):
                        raise ValueError('negative number cannot be raised'
                                         ' to a fractional power')
                    node_value **= 1. / len(children)
                except ValueError:
                    node_value = NULL
                    discarded_feat = DiscardedFeature(feat_id, 'Invalid value')
                    discarded_feats.add(discarded_feat)
            layer.changeAttributeValue(
                feat_id, node_attr_id, node_value)
    return discarded_feats
Beispiel #29
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}
    def processAlgorithm(self, parameters, context, feedback):
        path = self.parameterAsFile(parameters, self.INPUT, context)
        t_file = self.parameterAsVectorLayer(
            parameters,
            self.FILE_TABLE,
            context
        )
        t_troncon = self.parameterAsVectorLayer(
            parameters,
            self.SEGMENT_TABLE,
            context
        )
        t_obs = self.parameterAsVectorLayer(
            parameters,
            self.OBSERVATIONS_TABLE,
            context
        )
        t_regard = self.parameterAsVectorLayer(
            parameters,
            self.MANHOLES_TABLE,
            context
        )

        paths = path.split(';')
        if len(paths) != 1:
            raise QgsProcessingException(
                tr('* ERREUR: 1 fichier a la fois {}.').format(path)
            )

        if not os.path.exists(path):
            raise QgsProcessingException(
                tr('* ERREUR: {} n\'existe pas.').format(path)
            )

        # add date fields to itv file table
        # relation_aggregate(
        #     'itv_tronco_id_file_itv_file20_id',
        #     'max',
        #     "abf"
        # )
        if 'date_debut' not in t_file.dataProvider().fields().names():
            with edit(t_file):
                res = t_file.dataProvider().addAttributes([
                    QgsField("date_debut", QVariant.String),
                    QgsField("date_fin", QVariant.String)
                ])
                if res:
                    t_file.updateFields()

        feat_file = None
        with open(path, 'rb') as f:
            basename = os.path.basename(path)
            md = hashlib.md5()
            md.update(f.read())
            hashcontent = md.hexdigest()

            feat_file = QgsVectorLayerUtils.createFeature(t_file)
            feat_file.setAttribute('basename', basename)
            feat_file.setAttribute('hashcontent', hashcontent)

        if not feat_file:
            raise QgsProcessingException(
                tr(
                    '* ERREUR: le fichier {} n\'a pas été lu '
                    'correctement.'
                ).format(path)
            )

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

        exp_str = QgsExpression.createFieldEqualityExpression(
            'basename', feat_file['basename']
        ) + ' AND ' + QgsExpression.createFieldEqualityExpression(
            'hashcontent', feat_file['hashcontent']
        )
        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 _t in t_file.getFeatures(request):
            raise QgsProcessingException(
                tr('* ERREUR: le fichier {} a deja ete lu').format(
                    path
                )
            )

        exp_str = QgsExpression.createFieldEqualityExpression(
            'hashcontent', feat_file['hashcontent']
        )
        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 _t in t_file.getFeatures(request):
            raise QgsProcessingException(
                tr(
                    '* ERREUR: le fichier {} semble deja avoir ete lu'
                ).format(path)
            )

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

        exp_str = 'maximum("id")'
        exp = QgsExpression(exp_str)

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

        last_t_id = exp.evaluate(exp_context)
        if not last_t_id:
            last_t_id = 0

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

        exp_str = 'maximum("id")'
        exp = QgsExpression(exp_str)

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

        last_r_id = exp.evaluate(exp_context)
        if not last_r_id:
            last_r_id = 0

        # lecture des entetes
        ENCODING = 'ISO-8859-1'
        LANG = 'fr'
        DELIMITER = ','
        DECIMAL = '.'
        QUOTECHAR = '"'
        VERSION = ''

        with open(path, 'rb') as f:
            for line in f:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SUCCESS: 0}
                try:
                    line = line.decode()
                except UnicodeDecodeError:
                    raise QgsProcessingException(
                        'Error while reading {}'.format(path)
                    )
                # remove break line
                line = line.replace('\n', '').replace('\r', '')
                if line.startswith('#'):
                    if line.startswith('#A'):
                        if line.startswith('#A1'):
                            ENCODING = line[4:]
                            if ENCODING.find(':') != -1:
                                ENCODING = ENCODING[:ENCODING.find(':')]
                        elif line.startswith('#A2'):
                            LANG = line[4:]
                        elif line.startswith('#A3'):
                            DELIMITER = line[4:]
                        elif line.startswith('#A4'):
                            DECIMAL = line[4:]
                        elif line.startswith('#A5'):
                            QUOTECHAR = line[4:]
                    else:
                        break

        # Dialect CSV pour la lecture des tableaux de valeurs du
        # fichier d'ITV
        class itvDialect(csv.Dialect):
            strict = True
            skipinitialspace = True
            quoting = csv.QUOTE_MINIMAL
            delimiter = DELIMITER
            quotechar = QUOTECHAR
            lineterminator = '\r\n'

        # Liste des troncons, observations et regards
        troncons = []
        regards = []
        observations = []

        # Lectures des donnees
        with open(path, 'rb') as f:
            # Identifiant de départ
            t_id = last_t_id
            r_id = last_r_id
            # Entête
            header = []
            # Nom du tableau
            array = ''
            # Observations de troncons ?
            obs_for_troncon = False
            # initialisation du Dialect CSV pour ITV
            dia = itvDialect()
            # Lecture ligne à ligne du fichier
            for line in f:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SUCCESS: 0}
                # Decoding line en utilisant l'encoding du fichier
                line = line.decode(ENCODING)
                # remove break line
                line = line.replace('\n', '').replace('\r', '')
                # Ligne commençant par un # est une ligne d'entête
                if line.startswith('#'):
                    # Entête de troncon ou regard
                    if line.startswith('#B'):
                        l_b = io.StringIO(line[5:])
                        for l_r in csv.reader(l_b, dia):
                            header = l_r
                            break
                        array = line[1:4]
                        continue
                    # Entête d'observation
                    elif line.startswith('#C'):
                        if header[0].startswith('A'):
                            obs_for_troncon = True
                        else:
                            obs_for_troncon = False
                        l_b = io.StringIO(line[3:])
                        for l_r in csv.reader(l_b, dia):
                            header = l_r
                            break
                        array = line[1:2]
                        continue
                    # Fin d'observation
                    elif line.startswith('#Z'):
                        header = []
                        array = ''
                        obs_for_troncon = False
                        continue
                # La ligne contient des donnees
                else:
                    if not header:  # an error in the file structure
                        continue
                    l_b = io.StringIO(line)
                    for l_r in csv.reader(l_b, dia):
                        data = l_r
                        row = list(
                            zip(
                                [h.lower() for h in header],
                                [t for t in data]
                            )
                        )
                        # observation
                        if array == 'C':
                            if obs_for_troncon:
                                observations.append(
                                    row + [('id_troncon', t_id)]
                                )
                        # Premiere ligne de description d'un troncon ou regard
                        elif array == 'B01':
                            if header[0].startswith('A'):
                                t_id += 1
                                troncons.append([('id', t_id)] + row)
                            elif header[0].startswith('C'):
                                r_id += 1
                                regards.append([('id', r_id)] + row)
                        # Ligne complémentaire de description
                        else:
                            if header[0].startswith('A'):
                                troncons[-1] += row
                            elif header[0].startswith('C'):
                                regards[-1] += row

        # Recuperation des references de noeuds et dates
        itv_dates = []
        regard_node_refs = []
        regard_ref_id = {}
        max_r_id = last_r_id
        for reg in regards:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            d_rg = dict(reg)
            if d_rg['caa'] and d_rg['caa'] not in regard_node_refs:
                regard_node_refs.append(d_rg['caa'])
                regard_ref_id[d_rg['caa']] = d_rg['id']
            if d_rg['id'] and d_rg['id'] > max_r_id:
                max_r_id = d_rg['id']
            if 'cbf' in d_rg and d_rg['cbf'] not in itv_dates:
                itv_dates.append(d_rg['cbf'])

        node_refs = []
        for tro in troncons:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            d_tr = dict(tro)
            # The nodes ref are stored in AAB, AAD, AAF and AAT
            if 'aab' in d_tr and \
                    d_tr['aab'] and \
                    d_tr['aab'] not in regard_node_refs and \
                    d_tr['aab'] not in node_refs:
                node_refs.append(d_tr['aab'])
            if 'aad' in d_tr and \
                    d_tr['aad'] and \
                    d_tr['aad'] not in regard_node_refs and \
                    d_tr['aad'] not in node_refs:
                node_refs.append(d_tr['aad'])
            if 'aaf' in d_tr and \
                    d_tr['aaf'] and \
                    d_tr['aaf'] not in regard_node_refs and \
                    d_tr['aaf'] not in node_refs:
                node_refs.append(d_tr['aaf'])
            if 'aat' in d_tr and \
                    d_tr['aat'] and \
                    d_tr['aat'] not in regard_node_refs and \
                    d_tr['aat'] not in node_refs:
                node_refs.append(d_tr['aat'])
            if 'abf' in d_tr and d_tr['abf'] not in itv_dates:
                itv_dates.append(d_tr['abf'])

        # Ajout des regards manquant
        for n_ref in node_refs:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            max_r_id += 1
            regards.append([('id', max_r_id), ('caa', n_ref)])
            regard_ref_id[n_ref] = max_r_id

        # Ajout des identifiants de regards aux tronçons
        regard_refs = regard_ref_id.keys()
        for tro in troncons:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            d_tr = dict(tro)
            # If AAD is not defined then it is equal to AAB
            if 'aad' not in d:
                d['aad'] = d['aab']
            if d_tr['aad'] and \
                    d_tr['aad'] in regard_refs:
                tro += [('id_regard1', regard_ref_id[d_tr['aad']])]
            if d_tr['aaf'] and \
                    d_tr['aaf'] in regard_refs:
                tro += [('id_regard2', regard_ref_id[d_tr['aaf']])]
            if 'aat' in d_tr.keys() and \
                    d_tr['aat'] and \
                    d_tr['aat'] in regard_refs:
                tro += [('id_regard3', regard_ref_id[d_tr['aat']])]

        # Verification des champs
        fields = provider_fields(t_troncon.fields())
        for tro in troncons:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            for key, val in tro:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SUCCESS: 0}

                if fields.indexOf(key) == -1:
                    raise QgsProcessingException(
                        tr(
                            '* ERREUR dans le fichier : '
                            'le champs de tronçon "{}" est inconnue'
                        ).format(key)
                    )
                field = fields.field(key)
                if isinstance(val, str) and field.isNumeric():
                    if val:
                        try:
                            float(val.replace(DECIMAL, '.'))
                        except BaseException:
                            raise QgsProcessingException(
                                tr(
                                    '* ERREUR dans le fichier : '
                                    'le champs de tronçon "{}" est '
                                    'numérique mais pas la valeur "{}"'
                                ).format(key, val)
                            )

        fields = provider_fields(t_obs.fields())
        for obs in observations:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            for key, val in obs:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SUCCESS: 0}

                if fields.indexOf(key) == -1:
                    raise QgsProcessingException(
                        tr(
                            '* ERREUR dans le fichier : '
                            'le champs d\'observation "{}" est '
                            'inconnue'
                        ).format(key)
                    )
                field = fields.field(key)
                if isinstance(val, str) and field.isNumeric():
                    if val:
                        try:
                            float(val.replace(DECIMAL, '.'))
                        except BaseException:
                            raise QgsProcessingException(
                                tr(
                                    '* ERREUR dans le fichier : '
                                    'le champs d\'observation "{}" est '
                                    'numérique mais pas la valeur "{}"'
                                ).format(key, val)
                            )

        fields = provider_fields(t_regard.fields())
        for reg in regards:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            for key, val in reg:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SUCCESS: 0}

                if fields.indexOf(key) == -1:
                    raise QgsProcessingException(
                        tr(
                            '* ERREUR dans le fichier : '
                            'le champs de regard "{}" est inconnue'
                        ).format(key)
                    )
                field = fields.field(key)
                if isinstance(val, str) and field.isNumeric():
                    if val:
                        try:
                            float(val.replace(DECIMAL, '.'))
                        except BaseException:
                            raise QgsProcessingException(
                                tr(
                                    '* ERREUR dans le fichier : '
                                    'le champs de regard "{}" est '
                                    'numérique mais pas la valeur "{}"'
                                ).format(key, val)
                            )

        # Finalisation objet fichier
        feat_file.setAttribute('encoding', ENCODING)
        feat_file.setAttribute('lang', LANG)
        if VERSION:
            feat_file.setAttribute('version', VERSION)
        if itv_dates:
            feat_file.setAttribute('date_debut', min(itv_dates))
            feat_file.setAttribute('date_fin', max(itv_dates))

        # Stop the algorithm if cancel button has been clicked
        if feedback.isCanceled():
            return {self.SUCCESS: 0}

        # Ajout de l'objet fichier
        t_file.startEditing()
        (res, outFeats) = t_file.dataProvider().addFeatures([feat_file])
        if not res or not outFeats:
            raise QgsProcessingException(
                tr(
                    '* ERREUR: lors de l\'enregistrement du fichier {}'
                ).format(', '.join(t_file.dataProvider().errors()))
            )
        if not t_file.commitChanges():
            raise QgsProcessingException(
                tr('* ERROR: Commit {}.').format(t_file.commitErrors())
            )

        # Mise a jour de l'identifiant de l'objet fichier
        feat_file.setAttribute('id', outFeats[0]['id'])

        # Creation des objets troncons
        features = []
        fields = provider_fields(t_troncon.fields())
        for tro in troncons:
            feat_t = QgsVectorLayerUtils.createFeature(t_troncon)
            feat_t.setAttribute('id_file', feat_file['id'])
            for key, val in tro:
                field = fields.field(key)
                if isinstance(val, str) and field.isNumeric():
                    if val:
                        feat_t.setAttribute(
                            key, float(val.replace(DECIMAL, '.'))
                        )
                else:
                    feat_t.setAttribute(key, val)
            features.append(feat_t)

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

        # Creation des objets observations
        features = []
        fields = provider_fields(t_obs.fields())
        for obs in observations:
            feat_o = QgsVectorLayerUtils.createFeature(t_obs)
            feat_o.setAttribute('id_file', feat_file['id'])
            for key, val in obs:
                field = fields.field(key)
                if isinstance(val, str) and field.isNumeric():
                    if val:
                        feat_o.setAttribute(
                            key, float(val.replace(DECIMAL, '.'))
                        )
                else:
                    feat_o.setAttribute(key, val)
            features.append(feat_o)

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

        # Creation des objets regards
        features = []
        fields = provider_fields(t_regard.fields())
        for reg in regards:
            feat_r = QgsVectorLayerUtils.createFeature(t_regard)
            feat_r.setAttribute('id_file', feat_file['id'])
            for key, val in reg:
                field = fields.field(key)
                if isinstance(val, str) and field.isNumeric():
                    if val:
                        feat_r.setAttribute(
                            key, float(val.replace(DECIMAL, '.'))
                        )
                else:
                    feat_r.setAttribute(key, val)
            features.append(feat_r)

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

        # Returns empty dict if no outputs
        return {self.SUCCESS: 1}
    def processAlgorithm(self, parameters, context, feedback):

        t_troncon = self.parameterAsSource(parameters, self.SEGMENTS_TABLE,
                                           context)
        g_troncon = self.parameterAsSource(parameters, self.GEOM_SEGMENTS,
                                           context)
        t_obs = self.parameterAsSource(parameters, self.OBSERVATION_TABLE,
                                       context)
        g_obs = self.parameterAsVectorLayer(parameters, self.GEOM_OBSERVATION,
                                            context)

        # Get troncon ids and file ids
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project()))
        exp_context.appendScope(t_troncon.createExpressionContextScope())

        exp_str = '"id_geom_troncon" IS NOT NULL'
        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()))

        request = QgsFeatureRequest(exp, exp_context)
        request.setSubsetOfAttributes(
            ['id', 'aab', 'aad', 'aaf', 'abq', 'id_file', 'id_geom_troncon'],
            t_troncon.fields())
        has_geo_troncon = False
        troncons = {}
        file_ids = []
        for tro in t_troncon.getFeatures(request):
            troncons[tro['id']] = tro
            file_ids.append(tro['id_file'])
            has_geo_troncon = True

            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.OBSERVATIONS_CREATED: None}

        if not has_geo_troncon:
            raise QgsProcessingException(tr('* ERROR: No troncon geometries'))

        # Get observation ids
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project()))
        exp_context.appendScope(t_obs.createExpressionContextScope())

        exp_str = ('"id_troncon" IN ({}) AND '
                   '"id_file" IN ({})').format(
                       ','.join([str(i) for i in troncons.keys()]),
                       ','.join([str(i) for i in file_ids]))
        exp = QgsExpression(exp_str)

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

        obs_ids = []
        request = QgsFeatureRequest(exp, exp_context)
        for obs in t_obs.getFeatures(request):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.OBSERVATIONS_CREATED: None}

            troncon = troncons[obs['id_troncon']]

            # verifying ITV file
            if troncon['id_file'] != obs['id_file']:
                continue

            obs_ids.append(obs['id'])

        if not obs_ids:
            raise QgsProcessingException(
                tr('* ERROR: No observations to geolocalize found'))

        # Check observations already geolocalised on troncon
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project()))
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(g_obs))

        exp_str = '"id" IN ({})'.format(','.join([str(i) for i in obs_ids]))
        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)
        geo_observations = []
        for obs in g_obs.getFeatures(request):
            geo_observations.append(obs['id'])

            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.OBSERVATIONS_CREATED: None}

        # build observation geometry based on table
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project()))
        exp_context.appendScope(t_obs.createExpressionContextScope())

        exp_str = ('"id_troncon" IN ({}) AND '
                   '"id_file" IN ({})').format(
                       ','.join([str(i) for i in troncons.keys()]),
                       ','.join([str(i) for i in file_ids]))
        if geo_observations:
            exp_str += ' AND id NOT IN ({})'.format(','.join(
                [str(i) for i in geo_observations]))
        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)
        features = []
        fields = provider_fields(g_obs.fields())
        for obs in t_obs.getFeatures(request):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.OBSERVATIONS_CREATED: None}

            troncon = troncons[obs['id_troncon']]

            # verifying ITV file
            if troncon['id_file'] != obs['id_file']:
                continue

            geo_req = QgsFeatureRequest()
            geo_req.setFilterFid(troncon['id_geom_troncon'])
            for g_tro in g_troncon.getFeatures(geo_req):
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.OBSERVATIONS_CREATED: None}

                geom = g_tro.geometry()
                pt = None
                if troncon['aab'] == troncon['aad']:
                    pt = geom.interpolate(geom.length() * obs['i'] /
                                          troncon['abq'])
                else:
                    pt = geom.interpolate(geom.length() *
                                          (1 - obs['i'] / troncon['abq']))
                fet = QgsFeature(fields)
                fet.setGeometry(pt)
                fet.setAttribute('id', obs['id'])
                features.append(fet)

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

        # Returns empty dict if no outputs
        return {self.OBSERVATIONS_CREATED: len(features)}
Beispiel #32
0
 def capaAmbMapificacio(capa):
     return QgsExpressionContextUtils.layerScope(capa).variable(mv.MAP_ID)
def exportJSONLayer(layer, eachPopup, precision, tmpFileName, exp_crs,
                    layerFileName, safeLayerName, minify, canvas,
                    restrictToExtent, iface, extent):
    cleanedLayer = writeTmpLayer(layer, eachPopup, restrictToExtent, iface,
                                 extent)
    if is25d(layer, canvas, restrictToExtent, extent):
        provider = cleanedLayer.dataProvider()
        provider.addAttributes([
            QgsField("height", QVariant.Double),
            QgsField("wallColor", QVariant.String),
            QgsField("roofColor", QVariant.String)
        ])
        cleanedLayer.updateFields()
        fields = cleanedLayer.pendingFields()
        renderer = layer.rendererV2()
        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)
        cleanedLayer.startEditing()
        for feat in feats:
            context.setFeature(feat)
            height = expression.evaluate(context)
            if isinstance(renderer, QgsCategorizedSymbolRendererV2):
                classAttribute = renderer.classAttribute()
                attrValue = feat.attribute(classAttribute)
                catIndex = renderer.categoryIndexForValue(attrValue)
                categories = renderer.categories()
                symbol = categories[catIndex].symbol()
            elif isinstance(renderer, QgsGraduatedSymbolRendererV2):
                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.symbolForFeature2(feat, renderContext)
            wallColor = symbol.symbolLayer(1).subSymbol().color().name()
            roofColor = symbol.symbolLayer(2).subSymbol().color().name()
            cleanedLayer.changeAttributeValue(feat.id() + 1, heightField,
                                              height)
            cleanedLayer.changeAttributeValue(feat.id() + 1, wallField,
                                              wallColor)
            cleanedLayer.changeAttributeValue(feat.id() + 1, roofField,
                                              roofColor)
        cleanedLayer.commitChanges()
        renderer.stopRender(renderContext)

    writer = QgsVectorFileWriter
    options = []
    if precision != "maintain":
        options.append("COORDINATE_PRECISION=" + unicode(precision))
    writer.writeAsVectorFormat(cleanedLayer,
                               tmpFileName,
                               'utf-8',
                               exp_crs,
                               'GeoJson',
                               0,
                               layerOptions=options)

    with open(layerFileName, "w") as f2:
        f2.write("var json_" + unicode(safeLayerName) + "=")
        with open(tmpFileName, "r") as tmpFile:
            for line in tmpFile:
                if minify:
                    line = line.strip("\n\t ")
                    line = removeSpaces(line)
                f2.write(line)
        os.remove(tmpFileName)

    fields = layer.pendingFields()
    for field in fields:
        exportImages(layer, field.name(), layerFileName)
    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
    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 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
Beispiel #37
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)

        if output.value == '':
            ext = output.getDefaultFileExtension(self)
            output.value = system.getTempFilenameInTempFolder(output.name +
                                                              '.' + ext)

        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()))

        # add layer to registry to fix https://issues.qgis.org/issues/17300
        # it is necessary only for aggregate expressions that verify that layer
        # is registered
        removeRegistryAfterEvaluation = False
        if not QgsMapLayerRegistry.instance().mapLayer(layer.id()):
            removeRegistryAfterEvaluation = True
            QgsMapLayerRegistry.instance().addMapLayer(layer,
                                                       addToLegend=False)

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

        error = ''
        calculationSuccess = True

        features = vector.features(layer)
        total = 100.0 / len(features) if len(features) > 0 else 1

        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

        # remove from registry if added for expression requirement
        # see above comment about fix #17300
        if removeRegistryAfterEvaluation:
            QgsMapLayerRegistry.instance().removeMapLayer(layer)

        if not calculationSuccess:
            raise GeoAlgorithmExecutionException(
                self.tr('An error occurred while evaluating the calculation '
                        'string:\n%s' % error))
Beispiel #38
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))
Beispiel #39
0
    def expressionBasedUpdate( self, layer, dictProperties, featureId, index=None, value=None ):
        """ Defines the logic of the expression-based update to be applied.
            This SLOT listens to featureAdded, geometryChanged, and attributeValueChanged SIGNALS.
        """
        # Check if AutoField is there, otherwise return
        fieldIndex = layer.fieldNameIndex( dictProperties['field'] )
        if fieldIndex == -1:
            self.msg.show(
                QApplication.translate( "EventManager", "[Error] Updating AutoField " ) + \
                dictProperties['field'] + \
                QApplication.translate( "EventManager", " in layer " ) + \
                layer.name() + QApplication.translate( "EventManager", " was NOT possible." ) + \
                QApplication.translate( "EventManager", " Perhaps you just removed it but haven't saved the changes yet?" ),
                'warning' )
            return

        event = ""
        result = None

        expression = QgsExpression( dictProperties['expression'] )
        if expression.hasParserError():
            self.msg.show( QApplication.translate( "EventManager", "[Error] (Parsing) " ) + \
                expression.parserErrorString(), 'critical' )
            result = NULL

        # Avoid infinite recursion (changing the same attribute value infinitely).
        if not index is None: # Filters out the featureAdded SIGNAL       
            if type( index ) == int: # Filters out the geometryChanged SIGNAL
                
                if index == fieldIndex: # This call comes from the same AutoField, so return
                    return
                
                if self.afm.isFieldAnAutoField( layer, layer.fields()[index].name() ): # Call from AutoField, don't listen
                    # This is to prevent corrupting the layerEditBuffer and being bitten by:
                    #   Fatal: ASSERT: "mChangedAttributeValues.isEmpty()" in file /tmp/buildd/qgis-2.14.2+20trusty/src/core/qgsvectorlayereditbuffer.cpp, line 585
                    return 
                
                #if type(value)==QPyNullVariant: 
                    # Vector layers with numeric field whose value for 1st feature is NULL
                    #   trigger an attributeValueChanged SIGNAL when start editing from the
                    #   attribute table window. We use this conditional to avoid such SIGNAL.
                    #   The ideal case is that such NULL valued SIGNAL shouldn't be emitted by QGIS.
                #    return  
                # While the previous block reduces the number of times attributeValueChanged
                #   is called from the attribute table, it leads to a QGIS bug:
                #   Fatal: ASSERT: "mChangedAttributeValues.isEmpty()" in file /tmp/buildd/qgis-2.14.2+20trusty/src/core/qgsvectorlayereditbuffer.cpp, line 585
                #   I prefer the attributeValueChanged to be called multiple 
                #   times (inefficient) than to open the possibility to a bug. 
                # As soon as QGIS bug #15272 is solved, the number of calls will be reduced!
                
                event = "attributeValueChanged"
            else:
                event = "geometryChanged"
        else:
            event = "featureAdded"
            
        feature = layer.getFeatures( QgsFeatureRequest( featureId ) ).next()
        
        if result is None:
            context = QgsExpressionContext()
            context.appendScope( QgsExpressionContextUtils.globalScope() )
            context.appendScope( QgsExpressionContextUtils.projectScope() )
            context.appendScope( QgsExpressionContextUtils.layerScope( layer ) )
            context.setFields( feature.fields() )
            context.setFeature( feature )

            if expression.needsGeometry():
                if self.iface:
                    # 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] )
                    expression.setGeomCalculator( da )
                    if QGis.QGIS_VERSION_INT >= 21400: # Methods added in QGIS 2.14
                        expression.setDistanceUnits( QgsProject.instance().distanceUnits() ) 
                        expression.setAreaUnits( QgsProject.instance().areaUnits() )
            
            expression.prepare( context )
            result = expression.evaluate( context )
            
            if expression.hasEvalError():
                self.msg.show( QApplication.translate( "EventManager", "[Error] (Evaluating) " ) + \
                    expression.evalErrorString(), 'critical' )
                result = NULL
        
        field = layer.fields()[fieldIndex]
        res = field.convertCompatible( result )
        # If result is None, res will be None, but even in that case, QGIS knows
        #   what to do with it while saving, it seems it's treated as NULL.
        

        # TODO when bug #15311 is fixed, this block should work better
        #if dictProperties['expression'] in self.listProviderExpressions: 
        #    # Save directly to provider
        #    layer.dataProvider().changeAttributeValues( { featureId : { fieldIndex : res } } )
        #else: # Save to layer
        #    layer.changeAttributeValue( featureId, fieldIndex, res )
         
        # Workaround 
        if event == 'featureAdded': # Save directly to the provider
            layer.dataProvider().changeAttributeValues( { featureId : { fieldIndex : res } } )
        else: # Save to layer
            layer.changeAttributeValue( featureId, fieldIndex, res )
        
        self.msg.show( "[Info] * AutoField's value updated to " + unicode(res) + \
            ", (" + layer.name() + "." + dictProperties['field'] + ") by " + event +".", 'info', True )
Beispiel #40
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)
Beispiel #41
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)
Beispiel #42
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
Beispiel #43
0
def exportLayers(iface, layers, folder, precision, optimize, popupField, json,
                 restrictToExtent, extent, feedback):
    canvas = iface.mapCanvas()
    epsg4326 = QgsCoordinateReferenceSystem("EPSG:4326")
    layersFolder = os.path.join(folder, "layers")
    QDir().mkpath(layersFolder)
    for count, (layer, encode2json,
                popup) in enumerate(zip(layers, json, popupField)):
        if (layer.type() == layer.VectorLayer
                and (layer.providerType() != "WFS" or encode2json)):
            feedback.showFeedback("Exporting %s to JSON..." % layer.name())
            cleanLayer = writeTmpLayer(layer, popup, restrictToExtent, iface,
                                       extent)
            fields = layer.pendingFields()
            for field in fields:
                exportImages(layer, field.name(), layersFolder + "/tmp.tmp")
            if is25d(layer, canvas, restrictToExtent, extent):
                provider = cleanLayer.dataProvider()
                provider.addAttributes([
                    QgsField("height", QVariant.Double),
                    QgsField("wallColor", QVariant.String),
                    QgsField("roofColor", QVariant.String)
                ])
                cleanLayer.updateFields()
                fields = cleanLayer.pendingFields()
                renderer = layer.rendererV2()
                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, QgsCategorizedSymbolRendererV2):
                        classAttribute = renderer.classAttribute()
                        attrValue = feat.attribute(classAttribute)
                        catIndex = renderer.categoryIndexForValue(attrValue)
                        categories = renderer.categories()
                        symbol = categories[catIndex].symbol()
                    elif isinstance(renderer, QgsGraduatedSymbolRendererV2):
                        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.symbolForFeature2(
                            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)

            sln = safeName(cleanLayer.name()) + unicode(count)
            tmpPath = os.path.join(layersFolder, sln + ".json")
            path = os.path.join(layersFolder, sln + ".js")
            options = []
            if precision != "maintain":
                options.append("COORDINATE_PRECISION=" + unicode(precision))
            QgsVectorFileWriter.writeAsVectorFormat(cleanLayer,
                                                    tmpPath,
                                                    "utf-8",
                                                    epsg4326,
                                                    'GeoJson',
                                                    0,
                                                    layerOptions=options)
            with open(path, "w") as f:
                f.write("var %s = " % ("geojson_" + sln))
                with open(tmpPath, "r") as f2:
                    for line in f2:
                        if optimize:
                            line = line.strip("\n\t ")
                            line = removeSpaces(line)
                        f.write(line)
            os.remove(tmpPath)

        elif (layer.type() == layer.RasterLayer
              and layer.providerType() != "wms"):
            exportRaster(layer, count, layersFolder, feedback)
    feedback.completeStep()
Beispiel #44
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))
Beispiel #45
0
def exportLayers(iface, layers, folder, precision, optimize, popupField, json,
                 restrictToExtent, extent, feedback):
    canvas = iface.mapCanvas()
    epsg4326 = QgsCoordinateReferenceSystem("EPSG:4326")
    layersFolder = os.path.join(folder, "layers")
    QDir().mkpath(layersFolder)
    for count, (layer, encode2json,
                popup) in enumerate(zip(layers, json, popupField)):
        if (layer.type() == layer.VectorLayer
                and (layer.providerType() != "WFS" or encode2json)):
            feedback.showFeedback("Exporting %s to JSON..." % layer.name())
            cleanLayer = writeTmpLayer(layer, popup, restrictToExtent, iface,
                                       extent)
            fields = layer.pendingFields()
            for field in fields:
                exportImages(layer, field.name(), layersFolder + "/tmp.tmp")
            if is25d(layer, canvas, restrictToExtent, extent):
                provider = cleanLayer.dataProvider()
                provider.addAttributes([
                    QgsField("height", QVariant.Double),
                    QgsField("wallColor", QVariant.String),
                    QgsField("roofColor", QVariant.String)
                ])
                cleanLayer.updateFields()
                fields = cleanLayer.pendingFields()
                renderer = layer.rendererV2()
                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, QgsCategorizedSymbolRendererV2):
                        classAttribute = renderer.classAttribute()
                        attrValue = feat.attribute(classAttribute)
                        catIndex = renderer.categoryIndexForValue(attrValue)
                        categories = renderer.categories()
                        symbol = categories[catIndex].symbol()
                    elif isinstance(renderer, QgsGraduatedSymbolRendererV2):
                        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.symbolForFeature2(
                            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)

            sln = safeName(cleanLayer.name()) + unicode(count)
            tmpPath = os.path.join(layersFolder, sln + ".json")
            path = os.path.join(layersFolder, sln + ".js")
            options = []
            if precision != "maintain":
                options.append("COORDINATE_PRECISION=" + unicode(precision))
            QgsVectorFileWriter.writeAsVectorFormat(cleanLayer,
                                                    tmpPath,
                                                    "utf-8",
                                                    epsg4326,
                                                    'GeoJson',
                                                    0,
                                                    layerOptions=options)
            with open(path, "w") as f:
                f.write("var %s = " % ("geojson_" + sln))
                with open(tmpPath, "r") as f2:
                    for line in f2:
                        if optimize:
                            line = line.strip("\n\t ")
                            line = removeSpaces(line)
                        f.write(line)
            os.remove(tmpPath)

        elif (layer.type() == layer.RasterLayer
              and layer.providerType() != "wms"):

            feedback.showFeedback("Exporting %s to PNG..." % layer.name())
            name_ts = (safeName(layer.name()) + unicode(count) +
                       unicode(int(time.time())))

            # We need to create a new file to export style
            piped_file = os.path.join(tempfile.gettempdir(),
                                      name_ts + '_piped.tif')

            piped_extent = layer.extent()
            piped_width = layer.height()
            piped_height = layer.width()
            piped_crs = layer.crs()
            piped_renderer = layer.renderer()
            piped_provider = layer.dataProvider()

            pipe = QgsRasterPipe()
            pipe.set(piped_provider.clone())
            pipe.set(piped_renderer.clone())

            file_writer = QgsRasterFileWriter(piped_file)

            file_writer.writeRaster(pipe, piped_width, piped_height,
                                    piped_extent, piped_crs)

            # Extent of the layer in EPSG:3857
            crsSrc = layer.crs()
            crsDest = QgsCoordinateReferenceSystem(3857)
            xform = QgsCoordinateTransform(crsSrc, crsDest)
            extentRep = xform.transform(layer.extent())

            extentRepNew = ','.join([
                unicode(extentRep.xMinimum()),
                unicode(extentRep.xMaximum()),
                unicode(extentRep.yMinimum()),
                unicode(extentRep.yMaximum())
            ])

            # Reproject in 3857
            piped_3857 = os.path.join(tempfile.gettempdir(),
                                      name_ts + '_piped_3857.tif')
            # Export layer as PNG
            out_raster = os.path.join(
                layersFolder,
                safeName(layer.name()) + unicode(count) + ".png")

            qgis_version = QGis.QGIS_VERSION

            if int(qgis_version.split('.')[1]) < 15:
                processing.runalg("gdalogr:warpreproject", piped_file,
                                  layer.crs().authid(), "EPSG:3857", "", 0, 1,
                                  0, -1, 75, 6, 1, False, 0, False, "",
                                  piped_3857)
                processing.runalg("gdalogr:translate", piped_3857, 100, True,
                                  "", 0, "", extentRepNew, False, 0, 0, 75, 6,
                                  1, False, 0, False, "", out_raster)
            else:
                try:
                    warpArgs = {
                        "INPUT": piped_file,
                        "SOURCE_SRS": layer.crs().authid(),
                        "DEST_SRS": "EPSG:3857",
                        "NO_DATA": "",
                        "TR": 0,
                        "METHOD": 2,
                        "RAST_EXT": extentRepNew,
                        "EXT_CRS": "EPSG:3857",
                        "RTYPE": 0,
                        "COMPRESS": 4,
                        "JPEGCOMPRESSION": 75,
                        "ZLEVEL": 6,
                        "PREDICTOR": 1,
                        "TILED": False,
                        "BIGTIFF": 0,
                        "TFW": False,
                        "EXTRA": "",
                        "OUTPUT": piped_3857
                    }
                    procRtn = processing.runalg("gdalogr:warpreproject",
                                                warpArgs)
                    # force exception on algorithm fail
                    for val in procRtn:
                        pass
                except:
                    try:
                        warpArgs = {
                            "INPUT": piped_file,
                            "SOURCE_SRS": layer.crs().authid(),
                            "DEST_SRS": "EPSG:3857",
                            "NO_DATA": "",
                            "TR": 0,
                            "METHOD": 2,
                            "RAST_EXT": extentRepNew,
                            "RTYPE": 0,
                            "COMPRESS": 4,
                            "JPEGCOMPRESSION": 75,
                            "ZLEVEL": 6,
                            "PREDICTOR": 1,
                            "TILED": False,
                            "BIGTIFF": 0,
                            "TFW": False,
                            "EXTRA": "",
                            "OUTPUT": piped_3857
                        }
                        procRtn = processing.runalg("gdalogr:warpreproject",
                                                    warpArgs)
                        # force exception on algorithm fail
                        for val in procRtn:
                            pass
                    except:
                        try:
                            warpArgs = {
                                "INPUT": piped_file,
                                "SOURCE_SRS": layer.crs().authid(),
                                "DEST_SRS": "EPSG:3857",
                                "NO_DATA": "",
                                "TR": 0,
                                "METHOD": 2,
                                "RTYPE": 0,
                                "COMPRESS": 4,
                                "JPEGCOMPRESSION": 75,
                                "ZLEVEL": 6,
                                "PREDICTOR": 1,
                                "TILED": False,
                                "BIGTIFF": 0,
                                "TFW": False,
                                "EXTRA": "",
                                "OUTPUT": piped_3857
                            }
                            procRtn = processing.runalg(
                                "gdalogr:warpreproject", warpArgs)
                            # force exception on algorithm fail
                            for val in procRtn:
                                pass
                        except:
                            shutil.copyfile(piped_file, piped_3857)

                try:
                    processing.runalg("gdalogr:translate", piped_3857, 100,
                                      True, "", 0, "", extentRepNew, False, 5,
                                      4, 75, 6, 1, False, 0, False, "",
                                      out_raster)
                except:
                    shutil.copyfile(piped_3857, out_raster)
    feedback.completeStep()