def testCreateFieldEqualityExpression(self):
        e = QgsExpression()

        # test when value is null
        field = "myfield"
        value = QVariant()
        res = '"myfield" IS NULL'
        self.assertEqual(e.createFieldEqualityExpression(field, value), res)

        # test when value is null and field name has a quote
        field = "my'field"
        value = QVariant()
        res = '"my\'field" IS NULL'
        self.assertEqual(e.createFieldEqualityExpression(field, value), res)

        # test when field name has a quote and value is an int
        field = "my'field"
        value = 5
        res = '"my\'field" = 5'
        self.assertEqual(e.createFieldEqualityExpression(field, value), res)

        # test when field name has a quote and value is a string
        field = "my'field"
        value = '5'
        res = '"my\'field" = \'5\''
        self.assertEqual(e.createFieldEqualityExpression(field, value), res)

        # test when field name has a quote and value is a boolean
        field = "my'field"
        value = True
        res = '"my\'field" = TRUE'
        self.assertEqual(e.createFieldEqualityExpression(field, value), res)
示例#2
0
    def testCreateFieldEqualityExpression(self):
        e = QgsExpression()

        # test when value is null
        field = "myfield"
        value = QVariant()
        res = '"myfield" IS NULL'
        self.assertEqual(e.createFieldEqualityExpression(field, value), res)

        # test when value is null and field name has a quote
        field = "my'field"
        value = QVariant()
        res = '"my\'field" IS NULL'
        self.assertEqual(e.createFieldEqualityExpression(field, value), res)

        # test when field name has a quote and value is an int
        field = "my'field"
        value = 5
        res = '"my\'field" = 5'
        self.assertEqual(e.createFieldEqualityExpression(field, value), res)

        # test when field name has a quote and value is a string
        field = "my'field"
        value = '5'
        res = '"my\'field" = \'5\''
        self.assertEqual(e.createFieldEqualityExpression(field, value), res)

        # test when field name has a quote and value is a boolean
        field = "my'field"
        value = True
        res = '"my\'field" = TRUE'
        self.assertEqual(e.createFieldEqualityExpression(field, value), res)
    def paint(self, painter, option, widget):  # pylint: disable=missing-docstring, unused-argument, too-many-locals
        if self.image is not None:
            painter.drawImage(0, 0, self.image)
            return

        image_size = self.canvas.mapSettings().outputSize()
        self.image = QImage(image_size.width(), image_size.height(), QImage.Format_ARGB32)
        self.image.fill(0)
        image_painter = QPainter(self.image)
        render_context = QgsRenderContext.fromQPainter(image_painter)

        image_painter.setRenderHint(QPainter.Antialiasing, True)

        rect = self.canvas.mapSettings().visibleExtent()
        request = QgsFeatureRequest()
        request.setFilterRect(rect)
        request.setFilterExpression(QgsExpression.createFieldEqualityExpression('type', self.task))

        for f in self.electorate_layer.getFeatures(request):
            #    pole, dist = f.geometry().clipped(rect).poleOfInaccessibility(rect.width() / 30)
            pixel = self.toCanvasCoordinates(f.geometry().clipped(rect).centroid().asPoint())

            estimated_pop = self.new_populations[f.id()] if f.id() in self.new_populations else \
            self.original_populations[f.id()]

            variance = LinzElectoralDistrictRegistry.get_variation_from_quota_percent(self.quota, estimated_pop)
            text_string = ['{}'.format(f['name']),
                           '{}'.format(int(estimated_pop)),
                           '{}{}%'.format('+' if variance > 0 else '', variance)]
            QgsTextRenderer().drawText(QPointF(pixel.x(), pixel.y()), 0, QgsTextRenderer.AlignCenter,
                                       text_string, render_context, self.text_format)

        image_painter.end()

        painter.drawImage(0, 0, self.image)
    def __copy_records_from_scenario(
            source_meshblock_electorate_layer: QgsVectorLayer,
            source_scenario_id,
            dest_meshblock_electorate_layer: QgsVectorLayer, new_scenario_id):
        """
        Copies the records associated with a scenario from one table to
        another
        :param source_meshblock_electorate_layer: layer containing source meshblock->electorate mappings
        :param source_scenario_id: source scenario id
        :param dest_meshblock_electorate_layer: destination layer for copied meshblock->electorate mappings
        :param new_scenario_id: new scenario id for copied records
        """
        request = QgsFeatureRequest()
        request.setFilterExpression(
            QgsExpression.createFieldEqualityExpression(
                'scenario_id', source_scenario_id))
        current_meshblocks = source_meshblock_electorate_layer.getFeatures(
            request)
        scenario_id_idx = source_meshblock_electorate_layer.fields(
        ).lookupField('scenario_id')
        fid_idx = source_meshblock_electorate_layer.fields().lookupField('fid')
        new_features = []
        for f in current_meshblocks:
            f[scenario_id_idx] = new_scenario_id
            if fid_idx >= 0:
                f[fid_idx] = NULL
            new_features.append(f)

        dest_meshblock_electorate_layer.startEditing()
        dest_meshblock_electorate_layer.addFeatures(new_features)
        dest_meshblock_electorate_layer.commitChanges()
示例#5
0
文件: HubLines.py 项目: exlimit/QGIS
    def processAlgorithm(self, parameters, context, feedback):
        if parameters[self.SPOKES] == parameters[self.HUBS]:
            raise QgsProcessingException(
                self.tr('Same layer given for both hubs and spokes'))

        hub_source = self.parameterAsSource(parameters, self.HUBS, context)
        spoke_source = self.parameterAsSource(parameters, self.SPOKES, context)
        field_hub = self.parameterAsString(parameters, self.HUB_FIELD, context)
        field_hub_index = hub_source.fields().lookupField(field_hub)
        field_spoke = self.parameterAsString(parameters, self.SPOKE_FIELD, context)
        field_spoke_index = hub_source.fields().lookupField(field_spoke)

        fields = vector.combineFields(hub_source.fields(), spoke_source.fields())

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, QgsWkbTypes.LineString, hub_source.sourceCrs())

        hubs = hub_source.getFeatures()
        total = 100.0 / hub_source.featureCount() if hub_source.featureCount() else 0

        matching_field_types = hub_source.fields().at(field_hub_index).type() == spoke_source.fields().at(field_spoke_index).type()

        for current, hub_point in enumerate(hubs):
            if feedback.isCanceled():
                break

            if not hub_point.hasGeometry():
                continue

            p = hub_point.geometry().boundingBox().center()
            hub_x = p.x()
            hub_y = p.y()
            hub_id = str(hub_point[field_hub])
            hub_attributes = hub_point.attributes()

            request = QgsFeatureRequest().setDestinationCrs(hub_source.sourceCrs())
            if matching_field_types:
                request.setFilterExpression(QgsExpression.createFieldEqualityExpression(field_spoke, hub_attributes[field_hub_index]))

            spokes = spoke_source.getFeatures()
            for spoke_point in spokes:
                if feedback.isCanceled():
                    break

                spoke_id = str(spoke_point[field_spoke])
                if hub_id == spoke_id:
                    p = spoke_point.geometry().boundingBox().center()
                    spoke_x = p.x()
                    spoke_y = p.y()

                    f = QgsFeature()
                    f.setAttributes(hub_attributes + spoke_point.attributes())
                    f.setGeometry(QgsGeometry.fromPolyline(
                        [QgsPointXY(hub_x, hub_y), QgsPointXY(spoke_x, spoke_y)]))
                    sink.addFeature(f, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
示例#6
0
    def processAlgorithm(self, parameters, context, feedback):
        if parameters[self.SPOKES] == parameters[self.HUBS]:
            raise QgsProcessingException(
                self.tr('Same layer given for both hubs and spokes'))

        hub_source = self.parameterAsSource(parameters, self.HUBS, context)
        spoke_source = self.parameterAsSource(parameters, self.SPOKES, context)
        field_hub = self.parameterAsString(parameters, self.HUB_FIELD, context)
        field_hub_index = hub_source.fields().lookupField(field_hub)
        field_spoke = self.parameterAsString(parameters, self.SPOKE_FIELD, context)
        field_spoke_index = hub_source.fields().lookupField(field_spoke)

        fields = vector.combineFields(hub_source.fields(), spoke_source.fields())

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, QgsWkbTypes.LineString, hub_source.sourceCrs())

        hubs = hub_source.getFeatures()
        total = 100.0 / hub_source.featureCount() if hub_source.featureCount() else 0

        matching_field_types = hub_source.fields().at(field_hub_index).type() == spoke_source.fields().at(field_spoke_index).type()

        for current, hub_point in enumerate(hubs):
            if feedback.isCanceled():
                break

            if not hub_point.hasGeometry():
                continue

            p = hub_point.geometry().boundingBox().center()
            hub_x = p.x()
            hub_y = p.y()
            hub_id = str(hub_point[field_hub])
            hub_attributes = hub_point.attributes()

            request = QgsFeatureRequest().setDestinationCrs(hub_source.sourceCrs())
            if matching_field_types:
                request.setFilterExpression(QgsExpression.createFieldEqualityExpression(field_spoke, hub_attributes[field_hub_index]))

            spokes = spoke_source.getFeatures()
            for spoke_point in spokes:
                if feedback.isCanceled():
                    break

                spoke_id = str(spoke_point[field_spoke])
                if hub_id == spoke_id:
                    p = spoke_point.geometry().boundingBox().center()
                    spoke_x = p.x()
                    spoke_y = p.y()

                    f = QgsFeature()
                    f.setAttributes(hub_attributes + spoke_point.attributes())
                    f.setGeometry(QgsGeometry.fromPolyline(
                        [QgsPointXY(hub_x, hub_y), QgsPointXY(spoke_x, spoke_y)]))
                    sink.addFeature(f, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
示例#7
0
 def selectByAttribute(self, value):
     layer = getLayerFromId(self.LOCALITIES_LAYER)
     field_index = self.TAXON_FIELD_INDEX
     field_name = layer.fields()[int(field_index)].name()
     selected = []
     filter = QgsExpression.createFieldEqualityExpression(field_name, str(value))
     request = QgsFeatureRequest().setFilterExpression(filter)
     request.setSubsetOfAttributes([])
     for feature in layer.getFeatures(request):
         selected.append(feature.id())
     layer.selectByIds(selected)
 def district_name_exists(self, district) -> bool:
     request = QgsFeatureRequest()
     request.setFilterExpression(QgsExpression.createFieldEqualityExpression(self.title_field, district))
     request.setFlags(QgsFeatureRequest.NoGeometry)
     request.setSubsetOfAttributes([])
     request.setLimit(1)
     try:
         next(self.source_layer.getFeatures(request))
     except StopIteration:
         return False
     return True
示例#9
0
 def get_district_type(self, electorate_id) -> str:
     """
     Returns the district type (GN/GS/M) for the specified district
     :param electorate_id: electorate id
     """
     # lookup matching feature
     request = QgsFeatureRequest()
     request.setFilterExpression(QgsExpression.createFieldEqualityExpression(self.source_field, electorate_id))
     request.setFlags(QgsFeatureRequest.NoGeometry)
     request.setSubsetOfAttributes([self.type_field_index])
     request.setLimit(1)
     f = next(self.source_layer.getFeatures(request))
     return f[self.type_field_index]
示例#10
0
 def get_estimated_population(self, electorate_id) -> int:
     """
     Returns the estimated (offline) population for the district
     :param electorate_id: electorate code/id
     """
     # lookup matching feature
     request = QgsFeatureRequest()
     request.setFilterExpression(QgsExpression.createFieldEqualityExpression(self.source_field, electorate_id))
     request.setFlags(QgsFeatureRequest.NoGeometry)
     request.setSubsetOfAttributes([self.estimated_pop_field_index])
     request.setLimit(1)
     f = next(self.source_layer.getFeatures(request))
     return f[self.estimated_pop_field_index]
示例#11
0
 def modify_district_request(self, request):
     """
     Allows subclasses to modify the request used to fetch available
     districts from the source layer, e.g. to add filtering
     or sorting to the request.
     :param request: base feature request to modify
     :return: modified feature request
     """
     request.addOrderBy(self.source_field)
     if self.electorate_type:
         request.setFilterExpression(
             QgsExpression.createFieldEqualityExpression(self.type_field, self.electorate_type))
     request.combineFilterExpression('"{}" is null or not "{}"'.format(self.deprecated_field, self.deprecated_field))
     return request
示例#12
0
    def __init__(
            self,
            task_name: str,
            meshblock_layer: QgsVectorLayer,  # pylint: disable=too-many-locals
            meshblock_number_field_name: str,
            scenario_registry: ScenarioRegistry,
            scenario,
            task: str):
        """
        Constructor for ScenarioSwitchTask
        :param task_name: user-visible, translated name for task
        :param electorate_layer: electorate layer
        :param meshblock_layer: meshblock layer
        :param meshblock_number_field_name: name of meshblock number field
        :param scenario_registry: scenario registry
        :param scenario: target scenario id to switch to
        :param task: current task
        """
        super().__init__(task_name)

        self.scenario = scenario

        self.mb_number_idx = scenario_registry.meshblock_electorate_layer.fields(
        ).lookupField('meshblock_number')

        electorate_field_name = '{}_id'.format(task.lower())
        self.electorate_field_idx = scenario_registry.meshblock_electorate_layer.fields(
        ).lookupField(electorate_field_name)
        assert self.electorate_field_idx >= 0
        self.scenario_id_field_idx = scenario_registry.meshblock_electorate_layer.fields(
        ).lookupField('scenario_id')
        assert self.scenario_id_field_idx >= 0
        self.staged_electorate_field_idx = meshblock_layer.fields(
        ).lookupField('staged_electorate')
        assert self.staged_electorate_field_idx >= 0
        self.meshblock_number_idx = meshblock_layer.fields().lookupField(
            meshblock_number_field_name)
        assert self.meshblock_number_idx >= 0
        self.meshblock_layer = meshblock_layer

        request = QgsFeatureRequest()
        request.setFilterExpression(
            QgsExpression.createFieldEqualityExpression(
                'scenario_id', scenario))
        request.setSubsetOfAttributes(
            [self.mb_number_idx, self.electorate_field_idx])
        self.meshblocks_for_scenario = scenario_registry.meshblock_electorate_layer.getFeatures(
            request)

        self.setDependentLayers([meshblock_layer])
示例#13
0
    def get_quota_for_district_type(self, district_type: str) -> int:
        """
        Returns the quota for the specified district type
        :param district_type: district type, e.g. "GS"
        """
        quota_field_index = self.quota_layer.fields().lookupField('quota')
        assert quota_field_index >= 0

        request = QgsFeatureRequest()
        request.setFilterExpression(QgsExpression.createFieldEqualityExpression('type', district_type))
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes([quota_field_index])
        request.setLimit(1)
        f = next(self.quota_layer.getFeatures(request))
        return f[quota_field_index]
    def electorate_meshblocks(self, electorate_id, electorate_type: str,
                              scenario_id) -> QgsFeatureIterator:
        """
        Returns meshblock features currently assigned to an electorate in a
        given scenario
        :param electorate_id: electorate id
        :param electorate_type: electorate type, e.g. 'GN','GS','M'
        :param scenario_id: scenario id
        """
        request = QgsFeatureRequest()

        type_field = '{}_id'.format(electorate_type.lower())
        type_field_index = self.meshblock_electorate_layer.fields(
        ).lookupField(type_field)
        assert type_field_index >= 0

        request.setFilterExpression(
            QgsExpression.createFieldEqualityExpression(
                'scenario_id', scenario_id))
        request.combineFilterExpression(
            QgsExpression.createFieldEqualityExpression(
                type_field, electorate_id))

        return self.meshblock_electorate_layer.getFeatures(request)
    def get_scenario(self, scenario) -> QgsFeature:
        """
        Returns the feature corresponding to the given scenario
        :param scenario: scenario id to get feature for
        """

        # lookup matching feature
        request = QgsFeatureRequest()
        request.setFilterExpression(
            QgsExpression.createFieldEqualityExpression(
                self.id_field, scenario))
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setLimit(1)
        f = next(self.source_layer.getFeatures(request))
        return f
    def get_district_title(self, district):
        if self.title_field == self.source_field:
            return super().get_district_title(district)

        # lookup matching feature
        request = QgsFeatureRequest()
        request.setFilterExpression(QgsExpression.createFieldEqualityExpression(self.source_field, district))
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes([self.title_field_index])
        request.setLimit(1)
        try:
            f = next(self.source_layer.getFeatures(request))
        except StopIteration:
            return super().get_district_title(district)
        return f[self.title_field_index]
 def scenario_exists(self, scenario_id) -> bool:
     """
     Returns true if the given scenario exists
     :param scenario_id: ID for scenario
     """
     request = QgsFeatureRequest()
     request.setFlags(QgsFeatureRequest.NoGeometry)
     request.setSubsetOfAttributes([])
     request.setFilterExpression(
         QgsExpression.createFieldEqualityExpression(
             self.id_field, scenario_id))
     request.setLimit(1)
     for f in self.source_layer.getFeatures(request):  # pylint: disable=unused-variable
         # found a matching feature
         return True
     return False
 def scenario_name_exists(self, new_scenario_name: str) -> bool:
     """
     Returns true if the given scenario name already exists
     :param new_scenario_name: name of scenario to test
     """
     request = QgsFeatureRequest()
     request.setFlags(QgsFeatureRequest.NoGeometry)
     request.setSubsetOfAttributes([])
     request.setFilterExpression(
         QgsExpression.createFieldEqualityExpression(
             self.name_field, new_scenario_name))
     request.setLimit(1)
     for f in self.source_layer.getFeatures(request):  # pylint: disable=unused-variable
         # found a matching feature
         return True
     return False
    def get_scenario_name(self, scenario) -> str:
        """
        Returns a user-friendly name corresponding to the given scenario
        :param scenario: scenario id to get name for
        """

        # lookup matching feature
        request = QgsFeatureRequest()
        request.setFilterExpression(
            QgsExpression.createFieldEqualityExpression(
                self.id_field, scenario))
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes([self.name_field_index])
        request.setLimit(1)
        f = next(self.source_layer.getFeatures(request))
        return f[self.name_field_index]
示例#20
0
 def get_stats_nz_calculations(self, electorate_id) -> dict:
     """
     Returns a dictionary of Stats NZ calculations for the district
     :param electorate_id: electorate code/id
     """
     # lookup matching feature
     request = QgsFeatureRequest()
     request.setFilterExpression(QgsExpression.createFieldEqualityExpression(self.source_field, electorate_id))
     request.setFlags(QgsFeatureRequest.NoGeometry)
     request.setSubsetOfAttributes(
         [self.stats_nz_var_23_field_index, self.stats_nz_var_20_field_index, self.stats_nz_pop_field_index])
     request.setLimit(1)
     f = next(self.source_layer.getFeatures(request))
     return {'currentPopulation': f[self.stats_nz_pop_field_index],
             'varianceYear1': f[self.stats_nz_var_20_field_index],
             'varianceYear2': f[self.stats_nz_var_23_field_index]}
示例#21
0
    def flag_stats_nz_updating(self, electorate_id):
        """
        Flags an electorate's Statistics NZ api calculations as being currently updated
        :param electorate_id: electorate to update
        """

        # get feature ID
        request = QgsFeatureRequest()
        request.setFilterExpression(QgsExpression.createFieldEqualityExpression(self.source_field, electorate_id))
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes([])
        request.setLimit(1)
        f = next(self.source_layer.getFeatures(request))

        self.source_layer.dataProvider().changeAttributeValues({f.id(): {self.stats_nz_pop_field_index: -1,
                                                                         self.stats_nz_var_20_field_index: NULL,
                                                                         self.stats_nz_var_23_field_index: NULL}})
示例#22
0
    def toggle_electorate_deprecation(self, electorate):
        """
        Toggles the deprecation flag for an electorate
        :param electorate: electorate id
        """
        request = QgsFeatureRequest()
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes([self.deprecated_field_index])
        request.setFilterExpression(QgsExpression.createFieldEqualityExpression(self.source_field, electorate))
        f = next(self.source_layer.getFeatures(request))

        is_deprecated = f[self.deprecated_field_index]
        if is_deprecated == NULL:
            is_deprecated = False

        new_status = 0 if is_deprecated else 1
        self.source_layer.dataProvider().changeAttributeValues({f.id(): {self.deprecated_field_index: new_status}})
    def end_edit_group(self):
        if not self.pending_affected_districts:
            super().end_edit_group()
            return

        # step 1: get all electorate features corresponding to affected electorates
        electorate_features = {f[self.electorate_layer_field]: f for f in
                               self.get_affected_districts([self.electorate_layer_field])}

        # and update the electorate boundaries based on these changes.
        # Ideally we'd redissolve the whole boundary from meshblocks, but that's too
        # slow. So instead we adjust piece-by-piece by adding or chomping away
        # the affected meshblocks only.
        new_geometries = {}
        new_attributes = {}
        for district in self.pending_affected_districts.keys():  # pylint: disable=consider-iterating-dictionary
            district_geometry = electorate_features[district].geometry()
            # add new bits
            district_geometry = self.grow_district_with_added_meshblocks(district, district_geometry)
            # minus lost bits
            district_geometry = self.shrink_district_by_removed_meshblocks(district, district_geometry)

            new_geometries[electorate_features[district].id()] = district_geometry

            calc = QgsAggregateCalculator(self.target_layer)
            calc.setFilter(QgsExpression.createFieldEqualityExpression(self.target_field, district))
            estimated_pop, ok = calc.calculate(QgsAggregateCalculator.Sum,  # pylint: disable=unused-variable
                                               self.offline_pop_field)

            new_attributes[electorate_features[district].id()] = {self.estimated_pop_idx: estimated_pop,
                                                                  self.stats_nz_pop_field_index: NULL,
                                                                  self.stats_nz_var_20_field_index: NULL,
                                                                  self.stats_nz_var_23_field_index: NULL,
                                                                  self.invalid_field_index: NULL,
                                                                  self.invalid_reason_field_index: NULL}
        self.electorate_changes_queue.push_changes(new_attributes, new_geometries)

        self.user_log_layer.dataProvider().addFeatures(self.pending_log_entries)

        self.electorate_changes_queue.blocked = True
        super().end_edit_group()
        self.electorate_changes_queue.blocked = False

        self.pending_affected_districts = {}
        self.pending_log_entries = []
        self.redistrict_occured.emit()
示例#24
0
    def update_stats_nz_values(self, electorate_id, results: dict):
        """
        Updates an electorate's stored Statistics NZ api calculations
        :param electorate_id: electorate to update
        :param results: results dictionary from stats API
        """

        # get feature ID
        request = QgsFeatureRequest()
        request.setFilterExpression(QgsExpression.createFieldEqualityExpression(self.source_field, electorate_id))
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes([])
        request.setLimit(1)
        f = next(self.source_layer.getFeatures(request))

        self.source_layer.dataProvider().changeAttributeValues(
            {f.id(): {self.stats_nz_pop_field_index: results['currentPopulation'],
                      self.stats_nz_var_20_field_index: results['varianceYear1'],
                      self.stats_nz_var_23_field_index: results['varianceYear2']}})
示例#25
0
    def paint(self, painter, option, widget):  # pylint: disable=missing-docstring, unused-argument, too-many-locals
        if self.image is not None:
            painter.drawImage(0, 0, self.image)
            return

        image_size = self.canvas.mapSettings().outputSize()
        self.image = QImage(image_size.width(), image_size.height(),
                            QImage.Format_ARGB32)
        self.image.fill(0)
        image_painter = QPainter(self.image)
        render_context = QgsRenderContext.fromQPainter(image_painter)

        image_painter.setRenderHint(QPainter.Antialiasing, True)

        rect = self.canvas.mapSettings().visibleExtent()
        request = QgsFeatureRequest()
        request.setFilterRect(rect)
        request.setFilterExpression(
            QgsExpression.createFieldEqualityExpression('type', self.task))

        for f in self.electorate_layer.getFeatures(request):
            #    pole, dist = f.geometry().clipped(rect).poleOfInaccessibility(rect.width() / 30)
            pixel = self.toCanvasCoordinates(
                f.geometry().clipped(rect).centroid().asPoint())

            calc = QgsAggregateCalculator(self.meshblock_layer)
            calc.setFilter('staged_electorate={}'.format(f['electorate_id']))
            estimated_pop, ok = calc.calculate(QgsAggregateCalculator.Sum,
                                               'offline_pop_{}'.format(
                                                   self.task.lower()))  # pylint: disable=unused-variable

            text_string = [
                '{}'.format(f['name']), '{}'.format(int(estimated_pop))
            ]
            QgsTextRenderer().drawText(QPointF(pixel.x(), pixel.y()), 0,
                                       QgsTextRenderer.AlignCenter,
                                       text_string, render_context,
                                       self.text_format)

        image_painter.end()

        painter.drawImage(0, 0, self.image)
 def get_target_meshblock_ids_from_numbers(
         self, meshblock_numbers: List[int]) -> Dict[int, int]:
     """
     Returns a dictionary of target meshblock feature IDs corresponding to the
     specified meshblock numbers
     :param meshblock_numbers: list of meshblock numbers to lookup
     """
     assert self.scenario is not None
     request = QgsFeatureRequest()
     request.setSubsetOfAttributes([self.target_meshblock_number_idx])
     request.setFilterExpression(
         QgsExpression.createFieldEqualityExpression(
             'scenario_id', self.scenario))
     meshblock_filter = 'meshblock_number IN ({})'.format(','.join(
         [str(mb) for mb in meshblock_numbers]))
     request.combineFilterExpression(meshblock_filter)
     # create dictionary of meshblock number to id
     mb_number_to_target_id = {}
     for f in self.meshblock_scenario_layer.getFeatures(request):
         mb_number_to_target_id[f[
             self.target_meshblock_number_idx]] = f.id()
     return mb_number_to_target_id
    def redraw(self, handler):
        """
        Forces a redraw of the cached image
        """
        self.image = None

        if not self.original_populations:
            # first run, get initial estimates
            request = QgsFeatureRequest()
            request.setFilterExpression(QgsExpression.createFieldEqualityExpression('type', self.task))
            request.setFlags(QgsFeatureRequest.NoGeometry)
            for f in self.electorate_layer.getFeatures(request):
                estimated_pop = f.attribute(handler.stats_nz_pop_field_index)
                if estimated_pop is None or estimated_pop == NULL:
                    # otherwise just use existing estimated pop as starting point
                    estimated_pop = f.attribute(handler.estimated_pop_idx)
                self.original_populations[f.id()] = estimated_pop

        # step 1: get all electorate features corresponding to affected electorates
        electorate_features = {f[handler.electorate_layer_field]: f for f in
                               handler.get_affected_districts(
                                   [handler.electorate_layer_field, handler.stats_nz_pop_field, 'estimated_pop'],
                                   needs_geometry=False)}

        self.new_populations = {}

        for district in handler.pending_affected_districts.keys():  # pylint: disable=consider-iterating-dictionary
            # use stats nz pop as initial estimate, if available
            estimated_pop = electorate_features[district].attribute(handler.stats_nz_pop_field_index)
            if estimated_pop is None or estimated_pop == NULL:
                # otherwise just use existing estimated pop as starting point
                estimated_pop = electorate_features[district].attribute(handler.estimated_pop_idx)
            # add new bits
            estimated_pop = handler.grow_population_with_added_meshblocks(district, estimated_pop)
            # minus lost bits
            estimated_pop = handler.shrink_population_by_removed_meshblocks(district, estimated_pop)

            self.new_populations[electorate_features[district].id()] = estimated_pop
示例#28
0
    def processAlgorithm(self, parameters, context, feedback):
        # Get variables from dialog
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))

        field_name = self.parameterAsString(parameters, self.FIELD, context)
        kneighbors = self.parameterAsInt(parameters, self.KNEIGHBORS, context)
        use_field = bool(field_name)

        field_index = -1

        fields = QgsFields()
        fields.append(QgsField('id', QVariant.Int, '', 20))

        current = 0

        # Get properties of the field the grouping is based on
        if use_field:
            field_index = source.fields().lookupField(field_name)
            if field_index >= 0:
                fields.append(
                    source.fields()[field_index]
                )  # Add a field with the name of the grouping field

                # Initialize writer
                (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                                       context, fields,
                                                       QgsWkbTypes.Polygon,
                                                       source.sourceCrs())
                if sink is None:
                    raise QgsProcessingException(
                        self.invalidSinkError(parameters, self.OUTPUT))

                success = False
                fid = 0

                # Get unique values of grouping field
                unique_values = source.uniqueValues(field_index)
                total = 100.0 / float(
                    source.featureCount() * len(unique_values))

                for unique in unique_values:
                    points = []
                    filter = QgsExpression.createFieldEqualityExpression(
                        field_name, unique)
                    request = QgsFeatureRequest().setFilterExpression(filter)
                    request.setSubsetOfAttributes([])
                    # Get features with the grouping attribute equal to the current grouping value
                    features = source.getFeatures(request)
                    for in_feature in features:
                        if feedback.isCanceled():
                            break
                        # Add points or vertices of more complex geometry
                        points.extend(extract_points(in_feature.geometry()))
                        current += 1
                        feedback.setProgress(int(current * total))
                    # A minimum of 3 points is necessary to proceed
                    if len(points) >= 3:
                        out_feature = QgsFeature()
                        the_hull = concave_hull(points, kneighbors)
                        if the_hull:
                            vertex = [
                                QgsPointXY(point[0], point[1])
                                for point in the_hull
                            ]
                            poly = QgsGeometry().fromPolygonXY([vertex])

                            out_feature.setGeometry(poly)
                            # Give the polygon the same attribute as the point grouping attribute
                            out_feature.setAttributes([fid, unique])
                            sink.addFeature(out_feature,
                                            QgsFeatureSink.FastInsert)
                            success = True  # at least one polygon created
                    fid += 1
                if not success:
                    raise QgsProcessingException(
                        'No hulls could be created. Most likely there were not at least three unique points in any of the groups.'
                    )
            else:
                # Field parameter provided but can't read from it
                raise QgsProcessingException('Unable to find grouping field')

        else:
            # Not grouped by field
            # Initialize writer
            (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                                   context, fields,
                                                   QgsWkbTypes.Polygon,
                                                   source.sourceCrs())
            if sink is None:
                raise QgsProcessingException(
                    self.invalidSinkError(parameters, self.OUTPUT))

            points = []
            request = QgsFeatureRequest()
            request.setSubsetOfAttributes([])
            features = source.getFeatures(request)  # Get all features
            total = 100.0 / source.featureCount() if source.featureCount(
            ) else 0
            for in_feature in features:
                if feedback.isCanceled():
                    break
                # Add points or vertices of more complex geometry
                points.extend(extract_points(in_feature.geometry()))
                current += 1
                feedback.setProgress(int(current * total))

            # A minimum of 3 points is necessary to proceed
            if len(points) >= 3:
                out_feature = QgsFeature()
                the_hull = concave_hull(points, kneighbors)
                if the_hull:
                    vertex = [
                        QgsPointXY(point[0], point[1]) for point in the_hull
                    ]
                    poly = QgsGeometry().fromPolygonXY([vertex])

                    out_feature.setGeometry(poly)
                    out_feature.setAttributes([0])
                    sink.addFeature(out_feature, QgsFeatureSink.FastInsert)
                else:
                    # the_hull returns None only when there are less than three points after cleaning
                    raise QgsProcessingException(
                        'At least three unique points are required to create a concave hull.'
                    )
            else:
                raise QgsProcessingException(
                    'At least three points are required to create a concave hull.'
                )

        return {self.OUTPUT: dest_id}
    def processAlgorithm(self, parameters, context, feedback):

        t_file = self.parameterAsVectorLayer(
            parameters,
            self.FILE_TABLE,
            context
        )
        t_troncon = self.parameterAsVectorLayer(
            parameters,
            self.SEGMENTS_TABLE,
            context
        )
        t_obs = self.parameterAsVectorLayer(
            parameters,
            self.OBSERVATIONS_TABLE,
            context
        )
        t_regard = self.parameterAsVectorLayer(
            parameters,
            self.MANHOLES_TABLE,
            context
        )

        g_regard = self.parameterAsVectorLayer(
            parameters,
            self.GEOM_MANHOLES,
            context
        )
        g_troncon = self.parameterAsVectorLayer(
            parameters,
            self.GEOM_SEGMENT,
            context
        )
        g_obs = self.parameterAsVectorLayer(
            parameters,
            self.GEOM_OBSERVATION,
            context
        )

        v_regard = self.parameterAsVectorLayer(
            parameters,
            self.VIEW_MANHOLES_GEOLOCALIZED,
            context
        )

        # define variables
        variables = context.project().customVariables()
        variables['itv_rerau_t_file'] = t_file.id()
        variables['itv_rerau_t_troncon'] = t_troncon.id()
        variables['itv_rerau_t_obs'] = t_obs.id()
        variables['itv_rerau_t_regard'] = t_regard.id()

        variables['itv_rerau_g_regard'] = g_regard.id()
        variables['itv_rerau_g_troncon'] = g_troncon.id()
        variables['itv_rerau_g_obs'] = g_obs.id()

        context.project().setCustomVariables(variables)

        # define relations
        relations = [
            {
                'id': 'fk_obs_id_file',
                'name': tr('Link File - Observation'),
                'referencingLayer': t_obs.id(),
                'referencingField': 'id_file',
                'referencedLayer': t_file.id(),
                'referencedField': 'id'
            }, {
                'id': 'fk_regard_id_file',
                'name': tr('Link File - Manhole'),
                'referencingLayer': t_regard.id(),
                'referencingField': 'id_file',
                'referencedLayer': t_file.id(),
                'referencedField': 'id'
            }, {
                'id': 'fk_troncon_id_file',
                'name': tr('Link File - Pipe segment'),
                'referencingLayer': t_troncon.id(),
                'referencingField': 'id_file',
                'referencedLayer': t_file.id(),
                'referencedField': 'id'
            }, {
                'id': 'fk_obs_id_troncon',
                'name': tr('Link Pipe segment - Observation'),
                'referencingLayer': t_obs.id(),
                'referencingField': 'id_troncon',
                'referencedLayer': t_troncon.id(),
                'referencedField': 'id'
            }, {
                'id': 'fk_regard_id_geom_regard',
                'name': tr('Link Manhole inspection - Reference'),
                'referencingLayer': t_regard.id(),
                'referencingField': 'id_geom_regard',
                'referencedLayer': g_regard.id(),
                'referencedField': 'id'
            }, {
                'id': 'fk_troncon_id_geom_trononc',
                'name': tr('Link Pipe segment inspection - Reference'),
                'referencingLayer': t_troncon.id(),
                'referencingField': 'id_geom_troncon',
                'referencedLayer': g_troncon.id(),
                'referencedField': 'id'
            }
        ]

        relation_manager = context.project().relationManager()
        for rel_def in relations:
            feedback.pushInfo(
                'Link: {}'.format(rel_def['name'])
            )
            rel = QgsRelation()
            rel.setId(rel_def['id'])
            rel.setName(rel_def['name'])
            rel.setReferencingLayer(rel_def['referencingLayer'])
            rel.setReferencedLayer(rel_def['referencedLayer'])
            rel.addFieldPair(
                rel_def['referencingField'],
                rel_def['referencedField']
            )
            rel.setStrength(QgsRelation.Association)
            relation_manager.addRelation(rel)
            feedback.pushInfo(
                'Count relations {}'.format(
                    len(relation_manager.relations())
                )
            )

        joins = [
            {
                'layer': t_obs,
                'targetField': 'id_troncon',
                'joinLayer': t_troncon,
                'joinField': 'id',
                'fieldNamesSubset': ['ack']
            }, {
                'layer': g_obs,
                'targetField': 'id',
                'joinLayer': t_obs,
                'joinField': 'id',
                'fieldNamesSubset': []
            }
        ]
        for j_def in joins:
            layer = j_def['layer']

            join = QgsVectorLayerJoinInfo()
            join.setJoinFieldName(j_def['joinField'])
            join.setJoinLayerId(j_def['joinLayer'].id())
            join.setTargetFieldName(j_def['targetField'])

            if j_def['fieldNamesSubset']:
                join.setJoinFieldNamesSubset(j_def['fieldNamesSubset'])

            join.setUsingMemoryCache(False)
            join.setPrefix('')
            join.setEditable(False)
            join.setCascadedDelete(False)

            join.setJoinLayer(j_def['joinLayer'])

            layer.addJoin(join)
            layer.updateFields()

        # load styles
        styles = [
            {
                'layer': t_file,
                'namedStyles': [
                    {
                        'file': 'itv_file_fields.qml',
                        'type': QgsMapLayer.Fields
                    }, {
                        'file': 'itv_file_actions.qml',
                        'type': QgsMapLayer.Actions
                    }
                ]
            }, {
                'layer': t_troncon,
                'namedStyles': [
                    {
                        'file': 'itv_troncon_fields.qml',
                        'type': QgsMapLayer.Fields
                    }, {
                        'file': 'itv_troncon_table.qml',
                        'type': QgsMapLayer.AttributeTable
                    }
                ]
            }, {
                'layer': t_obs,
                'namedStyles': [
                    {
                        'file': 'itv_obs_fields.qml',
                        'type': QgsMapLayer.Fields
                    }, {
                        'file': 'itv_obs_table.qml',
                        'type': QgsMapLayer.AttributeTable
                    }
                ]
            }, {
                'layer': t_regard,
                'namedStyles': [
                    {
                        'file': 'itv_regard_fields.qml',
                        'type': QgsMapLayer.Fields
                    }, {
                        'file': 'itv_regard_forms.qml',
                        'type': QgsMapLayer.Forms
                    }, {
                        'file': 'itv_regard_table.qml',
                        'type': QgsMapLayer.AttributeTable
                    }
                ]
            }, {
                'layer': g_regard,
                'namedStyles': [
                    {
                        'file': 'itv_geom_regard_fields.qml',
                        'type': QgsMapLayer.Fields
                    }, {
                        'file': 'itv_geom_regard_symbology.qml',
                        'type': QgsMapLayer.Symbology
                    }
                ]
            }, {
                'layer': g_troncon,
                'namedStyles': [
                    {
                        'file': 'itv_geom_troncon_fields.qml',
                        'type': QgsMapLayer.Fields
                    }, {
                        'file': 'itv_geom_troncon_symbology.qml',
                        'type': QgsMapLayer.Symbology
                    }, {
                        'file': 'itv_geom_troncon_actions.qml',
                        'type': QgsMapLayer.Actions
                    }
                ]
            }, {
                'layer': g_obs,
                'namedStyles': [
                    {
                        'file': 'itv_geom_obs_fields.qml',
                        'type': QgsMapLayer.Fields
                    }, {
                        'file': 'itv_geom_obs_symbology.qml',
                        'type': QgsMapLayer.Symbology
                    }
                ]
            }, {
                'layer': v_regard,
                'namedStyles': [
                    {
                        'file': 'itv_view_regard_fields.qml',
                        'type': QgsMapLayer.Fields
                    }, {
                        'file': 'itv_view_regard_symbology.qml',
                        'type': QgsMapLayer.Symbology
                    }, {
                        'file': 'itv_view_regard_labeling.qml',
                        'type': QgsMapLayer.Labeling
                    }
                ]
            }
        ]
        for style in styles:
            layer = style['layer']
            for n_style in style['namedStyles']:
                layer.loadNamedStyle(
                    resources_path('styles', n_style['file']),
                    categories=n_style['type']
                )
                # layer.saveStyleToDatabase('style', 'default style', True, '')
                layer.triggerRepaint()

        # Creation de la symbologie g_obs
        g_obs_rules = (
            'BAA', 'BAB', 'BAC', 'BAD', 'BAE', 'BAF', 'BAG', 'BAH',
            'BAI', 'BAJ', 'BAK', 'BAL', 'BAM', 'BAN', 'BAO', 'BAP',
            'BBA', 'BBB', 'BBC', 'BBD', 'BBE', 'BBF', 'BBG', 'BBH',
            'BCA', 'BCB', 'BCC', 'BDA', 'BDB', 'BDC', 'BDD', 'BDE',
            'BDF', 'BDG'
        )
        g_obs_rule_descs = {
            'BAA': 'Déformation',
            'BAB': 'Fissure',
            'BAC': 'Rupture/Effondrement',
            'BAD': 'Elt maçonnerie',
            'BAE': 'Mortier manquant',
            'BAF': 'Dégradation de surface',
            'BAG': 'Branchement pénétrant',
            'BAH': 'Raccordement défectueux',
            'BAI': 'Joint étanchéité apparent',
            'BAJ': 'Déplacement d\'assemblage',
            'BAK': 'Défaut de révêtement',
            'BAL': 'Réparation défectueuse',
            'BAM': 'Défaut soudure',
            'BAN': 'Conduite poreuse',
            'BAO': 'Sol visible',
            'BAP': 'Trou visible',
            'BBA': 'Racines',
            'BBB': 'Dépots Adhérents',
            'BBC': 'Dépôts',
            'BBD': 'Entrée de terre',
            'BBE': 'Autres obstacles',
            'BBF': 'Infiltration',
            'BBG': 'Exfiltration',
            'BBH': 'Vermine',
            'BCA': 'Raccordement',
            'BCB': 'Réparation',
            'BCC': 'Courbure de collecteur',
            'BDA': 'Photographie générale',
            'BDB': 'Remarque générale',
            'BDC': 'Inspection abandonnée',
            'BDD': 'Niveau d\'eau',
            'BDE': 'Ecoulement dans une canlisation entrante',
            'BDF': 'Atmosphère canalisation',
            'BDG': 'Perte de visibilité'
        }
        g_obs_rootrule = QgsRuleBasedRenderer.Rule(None)
        rendering_pass_idx = len(g_obs_rules)
        for rule in g_obs_rules:
            # get svg path
            svg_path = resources_path('styles', 'img_obs', rule + '.svg')
            # create svg symbol layer
            svg_symbol_layer = QgsSvgMarkerSymbolLayer(svg_path)
            svg_symbol_layer.setRenderingPass(rendering_pass_idx)
            # create white square symbol layer for the backend
            simple_symbol_layer = QgsSimpleMarkerSymbolLayer(
                shape=QgsSimpleMarkerSymbolLayerBase.Circle,
                size=svg_symbol_layer.size(),
                color=QColor('white'),
                strokeColor=QColor('white')
            )
            simple_symbol_layer.setRenderingPass(rendering_pass_idx)
            # create marker
            svg_marker = QgsMarkerSymbol()
            # set the backend symbol layer
            svg_marker.changeSymbolLayer(0, simple_symbol_layer)
            # add svg symbol layer
            svg_marker.appendSymbolLayer(svg_symbol_layer)
            # create rule
            svg_rule = QgsRuleBasedRenderer.Rule(
                svg_marker, 0, 10000,
                QgsExpression.createFieldEqualityExpression('a', rule),
                rule
            )
            if rule in g_obs_rule_descs:
                svg_rule.setLabel(g_obs_rule_descs[rule])
                svg_rule.setDescription('{}: {}'.format(
                    rule,
                    g_obs_rule_descs[rule]
                ))
            # add rule
            g_obs_rootrule.appendChild(svg_rule)
            rendering_pass_idx -= 1
        g_obs_rootrule.appendChild(
            QgsRuleBasedRenderer.Rule(
                QgsMarkerSymbol.createSimple(
                    {
                        'name': 'circle',
                        'color': '#0000b2',
                        'outline_color': '#0000b2',
                        'size': '1'
                    }
                ),
                0, 10000, 'ELSE', 'Autres'
            )
        )
        g_obs.setRenderer(QgsRuleBasedRenderer(g_obs_rootrule))
        feedback.pushInfo('Project has been setup')
        return {}
def _remove_selected_from_relation(agg_id: int, src_primary_key: str,
                                   src_layer_name: str, rel_primary_key: str,
                                   rel_layer_name: str):

    # get project instance
    project = QgsProject.instance()

    # get layers needed
    src_layer = project.mapLayersByName(src_layer_name)

    # Control if layers exists
    if not len(src_layer):
        iface.messageBar().pushCritical(
            'Véloroutes',
            'La couche {} n\'a pas été trouvée'.format(src_layer_name))
        return
    src_layer = src_layer[0]

    rel_layer = project.mapLayersByName(rel_layer_name)
    if not len(rel_layer):
        iface.messageBar().pushCritical(
            'Véloroutes',
            'La table {} n\'a pas été trouvée'.format(rel_layer_name))
        return
    rel_layer = rel_layer[0]

    # count number of features selected from src_layer
    count = src_layer.selectedFeatureCount()
    if count < 1:
        iface.messageBar().pushCritical(
            'Véloroutes',
            'Vous devez sélectionner au moins un objet de la couche {}'.format(
                src_layer_name))
        return

    # get list rel_layer
    couple_rel = []
    for feat in rel_layer.getFeatures(
            QgsExpression.createFieldEqualityExpression(
                src_primary_key, agg_id)):
        couple_rel.append((feat[src_primary_key], feat[rel_primary_key]))

    # create list of couples between src_primary_key and rel_primary_key
    features = src_layer.getSelectedFeatures()
    couple_id = []
    for feat in features:
        couple_id.append((agg_id, feat[rel_primary_key]))

    # test between two lists
    match_list = []

    for item in couple_rel:
        if item in couple_id:
            match_list.append(item)

    # test if match_list is empty
    if len(match_list) < 1:
        iface.messageBar().pushInfo(
            'Véloroutes',
            'Pas d\'objet de la couche {} à supprimer'.format(rel_layer_name))
        return
    rel_layer.startEditing()
    for feat in rel_layer.getFeatures(
            QgsExpression.createFieldEqualityExpression(
                src_primary_key, agg_id)):
        if (feat[src_primary_key], feat[rel_primary_key]) in match_list:
            rel_layer.deleteFeature(feat.id())
    rel_layer.commitChanges()
    msg = "{} objet(s) ont été supprimées de la couche {}".format(
        len(match_list), rel_layer_name)
    iface.messageBar().pushInfo('Véloroutes', msg)
示例#31
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)

        request = QgsFeatureRequest()
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes(self.fields, source.fields())
        request.addOrderBy("|| ' - ' || ".join(self.fields))

        unique_couples = []
        non_unique_couples = []
        for src_feature in source.getFeatures(request):
            couple = (src_feature[self.fields[0]], src_feature[self.fields[1]])

            if couple not in unique_couples:
                unique_couples.append(couple)
            else:
                non_unique_couples.append(couple)

        if not non_unique_couples:
            feedback.pushInfo(
                'L\'ensemble des couples noms/faciès sont uniques')
            _, dest_id = self.parameterAsSink(parameters, self.OUTPUT, context,
                                              source.fields(),
                                              source.wkbType(),
                                              source.sourceCrs())
            return {
                self.OUTPUT: dest_id,
                self.NUMBER_OF_UNIQUE: len(unique_couples),
                self.NUMBER_OF_NON_UNIQUE: 0
            }

        feedback.pushInfo('Certains couples ne sont pas uniques :')
        for couple in non_unique_couples:
            feedback.pushInfo('   {} - {}'.format(couple[0], couple[1]))

        expressions = []
        for couple in non_unique_couples:
            exp = ' AND '.join([
                QgsExpression.createFieldEqualityExpression(
                    self.fields[0], couple[0]),
                QgsExpression.createFieldEqualityExpression(
                    self.fields[1], couple[1])
            ])
            expressions.append(exp)
        exp = '('
        exp += ') OR ('.join(expressions)
        exp += ')'
        feedback.pushDebugInfo(exp)
        exp_context = self.createExpressionContext(parameters, context, source)

        request = QgsFeatureRequest()
        request.setFilterExpression(exp)
        request.setExpressionContext(exp_context)

        layer = source.materialize(request)

        params = {
            'ALL_PARTS': True,
            'INPUT': layer,
            'OUTPUT': 'TEMPORARY_OUTPUT'
        }
        results = processing.run(
            "native:pointonsurface",
            params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

        params = {
            'INPUT': results['OUTPUT'],
            'FIELD': self.fields,
            'OUTPUT': parameters[self.OUTPUT]
        }
        results = processing.run(
            "native:collect",
            params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

        output_layer = results['OUTPUT']
        if context.willLoadLayerOnCompletion(output_layer):
            layer_details = context.layerToLoadOnCompletionDetails(
                output_layer)
            output_def = self.parameterDefinition(self.OUTPUT)
            layer_details.name = output_def.description()
            layer_details.setPostProcessor(
                SetLabelingPostProcessor.create(self.fields))

        return {
            self.OUTPUT: output_layer,
            self.NUMBER_OF_UNIQUE: len(unique_couples),
            self.NUMBER_OF_NON_UNIQUE: len(non_unique_couples)
        }
示例#32
0
    def processAlgorithm(self, parameters, context, feedback):
        habitat = self.parameterAsVectorLayer(parameters, self.HABITAT_LAYER, context)
        impact = self.parameterAsVectorLayer(parameters, self.IMPACT_LAYER, context)
        self.output_layer = self.parameterAsVectorLayer(
            parameters, self.HABITAT_IMPACT_ETAT_ECOLOGIQUE_LAYER, context)

        multi_feedback = QgsProcessingMultiStepFeedback(4, feedback)

        multi_feedback.setCurrentStep(0)
        multi_feedback.pushInfo(
            "Calcul de l'intersection entre les couches habitat et {}".format(self.project_type.label))
        params = {
            'INPUT': habitat,
            'OVERLAY': impact,
            'INPUT_FIELDS': [],  # All fields
            'OVERLAY_FIELDS': ['id', 'scenario_id'],
            'OVERLAY_FIELDS_PREFIX': '{}_'.format(self.project_type.label),
            'OUTPUT': 'TEMPORARY_OUTPUT',
        }
        results = processing.run(
            "native:intersection",
            params,
            context=context,
            feedback=multi_feedback,
            is_child_algorithm=True,
        )
        intersection = QgsProcessingUtils.mapLayerFromString(results['OUTPUT'], context, True)

        multi_feedback.pushInfo("Renommage des champs")
        multi_feedback.setCurrentStep(1)
        # Rename id to habitat_id
        # Rename pression_scenario_id to scenario_id (or compensation_id)
        index_habitat = intersection.fields().indexOf('id')
        index_impact_id = intersection.fields().indexOf('{}_scenario_id'.format(self.project_type.label))

        with edit(intersection):
            intersection.renameAttribute(index_habitat, 'habitat_id')
            intersection.renameAttribute(index_impact_id, 'scenario_id')

        intersection.updateFields()

        multi_feedback.pushInfo(
            "Collect des géométries ayant les couples {} identiques".format(' ,'.join(self.fields_id)))
        multi_feedback.setCurrentStep(2)
        params = {
            'INPUT': intersection,
            'FIELD': list(self.fields_id),
            'OUTPUT': 'TEMPORARY_OUTPUT',
        }
        results = processing.run(
            "native:collect",
            params,
            context=context,
            feedback=multi_feedback,
            is_child_algorithm=True,
        )

        multi_feedback.pushInfo("Correction des géométries")
        multi_feedback.setCurrentStep(3)

        params = {
            'INPUT': results['OUTPUT'],
            'DISTANCE': 0,
            'OUTPUT': 'TEMPORARY_OUTPUT',
        }
        results = processing.run(
            "native:buffer",
            params,
            context=context,
            feedback=multi_feedback,
            is_child_algorithm=True)

        layer = QgsProcessingUtils.mapLayerFromString(results['OUTPUT'], context, True)

        multi_feedback.pushInfo("Import des entités dans la couche du geopackage")
        multi_feedback.setCurrentStep(4)
        field_map = {}
        for i, field in enumerate(self.output_layer.fields()):
            field_map[field.name()] = i

        with edit(self.output_layer):
            for feature in layer.getFeatures():
                habitat_id = feature['habitat_id']
                impact_id = feature[self.impact_id]
                scenario_id = feature['scenario_id']

                # TODO need to check for compensation this behavior
                if self.impact_field:
                    # Test du type de pression associé
                    filter_expression = QgsExpression.createFieldEqualityExpression('id', impact_id)
                    filter_request = QgsFeatureRequest(QgsExpression(filter_expression))
                    filter_request.setLimit(1)
                    for press in impact.getFeatures(filter_expression):
                        pression_emprise = (press['type_pression'] == 6)
                        break
                else:
                    pression_emprise = False

                exists, existing_feature = self.feature_exists(
                    self.output_layer, habitat_id, impact_id, scenario_id)

                if exists:
                    self.output_layer.changeGeometry(existing_feature.id(), feature.geometry())

                    attribute_map = {}
                    for field in layer.fields():
                        field_name = field.name()
                        if field_name in self.fields_id:
                            continue

                        if field_name in self.output_layer.fields().names():
                            if pression_emprise and field_name in self.fields():
                                attribute_map[field_map[field_name]] = 0
                            else:
                                attribute_map[field_map[field_name]] = feature[field_name]

                    self.output_layer.changeAttributeValues(existing_feature.id(), attribute_map)

                else:
                    # We create a new feature
                    out_feature = QgsFeature(self.output_layer.fields())
                    out_feature.setAttribute('habitat_id', habitat_id)
                    out_feature.setAttribute(self.impact_id, impact_id)
                    out_feature.setAttribute('scenario_id', scenario_id)
                    out_feature.setGeometry(feature.geometry())

                    for field in layer.fields():
                        field_name = field.name()
                        if field_name in self.fields_id:
                            continue

                        if field_name in self.output_layer.fields().names():
                            if pression_emprise and field_name in self.fields():
                                out_feature.setAttribute(field_name, 0)
                            else:
                                out_feature.setAttribute(field_name, feature[field_name])

                    self.output_layer.addFeature(out_feature)

        return {}
示例#33
0
    def processAlgorithm(self, parameters, context, feedback):
        # Get variables from dialog
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))

        field_name = self.parameterAsString(parameters, self.FIELD, context)
        kneighbors = self.parameterAsInt(parameters, self.KNEIGHBORS, context)
        use_field = bool(field_name)

        field_index = -1

        fields = QgsFields()
        fields.append(QgsField('id', QVariant.Int, '', 20))

        current = 0

        # Get properties of the field the grouping is based on
        if use_field:
            field_index = source.fields().lookupField(field_name)
            if field_index >= 0:
                fields.append(source.fields()[field_index]) # Add a field with the name of the grouping field

                # Initialize writer
                (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                                       fields, QgsWkbTypes.Polygon, source.sourceCrs())
                if sink is None:
                    raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

                success = False
                fid = 0

                # Get unique values of grouping field
                unique_values = source.uniqueValues(field_index)
                total = 100.0 / float(source.featureCount() * len(unique_values))

                for unique in unique_values:
                    points = []
                    filter = QgsExpression.createFieldEqualityExpression(field_name, unique)
                    request = QgsFeatureRequest().setFilterExpression(filter)
                    request.setSubsetOfAttributes([])
                    # Get features with the grouping attribute equal to the current grouping value
                    features = source.getFeatures(request)
                    for in_feature in features:
                        if feedback.isCanceled():
                            break
                        # Add points or vertices of more complex geometry
                        points.extend(extract_points(in_feature.geometry()))
                        current += 1
                        feedback.setProgress(int(current * total))
                    # A minimum of 3 points is necessary to proceed
                    if len(points) >= 3:
                        out_feature = QgsFeature()
                        the_hull = concave_hull(points, kneighbors)
                        if the_hull:
                            vertex = [QgsPointXY(point[0], point[1]) for point in the_hull]
                            poly = QgsGeometry().fromPolygonXY([vertex])

                            out_feature.setGeometry(poly)
                            # Give the polygon the same attribute as the point grouping attribute
                            out_feature.setAttributes([fid, unique])
                            sink.addFeature(out_feature, QgsFeatureSink.FastInsert)
                            success = True  # at least one polygon created
                    fid += 1
                if not success:
                    raise QgsProcessingException('No hulls could be created. Most likely there were not at least three unique points in any of the groups.')
            else:
                # Field parameter provided but can't read from it
                raise QgsProcessingException('Unable to find grouping field')

        else:
            # Not grouped by field
            # Initialize writer
            (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                                   fields, QgsWkbTypes.Polygon, source.sourceCrs())
            if sink is None:
                raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

            points = []
            request = QgsFeatureRequest()
            request.setSubsetOfAttributes([])
            features = source.getFeatures(request) # Get all features
            total = 100.0 / source.featureCount() if source.featureCount() else 0
            for in_feature in features:
                if feedback.isCanceled():
                    break
                # Add points or vertices of more complex geometry
                points.extend(extract_points(in_feature.geometry()))
                current += 1
                feedback.setProgress(int(current * total))

            # A minimum of 3 points is necessary to proceed
            if len(points) >= 3:
                out_feature = QgsFeature()
                the_hull = concave_hull(points, kneighbors)
                if the_hull:
                    vertex = [QgsPointXY(point[0], point[1]) for point in the_hull]
                    poly = QgsGeometry().fromPolygonXY([vertex])

                    out_feature.setGeometry(poly)
                    out_feature.setAttributes([0])
                    sink.addFeature(out_feature, QgsFeatureSink.FastInsert)
                else:
                    # the_hull returns None only when there are less than three points after cleaning
                    raise QgsProcessingException('At least three unique points are required to create a concave hull.')
            else:
                raise QgsProcessingException('At least three points are required to create a concave hull.')

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