コード例 #1
0
 def test_add_attributes(self):
     field_one = QgsField('first', QVariant.String)
     field_one.setTypeName(STRING_FIELD_TYPE_NAME)
     field_two = QgsField('second', QVariant.Int)
     field_two.setTypeName(INT_FIELD_TYPE_NAME)
     attributes = [field_one, field_two]
     added_attributes = ProcessLayer(self.layer).add_attributes(attributes)
     expected_dict = {'first': 'first', 'second': 'second'}
     self.assertDictEqual(added_attributes, expected_dict)
     # Let's add 2 other fields with the same names of the previous ones
     # ==> Since the names are already taken, we expect to add fields with
     # the same names plus '_1'
     field_three = QgsField('first', QVariant.String)
     field_three.setTypeName(STRING_FIELD_TYPE_NAME)
     field_four = QgsField('second', QVariant.Int)
     field_four.setTypeName(INT_FIELD_TYPE_NAME)
     attributes = [field_three, field_four]
     added_attributes = ProcessLayer(self.layer).add_attributes(attributes)
     expected_dict = {'first': 'first_1', 'second': 'second_1'}
     self.assertEqual(added_attributes, expected_dict)
     # Let's add 2 other fields with the same names of the previous ones
     # ==> Since the names are already taken, as well as the corresponding
     # '_1' versions, we expect to add fields with the same names plus '_2'
     field_five = QgsField('first', QVariant.String)
     field_five.setTypeName(STRING_FIELD_TYPE_NAME)
     field_six = QgsField('second', QVariant.Int)
     field_six.setTypeName(INT_FIELD_TYPE_NAME)
     attributes = [field_five, field_six]
     added_attributes = ProcessLayer(self.layer).add_attributes(attributes)
     expected_dict = {'first': 'first_2', 'second': 'second_2'}
     self.assertEqual(added_attributes, expected_dict)
コード例 #2
0
 def _check_output_layer(self, output_layer, expected_layer):
     if not ProcessLayer(output_layer).has_same_content_as(
             expected_layer):
         ProcessLayer(output_layer).pprint(usage='testing')
         ProcessLayer(expected_layer).pprint(usage='testing')
         raise Exception(
             'The output layer is different than expected (see above)')
コード例 #3
0
    def test_custom_operator(self):
        proj_def = deepcopy(self.project_definition)
        operator = OPERATORS_DICT['CUSTOM']
        # set economy's operator to custom and use a custom formula
        proj_def['children'][1]['children'][0]['operator'] = operator
        proj_def['children'][1]['children'][0]['fieldDescription'] = \
            'EDUEOCSAF plus one'
        proj_def['children'][1]['children'][0]['customFormula'] = \
            '"EDUEOCSAF" + 1'
        node_attr_id, node_attr_name, discarded_feats = \
            calculate_education_node(proj_def, operator, self.layer)

        if self.REBUILD_OUTPUTS:
            res_layer_name = 'custom_operator'
            write_output(self.layer, self.data_dir_name, res_layer_name)

        expected_layer_path = os.path.join(self.data_dir_name,
                                           'custom_operator.gpkg')
        expected_layer = QgsVectorLayer(expected_layer_path, 'custom_operator',
                                        'ogr')
        res = ProcessLayer(self.layer).has_same_content_as(expected_layer)
        try:
            self.assertEqual(res, True)
        except AssertionError:
            sys.stderr.write("The resulting layer is different than expected")
            sys.stderr.write("\n\n\nCalculated EDUCATION as EDUEOCSAF+1:\n")
            ProcessLayer(self.layer).pprint(usage='testing')
            sys.stderr.write("\n\n\nExpected layer (custom_operator.gpkg):\n")
            ProcessLayer(expected_layer).pprint(usage='testing')
            raise
コード例 #4
0
    def _aggregate_using_geometries(self, extra=True):
        loss_layer_path = os.path.join(
            self.data_dir_name, 'loss_points.shp')
        orig_loss_layer = QgsVectorLayer(loss_layer_path, 'Loss points', 'ogr')
        zonal_layer_path = os.path.join(
            self.data_dir_name, 'svi_zones.shp')
        orig_zonal_layer = QgsVectorLayer(
            zonal_layer_path, 'SVI zones', 'ogr')
        # avoid modifying the original layers
        copied_loss_layer = ProcessLayer(orig_loss_layer).duplicate_in_memory()
        copied_zonal_layer = ProcessLayer(
            orig_zonal_layer).duplicate_in_memory()
        zone_id_in_losses_attr_name = None
        zone_id_in_zones_attr_name = None

        res = calculate_zonal_stats(copied_loss_layer,
                                    copied_zonal_layer,
                                    self.loss_attr_names,
                                    self.loss_layer_is_vector,
                                    zone_id_in_losses_attr_name,
                                    zone_id_in_zones_attr_name,
                                    IFACE,
                                    extra=extra)
        (output_loss_layer, output_zonal_layer, output_loss_attrs_dict) = res
        _, output_loss_layer_shp_path = tempfile.mkstemp(suffix='.shp')
        _, output_zonal_layer_shp_path = tempfile.mkstemp(suffix='.shp')
        save_layer_as_shapefile(output_loss_layer, output_loss_layer_shp_path)
        save_layer_as_shapefile(output_zonal_layer,
                                output_zonal_layer_shp_path)
        output_loss_layer = QgsVectorLayer(
            output_loss_layer_shp_path, 'Loss points plus zone ids', 'ogr')
        output_zonal_layer = QgsVectorLayer(
            output_zonal_layer_shp_path, 'Zonal layer', 'ogr')
        expected_loss_layer_path = os.path.join(
            self.data_dir_name, 'loss_points_added_zone_ids.shp')
        expected_loss_layer = QgsVectorLayer(expected_loss_layer_path,
                                             'Loss points plus zone ids',
                                             'ogr')
        if extra:  # adding also count and avg
            expected_zonal_layer_path = os.path.join(
                self.data_dir_name, 'svi_zones_plus_loss_stats_zone_ids.shp')
        else:  # sum only
            expected_zonal_layer_path = os.path.join(
                self.data_dir_name,
                'svi_zones_plus_loss_stats_zone_ids_sum_only.shp')
        expected_zonal_layer = QgsVectorLayer(
            expected_zonal_layer_path, 'Expected zonal layer', 'ogr')
        self._check_output_layer(output_loss_layer, expected_loss_layer)
        self._check_output_layer(output_zonal_layer, expected_zonal_layer)
コード例 #5
0
 def update_default_fieldname(self):
     if self.fields_multiselect.selected_widget.count() != 1:
         self.new_field_name_txt.setText('')
         self.attr_name_user_def = False
         return
     if (not self.attr_name_user_def or not self.new_field_name_txt.text()):
         attribute_name = self._extract_field_name(
             self.fields_multiselect.selected_widget.item(0).text())
         algorithm_name = self.algorithm_cbx.currentText()
         variant = self.variant_cbx.currentText()
         inverse = self.inverse_ckb.isChecked()
         if self.overwrite_ckb.isChecked():
             new_attr_name = attribute_name
         else:
             try:
                 new_attr_name = ProcessLayer(
                     self.iface.activeLayer()).transform_attribute(
                         attribute_name,
                         algorithm_name,
                         variant,
                         inverse,
                         simulate=True)
             except TypeError as exc:
                 log_msg(str(exc),
                         level='C',
                         message_bar=self.iface.messageBar())
                 return
         self.new_field_name_txt.setText(new_attr_name)
         self.attr_name_user_def = False
コード例 #6
0
def get_node_attr_id_and_name(node, layer):
    """
    Get the field (id and name) to be re-used to store the results of the
    calculation, if possible. Otherwise, add a new field to the layer and
    return its id and name.
    Also return True if a new field was added, or False if an old field
    was re-used.
    """
    field_was_added = False
    if 'field' in node:
        node_attr_name = node['field']
        # check that the field is still in the layer (the user might have
        # deleted it). If it is not there anymore, add a new field
        if layer.fieldNameIndex(node_attr_name) == -1:  # not found
            proposed_node_attr_name = node_attr_name
            node_attr_name = add_numeric_attribute(proposed_node_attr_name,
                                                   layer)
            field_was_added = True
        elif DEBUG:
            log_msg('Reusing field %s' % node_attr_name)
    elif 'name' in node:
        proposed_node_attr_name = node['name']
        node_attr_name = add_numeric_attribute(proposed_node_attr_name, layer)
        field_was_added = True
    else:  # this corner case should never happen (hopefully)
        raise InvalidNode('This node has no name and it does'
                          ' not correspond to any field')
    # get the id of the new attribute
    node_attr_id = ProcessLayer(layer).find_attribute_id(node_attr_name)
    return node_attr_id, node_attr_name, field_was_added
コード例 #7
0
    def test_geometric_mean_negative_argument(self):
        proj_def = deepcopy(self.project_definition)
        # do not invert EDUEOCSAM ==> it should cause the geometric mean to
        # attempt calculating the root of a negative number, so we should have
        # the corresponding field discarded with 'Invalid value' reason
        assert proj_def['children'][1]['children'][0]['children'][1]['field'] \
            == 'EDUEOCSAM'
        #                   SVI            Education      EDUEOCSAM
        proj_def['children'][1]['children'][0]['children'][1]['isInverted'] \
            = False
        operator = OPERATORS_DICT['GEOM_MEAN']
        node_attr_id, node_attr_name, discarded_feats = \
            calculate_education_node(proj_def, operator, self.layer)

        if self.REBUILD_OUTPUTS:
            res_layer_name = 'geometric_mean_negative_argument'
            write_output(self.layer, self.data_dir_name, res_layer_name)

        expected_layer_path = os.path.join(
            self.data_dir_name, 'geometric_mean_negative_argument.gpkg')
        expected_layer = QgsVectorLayer(expected_layer_path,
                                        'geometric_mean_negative_argument',
                                        'ogr')
        res = ProcessLayer(self.layer).has_same_content_as(expected_layer)
        self.assertEqual(res, True)
コード例 #8
0
 def test_find_attribute_id(self):
     field_names = ['first', 'second']
     field_one = QgsField(field_names[0], QVariant.String)
     field_one.setTypeName(STRING_FIELD_TYPE_NAME)
     field_two = QgsField(field_names[1], QVariant.Int)
     field_two.setTypeName(INT_FIELD_TYPE_NAME)
     attributes = [field_one, field_two]
     ProcessLayer(self.layer).add_attributes(attributes)
     added_field_names = [field.name() for field in self.layer.fields()]
     # Check that both attributes are correctly found
     for attr_name in added_field_names:
         # it raises AttributeError if not found
         ProcessLayer(self.layer).find_attribute_id(attr_name)
     # Check that an inexistent field doesn't get found and that the
     # AttributeError exception is correctly raised
     with self.assertRaises(AttributeError):
         ProcessLayer(self.layer).find_attribute_id('dummy')
コード例 #9
0
    def accept(self):
        loss_layer_id = self.loss_layer_cbx.itemData(
            self.loss_layer_cbx.currentIndex())
        loss_layer = QgsMapLayerRegistry.instance().mapLayer(
            loss_layer_id)
        zonal_layer_id = self.zonal_layer_cbx.itemData(
            self.zonal_layer_cbx.currentIndex())
        zonal_layer = QgsMapLayerRegistry.instance().mapLayer(
            zonal_layer_id)

        # if the two layers have different projections, display an error
        # message and return
        have_same_projection, check_projection_msg = ProcessLayer(
            loss_layer).has_same_projection_as(zonal_layer)
        if not have_same_projection:
            log_msg(check_projection_msg, level='C',
                    message_bar=self.iface.messageBar())
            return

        # check if loss layer is raster or vector (aggregating by zone
        # is different in the two cases)
        loss_layer_is_vector = self.loss_layer_is_vector

        # Open dialog to ask the user to specify attributes
        # * loss from loss_layer
        # * zone_id from loss_layer
        # * svi from zonal_layer
        # * zone_id from zonal_layer
        ret_val = self.attribute_selection(
            loss_layer, zonal_layer)
        if not ret_val:
            return
        (loss_attr_names,
         zone_id_in_losses_attr_name,
         zone_id_in_zones_attr_name) = ret_val
        # aggregate losses by zone (calculate count of points in the
        # zone, sum and average loss values for the same zone)
        try:
            res = calculate_zonal_stats(loss_layer,
                                        zonal_layer,
                                        loss_attr_names,
                                        loss_layer_is_vector,
                                        zone_id_in_losses_attr_name,
                                        zone_id_in_zones_attr_name,
                                        self.iface)
        except TypeError as exc:
            log_msg(str(exc), level='C', message_bar=self.iface.messageBar())
            return
        (loss_layer, zonal_layer, loss_attrs_dict) = res

        if self.purge_chk.isChecked():
            purge_zones_without_loss_points(
                zonal_layer, loss_attrs_dict, self.iface)
        super(SelectInputLayersDialog, self).accept()
コード例 #10
0
    def setUp(self):

        self.project_definition = PROJ_DEF_STD_OPERATORS

        # Load layer
        curr_dir_name = os.path.dirname(__file__)
        self.data_dir_name = os.path.join(curr_dir_name, os.pardir, 'data',
                                          'calculate_indices')
        layer_path = os.path.join(self.data_dir_name, 'socioeconomic_data.shp')
        orig_layer = QgsVectorLayer(layer_path, 'Zonal Layer', 'ogr')
        # Avoid modifying the original files
        self.layer = ProcessLayer(orig_layer).duplicate_in_memory()
コード例 #11
0
 def load_zonal_layer(self, zonal_layer_path, make_a_copy=False):
     # Load zonal layer
     zonal_layer = QgsVectorLayer(zonal_layer_path, tr('Zonal data'), 'ogr')
     if not zonal_layer.geometryType() == QGis.Polygon:
         msg = 'Zonal layer must contain zone polygons'
         log_msg(msg, level='C', message_bar=self.iface.messageBar())
         return False
     if make_a_copy:
         # Make a copy, where stats will be added
         zonal_layer_plus_stats = ProcessLayer(
             zonal_layer).duplicate_in_memory()
     else:
         zonal_layer_plus_stats = zonal_layer
     # Add zonal layer to registry
     if zonal_layer_plus_stats.isValid():
         QgsMapLayerRegistry.instance().addMapLayer(zonal_layer_plus_stats)
     else:
         msg = 'Invalid zonal layer'
         log_msg(msg, level='C', message_bar=self.iface.messageBar())
         return None
     return zonal_layer_plus_stats
コード例 #12
0
    def test_aggregate_using_zone_id(self):
        loss_layer_path = os.path.join(
            self.data_dir_name, 'loss_points_having_zone_ids.shp')
        orig_loss_layer = QgsVectorLayer(
            loss_layer_path, 'Loss points having zone ids', 'ogr')
        zonal_layer_path = os.path.join(
            self.data_dir_name, 'svi_zones.shp')
        orig_zonal_layer = QgsVectorLayer(
            zonal_layer_path, 'SVI zones', 'ogr')
        # avoid modifying the original layers
        copied_loss_layer = ProcessLayer(orig_loss_layer).duplicate_in_memory()
        copied_zonal_layer = ProcessLayer(
            orig_zonal_layer).duplicate_in_memory()
        zone_id_in_zones_attr_name = 'ZONE_NAME'
        zone_id_in_losses_attr_name = 'ZONE_NAME'
        res = calculate_zonal_stats(copied_loss_layer,
                                    copied_zonal_layer,
                                    self.loss_attr_names,
                                    self.loss_layer_is_vector,
                                    zone_id_in_losses_attr_name,
                                    zone_id_in_zones_attr_name,
                                    IFACE)
        (output_loss_layer, output_zonal_layer, output_loss_attrs_dict) = res
        _, output_loss_layer_shp_path = tempfile.mkstemp(suffix='.shp')
        _, output_zonal_layer_shp_path = tempfile.mkstemp(suffix='.shp')
        save_layer_as_shapefile(output_loss_layer, output_loss_layer_shp_path)
        save_layer_as_shapefile(output_zonal_layer,
                                output_zonal_layer_shp_path)
        output_loss_layer = QgsVectorLayer(
            output_loss_layer_shp_path, 'Loss points having zone ids', 'ogr')
        output_zonal_layer = QgsVectorLayer(
            output_zonal_layer_shp_path, 'Zonal layer', 'ogr')

        expected_zonal_layer_path = os.path.join(
            self.data_dir_name, 'svi_zones_plus_loss_stats_zone_names.shp')
        expected_zonal_layer = QgsVectorLayer(
            expected_zonal_layer_path, 'Expected zonal layer', 'ogr')
        self._check_output_layer(output_zonal_layer, expected_zonal_layer)
コード例 #13
0
    def test_average(self):
        proj_def = deepcopy(self.project_definition)
        operator = OPERATORS_DICT['AVG']
        node_attr_id, node_attr_name, discarded_feats = \
            calculate_education_node(proj_def, operator, self.layer)

        if self.REBUILD_OUTPUTS:
            res_layer_name = 'average'
            write_output(self.layer, self.data_dir_name, res_layer_name)

        expected_layer_path = os.path.join(self.data_dir_name, 'average.gpkg')
        expected_layer = QgsVectorLayer(expected_layer_path, 'average', 'ogr')
        res = ProcessLayer(self.layer).has_same_content_as(expected_layer)
        self.assertEqual(res, True)
コード例 #14
0
    def test_weighted_multiplication(self):
        proj_def = deepcopy(self.project_definition)
        operator = OPERATORS_DICT['MUL_W']
        node_attr_id, node_attr_name, discarded_feats = \
            calculate_education_node(proj_def, operator, self.layer)

        # # to rebuild the outputs
        # res_layer_name = 'weighted_multiplication'
        # write_output(self.layer, self.data_dir_name, res_layer_name)

        expected_layer_path = os.path.join(self.data_dir_name,
                                           'weighted_multiplication.shp')
        expected_layer = QgsVectorLayer(expected_layer_path,
                                        'weighted_multiplication', 'ogr')
        res = ProcessLayer(self.layer).has_same_content_as(expected_layer)
        self.assertEqual(res, True)
コード例 #15
0
def add_attribute(proposed_attr_name, dtype, layer):
    if dtype == 'S':
        qtype = QVariant.String
        qname = 'String'
    elif dtype in ('U', 'I'):  # FIXME: what for unsigned int?
        qtype = QVariant.Int
        qname = 'integer'
    else:  # FIXME: treating everything else as double (it might be wrong)
        qtype = QVariant.Double
        qname = 'double'
    field = QgsField(proposed_attr_name, qtype)
    field.setTypeName(qname)
    assigned_attr_names = ProcessLayer(layer).add_attributes(
        [field])
    assigned_attr_name = assigned_attr_names[proposed_attr_name]
    return assigned_attr_name
コード例 #16
0
 def import_loss_layer_from_csv(self,
                                csv_file_path,
                                dest_shp=None,
                                delete_lon_lat=False):
     # FIXME: hardcoded field names
     longitude_field = 'LON'
     latitude_field = 'LAT'
     lines_to_skip_count = count_heading_commented_lines(csv_file_path)
     url = QUrl.fromLocalFile(csv_file_path)
     url.addQueryItem('type', 'csv')
     url.addQueryItem('xField', longitude_field)
     url.addQueryItem('yField', latitude_field)
     url.addQueryItem('spatialIndex', 'no')
     url.addQueryItem('subsetIndex', 'no')
     url.addQueryItem('watchFile', 'no')
     url.addQueryItem('delimiter', ',')
     url.addQueryItem('crs', 'epsg:4326')
     url.addQueryItem('skipLines', str(lines_to_skip_count))
     url.addQueryItem('trimFields', 'yes')
     layer_uri = str(url.toEncoded())
     csv_layer = QgsVectorLayer(layer_uri, 'Loss', "delimitedtext")
     dest_filename = dest_shp or QFileDialog.getSaveFileName(
         self,
         'Save loss shapefile as...',
         os.path.expanduser("~"),
         'Shapefiles (*.shp)')
     if dest_filename:
         if dest_filename[-4:] != ".shp":
             dest_filename += ".shp"
     else:
         return
     result = save_layer_as_shapefile(csv_layer, dest_filename)
     if result != QgsVectorFileWriter.NoError:
         raise RuntimeError('Could not save shapefile')
     shp_layer = QgsVectorLayer(
         dest_filename, 'Loss data', 'ogr')
     if delete_lon_lat:
         ProcessLayer(shp_layer).delete_attributes(
             [longitude_field, latitude_field]),
     if shp_layer.isValid():
         QgsMapLayerRegistry.instance().addMapLayer(shp_layer)
     else:
         msg = 'Invalid loss map'
         log_msg(msg, level='C', message_bar=self.iface.messageBar())
         return None
     return shp_layer
コード例 #17
0
    def test_geometric_mean_positive_argument(self):
        proj_def = deepcopy(self.project_definition)
        operator = OPERATORS_DICT['GEOM_MEAN']
        node_attr_id, node_attr_name, discarded_feats = \
            calculate_education_node(proj_def, operator, self.layer)

        # # to rebuild the outputs
        # res_layer_name = 'geometric_mean_positive_argument'
        # write_output(self.layer, self.data_dir_name, res_layer_name)

        expected_layer_path = os.path.join(
            self.data_dir_name, 'geometric_mean_positive_argument.shp')
        expected_layer = QgsVectorLayer(expected_layer_path,
                                        'geometric_mean_positive_argument',
                                        'ogr')
        res = ProcessLayer(self.layer).has_same_content_as(expected_layer)
        self.assertEqual(res, True)
コード例 #18
0
 def test_import_loss_from_csv_exported_by_oqengine(self):
     curr_dir_name = os.path.dirname(__file__)
     data_dir_name = os.path.join(curr_dir_name, os.pardir, 'data', 'loss',
                                  'from_oqengine')
     csv_file_path = os.path.join(data_dir_name,
                                  'output-161-avg_losses-rlz-000_61.csv')
     out_dir = tempfile.gettempdir()
     dest_shp_file_path = os.path.join(out_dir, 'loss_layer.shp')
     dlg = SelectInputLayersDialog(IFACE)
     shp_layer = dlg.import_loss_layer_from_csv(csv_file_path,
                                                dest_shp_file_path)
     expected_layer_path = os.path.join(data_dir_name, 'expected_layer.shp')
     expected_layer = QgsVectorLayer(expected_layer_path, 'expected_layer',
                                     'ogr')
     res = ProcessLayer(shp_layer).has_same_content_as(expected_layer)
     self.assertEqual(res,
                      True,
                      msg='Please check the content of the imported layer')
コード例 #19
0
    def __init__(self, iface, suppl_info, file_stem):
        QDialog.__init__(self)
        # Set up the user interface from Designer.
        self.setupUi(self)
        self.ok_button = self.buttonBox.button(QDialogButtonBox.Ok)
        self.ok_button.setEnabled(False)
        self.iface = iface
        self.vertices_count = None
        self.file_stem = file_stem
        self.xml_file = file_stem + '.xml'
        self.suppl_info = suppl_info
        self.selected_idx = self.suppl_info['selected_project_definition_idx']
        self.project_definition = self.suppl_info['project_definitions'][
            self.selected_idx]
        if 'title' in self.project_definition:
            self.title_le.setText(self.project_definition['title'])
        else:
            self.title_le.setText(DEFAULTS['ISO19115_TITLE'])

        if 'description' in self.project_definition:
            self.description_te.setPlainText(self.project_definition[
                'description'])

        # if no field is selected, we should not allow uploading
        self.zone_label_field_is_specified = False
        reload_attrib_cbx(
            self.zone_label_field_cbx, iface.activeLayer(), True)

        self.set_zone_label_field()
        self.set_license()

        self.exists_on_platform = 'platform_layer_id' in self.suppl_info
        self.do_update = False

        self.update_radio.setEnabled(self.exists_on_platform)
        self.update_radio.setChecked(self.exists_on_platform)
        self.set_labels()

        with WaitCursorManager("Counting layer's vertices",
                               iface.messageBar()):
            self.vertices_count = ProcessLayer(
                iface.activeLayer()).count_vertices()
コード例 #20
0
 def test_purge_empty_zones(self):
     loss_attrs_dict = {
         'count': u'NUM_POINTS',
         'FATALITIES': {'sum': u'SUM_FATALITIES',
                        'avg': u'AVG_FATALITIES'},
         'STRUCTURAL': {'sum': u'SUM_STRUCTURAL',
                        'avg': u'AVG_STRUCTURAL'}}
     orig_zonal_layer_path = os.path.join(
         self.data_dir_name, 'svi_zones_plus_loss_stats_zone_ids.shp')
     orig_zonal_layer = QgsVectorLayer(
         orig_zonal_layer_path, 'Zonal layer plus stats', 'ogr')
     # avoid modifying the original layers
     copied_zonal_layer = \
         ProcessLayer(orig_zonal_layer).duplicate_in_memory()
     output_zonal_layer = purge_zones_without_loss_points(
         copied_zonal_layer, loss_attrs_dict, IFACE)
     expected_zonal_layer_path = os.path.join(
         self.data_dir_name, 'svi_zones_plus_loss_stats_purged.shp')
     expected_zonal_layer = QgsVectorLayer(
         expected_zonal_layer_path, 'Expected zonal purged layer', 'ogr')
     self._check_output_layer(output_zonal_layer, expected_zonal_layer)
コード例 #21
0
 def test_load_ruptures(self):
     filepath = os.path.join(self.data_dir_name, 'hazard', 'ruptures',
                             'output-607-ruptures_162.csv')
     # TODO: in the future, we will move this to integration tests, using
     #       session, hostname  and calc_id and the extract api, instead of
     #       mocking
     dlg = LoadRupturesAsLayerDialog(IFACE,
                                     self.viewer_dock,
                                     Mock(),
                                     Mock(),
                                     Mock(),
                                     'ruptures',
                                     filepath,
                                     mode='testing')
     dlg.save_as_shp_ckb.setChecked(True)
     dlg.accept()
     current_layer = IFACE.activeLayer()
     reference_path = os.path.join(self.data_dir_name, 'hazard', 'ruptures',
                                   'output-607-ruptures_162.shp')
     reference_layer = QgsVectorLayer(reference_path, 'reference_ruptures',
                                      'ogr')
     ProcessLayer(current_layer).has_same_content_as(reference_layer)
コード例 #22
0
 def aggregate_by_zone(self):
     loss_layer = self.layer
     zonal_layer_id = self.zonal_layer_cbx.itemData(
         self.zonal_layer_cbx.currentIndex())
     zonal_layer = QgsProject.instance().mapLayer(zonal_layer_id)
     QgsProject.instance().layerTreeRoot().findLayer(
         zonal_layer.id()).setItemVisibilityChecked(False)
     # if the two layers have different projections, display a
     # warning, but try proceeding anyway
     have_same_projection, check_projection_msg = ProcessLayer(
         loss_layer).has_same_projection_as(zonal_layer)
     if not have_same_projection:
         log_msg(check_projection_msg,
                 level='W',
                 message_bar=self.iface.messageBar())
     try:
         [self.loss_attr_name
          ] = [field.name() for field in loss_layer.fields()]
     except ValueError:
         self.loss_attr_name = self.default_field_name
     zonal_layer_plus_sum_name = "%s: %s_sum" % (zonal_layer.name(),
                                                 self.loss_attr_name)
     discard_nonmatching = self.discard_nonmatching_chk.isChecked()
     try:
         calculate_zonal_stats(self.on_calculate_zonal_stats_completed,
                               zonal_layer,
                               loss_layer, [self.loss_attr_name],
                               zonal_layer_plus_sum_name,
                               discard_nonmatching=discard_nonmatching,
                               predicates=('intersects', ),
                               summaries=('sum', ))
     except Exception as exc:
         log_msg(str(exc),
                 level='C',
                 message_bar=self.iface.messageBar(),
                 exception=exc)
コード例 #23
0
 def test_same_content_case_second_layer_has_more_features(self):
     res = ProcessLayer(self.layer_a).has_same_content_as(self.layer_c)
     self.assertEqual(res, False)
コード例 #24
0
 def test_same_content_case_layers_are_equal(self):
     res = ProcessLayer(self.layer_a).has_same_content_as(self.layer_b)
     self.assertEqual(res, True)
コード例 #25
0
 def test_different_projections(self):
     res, msg = \
         ProcessLayer(self.loss_layer_epsg4326).has_same_projection_as(
             self.zonal_layer_epsg4269)
     self.assertEqual(res, False)
コード例 #26
0
 def test_same_projections(self):
     res, msg = \
         ProcessLayer(self.loss_layer_epsg4326).has_same_projection_as(
             self.zonal_layer_epsg4326)
     self.assertEqual(res, True)
コード例 #27
0
def add_numeric_attribute(proposed_attr_name, layer):
    field = QgsField(proposed_attr_name, QVariant.Double)
    field.setTypeName(DOUBLE_FIELD_TYPE_NAME)
    assigned_attr_names = ProcessLayer(layer).add_attributes([field])
    assigned_attr_name = assigned_attr_names[proposed_attr_name]
    return assigned_attr_name
コード例 #28
0
 def test_same_content_case_layers_are_completely_different(self):
     res = ProcessLayer(self.layer_a).has_same_content_as(self.layer_d)
     self.assertEqual(res, False)
コード例 #29
0
def calculate_composite_variable(iface, layer, node):
    """
    Calculate a composite variable (a tree node that has children) starting
    from the children's values and inverters and using the operator defined
    for the node.
    While calculating a composite index, the tree can be modified. For
    instance, a theme that was not associated to any field in the layer, could
    be linked to a new field that is created before performing the calculation.
    For this reason, the function must return the modified tree, and the
    original tree needs to be modified accordingly.
    If the children of the node are not final leaves of the tree, the function
    will be called recursively to compute those children, before proceeding
    with the calculation of the node.

    :param iface: the iface, to be used to access the messageBar
    :param layer: the layer that contains the data to be used in the
         calculation and that will be modified adding new fields if
         needed
    :param node: the root node of the project definition's sub-tree to be
         calculated

    :returns (added_attrs_ids, discarded_feats, node, any_change):
        added_attrs_ids: the set of ids of the attributes added to the layer
        during the calculation
        discarded_feats: the set of DiscardedFeature that can't contribute to
        the calculation, because of missing data or invalid
        data (e.g. when it's impossible to calculate the
        geometric mean because it causes the calculation of
        the fractionary power of a negative value)
        node: the transformed (or unmodified) sub-tree
        any_change: True if the calculation caused any change in the
        subtree
    """
    # Avoid touching the original node, and manipulate a copy instead.
    # If anything fails, the original node will be returned
    edited_node = deepcopy(node)
    # keep a list of attributes added to the layer, so they can be deleted if
    # the calculation can not be completed, and they can be notified to the
    # user if the calculation is done without errors
    added_attrs_ids = set()
    discarded_feats = set()
    any_change = False

    children = edited_node.get('children', [])
    if not children:
        # we don't calculate the values for a node that has no children
        return set(), set(), node, False
    for child_idx, child in enumerate(children):
        child_results = calculate_composite_variable(iface, layer, child)
        (child_added_attrs_ids, child_discarded_feats, child,
         child_was_changed) = child_results
        if child_added_attrs_ids:
            added_attrs_ids.update(child_added_attrs_ids)
        if child_discarded_feats:
            discarded_feats.update(child_discarded_feats)
        if child_was_changed:
            # update the subtree with the modified child
            # e.g., a theme might have been linked to a new layer's field
            edited_node['children'][child_idx] = deepcopy(child)
    try:
        node_attr_id, node_attr_name, field_was_added = \
            get_node_attr_id_and_name(edited_node, layer)
    except InvalidNode as e:
        log_msg(str(e), level='C', message_bar=iface.messageBar())
        if added_attrs_ids:
            ProcessLayer(layer).delete_attributes(added_attrs_ids)
        return set(), set(), node, False
    if field_was_added:
        added_attrs_ids.add(node_attr_id)
    try:
        node_discarded_feats = calculate_node(edited_node, node_attr_name,
                                              node_attr_id, layer,
                                              discarded_feats)
    except (InvalidOperator, InvalidChild, InvalidFormula) as e:
        log_msg(str(e), level='C', message_bar=iface.messageBar())
        if added_attrs_ids:
            ProcessLayer(layer).delete_attributes(added_attrs_ids)
        return set(), set(), node, False
    except TypeError as e:
        msg = ('Could not calculate the composite variable due'
               ' to data problems: %s' % e)
        log_msg(msg, level='C', message_bar=iface.messageBar())
        if added_attrs_ids:
            ProcessLayer(layer).delete_attributes(added_attrs_ids)
        return set(), set(), node, False

    discarded_feats.update(node_discarded_feats)
    edited_node['field'] = node_attr_name
    any_change = True
    return added_attrs_ids, discarded_feats, edited_node, any_change
コード例 #30
0
def add_textual_attribute(proposed_attr_name, layer):
    field = QgsField(proposed_attr_name, QVariant.String)
    field.setTypeName(STRING_FIELD_TYPE_NAME)
    assigned_attr_names = ProcessLayer(layer).add_attributes([field])
    assigned_attr_name = assigned_attr_names[proposed_attr_name]
    return assigned_attr_name