Пример #1
0
    def __init__(self, iface, template, layer, extra_layers=[]):
        """Constructor for the Composition Report class.

        :param iface: Reference to the QGIS iface object.
        :type iface: QgsAppInterface

        :param template: The QGIS template path.
        :type template: str
        """
        LOGGER.debug('InaSAFE Impact Report class initialised')
        self._iface = iface
        self._template = None
        self.template = template
        self._layer = layer
        self._extra_layers = extra_layers
        self._extent = self._iface.mapCanvas().extent()
        self._page_dpi = 300.0
        self._black_inasafe_logo = black_inasafe_logo_path()
        self._white_inasafe_logo = white_inasafe_logo_path()
        # User can change this path in preferences
        self._organisation_logo = supporters_logo_path()
        self._supporters_logo = supporters_logo_path()
        self._north_arrow = default_north_arrow_path()
        self._disclaimer = disclaimer()

        # For QGIS < 2.4 compatibility
        # QgsMapSettings is added in 2.4
        if qgis_version() < 20400:
            map_settings = self._iface.mapCanvas().mapRenderer()
        else:
            map_settings = self._iface.mapCanvas().mapSettings()

        self._template_composition = TemplateComposition(
            template_path=self.template,
            map_settings=map_settings)
        self._keyword_io = KeywordIO()
Пример #2
0
    def test_clip_vector_with_unicode(self):
        """Test clipping vector layer with unicode attribute in feature.

        This issue is described at Github #2262 and #2233
        TODO: FIXME:
        This is a hacky fix. See above ticket for further explanation.
        To fix this, we should be able to specify UTF-8 encoding for
        QgsVectorFileWriter
        """
        # this layer contains unicode values in the
        layer_path = test_data_path('boundaries', 'district_osm_jakarta.shp')
        vector_layer = QgsVectorLayer(layer_path, 'District Jakarta', 'ogr')
        keyword_io = KeywordIO()
        aggregation_keyword = get_defaults()['AGGR_ATTR_KEY']
        aggregation_attribute = keyword_io.read_keywords(
            vector_layer, keyword=aggregation_keyword)
        source_extent = vector_layer.extent()
        extent = [source_extent.xMinimum(), source_extent.yMinimum(),
                  source_extent.xMaximum(), source_extent.yMaximum()]
        clipped_layer = clip_layer(
            layer=vector_layer,
            extent=extent,
            explode_flag=True,
            explode_attribute=aggregation_attribute)

        # cross check vector layer attribute in clipped layer
        vector_values = []
        for f in vector_layer.getFeatures():
            vector_values.append(f.attributes())

        clipped_values = []
        for f in clipped_layer.getFeatures():
            clipped_values.append(f.attributes())

        for val in clipped_values:
            self.assertIn(val, vector_values)
Пример #3
0
def _clip_vector_layer(
        layer,
        extent,
        extra_keywords=None,
        explode_flag=True,
        hard_clip_flag=False,
        explode_attribute=None):
    """Clip a Hazard or Exposure layer to the extents provided.

    The layer must be a vector layer or an exception will be thrown.

    The output layer will always be in WGS84/Geographic.

    :param layer: A valid QGIS vector or raster layer
    :type layer:

    :param extent: Either an array representing the exposure layer extents
        in the form [xmin, ymin, xmax, ymax]. It is assumed that the
        coordinates are in EPSG:4326 although currently no checks are made to
        enforce this.
        or:
        A QgsGeometry of type polygon.
        **Polygon clipping is currently only supported for vector datasets.**
    :type extent: list(float, float, float, float)

    :param extra_keywords: Optional keywords dictionary to be added to
        output layer.
    :type extra_keywords: dict

    :param explode_flag: A bool specifying whether multipart features
        should be 'exploded' into singleparts.
        **This parameter is ignored for raster layer clipping.**
    :type explode_flag: bool

    :param hard_clip_flag: A bool specifying whether line and polygon
        features that extend beyond the extents should be clipped such that
        they are reduced in size to the part of the geometry that intersects
        the extent only. Default is False.
        **This parameter is ignored for raster layer clipping.**
    :type hard_clip_flag: bool

    :param explode_attribute: A str specifying to which attribute #1,
        #2 and so on will be added in case of explode_flag being true. The
        attribute is modified only if there are at least 2 parts.
    :type explode_attribute: str

    :returns: Clipped layer (placed in the system temp dir). The output layer
        will be reprojected to EPSG:4326 if needed.
    :rtype: QgsVectorLayer

    """
    if not layer or not extent:
        message = tr('Layer or Extent passed to clip is None.')
        raise InvalidParameterError(message)

    if layer.type() != QgsMapLayer.VectorLayer:
        message = tr(
            'Expected a vector layer but received a %s.' %
            str(layer.type()))
        raise InvalidParameterError(message)

    # handle, file_name = tempfile.mkstemp('.sqlite', 'clip_',
    #    temp_dir())
    handle, file_name = tempfile.mkstemp(
        '.shp', 'clip_', temp_dir())

    # Ensure the file is deleted before we try to write to it
    # fixes windows specific issue where you get a message like this
    # ERROR 1: c:\temp\inasafe\clip_jpxjnt.shp is not a directory.
    # This is because mkstemp creates the file handle and leaves
    # the file open.
    os.close(handle)
    os.remove(file_name)

    # Get the clip extents in the layer's native CRS
    geo_crs = QgsCoordinateReferenceSystem()
    geo_crs.createFromSrid(4326)
    transform = QgsCoordinateTransform(geo_crs, layer.crs())
    allowed_clip_values = [QGis.WKBPolygon, QGis.WKBPolygon25D]
    if isinstance(extent, list):
        rectangle = QgsRectangle(
            extent[0], extent[1],
            extent[2], extent[3])
        # noinspection PyCallByClass
        # noinspection PyTypeChecker
        polygon = QgsGeometry.fromRect(rectangle)
    elif (isinstance(extent, QgsGeometry) and
          extent.wkbType in allowed_clip_values):
        rectangle = extent.boundingBox().toRectF()
        polygon = extent
    else:
        raise InvalidClipGeometryError(
            tr(
                'Clip geometry must be an extent or a single part'
                'polygon based geometry.'))

    projected_extent = transform.transformBoundingBox(rectangle)

    # Get vector layer
    provider = layer.dataProvider()
    if provider is None:
        message = tr(
            'Could not obtain data provider from '
            'layer "%s"' % layer.source())
        raise Exception(message)

    # Get the layer field list, select by our extent then write to disk
    # .. todo:: FIXME - for different geometry types we should implement
    #    different clipping behaviour e.g. reject polygons that
    #    intersect the edge of the bbox. Tim
    request = QgsFeatureRequest()
    if not projected_extent.isEmpty():
        request.setFilterRect(projected_extent)
        request.setFlags(QgsFeatureRequest.ExactIntersect)

    field_list = provider.fields()

    writer = QgsVectorFileWriter(
        file_name,
        'UTF-8',
        field_list,
        layer.wkbType(),
        geo_crs,
        # 'SQLite')  # FIXME (Ole): This works but is far too slow
        'ESRI Shapefile')
    if writer.hasError() != QgsVectorFileWriter.NoError:
        message = tr(
            'Error when creating shapefile: <br>Filename:'
            '%s<br>Error: %s' %
            (file_name, writer.hasError()))
        raise Exception(message)

    # Reverse the coordinate xform now so that we can convert
    # geometries from layer crs to geocrs.
    transform = QgsCoordinateTransform(layer.crs(), geo_crs)
    # Retrieve every feature with its geometry and attributes
    count = 0
    has_multipart = False

    for feature in provider.getFeatures(request):
        geometry = feature.geometry()

        # Loop through the parts adding them to the output file
        # we write out single part features unless explode_flag is False
        if explode_flag:
            geometry_list = explode_multipart_geometry(geometry)
        else:
            geometry_list = [geometry]

        for part_index, part in enumerate(geometry_list):
            part.transform(transform)
            if hard_clip_flag:
                # Remove any dangling bits so only intersecting area is
                # kept.
                part = clip_geometry(polygon, part)
            if part is None:
                continue

            feature.setGeometry(part)
            # There are multiple parts and we want to show it in the
            # explode_attribute
            if part_index > 0 and explode_attribute is not None:
                has_multipart = True

            writer.addFeature(feature)
        count += 1
    del writer  # Flush to disk

    if count < 1:
        message = tr(
            'No features fall within the clip extents. Try panning / zooming '
            'to an area containing data and then try to run your analysis '
            'again. If hazard and exposure data doesn\'t overlap at all, it '
            'is not possible to do an analysis. Another possibility is that '
            'the layers do overlap but because they may have different '
            'spatial references, they appear to be disjointed. If this is the '
            'case, try to turn on reproject on-the-fly in QGIS.')
        raise NoFeaturesInExtentError(message)

    keyword_io = KeywordIO()
    if extra_keywords is None:
        extra_keywords = {}
    extra_keywords['had multipart polygon'] = has_multipart
    keyword_io.copy_keywords(
        layer, file_name, extra_keywords=extra_keywords)
    base_name = '%s clipped' % layer.name()
    layer = QgsVectorLayer(file_name, base_name, 'ogr')

    return layer
    def __init__(self, parent=None, iface=None):
        """Constructor."""
        QDialog.__init__(self, parent)
        self.setupUi(self)
        icon = resources_path('img', 'icons', 'show-metadata-converter.svg')
        self.setWindowIcon(QIcon(icon))
        self.setWindowTitle(self.tr('InaSAFE Metadata Converter'))
        self.parent = parent
        self.iface = iface

        self.keyword_io = KeywordIO()
        self.layer = None

        # Setup header label
        self.header_label.setText(
            tr('In this tool, you can convert a metadata 4.x of a layer to '
               'metadata 3.5. You will get a directory contains all the files of '
               'the layer and the new 3.5 metadata. If you want to convert '
               'hazard layer, you need to choose what exposure that you want to '
               'work with.'))

        # Setup input layer combo box
        # Filter our layers
        excepted_layers = []
        for i in range(self.input_layer_combo_box.count()):
            layer = self.input_layer_combo_box.layer(i)
            try:
                keywords = self.keyword_io.read_keywords(layer)
            except (KeywordNotFoundError, NoKeywordsFoundError):
                # Filter out if no keywords
                excepted_layers.append(layer)
                continue
            layer_purpose = keywords.get('layer_purpose')
            if not layer_purpose:
                # Filter out if no layer purpose
                excepted_layers.append(layer)
                continue
            if layer_purpose not in accepted_layer_purposes:
                # Filter out if not aggregation, hazard, or exposure layer
                excepted_layers.append(layer)
                continue
            keyword_version = keywords.get('keyword_version')
            if not keyword_version:
                # Filter out if no keyword version
                excepted_layers.append(layer)
                continue
            if not is_keyword_version_supported(keyword_version):
                # Filter out if the version is not supported (4.x)
                excepted_layers.append(layer)
                continue

        self.input_layer_combo_box.setExceptedLayerList(excepted_layers)

        # Select the active layer.
        if self.iface.activeLayer():
            found = self.input_layer_combo_box.findText(
                self.iface.activeLayer().name())
            if found > -1:
                self.input_layer_combo_box.setLayer(self.iface.activeLayer())

        # Set current layer as the active layer
        if self.input_layer_combo_box.currentLayer():
            self.set_layer(self.input_layer_combo_box.currentLayer())

        # Signals
        self.input_layer_combo_box.layerChanged.connect(self.set_layer)
        self.output_path_tool.clicked.connect(self.select_output_directory)

        # Set widget to show the main page (not help page)
        self.main_stacked_widget.setCurrentIndex(1)

        # Set up things for context help
        self.help_button = self.button_box.button(QDialogButtonBox.Help)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)

        # Set up things for ok button
        self.ok_button = self.button_box.button(QDialogButtonBox.Ok)
        self.ok_button.clicked.connect(self.accept)

        # Set up things for cancel button
        self.cancel_button = self.button_box.button(QDialogButtonBox.Cancel)
        self.cancel_button.clicked.connect(self.reject)

        # The bug is fixed in QT 5.4
        if QT_VERSION < 0x050400:
            self.resized.connect(self.after_resize)
Пример #5
0
    def __init__(self, parent=None, iface=None, setting=None):
        """Constructor."""
        QDialog.__init__(self, parent)
        self.setupUi(self)

        self.setWindowTitle(self.tr('InaSAFE Field Mapping Tool'))
        icon = resources_path('img', 'icons', 'show-mapping-tool.svg')
        self.setWindowIcon(QIcon(icon))
        self.parent = parent
        self.iface = iface
        if setting is None:
            setting = QSettings()
        self.setting = setting

        self.keyword_io = KeywordIO()

        self.layer = None
        self.metadata = {}

        self.layer_input_layout = QHBoxLayout()
        self.layer_label = QLabel(tr('Layer'))
        self.layer_combo_box = QgsMapLayerComboBox()
        # Filter only for Polygon and Point
        self.layer_combo_box.setFilters(
            QgsMapLayerProxyModel.PolygonLayer |
            QgsMapLayerProxyModel.PointLayer)
        # Filter out a layer that don't have layer groups
        excepted_layers = []
        for i in range(self.layer_combo_box.count()):
            layer = self.layer_combo_box.layer(i)
            try:
                keywords = self.keyword_io.read_keywords(layer)
            except (KeywordNotFoundError, NoKeywordsFoundError):
                excepted_layers.append(layer)
                continue
            layer_purpose = keywords.get('layer_purpose')
            if not layer_purpose:
                excepted_layers.append(layer)
                continue
            if layer_purpose == layer_purpose_exposure['key']:
                layer_subcategory = keywords.get('exposure')
            elif layer_purpose == layer_purpose_hazard['key']:
                layer_subcategory = keywords.get('hazard')
            else:
                layer_subcategory = None

            field_groups = get_field_groups(layer_purpose, layer_subcategory)
            if len(field_groups) == 0:
                excepted_layers.append(layer)
                continue
        self.layer_combo_box.setExceptedLayerList(excepted_layers)

        # Select the active layer.
        if self.iface.activeLayer():
            found = self.layer_combo_box.findText(
                self.iface.activeLayer().name())
            if found > -1:
                self.layer_combo_box.setLayer(self.iface.activeLayer())

        self.field_mapping_widget = None
        self.main_stacked_widget.setCurrentIndex(1)

        # Input
        self.layer_input_layout.addWidget(self.layer_label)
        self.layer_input_layout.addWidget(self.layer_combo_box)

        self.header_label = QLabel()
        self.header_label.setWordWrap(True)
        self.main_layout.addWidget(self.header_label)
        self.main_layout.addLayout(self.layer_input_layout)

        # Signal
        self.layer_combo_box.layerChanged.connect(self.set_layer)

        if self.layer_combo_box.currentLayer():
            self.set_layer(self.layer_combo_box.currentLayer())

        # Set up things for context help
        self.help_button = self.button_box.button(QDialogButtonBox.Help)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)

        # Set up things for ok button
        self.ok_button = self.button_box.button(QDialogButtonBox.Ok)
        self.ok_button.clicked.connect(self.accept)

        # Set up things for cancel button
        self.cancel_button = self.button_box.button(QDialogButtonBox.Cancel)
        self.cancel_button.clicked.connect(self.reject)
Пример #6
0
    def __init__(self, parent=None, iface=None):
        """Constructor for dialog.

        :param parent: Optional widget to use as parent
        :type parent: QWidget

        :param iface: An instance of QGisInterface
        :type iface: QGisInterface
        """
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setupUi(self)
        self.setWindowTitle(self.tr('InaSAFE Impact Layer Merge Tool'))
        self.iface = iface
        self.keyword_io = KeywordIO()

        # Template Path for composer
        self.template_path = resources_path('qgis-composer-templates',
                                            'merged_report.qpt')

        # Safe Logo Path
        self.safe_logo_path = white_inasafe_logo_path()

        # Organisation Logo Path - defaults to supporters logo, will be
        # updated to user defined organisation logo path in read_settings in
        # user has specified a custom logo.
        self.organisation_logo_path = supporters_logo_path()

        # Disclaimer text
        self.disclaimer = disclaimer()

        # The output directory
        self.out_dir = None

        # Stored information from first impact layer
        self.first_impact = {
            'layer': None,
            'map_title': None,
            'hazard_title': None,
            'exposure_title': None,
            'postprocessing_report': None,
        }

        # Stored information from second impact layer
        self.second_impact = {
            'layer': None,
            'map_title': None,
            'hazard_title': None,
            'exposure_title': None,
            'postprocessing_report': None,
        }

        # Stored information from aggregation layer
        self.aggregation = {'layer': None, 'aggregation_attribute': None}

        # Available aggregation layer
        self.available_aggregation = []

        # The summary report, contains report for each aggregation area
        self.summary_report = OrderedDict()

        # The html reports and its file path
        self.html_reports = OrderedDict()

        # A boolean flag whether to merge entire area or aggregated
        self.entire_area_mode = False

        # Get the global settings and override some variable if exist
        self.read_settings()

        # Get all current project layers for combo box
        self.get_project_layers()

        # Set up things for context help
        self.help_button = self.button_box.button(QtGui.QDialogButtonBox.Help)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)
        self.main_stacked_widget.setCurrentIndex(1)

        # Show usage info
        self.restore_state()
Пример #7
0
    def save_hazard_data(self):
        hazard_geojson = PetaJakartaAPI.get_aggregate_report(
            self.duration, self.level)

        if not hazard_geojson:
            raise PetaJakartaAPIError("Can't access PetaJakarta REST API")

        with open(self.hazard_path, 'w+') as f:
            f.write(hazard_geojson)

        # Save the layer as shp
        file_info = QFileInfo(self.hazard_path)
        hazard_layer = QgsVectorLayer(self.hazard_path, file_info.baseName(),
                                      'ogr', False)

        target_name = 'flood_data.shp'
        self.hazard_path = os.path.join(self.report_path, target_name)
        QgsVectorFileWriter.writeAsVectorFormat(hazard_layer, self.hazard_path,
                                                'CP1250', None,
                                                'ESRI Shapefile')

        file_info = QFileInfo(self.hazard_path)
        hazard_layer = QgsVectorLayer(self.hazard_path, file_info.baseName(),
                                      'ogr')

        hazard_layer.startEditing()
        field = QgsField('flooded', QVariant.Int)
        hazard_layer.dataProvider().addAttributes([field])
        hazard_layer.commitChanges()
        idx = hazard_layer.fieldNameIndex('flooded')
        expression = QgsExpression('count > 0')
        expression.prepare(hazard_layer.pendingFields())

        hazard_layer.startEditing()
        for feature in hazard_layer.getFeatures():
            feature[idx] = expression.evaluate(feature)
            hazard_layer.updateFeature(feature)

        hazard_layer.commitChanges()

        # writing keywords
        keyword_io = KeywordIO()

        keywords = {
            'field': 'flooded',
            'hazard': 'flood',
            'hazard_category': 'single_event',
            'keyword_version': '3.3',
            'layer_geometry': 'polygon',
            'layer_mode': 'classified',
            'layer_purpose': 'hazard',
            'title': 'Flood',
            'value_map': '{"wet": [1], "dry": [0]}',
            'vector_hazard_classification': 'flood_vector_hazard_classes'
        }

        keyword_io.write_keywords(hazard_layer, keywords)

        # archiving hazard layer
        with ZipFile(self.hazard_zip_path, 'w') as zf:
            for root, dirs, files in os.walk(self.report_path):
                for f in files:
                    _, ext = os.path.splitext(f)
                    if 'flood_data' in f:
                        filename = os.path.join(root, f)
                        zf.write(filename, arcname=f)
Пример #8
0
def get_analysis_dir(exposure_key=None):
    """Retrieve an output directory of an analysis/ImpactFunction from a
    multi exposure analysis/ImpactFunction based on exposure type.

    :param exposure_key: An exposure keyword.
    :type exposure_key: str

    :return: A directory contains analysis outputs.
    :rtype: str
    """
    keyword_io = KeywordIO()
    layer_tree_root = QgsProject.instance().layerTreeRoot()
    all_groups = [
        child for child in layer_tree_root.children()
        if (isinstance(child, QgsLayerTreeGroup))
    ]
    multi_exposure_group = None
    for group in all_groups:
        if group.customProperty(MULTI_EXPOSURE_ANALYSIS_FLAG):
            multi_exposure_group = group
            break

    if multi_exposure_group:
        multi_exposure_tree_layers = [
            child for child in multi_exposure_group.children()
            if (isinstance(child, QgsLayerTreeLayer))
        ]
        exposure_groups = [
            child for child in multi_exposure_group.children()
            if (isinstance(child, QgsLayerTreeGroup))
        ]

        def get_report_ready_layer(tree_layers):
            """Get a layer which has a report inn its directory.

            :param tree_layers: A list of tree layer nodes (QgsLayerTreeLayer)
            :type tree_layers: list

            :return: A vector layer
            :rtype: QgsMapLayer
            """
            for tree_layer in tree_layers:
                layer = tree_layer.layer()
                keywords = keyword_io.read_keywords(layer)
                extra_keywords_found = keywords.get('extra_keywords')
                provenance = keywords.get('provenance_data')
                if provenance:
                    exposure_keywords = provenance.get('exposure_keywords', {})
                    exposure_key_found = exposure_keywords.get('exposure')
                    if exposure_key_found and (exposure_key
                                               == exposure_key_found):
                        return layer
                if not exposure_key and extra_keywords_found and (
                        extra_keywords_found[
                            extra_keyword_analysis_type['key']]
                        == (MULTI_EXPOSURE_ANALYSIS_FLAG)):
                    return layer
            return None

        layer = get_report_ready_layer(multi_exposure_tree_layers)

        if not layer:
            for exposure_group in exposure_groups:
                tree_layers = [
                    child for child in exposure_group.children()
                    if (isinstance(child, QgsLayerTreeLayer))
                ]
                layer = get_report_ready_layer(tree_layers)
                if layer:
                    break

        if layer:
            return dirname(layer.source())

    return None
Пример #9
0
def buffer_points(point_layer, radii, hazard_zone_attribute, output_crs):
    """Buffer points for each point with defined radii.

    This function is used for making buffer of volcano point hazard.

    :param point_layer: A point layer to buffer.
    :type point_layer: QgsVectorLayer

    :param radii: Desired approximate radii in kilometers (must be
        monotonically ascending). Can be either one number or list of numbers
    :type radii: int, list

    :param hazard_zone_attribute: The name of the attributes representing
        hazard zone.
    :type hazard_zone_attribute: str

    :param output_crs: The output CRS.
    :type output_crs: QgsCoordinateReferenceSystem

    :return: Vector polygon layer representing circle in point layer CRS.
    :rtype: QgsVectorLayer
    """
    if not isinstance(radii, list):
        radii = [radii]

    if not is_point_layer(point_layer):
        message = ('Input hazard must be a vector point layer. I got %s '
                   'with layer type %s' %
                   (point_layer.name(), point_layer.type()))
        raise Exception(message)

    # Check that radii are monotonically increasing
    monotonically_increasing_flag = all(x < y
                                        for x, y in zip(radii, radii[1:]))
    if not monotonically_increasing_flag:
        raise RadiiException(RadiiException.suggestion)

    hazard_file_path = unique_filename(suffix='-polygon-volcano.shp')
    fields = point_layer.pendingFields()
    fields.append(QgsField(hazard_zone_attribute, QVariant.Double))
    writer = QgsVectorFileWriter(hazard_file_path, 'utf-8', fields,
                                 QGis.WKBPolygon, output_crs, 'ESRI Shapefile')
    input_crs = point_layer.crs()

    center = point_layer.extent().center()
    utm = None
    if output_crs.authid() == 'EPSG:4326':
        utm = QgsCoordinateReferenceSystem(
            get_utm_epsg(center.x(), center.y(), input_crs))
        transform = QgsCoordinateTransform(point_layer.crs(), utm)

    else:
        transform = QgsCoordinateTransform(point_layer.crs(), output_crs)

    for point in point_layer.getFeatures():
        geom = point.geometry()
        geom.transform(transform)

        inner_rings = None
        for radius in radii:
            attributes = point.attributes()
            # Generate circle polygon

            circle = geom.buffer(radius * 1000.0, 30)

            if inner_rings:
                circle.addRing(inner_rings)
            inner_rings = circle.asPolygon()[0]

            new_buffer = QgsFeature()

            if output_crs.authid() == 'EPSG:4326':
                circle.transform(QgsCoordinateTransform(utm, output_crs))

            new_buffer.setGeometry(circle)
            attributes.append(radius)
            new_buffer.setAttributes(attributes)

            writer.addFeature(new_buffer)

    del writer
    vector_layer = QgsVectorLayer(hazard_file_path, 'Polygons', 'ogr')

    keyword_io = KeywordIO()
    try:
        keywords = keyword_io.read_keywords(point_layer)
        keyword_io.write_keywords(vector_layer, keywords)
    except NoKeywordsFoundError:
        pass
    return vector_layer
Пример #10
0
    def set_style(self):
        # get requested style of impact

        qml_path = self.flood_fixtures_dir('impact-template.qml')
        target_style_path = os.path.join(self.report_path,
                                         'population_aggregate.qml')
        keyword_io = KeywordIO()
        qgis_exposure_layer = self.exposure_layer.as_qgis_native()
        attribute_field = keyword_io.read_keywords(qgis_exposure_layer,
                                                   'field')
        with open(qml_path) as f_template:
            str_template = f_template.read()

            # search max_population in an RW
            # use generator for memory efficiency
            maximum_population = max(
                f[attribute_field] for f in qgis_exposure_layer.getFeatures())
            range_increment = maximum_population / 5
            legend_expressions = {}
            for i in range(5):
                if i == 0:
                    marker_label = '&lt; %s' % format_int(range_increment)
                elif i < 4:
                    marker_label = '%s - %s' % (format_int(
                        range_increment *
                        i), format_int(range_increment * (i + 1)))
                else:
                    marker_label = '> %s' % format_int(range_increment * 4)
                marker_size = \
                    'scale_linear(' \
                    'attribute($currentfeature, &quot;%s&quot;) ' \
                    ', %d, %d, %d, %d)' % (
                        self.affect_field,
                        int(range_increment * i),
                        int(range_increment * (i + 1)),
                        i * 2,
                        (i + 1) * 2,
                    )
                marker_color = \
                    "set_color_part(" \
                    "set_color_part('0,0,255','saturation', %d ), 'alpha', " \
                    "if(attribute($currentfeature, '%s') = 0, " \
                    "0, 255))" % (i * 20, self.affect_field)
                marker_border = \
                    "set_color_part(" \
                    "'0,0,0','alpha', " \
                    "if (to_int(attribute(" \
                    "$currentfeature, '%s')) = 0, 0, 255))" % \
                    self.affect_field

                legend_expressions['marker-min-range-%d' % i] = \
                    '%s' % (range_increment * i)
                legend_expressions['marker-max-range-%d' % i] = \
                    '%s' % (range_increment * (i + 1))
                legend_expressions['marker-label-%d' % i] = marker_label
                legend_expressions['marker-size-%d' % i] = marker_size
                legend_expressions['marker-color-%d' % i] = marker_color
                legend_expressions['marker-border-%d' % i] = marker_border

            for k, v in legend_expressions.iteritems():
                str_template = str_template.replace('[%s]' % k, v)

            with open(target_style_path, mode='w') as target_f:
                target_f.write(str_template)

        # Get requested style for impact layer of either kind
        impact = self.impact_layer
        style = impact.get_style_info()
        style_type = impact.get_style_type()

        # Determine styling for QGIS layer
        qgis_impact_layer = impact.as_qgis_native()
        if impact.is_vector:
            LOGGER.debug('myEngineImpactLayer.is_vector')
            if not style:
                # Set default style if possible
                pass
            elif style_type == 'categorizedSymbol':
                LOGGER.debug('use categorized')
                set_vector_categorized_style(qgis_impact_layer, style)
            elif style_type == 'graduatedSymbol':
                LOGGER.debug('use graduated')
                set_vector_graduated_style(qgis_impact_layer, style)

        elif impact.is_raster:
            LOGGER.debug('myEngineImpactLayer.is_raster')
            if not style:
                qgis_impact_layer.setDrawingStyle("SingleBandPseudoColor")
            else:
                setRasterStyle(qgis_impact_layer, style)
Пример #11
0
    def calculate_impact(self):
        if_manager = ImpactFunctionManager()
        function_id = self.function_id
        impact_function = if_manager.get_instance(function_id)

        impact_function.hazard = self.hazard_layer.as_qgis_native()
        impact_function.exposure = self.exposure_layer.as_qgis_native()
        extent = impact_function.hazard.extent()
        impact_function.requested_extent = [
            extent.xMinimum(),
            extent.yMinimum(),
            extent.xMaximum(),
            extent.yMaximum()
        ]
        impact_function.requested_extent_crs = impact_function.hazard.crs()
        # impact_function.setup_aggregator()
        # impact_function.aggregator.validate_keywords()

        try:
            # skip process if hazard not contain significant flood
            skip_process = True
            qgis_hazard_layer = self.hazard_layer.as_qgis_native()
            keyword_io = KeywordIO()
            hazard_attribute_key = keyword_io.read_keywords(
                qgis_hazard_layer, 'field')

            # Do not skip if there are significant hazard class (2,3,4)
            for f in qgis_hazard_layer.getFeatures():
                try:
                    # try cast to int
                    hazard_state = f[hazard_attribute_key]
                    hazard_state = int(str(hazard_state))
                    if hazard_state >= 2:
                        skip_process = False
                        break
                except ValueError:
                    # this is expected
                    pass

            if skip_process:
                return

            impact_function.run_analysis()
            self.impact_layer = impact_function.impact
            # impact_function.aggregator.set_layers(
            #     self.hazard_layer.as_qgis_native(),
            #     self.exposure_layer.as_qgis_native())
            self.calculate_aggregate_impact(impact_function)
            # impact_function.run_aggregator()
            # impact_function.run_post_processor()
            # self.generate_aggregation(impact_function)
            self.generate_population_aggregation()
            self.set_style()
        except ZeroImpactException as e:
            # in case zero impact, just return
            LOGGER.info('No impact detected')
            LOGGER.info(e.message)
            return

        # copy results of impact to report_path directory
        base_name, _ = os.path.splitext(self.impact_layer.filename)
        dir_name = os.path.dirname(self.impact_layer.filename)
        for (root, dirs, files) in os.walk(dir_name):
            for f in files:
                source_filename = os.path.join(root, f)
                if source_filename.find(base_name) >= 0:
                    extensions = source_filename.replace(base_name, '')
                    new_path = os.path.join(self.report_path,
                                            'impact' + extensions)
                    shutil.copy(source_filename, new_path)

        self.impact_layer = read_layer(self.impact_path)
Пример #12
0
    def save_hazard_data(self):
        if self.dummy_report_folder:
            filename = os.path.join(self.working_dir, self.dummy_report_folder,
                                    'flood_data.json')
            hazard_geojson = DummySourceAPI.get_aggregate_report(filename)
        else:
            hazard_geojson = PetaJakartaAPI.get_aggregate_report(
                self.duration, self.level)

        if not hazard_geojson:
            raise PetaJakartaAPIError("Can't access PetaJakarta REST API")

        with open(self.hazard_path, 'w+') as f:
            f.write(hazard_geojson)

        # Save the layer as shp
        file_info = QFileInfo(self.hazard_path)
        hazard_layer = QgsVectorLayer(self.hazard_path, file_info.baseName(),
                                      'ogr', False)

        target_name = 'flood_data.shp'
        self.hazard_path = os.path.join(self.report_path, target_name)
        QgsVectorFileWriter.writeAsVectorFormat(hazard_layer, self.hazard_path,
                                                'CP1250', None,
                                                'ESRI Shapefile')

        file_info = QFileInfo(self.hazard_path)
        hazard_layer = QgsVectorLayer(self.hazard_path, file_info.baseName(),
                                      'ogr')

        # hazard_layer.startEditing()
        # field = QgsField('flooded', QVariant.Int)
        # hazard_layer.dataProvider().addAttributes([field])
        # hazard_layer.commitChanges()
        # idx = hazard_layer.fieldNameIndex('flooded')
        # expression = QgsExpression('count > 0')
        # expression.prepare(hazard_layer.pendingFields())
        #
        # hazard_layer.startEditing()
        # for feature in hazard_layer.getFeatures():
        #     feature[idx] = expression.evaluate(feature)
        #     hazard_layer.updateFeature(feature)
        #
        # hazard_layer.commitChanges()

        # writing keywords
        keyword_io = KeywordIO()

        keywords = {
            'field': 'state',
            'hazard': 'generic',
            'hazard_category': 'single_event',
            'keyword_version': '3.5',
            'layer_geometry': 'polygon',
            'layer_mode': 'classified',
            'layer_purpose': 'hazard',
            'title': 'Flood',
            'value_map': '{"high": [4], "medium": [3], '
            '"low": [2], '
            '"unaffected": ["None","","NULL",0,1]}',
            'vector_hazard_classification': 'generic_vector_hazard_classes'
        }

        keyword_io.write_keywords(hazard_layer, keywords)

        # copy layer styles
        style_path = self.flood_fixtures_dir('flood_data_classified_state.qml')
        target_style_path = os.path.join(self.report_path, 'flood_data.qml')
        shutil.copy(style_path, target_style_path)

        # archiving hazard layer
        with ZipFile(self.hazard_zip_path, 'w') as zf:
            for root, dirs, files in os.walk(self.report_path):
                for f in files:
                    _, ext = os.path.splitext(f)
                    if 'flood_data' in f:
                        filename = os.path.join(root, f)
                        zf.write(filename, arcname=f)
Пример #13
0
    def test_regression_2553_no_resample(self):
        """Test for regression 2553 (no resampling).

        see :

        https://github.com/inasafe/inasafe/issues/2553

        We want to verify that population with resampling should produce
        a result within a reasonable range of the same analysis but doing
        population with no resampling.

        """
        hazard_path = test_data_path(
            'hazard', 'continuous_flood_unaligned_big_size.tif')
        exposure_path = test_data_path(
            'exposure', 'people_allow_resampling_false.tif')

        hazard_layer, hazard_layer_purpose = load_layer(hazard_path)
        # Check if there is a regression about keywords being updated from
        # another layer - see #2605
        keywords = KeywordIO(hazard_layer)
        self.assertIn('flood unaligned', keywords.to_message().to_text())

        exposure_layer, exposure_layer_purpose = load_layer(
            exposure_path)
        keywords = KeywordIO(exposure_layer)
        self.assertIn(
            '*Allow resampling*, false------',
            keywords.to_message().to_text())

        QgsMapLayerRegistry.instance().addMapLayers(
            [hazard_layer, exposure_layer])

        # Count the total value of all exposure pixels
        # this is arse about face but width is actually giving height
        height = exposure_layer.width()
        # this is arse about face but height is actually giving width
        width = exposure_layer.height()
        provider = exposure_layer.dataProvider()
        # Bands count from 1!
        block = provider.block(1, provider.extent(), height, width)
        # Enable on-the-fly reprojection
        set_canvas_crs(GEOCRS, True)

        # This is the nicer way but wierdly it gets nan for every cell
        total_population = 0.0
        cell_count = 0
        row = 0
        # Iterate down each column to match the layout produced by r.stats
        while row < width:
            column = 0
            while column < height:
                cell_count += 1
                value = block.value(row, column)
                if value > 0:
                    total_population += value
                column += 1
            row += 1
        print "Total value of all cells is: %d" % total_population
        print "Number of cells counted: %d" % cell_count

        # 131 computed using r.sum
        self.assertAlmostEqual(total_population, 131.0177006121)

        result, message = setup_scenario(
            self.dock,
            hazard='flood unaligned',
            exposure='People never resample',
            function='Need evacuation',
            function_id='FloodEvacuationRasterHazardFunction')
        self.assertTrue(result, message)
        # Press RUN
        self.dock.accept()

        safe_layer = self.dock.analysis.impact_layer
        keywords = safe_layer.get_keywords()
        evacuated = float(keywords['evacuated'])
        self.assertLess(evacuated, total_population)
        expected_evacuated = 131.0
        self.assertEqual(evacuated, expected_evacuated)
Пример #14
0
    def __init__(self, parent=None, iface=None):
        """Constructor for Raster Reclassification to Vector Polygon.

        .. versionadded: 3.4

        :param parent: Optional widget to use as parent
        :type parent: QWidget

        :param iface: An instance of QGisInterface
        :type iface: QGisInterface
        """
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setupUi(self)

        self.setWindowTitle(self.tr('Raster Reclassification'))

        self.iface = iface

        self.if_registry = Registry()
        self.keyword_io = KeywordIO()

        # populate raster input
        self.cbo_raster_input.clear()
        registry = QgsMapLayerRegistry.instance()
        # MapLayers returns a QMap<QString id, QgsMapLayer layer>
        layers = registry.mapLayers().values()
        for layer in layers:
            try:
                name = layer.name()
                source = layer.id()
                layer_purpose = self.keyword_io.read_keywords(
                    layer, 'layer_purpose')
                if (isinstance(layer, QgsRasterLayer) and
                            layer_purpose == 'hazard'):
                    add_ordered_combo_item(
                        self.cbo_raster_input, self.tr(name), source)

            except Exception as e:
                raise e

        # self.input_list_parameter = InputListParameter()
        # self.input_list_parameter.name = 'Thresholds'
        # self.input_list_parameter.description = (
        #     'List of thresholds of values used in reclassification.')
        # self.input_list_parameter.help_text = 'list of thresholds used'
        # self.input_list_parameter.maximum_item_count = 100
        # self.input_list_parameter.minimum_item_count = 1
        # self.input_list_parameter.element_type = float
        # self.input_list_parameter.value = [0.0, 1.0]
        # self.input_list_parameter.ordering = \
        #     InputListParameter.AscendingOrder
        # self.thresholds_widget = InputListParameterWidget(
        #     self.input_list_parameter)

        # self.threshold_editor.layout().addWidget(self.thresholds_widget)

        # Set up context help
        self.help_button = self.button_box.button(QtGui.QDialogButtonBox.Help)
        self.ok_button = self.button_box.button(QtGui.QDialogButtonBox.Ok)
        # self.cancel_button = self.button_box.button(
        #     QtGui.QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)
        self.main_stacked_widget.setCurrentIndex(1)
        # adapt layer changed
        self.cbo_raster_input.currentIndexChanged.connect(self.raster_changed)
        self.raster_changed(self.cbo_raster_input.currentIndex())
Пример #15
0
    def render_nearby_table(self):

        hazard_mapping = {
            0: 'Very Low',
            1: 'Low',
            2: 'Moderate',
            3: 'High',
            4: 'Very High'
        }

        # load PLACES
        keyword_io = KeywordIO()
        try:
            cities_impact = read_qgis_layer(
                self.working_dir_path('cities_impact.shp'), 'Cities')
            hazard = keyword_io.read_keywords(cities_impact, 'target_field')
            hazard_field_index = cities_impact.fieldNameIndex(hazard)

            name_field = keyword_io.read_keywords(self.cities_layer,
                                                  'name_field')
            name_field_index = cities_impact.fieldNameIndex(name_field)

            try:
                population_field = keyword_io.read_keywords(
                    self.cities_layer, 'population_field')
                population_field_index = cities_impact.fieldNameIndex(
                    population_field)
            except KeywordNotFoundError:
                population_field = None
                population_field_index = None

            table_places = []
            for f in cities_impact.getFeatures():
                haz_class = f.attributes()[hazard_field_index]
                city_name = f.attributes()[name_field_index]
                if population_field_index >= 0:
                    city_pop = f.attributes()[population_field_index]
                else:
                    city_pop = 1
                # format:
                # [
                # 'hazard class',
                # 'city's population',
                # 'city's name',
                # 'the type'
                # ]
                haz = hazard_mapping[haz_class]
                item = {
                    'class': haz_class,
                    'hazard': haz,
                    'css': haz.lower().replace(' ', '-'),
                    'population':
                    format_int(population_rounding(city_pop / 1000)),
                    'name': city_name.title(),
                    'type': 'places'
                }
                table_places.append(item)

            # sort table by hazard zone, then population
            table_places = sorted(table_places,
                                  key=lambda x:
                                  (-x['class'], -x['population']))
        except Exception as e:
            LOGGER.exception(e)
            table_places = []

        # load AIRPORTS
        try:
            airport_impact = read_qgis_layer(
                self.working_dir_path('airport_impact.shp'), 'Airport')
            hazard = keyword_io.read_keywords(airport_impact, 'target_field')
            hazard_field_index = airport_impact.fieldNameIndex(hazard)

            name_field = keyword_io.read_keywords(self.airport_layer,
                                                  'name_field')
            name_field_index = airport_impact.fieldNameIndex(name_field)

            # airport doesnt have population, so enter 0 for population
            table_airports = []
            for f in airport_impact.getFeatures():
                haz_class = f.attributes()[hazard_field_index]
                airport_name = f.attributes()[name_field_index]
                haz = hazard_mapping[haz_class]
                item = {
                    'class': haz_class,
                    'hazard': haz,
                    'css': haz.lower().replace(' ', '-'),
                    'population': 0,
                    'name': airport_name.title(),
                    'type': 'airport'
                }
                table_airports.append(item)

            # Sort by hazard class
            table_airports = sorted(table_airports, key=lambda x: -x['class'])
        except Exception as e:
            LOGGER.exception(e)
            table_airports = []

        # decide which to show
        # maximum 2 airport
        max_airports = 2
        airport_count = min(max_airports, len(table_airports))
        # maximum total 7 entries to show
        max_rows = 6
        places_count = min(len(table_places), max_rows - airport_count)

        # get top airport
        table_airports = table_airports[:airport_count]
        # get top places
        table_places = table_places[:places_count]

        item_list = table_places + table_airports

        # sort entry by hazard level
        item_list = sorted(item_list,
                           key=lambda x: (-x['class'], -x['population']))

        nearby_template = self.ash_fixtures_dir('nearby-table.template.html')
        with open(nearby_template) as f:
            template = Template(f.read())
            # generate table here
            html_string = template.render(item_list=item_list)

        with open(self.nearby_html_path, 'w') as f:
            f.write(html_string)

        # copy airport logo
        shutil.copy(self.ash_fixtures_dir('logo/airport.jpg'),
                    self.working_dir_path('airport.jpg'))
Пример #16
0
def _clip_raster_layer(
        layer, extent, cell_size=None, extra_keywords=None):
    """Clip a Hazard or Exposure raster layer to the extents provided.

    The layer must be a raster layer or an exception will be thrown.

    .. note:: The extent *must* be in EPSG:4326.

    The output layer will always be in WGS84/Geographic.

    :param layer: A valid QGIS raster layer in EPSG:4326
    :type layer: QgsRasterLayer

    :param extent:  An array representing the exposure layer
           extents in the form [xmin, ymin, xmax, ymax]. It is assumed
           that the coordinates are in EPSG:4326 although currently
           no checks are made to enforce this.
           or:
           A QgsGeometry of type polygon.
           **Polygon clipping currently only supported for vector datasets.**
    :type extent: list(float), QgsGeometry

    :param cell_size: Cell size (in GeoCRS) which the layer should
            be resampled to. If not provided for a raster layer (i.e.
            theCellSize=None), the native raster cell size will be used.
    :type cell_size: float

    :returns: Output clipped layer (placed in the system temp dir).
    :rtype: QgsRasterLayer

    :raises: InvalidProjectionError - if input layer is a density
        layer in projected coordinates. See issue #123.

    """
    if not layer or not extent:
        message = tr('Layer or Extent passed to clip is None.')
        raise InvalidParameterError(message)

    if layer.type() != QgsMapLayer.RasterLayer:
        message = tr(
            'Expected a raster layer but received a %s.' %
            str(layer.type()))
        raise InvalidParameterError(message)

    working_layer = layer.source()

    # Check for existence of keywords file
    base, _ = os.path.splitext(working_layer)
    keywords_path = base + '.keywords'
    message = tr(
        'Input file to be clipped "%s" does not have the '
        'expected keywords file %s' % (
            working_layer,
            keywords_path
        ))
    verify(os.path.isfile(keywords_path), message)

    # Raise exception if layer is projected and refers to density (issue #123)
    # FIXME (Ole): Need to deal with it - e.g. by automatically reprojecting
    # the layer at this point and setting the native resolution accordingly
    # in its keywords.
    keywords = read_file_keywords(keywords_path)
    if 'datatype' in keywords and keywords['datatype'] == 'count':
        if str(layer.crs().authid()) != 'EPSG:4326':

            # This layer is not WGS84 geographic
            message = (
                'Layer %s represents count but has spatial reference "%s". '
                'Count layers must be given in WGS84 geographic coordinates, '
                'so please reproject and try again. For more information, see '
                'issue https://github.com/AIFDR/inasafe/issues/123' % (
                    working_layer,
                    layer.crs().toProj4()
                ))
            raise InvalidProjectionError(message)

    # We need to provide gdalwarp with a dataset for the clip
    # because unlike gdal_translate, it does not take projwin.
    clip_kml = extent_to_kml(extent)

    # Create a filename for the clipped, resampled and reprojected layer
    handle, filename = tempfile.mkstemp('.tif', 'clip_', temp_dir())
    os.close(handle)
    os.remove(filename)

    # If no cell size is specified, we need to run gdalwarp without
    # specifying the output pixel size to ensure the raster dims
    # remain consistent.
    binary_list = which('gdalwarp')
    LOGGER.debug('Path for gdalwarp: %s' % binary_list)
    if len(binary_list) < 1:
        raise CallGDALError(
            tr('gdalwarp could not be found on your computer'))
    # Use the first matching gdalwarp found
    binary = binary_list[0]
    if cell_size is None:
        command = (
            '"%s" -q -t_srs EPSG:4326 -r near -cutline %s -crop_to_cutline '
            '-ot Float64 -of GTiff "%s" "%s"' % (
                binary,
                clip_kml,
                working_layer,
                filename))
    else:
        command = (
            '"%s" -q -t_srs EPSG:4326 -r near -tr %s %s -cutline %s '
            '-crop_to_cutline -ot Float64 -of GTiff "%s" "%s"' % (
                binary,
                repr(cell_size),
                repr(cell_size),
                clip_kml,
                working_layer,
                filename))

    LOGGER.debug(command)
    result = QProcess().execute(command)

    # For QProcess exit codes see
    # http://qt-project.org/doc/qt-4.8/qprocess.html#execute
    if result == -2:  # cannot be started
        message_detail = tr('Process could not be started.')
        message = tr(
            '<p>Error while executing the following shell command:'
            '</p><pre>%s</pre><p>Error message: %s'
            % (command, message_detail))
        raise CallGDALError(message)
    elif result == -1:  # process crashed
        message_detail = tr('Process crashed.')
        message = tr(
            '<p>Error while executing the following shell command:</p>'
            '<pre>%s</pre><p>Error message: %s' % (command, message_detail))
        raise CallGDALError(message)

    # .. todo:: Check the result of the shell call is ok
    keyword_io = KeywordIO()
    keyword_io.copy_keywords(layer, filename, extra_keywords=extra_keywords)
    base_name = '%s clipped' % layer.name()
    layer = QgsRasterLayer(filename, base_name)

    return layer
Пример #17
0
    def create_keyword_file(self, algorithm):
        """Create keyword file for the raster file created.

        Basically copy a template from keyword file in converter data
        and add extra keyword (usually a title)

        :param algorithm: Which re-sampling algorithm to use.
            valid options are 'nearest' (for nearest neighbour), 'invdist'
            (for inverse distance), 'average' (for moving average). Defaults
            to 'nearest' if not specified. Note that passing re-sampling alg
            parameters is currently not supported. If None is passed it will
            be replaced with 'nearest'.
        :type algorithm: str
        """
        keyword_io = KeywordIO()

        # Set thresholds for each exposure
        mmi_default_classes = default_classification_thresholds(
            earthquake_mmi_scale)
        mmi_default_threshold = {
            earthquake_mmi_scale['key']: {
                'active': True,
                'classes': mmi_default_classes
            }
        }
        generic_default_classes = default_classification_thresholds(
            generic_hazard_classes)
        generic_default_threshold = {
            generic_hazard_classes['key']: {
                'active': True,
                'classes': generic_default_classes
            }
        }

        threshold_keyword = {}
        for exposure in exposure_all:
            # Not all exposure is supported by earthquake_mmi_scale
            if exposure in earthquake_mmi_scale['exposures']:
                threshold_keyword[exposure['key']] = mmi_default_threshold
            else:
                threshold_keyword[exposure['key']] = generic_default_threshold

        extra_keywords = {
            extra_keyword_earthquake_latitude['key']:
            self.latitude,
            extra_keyword_earthquake_longitude['key']:
            self.longitude,
            extra_keyword_earthquake_magnitude['key']:
            self.magnitude,
            extra_keyword_earthquake_depth['key']:
            self.depth,
            extra_keyword_earthquake_description['key']:
            self.description,
            extra_keyword_earthquake_location['key']:
            self.location,
            extra_keyword_earthquake_event_time['key']:
            self.time.strftime('%Y-%m-%dT%H:%M:%S'),
            extra_keyword_time_zone['key']:
            self.time_zone,
            extra_keyword_earthquake_x_minimum['key']:
            self.x_minimum,
            extra_keyword_earthquake_x_maximum['key']:
            self.x_maximum,
            extra_keyword_earthquake_y_minimum['key']:
            self.y_minimum,
            extra_keyword_earthquake_y_maximum['key']:
            self.y_maximum,
            extra_keyword_earthquake_event_id['key']:
            self.event_id
        }
        for key, value in self.extra_keywords.items():
            extra_keywords[key] = value

        # Delete empty element.
        empty_keys = []
        for key, value in extra_keywords.items():
            if value is None:
                empty_keys.append(key)
        for empty_key in empty_keys:
            extra_keywords.pop(empty_key)
        keywords = {
            'hazard': hazard_earthquake['key'],
            'hazard_category': hazard_category_single_event['key'],
            'keyword_version': inasafe_keyword_version,
            'layer_geometry': layer_geometry_raster['key'],
            'layer_mode': layer_mode_continuous['key'],
            'layer_purpose': layer_purpose_hazard['key'],
            'continuous_hazard_unit': unit_mmi['key'],
            'classification': earthquake_mmi_scale['key'],
            'thresholds': threshold_keyword,
            'extra_keywords': extra_keywords,
            'active_band': 1
        }

        if self.algorithm_name:
            layer_path = os.path.join(
                self.output_dir,
                '%s-%s.tif' % (self.output_basename, algorithm))
        else:
            layer_path = os.path.join(self.output_dir,
                                      '%s.tif' % self.output_basename)

        # append title and source to the keywords file
        if len(self.title.strip()) == 0:
            keyword_title = self.output_basename
        else:
            keyword_title = self.title

        keywords['title'] = keyword_title

        hazard_layer = QgsRasterLayer(layer_path, keyword_title)

        if not hazard_layer.isValid():
            raise InvalidLayerError()

        keyword_io.write_keywords(hazard_layer, keywords)
Пример #18
0
    def __init__(self,
                 iface,
                 template_metadata,
                 impact_function=None,
                 hazard=None,
                 exposure=None,
                 impact=None,
                 analysis=None,
                 exposure_summary_table=None,
                 aggregation_summary=None,
                 extra_layers=None,
                 ordered_layers=None,
                 legend_layers=None,
                 minimum_needs_profile=None,
                 multi_exposure_impact_function=None,
                 use_template_extent=False):
        """Constructor for the Composition Report class.

        :param iface: Reference to the QGIS iface object.
        :type iface: QgsAppInterface

        :param template_metadata: InaSAFE template metadata.
        :type template_metadata: ReportMetadata

        :param impact_function: Impact function instance for the report
        :type impact_function:
            safe.impact_function.impact_function.ImpactFunction

        .. versionadded:: 4.0
        """
        LOGGER.debug('InaSAFE Impact Report class initialised')
        self._iface = iface
        self._metadata = template_metadata
        self._output_folder = None
        self._impact_function = impact_function or (
            multi_exposure_impact_function)
        self._hazard = hazard or self._impact_function.hazard
        self._analysis = (analysis or self._impact_function.analysis_impacted)
        if impact_function:
            self._exposure = (exposure or self._impact_function.exposure)
            self._impact = (impact or self._impact_function.impact)
            self._exposure_summary_table = (
                exposure_summary_table
                or self._impact_function.exposure_summary_table)
            self._aggregation_summary = (
                aggregation_summary
                or self._impact_function.aggregation_summary)
        if extra_layers is None:
            extra_layers = []
        self._extra_layers = extra_layers
        self._ordered_layers = ordered_layers
        self._legend_layers = legend_layers
        self._minimum_needs = minimum_needs_profile
        self._multi_exposure_impact_function = multi_exposure_impact_function
        self._use_template_extent = use_template_extent
        self._inasafe_context = InaSAFEReportContext()

        # QgsMapSettings is added in 2.4
        if self._iface:
            map_settings = self._iface.mapCanvas().mapSettings()
        else:
            map_settings = QgsMapSettings()

        self._qgis_composition_context = QgsLayoutContext(
            None, map_settings, ImpactReport.DEFAULT_PAGE_DPI)
        self._keyword_io = KeywordIO()
Пример #19
0
    def __init__(self, parent, iface, dock=None, layer=None):
        """Constructor for the dialog.

        .. note:: In QtDesigner the advanced editor's predefined keywords
           list should be shown in english always, so when adding entries to
           cboKeyword, be sure to choose :safe_qgis:`Properties<<` and untick
           the :safe_qgis:`translatable` property.

        :param parent: Parent widget of this dialog.
        :type parent: QWidget

        :param iface: Quantum GIS QGisAppInterface instance.
        :type iface: QGisAppInterface

        :param dock: Dock widget instance that we can notify of changes to
            the keywords. Optional.
        :type dock: Dock
        """
        QtGui.QDialog.__init__(self, parent)
        self.setupUi(self)
        self.setWindowTitle(
            self.tr('InaSAFE %s Keywords Editor' % get_version()))
        # Save reference to the QGIS interface and parent
        self.iface = iface
        self.parent = parent
        self.dock = dock
        self.defaults = None

        # string constants
        self.global_default_string = definitions.global_default_attribute[
            'name']
        self.do_not_use_string = definitions.do_not_use_attribute['name']
        self.global_default_data = definitions.global_default_attribute['id']
        self.do_not_use_data = definitions.do_not_use_attribute['id']

        if layer is None:
            self.layer = self.iface.activeLayer()
        else:
            self.layer = layer

        self.keyword_io = KeywordIO()

        # note the keys should remain untranslated as we need to write
        # english to the keywords file. The keys will be written as user data
        # in the combo entries.
        # .. seealso:: http://www.voidspace.org.uk/python/odict.html
        self.standard_exposure_list = OrderedDict([
            ('population', self.tr('population')),
            ('structure', self.tr('structure')), ('road', self.tr('road')),
            ('Not Set', self.tr('Not Set'))
        ])
        self.standard_hazard_list = OrderedDict([
            ('earthquake [MMI]', self.tr('earthquake [MMI]')),
            ('tsunami [m]', self.tr('tsunami [m]')),
            ('tsunami [wet/dry]', self.tr('tsunami [wet/dry]')),
            ('tsunami [feet]', self.tr('tsunami [feet]')),
            ('flood [m]', self.tr('flood [m]')),
            ('flood [wet/dry]', self.tr('flood [wet/dry]')),
            ('flood [feet]', self.tr('flood [feet]')),
            ('tephra [kg2/m2]', self.tr('tephra [kg2/m2]')),
            ('volcano', self.tr('volcano')),
            ('generic [categorised]', self.tr('generic [categorised]')),
            ('Not Set', self.tr('Not Set'))
        ])

        # noinspection PyUnresolvedReferences
        self.lstKeywords.itemClicked.connect(self.edit_key_value_pair)

        # Set up help dialog showing logic.
        help_button = self.buttonBox.button(QtGui.QDialogButtonBox.Help)
        help_button.clicked.connect(self.show_help)

        if self.layer is not None and is_polygon_layer(self.layer):
            # set some initial ui state:
            self.defaults = get_defaults()
            self.radPredefined.setChecked(True)
            self.dsbFemaleRatioDefault.blockSignals(True)
            self.dsbFemaleRatioDefault.setValue(self.defaults['FEMALE_RATIO'])
            self.dsbFemaleRatioDefault.blockSignals(False)

            self.dsbYouthRatioDefault.blockSignals(True)
            self.dsbYouthRatioDefault.setValue(self.defaults['YOUTH_RATIO'])
            self.dsbYouthRatioDefault.blockSignals(False)

            self.dsbAdultRatioDefault.blockSignals(True)
            self.dsbAdultRatioDefault.setValue(self.defaults['ADULT_RATIO'])
            self.dsbAdultRatioDefault.blockSignals(False)

            self.dsbElderlyRatioDefault.blockSignals(True)
            self.dsbElderlyRatioDefault.setValue(
                self.defaults['ELDERLY_RATIO'])
            self.dsbElderlyRatioDefault.blockSignals(False)
        else:
            self.radPostprocessing.hide()
            self.tab_widget.removeTab(1)

        if self.layer:
            self.load_state_from_keywords()

        # add a reload from keywords button
        reload_button = self.buttonBox.addButton(
            self.tr('Reload'), QtGui.QDialogButtonBox.ActionRole)
        reload_button.clicked.connect(self.load_state_from_keywords)
        self.resize_dialog()
        self.tab_widget.setCurrentIndex(0)
        # TODO No we should not have test related stuff in prod code. TS
        self.test = False
Пример #20
0
def check_input_layer(layer, purpose):
    """Function to check if the layer is valid.

    The function will also set the monkey patching if needed.

    :param layer: The layer to test.
    :type layer: QgsMapLayer

    :param purpose: The expected purpose of the layer.
    :type purpose: basestring

    :return: A tuple with the status of the layer and an error message if
        needed.
        The status is 0 if everything was fine.
        The status is 1 if the client should fix something.
    :rtype: (int, m.Message)
    """
    if not layer.isValid():
        title = tr('The {purpose} layer is invalid').format(purpose=purpose)
        content = tr('The impact function needs a {exposure} layer to run. '
                     'You must provide a valid {exposure} layer.').format(
                         purpose=purpose)
        message = generate_input_error_message(title, m.Paragraph(content))
        return PREPARE_FAILED_BAD_INPUT, message

    # We should read it using KeywordIO for the very beginning. To avoid
    # get the modified keywords in the patching.
    try:
        keywords = KeywordIO().read_keywords(layer)
    except NoKeywordsFoundError:

        title = tr('The {purpose} layer does not have keywords.').format(
            purpose=purpose)
        content = tr(
            'The {purpose} layer does not have keywords. Use the wizard '
            'to assign keywords to the layer.').format(purpose=purpose)
        message = generate_input_error_message(title, m.Paragraph(content))
        return PREPARE_FAILED_BAD_INPUT, message

    if keywords.get('layer_purpose') != purpose:
        title = tr('The expected {purpose} layer is not an {purpose}.') \
            .format(purpose=purpose)
        content = tr('The expected {purpose} layer is not an {purpose}.') \
            .format(purpose=purpose)
        message = generate_input_error_message(title, m.Paragraph(content))
        return PREPARE_FAILED_BAD_INPUT, message

    version = keywords.get(inasafe_keyword_version_key)
    supported = is_keyword_version_supported(version)
    if not supported:
        parameters = {
            'version': inasafe_keyword_version,
            'source': layer.publicSource()
        }
        title = tr('The {purpose} layer is not up to date.').format(
            purpose=purpose)
        content = tr('The layer {source} must be updated to {version}.'
                     ).format(**parameters)
        message = generate_input_error_message(title, m.Paragraph(content))
        return PREPARE_FAILED_BAD_INPUT, message

    layer.keywords = keywords

    if is_vector_layer(layer):
        try:
            check_inasafe_fields(layer, keywords_only=True)
        except InvalidLayerError:
            title = tr('The {purpose} layer is not up to date.').format(
                purpose=purpose)
            content = tr(
                'The layer {source} must be updated with the keyword '
                'wizard. Your fields which have been set in the keywords '
                'previously are not matching your layer.').format(
                    source=layer.publicSource())
            message = generate_input_error_message(title, m.Paragraph(content))
            del layer.keywords
            return PREPARE_FAILED_BAD_INPUT, message

    return PREPARE_SUCCESS, None
Пример #21
0
    def __init__(self, parent=None, iface=None, dock=None):
        """Constructor for the dialog.

        .. note:: In QtDesigner the advanced editor's predefined keywords
           list should be shown in english always, so when adding entries to
           cboKeyword, be sure to choose :safe_qgis:`Properties<<` and untick
           the :safe_qgis:`translatable` property.

        :param parent: Parent widget of this dialog.
        :type parent: QWidget

        :param iface: QGIS QGisAppInterface instance.
        :type iface: QGisAppInterface

        :param dock: Dock widget instance that we can notify of changes to
            the keywords. Optional.
        :type dock: Dock
        """
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.setWindowTitle('InaSAFE')
        # Constants
        self.keyword_creation_wizard_name = tr(
            'InaSAFE Keywords Creation Wizard')
        self.ifcw_name = tr('InaSAFE Impact Function Centric Wizard')
        # Note the keys should remain untranslated as we need to write
        # english to the keywords file.
        # Save reference to the QGIS interface and parent
        self.iface = iface
        self.parent = parent
        self.dock = dock
        self.suppress_warning_dialog = False
        self.lblStep.clear()
        # Set icons
        self.lblMainIcon.setPixmap(
            QPixmap(resources_path('img', 'icons', 'icon-white.svg')))

        self.keyword_io = KeywordIO()

        self.is_selected_layer_keywordless = False
        self.parent_step = None

        self.pbnBack.setEnabled(False)
        self.pbnNext.setEnabled(False)
        self.pbnCancel.released.connect(self.reject)

        # Initialize attributes
        self.existing_keywords = None
        self.layer = None
        self.hazard_layer = None
        self.exposure_layer = None
        self.aggregation_layer = None

        self.step_kw_purpose = StepKwPurpose(self)
        self.step_kw_subcategory = StepKwSubcategory(self)
        self.step_kw_hazard_category = StepKwHazardCategory(self)
        self.step_kw_band_selector = StepKwBandSelector(self)
        self.step_kw_layermode = StepKwLayerMode(self)
        self.step_kw_unit = StepKwUnit(self)
        self.step_kw_classification = StepKwClassification(self)
        self.step_kw_field = StepKwField(self)
        self.step_kw_multi_classifications = StepKwMultiClassifications(self)
        self.step_kw_classify = StepKwClassify(self)
        self.step_kw_threshold = StepKwThreshold(self)
        self.step_kw_fields_mapping = StepKwFieldsMapping(self)
        self.step_kw_inasafe_fields = StepKwInaSAFEFields(self)
        self.step_kw_default_inasafe_fields = StepKwDefaultInaSAFEFields(self)
        self.step_kw_inasafe_raster_default_values = \
            StepKwInaSAFERasterDefaultValues(self)
        self.step_kw_source = StepKwSource(self)
        self.step_kw_title = StepKwTitle(self)
        self.step_kw_summary = StepKwSummary(self)

        self.step_fc_functions1 = StepFcFunctions1(self)
        self.step_fc_functions2 = StepFcFunctions2(self)
        self.step_fc_hazlayer_origin = StepFcHazLayerOrigin(self)
        self.step_fc_hazlayer_from_canvas = StepFcHazLayerFromCanvas(self)
        self.step_fc_hazlayer_from_browser = StepFcHazLayerFromBrowser(self)
        self.step_fc_explayer_origin = StepFcExpLayerOrigin(self)
        self.step_fc_explayer_from_canvas = StepFcExpLayerFromCanvas(self)
        self.step_fc_explayer_from_browser = StepFcExpLayerFromBrowser(self)
        self.step_fc_disjoint_layers = StepFcDisjointLayers(self)
        self.step_fc_agglayer_origin = StepFcAggLayerOrigin(self)
        self.step_fc_agglayer_from_canvas = StepFcAggLayerFromCanvas(self)
        self.step_fc_agglayer_from_browser = StepFcAggLayerFromBrowser(self)
        self.step_fc_agglayer_disjoint = StepFcAggLayerDisjoint(self)
        self.step_fc_extent = StepFcExtent(self)
        self.step_fc_extent_disjoint = StepFcExtentDisjoint(self)
        self.step_fc_summary = StepFcSummary(self)
        self.step_fc_analysis = StepFcAnalysis(self)

        self.wizard_help = WizardHelp(self)

        self.stackedWidget.addWidget(self.step_kw_purpose)
        self.stackedWidget.addWidget(self.step_kw_subcategory)
        self.stackedWidget.addWidget(self.step_kw_hazard_category)
        self.stackedWidget.addWidget(self.step_kw_band_selector)
        self.stackedWidget.addWidget(self.step_kw_layermode)
        self.stackedWidget.addWidget(self.step_kw_unit)
        self.stackedWidget.addWidget(self.step_kw_classification)
        self.stackedWidget.addWidget(self.step_kw_field)
        self.stackedWidget.addWidget(self.step_kw_multi_classifications)
        self.stackedWidget.addWidget(self.step_kw_classify)
        self.stackedWidget.addWidget(self.step_kw_threshold)
        self.stackedWidget.addWidget(self.step_kw_fields_mapping)
        self.stackedWidget.addWidget(self.step_kw_inasafe_fields)
        self.stackedWidget.addWidget(self.step_kw_default_inasafe_fields)
        self.stackedWidget.addWidget(
            self.step_kw_inasafe_raster_default_values)
        self.stackedWidget.addWidget(self.step_kw_source)
        self.stackedWidget.addWidget(self.step_kw_title)
        self.stackedWidget.addWidget(self.step_kw_summary)
        self.stackedWidget.addWidget(self.step_fc_functions1)
        self.stackedWidget.addWidget(self.step_fc_functions2)
        self.stackedWidget.addWidget(self.step_fc_hazlayer_origin)
        self.stackedWidget.addWidget(self.step_fc_hazlayer_from_canvas)
        self.stackedWidget.addWidget(self.step_fc_hazlayer_from_browser)
        self.stackedWidget.addWidget(self.step_fc_explayer_origin)
        self.stackedWidget.addWidget(self.step_fc_explayer_from_canvas)
        self.stackedWidget.addWidget(self.step_fc_explayer_from_browser)
        self.stackedWidget.addWidget(self.step_fc_disjoint_layers)
        self.stackedWidget.addWidget(self.step_fc_agglayer_origin)
        self.stackedWidget.addWidget(self.step_fc_agglayer_from_canvas)
        self.stackedWidget.addWidget(self.step_fc_agglayer_from_browser)
        self.stackedWidget.addWidget(self.step_fc_agglayer_disjoint)
        self.stackedWidget.addWidget(self.step_fc_extent)
        self.stackedWidget.addWidget(self.step_fc_extent_disjoint)
        self.stackedWidget.addWidget(self.step_fc_summary)
        self.stackedWidget.addWidget(self.step_fc_analysis)

        self.stackedWidget.addWidget(self.wizard_help)

        # QSetting
        self.setting = QSettings()

        # Wizard Steps
        self.impact_function_steps = []
        self.keyword_steps = []
        self.on_help = False

        self.resized.connect(self.after_resize)
Пример #22
0
    def __init__(self, parent=None, iface=None):
        """Constructor for dialog.

        :param parent: Optional widget to use as parent
        :type parent: QWidget

        :param iface: An instance of QGisInterface
        :type iface: QGisInterface
        """
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setupUi(self)
        self.setWindowTitle(self.tr('InaSAFE Impact Layer Merge Tool'))
        self.iface = iface
        self.keyword_io = KeywordIO()

        # Template Path for composer
        self.template_path = resources_path('qgis-composer-templates',
                                            'merged_report.qpt')

        # Safe Logo Path
        self.safe_logo_path = resources_path('img', 'logos',
                                             'inasafe-logo-url.png')

        # Organisation Logo Path
        self.organisation_logo_path = resources_path('img', 'logos',
                                                     'supporters.png')

        # Disclaimer text
        self.disclaimer = disclaimer()

        # The output directory
        self.out_dir = None

        # Stored information from first impact layer
        self.first_impact = {
            'layer': None,
            'map_title': None,
            'hazard_title': None,
            'exposure_title': None,
            'postprocessing_report': None,
        }

        # Stored information from second impact layer
        self.second_impact = {
            'layer': None,
            'map_title': None,
            'hazard_title': None,
            'exposure_title': None,
            'postprocessing_report': None,
        }

        # Stored information from aggregation layer
        self.aggregation = {'layer': None, 'aggregation_attribute': None}

        # The summary report, contains report for each aggregation area
        self.summary_report = {}

        # The html reports and its file path
        self.html_reports = {}

        # A boolean flag whether to merge entire area or aggregated
        self.entire_area_mode = False

        # Get the global settings and override some variable if exist
        self.read_settings()

        # Get all current project layers for combo box
        self.get_project_layers()

        # Set up context help
        help_button = self.button_box.button(QtGui.QDialogButtonBox.Help)
        help_button.clicked.connect(self.show_help)

        # Show usage info
        self.show_info()
        self.restore_state()