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()
Пример #2
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)
Пример #3
0
 def test_sum_point_values_by_zone(self):
     # NOTE: it shouldn't be necessary to make copies of gpkg files, because
     # they are not modified by the test. However, when QGIS opens a gpkg
     # (and it's not protected from writing), it modifies the file even if
     # no changes are made and if it's not saved afterwards. It seems like a
     # bug in QGIS.
     points_layer_path = os.path.join(self.data_dir_name,
                                      'loss_points.gpkg')
     self.points_copy_path = tempfile.NamedTemporaryFile(
         suffix='.gpkg').name
     shutil.copyfile(points_layer_path, self.points_copy_path)
     points_layer = QgsVectorLayer(self.points_copy_path,
                                   'Loss points having zone ids', 'ogr')
     zonal_layer_path = os.path.join(self.data_dir_name, 'svi_zones.gpkg')
     self.zonal_copy_path = tempfile.NamedTemporaryFile(suffix='.gpkg').name
     shutil.copyfile(zonal_layer_path, self.zonal_copy_path)
     zonal_layer = QgsVectorLayer(self.zonal_copy_path, 'SVI zones', 'ogr')
     self.is_test_complete = False
     calculate_zonal_stats(self.on_calculate_zonal_stats_finished,
                           zonal_layer,
                           points_layer,
                           self.loss_attr_names,
                           'output',
                           discard_nonmatching=False,
                           predicates=('intersects', ),
                           summaries=('sum', ))
     timeout = 5
     start_time = time.time()
     while time.time() - start_time < timeout:
         QGIS_APP.processEvents()
         time.sleep(0.01)
         print(self.is_test_complete)
         if self.is_test_complete:
             return
     raise TimeoutError('Unable to run the aggregation within %s seconds' %
                        timeout)
 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)
Пример #5
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)
 def accept(self):
     if self.output_type in OQ_EXTRACT_TO_LAYER_TYPES:
         self.load_from_npz()
         if self.output_type in ('losses_by_asset', 'dmg_by_asset',
                                 'avg_losses-stats'):
             # check if also aggregating by zone or not
             if (not self.zonal_layer_cbx.currentText()
                     or not self.zonal_layer_gbx.isChecked()):
                 super(LoadOutputAsLayerDialog, self).accept()
                 return
             loss_layer = self.layer
             self.iface.legendInterface().setLayerVisible(loss_layer, False)
             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())
                 # TODO: load only loss layer
                 super(LoadOutputAsLayerDialog, self).accept()
                 return
             loss_attr_names = [
                 field.name() for field in loss_layer.fields()
             ]
             zone_id_in_losses_attr_name = None
             # index 0 is for "Add field with unique zone id"
             if self.zone_id_field_cbx.currentIndex() == 0:
                 zone_id_in_zones_attr_name = None
             else:
                 zone_id_in_zones_attr_name = \
                     self.zone_id_field_cbx.currentText()
             # aggregate losses by zone (calculate count of points in the
             # zone, sum and average loss values for the same zone)
             loss_layer_is_vector = True
             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,
                                             extra=False)
             except TypeError as exc:
                 log_msg(str(exc),
                         level='C',
                         message_bar=self.iface.messageBar())
                 return
             (loss_layer, zonal_layer, loss_attrs_dict) = res
             # sanity check
             assert len(loss_attrs_dict) == 1, (
                 "Only one attribute should be added to the zonal layer."
                 " %s were added insted" % len(loss_attrs_dict))
             # NOTE: in scenario damage, keys are like
             #       u'structural_no_damage_mean', and not just
             #       u'structural', therefore we can't just use the selected
             #       loss type, but we must use the actual only key in the
             #       dict
             [added_loss_attr] = loss_attrs_dict
             style_by = loss_attrs_dict[added_loss_attr]['sum']
             self.style_maps(layer=zonal_layer, style_by=style_by)
     elif self.output_type in OQ_CSV_TO_LAYER_TYPES:
         self.load_from_csv()
     super(LoadOutputAsLayerDialog, self).accept()