Ejemplo n.º 1
0
    def test_empty_layer(self):
        """Test if we import an empty layer."""
        layer = create_memory_layer(
            'test', QgsWkbTypes.PolygonGeometry,
            QgsCoordinateReferenceSystem(3857), [
                QgsField('my_field_1', QVariant.Int),
                QgsField('my_field_2', QVariant.Double),
                QgsField('my_field_3', QVariant.String)
            ])
        self.assertTrue(layer.isValid())
        self.assertEqual(len(layer.fields()), 3)

        # These following tests doesn't work if we add 'geosjon' in the formats
        # list. https://issues.qgis.org/issues/18370
        formats = ['shp']
        for extension in formats:
            path = QDir(mkdtemp())
            data_store = Folder(path)
            data_store.default_vector_format = extension
            name = 'test'
            result, message = data_store.add_layer(layer, name)
            self.assertTrue(result, message)

            # Fetch the layer
            imported_layer = data_store.layer(name)
            self.assertTrue(imported_layer.isValid())
            self.assertListEqual([f.name() for f in imported_layer.fields()],
                                 ['my_field_1', 'my_field_2', 'my_field_3'])
Ejemplo n.º 2
0
    def accept(self):
        """Process the layer for multi buffering and generate a new layer.

        .. note:: This is called on OK click.
        """
        # set parameter from dialog
        input_layer = self.layer.currentLayer()
        output_path = self.output_form.text()
        radius = self.get_classification()
        # monkey patch keywords so layer works on multi buffering function
        input_layer.keywords = {'inasafe_fields': {}}

        # run multi buffering
        self.output_layer = multi_buffering(input_layer, radius)

        # save output layer to data store and check whether user
        # provide the output path.
        if output_path:
            self.output_directory, self.output_filename = (
                os.path.split(output_path))
            self.output_filename, self.output_extension = (
                os.path.splitext(self.output_filename))

        # if user do not provide the output path, create a temporary file.
        else:
            self.output_directory = temp_dir(sub_dir='work')
            self.output_filename = (
                unique_filename(
                    prefix='hazard_layer',
                    suffix='.geojson',
                    dir=self.output_directory))
            self.output_filename = os.path.split(self.output_filename)[1]
            self.output_filename, self.output_extension = (
                os.path.splitext(self.output_filename))

        self.data_store = Folder(self.output_directory)
        if self.output_extension == '.shp':
            self.data_store.default_vector_format = 'shp'
        elif self.output_extension == '.geojson':
            self.data_store.default_vector_format = 'geojson'
        self.data_store.add_layer(self.output_layer, self.output_filename)

        # add output layer to map canvas
        self.output_layer = self.data_store.layer(self.output_filename)

        QgsMapLayerRegistry.instance().addMapLayers(
            [self.output_layer])
        self.iface.setActiveLayer(self.output_layer)
        self.iface.zoomToActiveLayer()
        self.done(QtGui.QDialog.Accepted)

        if self.keyword_wizard_checkbox.isChecked():
            self.launch_keyword_wizard()
Ejemplo n.º 3
0
def save_layer_to_file(layer):
    """Save a QGIS layer to disk.

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

    :return: The path to the file.
    :rtype: str
    """
    path = mkdtemp()
    data_store = Folder(path)
    data_store.default_vector_format = 'geojson'
    result = data_store.add_layer(layer, 'debug_layer')
    return data_store.layer_uri(result[1])
Ejemplo n.º 4
0
def save_layer_to_file(layer):
    """Save a QGIS layer to disk.

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

    :return: The path to the file.
    :rtype: str
    """
    path = mkdtemp()
    data_store = Folder(path)
    data_store.default_vector_format = 'geojson'
    result = data_store.add_layer(layer, 'debug_layer')
    return data_store.layer_uri(result[1])
    def accept(self):
        """Process the layer and field and generate a new layer.

        .. note:: This is called on OK click.

        """
        # run minimum needs calculator
        try:
            success, self.result_layer = (self.minimum_needs(
                self.layer.currentLayer()))
            if not success:
                return
        except Exception as e:
            error_name, traceback = humanise_exception(e)
            message = ('Problem(s) occured. \n%s \nDiagnosis: \n%s' %
                       (error_name, traceback))
            display_critical_message_box(
                title=self.tr('Error while calculating minimum needs'),
                message=message)
            return

        # remove monkey patching keywords
        del self.result_layer.keywords

        # write memory layer to file system
        settings = QSettings()
        default_user_directory = settings.value('inasafe/defaultUserDirectory',
                                                defaultValue='')

        if default_user_directory:
            path = os.path.join(default_user_directory,
                                self.result_layer.name())
            if not os.path.exists(path):
                os.makedirs(path)
            data_store = Folder(path)
        else:
            data_store = Folder(temp_dir(sub_dir=self.result_layer.name()))

        data_store.default_vector_format = 'geojson'
        data_store.add_layer(self.result_layer, self.result_layer.name())

        self.result_layer = data_store.layer(self.result_layer.name())

        # noinspection PyArgumentList
        QgsMapLayerRegistry.instance().addMapLayers(
            [data_store.layer(self.result_layer.name())])
        self.done(QtGui.QDialog.Accepted)
Ejemplo n.º 6
0
    def accept(self):
        """Process the layer and field and generate a new layer.

        .. note:: This is called on OK click.

        """
        # run minimum needs calculator
        try:
            success, self.result_layer = (
                self.minimum_needs(self.layer.currentLayer()))
            if not success:
                return
        except Exception as e:
            error_name, traceback = humanise_exception(e)
            message = (
                'Problem(s) occured. \n%s \nDiagnosis: \n%s' % (
                    error_name, traceback))
            display_critical_message_box(
                title=self.tr('Error while calculating minimum needs'),
                message=message)
            return

        # remove monkey patching keywords
        del self.result_layer.keywords

        # write memory layer to file system
        settings = QSettings()
        default_user_directory = settings.value(
            'inasafe/defaultUserDirectory', defaultValue='')

        if default_user_directory:
            output_directory = os.path.join(
                default_user_directory, 'minimum_needs_calculator')
            if not os.path.exists(output_directory):
                os.makedirs(output_directory)
        else:
            output_directory = temp_dir(sub_dir='minimum_needs_calculator')

        output_layer_name = os.path.split(self.result_layer.name())[1]

        # If normal filename doesn't exist, then use normal filename
        random_string_length = len(output_layer_name.split('_')[-1])
        normal_filename = output_layer_name[:-(random_string_length + 1)]
        if not os.path.exists(os.path.join(output_directory, normal_filename)):
            output_layer_name = normal_filename

        data_store = Folder(output_directory)
        data_store.default_vector_format = 'geojson'
        data_store.add_layer(self.result_layer, output_layer_name)

        self.result_layer = data_store.layer(output_layer_name)

        # noinspection PyArgumentList
        QgsProject.instance().addMapLayers(
            [data_store.layer(self.result_layer.name())])
        self.done(QtWidgets.QDialog.Accepted)
Ejemplo n.º 7
0
    def accept(self):
        """Process the layer for multi buffering and generate a new layer.

        .. note:: This is called on OK click.
        """
        # set parameter from dialog
        input_layer = self.layer.currentLayer()
        output_path = self.output_form.text()
        radius = self.get_classification()
        # monkey patch keywords so layer works on multi buffering function
        input_layer.keywords = {'inasafe_fields': {}}

        # run multi buffering
        self.output_layer = multi_buffering(input_layer, radius)

        # save output layer to data store and check whether user
        # provide the output path.
        if output_path:
            self.output_directory, self.output_filename = (
                os.path.split(output_path))
            self.output_filename, self.output_extension = (
                os.path.splitext(self.output_filename))

        # if user do not provide the output path, create a temporary file.
        else:
            self.output_directory = temp_dir(sub_dir='work')
            self.output_filename = (
                unique_filename(
                    prefix='hazard_layer',
                    suffix='.geojson',
                    dir=self.output_directory))
            self.output_filename = os.path.split(self.output_filename)[1]
            self.output_filename, self.output_extension = (
                os.path.splitext(self.output_filename))

        self.data_store = Folder(self.output_directory)
        if self.output_extension == '.shp':
            self.data_store.default_vector_format = 'shp'
        elif self.output_extension == '.geojson':
            self.data_store.default_vector_format = 'geojson'
        self.data_store.add_layer(self.output_layer, self.output_filename)

        # add output layer to map canvas
        self.output_layer = self.data_store.layer(self.output_filename)

        QgsMapLayerRegistry.instance().addMapLayers(
            [self.output_layer])
        self.iface.setActiveLayer(self.output_layer)
        self.iface.zoomToActiveLayer()
        self.done(QtGui.QDialog.Accepted)

        if self.keyword_wizard_checkbox.isChecked():
            self.launch_keyword_wizard()
Ejemplo n.º 8
0
def run_impact_function(cli_arguments):
    """Runs an analysis and delegates producing pdf and .geojson output layers.

    .. versionadded:: 3.2

    :param cli_arguments: User inputs.
    :type cli_arguments: CommandLineArguments
    """
    hazard = get_layer(cli_arguments.hazard, 'Hazard Layer')
    exposure = get_layer(cli_arguments.exposure, 'Exposure Layer')
    aggregation = None
    if cli_arguments.aggregation:
        aggregation = get_layer(cli_arguments.aggregation, 'Aggregation Layer')

    # Set up impact function
    impact_function = ImpactFunction()
    impact_function.hazard = hazard
    impact_function.exposure = exposure
    impact_function.aggregation = aggregation
    # Set the datastore
    impact_function.datastore = Folder(cli_arguments.output_dir)
    impact_function.datastore.default_vector_format = 'geojson'

    # Set the extent
    if cli_arguments.extent:
        impact_function.requested_extent_crs = \
            QgsCoordinateReferenceSystem(4326)
        try:
            impact_function.requested_extent = QgsRectangle(
                float(cli_arguments.extent[0]),
                float(cli_arguments.extent[1]),
                float(cli_arguments.extent[2]),
                float(cli_arguments.extent[3])
            )
        except AttributeError:
            print "Extent is not valid..."
            pass

    # Prepare impact function
    status, message = impact_function.prepare()
    if status != PREPARE_SUCCESS:
        print message.to_text()
        return status, message, None

    status, message = impact_function.run()
    if status != ANALYSIS_SUCCESS:
        print message.to_text()
        return status, message, None

    return status, message, impact_function
Ejemplo n.º 9
0
    def accept(self):
        """Process the layer and field and generate a new layer.

        .. note:: This is called on OK click.

        """
        # run minimum needs calculator
        try:
            success, self.result_layer = (
                self.minimum_needs(self.layer.currentLayer()))
            if not success:
                return
        except Exception as e:
            error_name, traceback = humanise_exception(e)
            message = (
                'Problem(s) occured. \n%s \nDiagnosis: \n%s' % (
                    error_name, traceback))
            display_critical_message_box(
                title=self.tr('Error while calculating minimum needs'),
                message=message)
            return

        # remove monkey patching keywords
        del self.result_layer.keywords

        # write memory layer to file system
        settings = QSettings()
        default_user_directory = settings.value(
            'inasafe/defaultUserDirectory', defaultValue='')

        if default_user_directory:
            path = os.path.join(
                default_user_directory, self.result_layer.name())
            if not os.path.exists(path):
                os.makedirs(path)
            data_store = Folder(path)
        else:
            data_store = Folder(temp_dir(sub_dir=self.result_layer.name()))

        data_store.default_vector_format = 'geojson'
        data_store.add_layer(self.result_layer, self.result_layer.name())

        self.result_layer = data_store.layer(self.result_layer.name())

        # noinspection PyArgumentList
        QgsMapLayerRegistry.instance().addMapLayers(
            [data_store.layer(self.result_layer.name())])
        self.done(QtGui.QDialog.Accepted)
Ejemplo n.º 10
0
def rasterize_vector_layer(layer, width, height, extent):
    """Rasterize a vector layer to the grid given by extent and width/height.

    :param layer: The vector layer.
    :type layer: QgsVectorLayer

    :param width: The width of the output.
    :type width: int

    :param height: The height of the output.
    :type height: int

    :param extent: The extent to use.
    :type extent: QgsRectangle

    :return: The new raster layer.
    :rtype: QgsRasterLayer
    """
    name = rasterize_steps['gdal_layer_name']
    output_filename = unique_filename(prefix=name, suffix='.tif')

    extent_str = '%f,%f,%f,%f' % (extent.xMinimum(), extent.xMaximum(),
                                  extent.yMinimum(), extent.yMaximum())

    keywords = dict(layer.keywords)

    # The layer is in memory, we need to save it to a file for Processing.
    data_store = Folder(mkdtemp())
    data_store.default_vector_format = 'geojson'
    result = data_store.add_layer(layer, 'vector_layer')
    layer = data_store.layer(result[1])
    assert layer.isValid()

    field = layer.keywords['inasafe_fields'][aggregation_id_field['key']]

    # ET 21/02/17. I got some issues using rasterize algorithm from Processing.
    # I keep it in case of we need it later. Let's use gdal command line.
    use_gdal_command_line = True

    if use_gdal_command_line:
        startupinfo = None
        if sys.platform == 'win32':
            # On windows, we don't want to display the bash shell.
            # https://github.com/inasafe/inasafe/issues/3980
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        commands = [which('gdal_rasterize')[0]]
        commands += ['-a', field]
        commands += ['-ts', str(width), str(height)]
        commands += ['-ot', 'Int16']
        commands += ['-a_nodata', "'-1'"]
        commands += [layer.source(), output_filename]

        LOGGER.info(' '.join(commands))
        result = subprocess.check_call(commands, startupinfo=startupinfo)
        LOGGER.info('Result : %s' % result)
    else:
        parameters = dict()
        parameters['INPUT'] = layer
        parameters['FIELD'] = field
        parameters['DIMENSIONS'] = 0  # output size is given in pixels
        parameters['WIDTH'] = width
        parameters['HEIGHT'] = height
        parameters['RASTER_EXT'] = extent_str
        parameters['TFW'] = False  # force generation of ESRI TFW
        parameters['RTYPE'] = 1  # raster type: Int16
        parameters['NO_DATA'] = '-1'  # nodata value
        parameters['COMPRESS'] = 4  # GeoTIFF compression: DEFLATE
        parameters['JPEGCOMPRESSION'] = 75  # JPEG compression level: 75
        parameters['ZLEVEL'] = 6  # DEFLATE compression level
        parameters['PREDICTOR'] = 1  # predictor for JPEG/DEFLATE
        parameters['TILED'] = False  # Tiled GeoTIFF?
        parameters['BIGTIFF'] = 0  # whether to make big TIFF
        parameters['EXTRA'] = ''  # additional creation parameters
        parameters['OUTPUT'] = output_filename

        result = runalg('gdalogr:rasterize', parameters)
        if result is None:
            # Let's try be removing a new parameter added between 2.14 and 2.16
            del parameters['RASTER_EXT']

        result = runalg('gdalogr:rasterize', parameters)
        assert result is not None

    layer_aligned = QgsRasterLayer(output_filename, name, 'gdal')
    assert layer_aligned.isValid()

    layer_aligned.keywords = keywords
    layer_aligned.keywords['title'] = (rasterize_steps['output_layer_name'] %
                                       'aggregation')
    layer_aligned.keywords['layer_purpose'] = (
        layer_purpose_aggregation_summary['key'])
    del layer_aligned.keywords['inasafe_fields']

    check_layer(layer_aligned)
    return layer_aligned
Ejemplo n.º 11
0
class MultiBufferDialog(QtWidgets.QDialog, FORM_CLASS):
    """Dialog implementation class for the InaSAFE multi buffer tool."""
    def __init__(self, parent=None, iface=None, dock_widget=None):
        """Constructor for the multi buffer dialog.

        :param parent: Parent widget of this dialog.
        :type parent: QWidget
        """
        QtWidgets.QDialog.__init__(self, parent)
        self.setupUi(self)
        self.setWindowTitle(self.tr('InaSAFE Multi Buffer Tool'))
        icon = resources_path('img', 'icons', 'show-multi-buffer.svg')
        self.setWindowIcon(QtGui.QIcon(icon))
        self.parent = parent
        self.iface = iface
        self.dock_widget = dock_widget
        self.keyword_wizard = None

        # output file properties initialisation
        self.data_store = None
        self.output_directory = None
        self.output_filename = None
        self.output_extension = None
        self.output_layer = None
        self.classification = []

        # set icon
        self.add_class_button.setIcon(
            QIcon(resources_path('img', 'icons', 'add.svg')))
        self.remove_class_button.setIcon(
            QIcon(resources_path('img', 'icons', 'remove.svg')))

        # prepare dialog initialisation
        self.layer.setFilters(QgsMapLayerProxyModel.VectorLayer)
        self.directory_button_status()
        self.add_class_button_status()
        self.ok_button_status()
        self.output_form.setPlaceholderText(
            self.tr('[Create a temporary layer]'))
        self.keyword_wizard_checkbox.setChecked(True)

        # set signal
        self.layer.layerChanged.connect(self.directory_button_status)
        self.layer.layerChanged.connect(self.ok_button_status)
        self.output_form.textChanged.connect(self.ok_button_status)
        self.directory_button.clicked.connect(
            self.on_directory_button_tool_clicked)
        self.radius_form.valueChanged.connect(self.add_class_button_status)
        self.class_form.textChanged.connect(self.add_class_button_status)
        self.add_class_button.clicked.connect(
            self.populate_hazard_classification)
        self.add_class_button.clicked.connect(self.ok_button_status)
        self.remove_class_button.clicked.connect(
            self.remove_selected_classification)
        self.remove_class_button.clicked.connect(self.ok_button_status)

        # Set up things for context help
        self.help_button = self.button_box.button(
            QtWidgets.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)

        # Fix for issue 1699 - cancel button does nothing
        cancel_button = self.button_box.button(
            QtWidgets.QDialogButtonBox.Cancel)
        cancel_button.clicked.connect(self.reject)
        # Fix ends
        ok_button = self.button_box.button(QtWidgets.QDialogButtonBox.Ok)
        ok_button.clicked.connect(self.accept)

    def accept(self):
        """Process the layer for multi buffering and generate a new layer.

        .. note:: This is called on OK click.
        """
        # set parameter from dialog
        input_layer = self.layer.currentLayer()
        output_path = self.output_form.text()
        radius = self.get_classification()
        # monkey patch keywords so layer works on multi buffering function
        input_layer.keywords = {'inasafe_fields': {}}

        # run multi buffering
        self.output_layer = multi_buffering(input_layer, radius)

        # save output layer to data store and check whether user
        # provide the output path.
        if output_path:
            self.output_directory, self.output_filename = (
                os.path.split(output_path))
            self.output_filename, self.output_extension = (os.path.splitext(
                self.output_filename))

        # if user do not provide the output path, create a temporary file.
        else:
            self.output_directory = temp_dir(sub_dir='work')
            self.output_filename = (unique_filename(prefix='hazard_layer',
                                                    suffix='.geojson',
                                                    dir=self.output_directory))
            self.output_filename = os.path.split(self.output_filename)[1]
            self.output_filename, self.output_extension = (os.path.splitext(
                self.output_filename))

        self.data_store = Folder(self.output_directory)
        if self.output_extension == '.shp':
            self.data_store.default_vector_format = 'shp'
        elif self.output_extension == '.geojson':
            self.data_store.default_vector_format = 'geojson'
        self.data_store.add_layer(self.output_layer, self.output_filename)

        # add output layer to map canvas
        self.output_layer = self.data_store.layer(self.output_filename)

        QgsProject.instance().addMapLayers([self.output_layer])
        self.iface.setActiveLayer(self.output_layer)
        self.iface.zoomToActiveLayer()
        self.done(QtWidgets.QDialog.Accepted)

        if self.keyword_wizard_checkbox.isChecked():
            self.launch_keyword_wizard()

    @pyqtSlot()  # prevents actions being handled twice
    def on_directory_button_tool_clicked(self):
        """Autoconnect slot activated when directory button is clicked."""
        # noinspection PyCallByClass,PyTypeChecker
        # set up parameter from dialog
        input_path = self.layer.currentLayer().source()
        input_directory, self.output_filename = os.path.split(input_path)
        file_extension = os.path.splitext(self.output_filename)[1]
        self.output_filename = os.path.splitext(self.output_filename)[0]
        # show Qt file directory dialog
        output_path, __ = QtWidgets.QFileDialog.getSaveFileName(
            self, self.tr('Output file'), '%s_multi_buffer%s' % (os.path.join(
                input_directory, self.output_filename), file_extension),
            'GeoJSON (*.geojson);;Shapefile (*.shp)')
        # set selected path to the dialog
        self.output_form.setText(output_path)

    def get_output_from_input(self):
        """Populate output form with default output path based on input layer.
        """
        input_path = self.layer.currentLayer().source()
        output_path = (os.path.splitext(input_path)[0] + '_multi_buffer' +
                       os.path.splitext(input_path)[1])
        self.output_form.setText(output_path)

    def populate_hazard_classification(self):
        """Populate hazard classification on hazard class form."""
        new_class = {
            'value': self.radius_form.value(),
            'name': self.class_form.text()
        }
        self.classification.append(new_class)
        self.classification = sorted(self.classification,
                                     key=itemgetter('value'))

        self.hazard_class_form.clear()
        for item in self.classification:
            new_item = '{value} - {name}'.format(value=item['value'],
                                                 name=item['name'])
            self.hazard_class_form.addItem(new_item)

        self.radius_form.setValue(0)
        self.class_form.clear()
        self.ok_button_status()

    def remove_selected_classification(self):
        """Remove selected item on hazard class form."""
        removed_classes = self.hazard_class_form.selectedItems()
        current_item = self.hazard_class_form.currentItem()
        removed_index = self.hazard_class_form.indexFromItem(current_item)
        del self.classification[removed_index.row()]
        for item in removed_classes:
            self.hazard_class_form.takeItem(self.hazard_class_form.row(item))

    def get_classification(self):
        """Get all hazard class created by user.

        :return: Hazard class definition created by user.
        :rtype: OrderedDict
        """
        classification_dictionary = {}
        for item in self.classification:
            classification_dictionary[item['value']] = item['name']

        classification_dictionary = OrderedDict(
            sorted(classification_dictionary.items()))

        return classification_dictionary

    def directory_button_status(self):
        """Function to enable or disable directory button."""
        if self.layer.currentLayer():
            self.directory_button.setEnabled(True)
        else:
            self.directory_button.setEnabled(False)

    def add_class_button_status(self):
        """Function to enable or disable add class button."""
        if self.class_form.text() and self.radius_form.value() >= 0:
            self.add_class_button.setEnabled(True)
        else:
            self.add_class_button.setEnabled(False)

    def ok_button_status(self):
        """Function to enable or disable OK button."""
        if not self.layer.currentLayer():
            self.button_box.button(
                QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
        elif (self.hazard_class_form.count() > 0
              and self.layer.currentLayer().name()
              and len(self.output_form.text()) >= 0):
            self.button_box.button(
                QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
        else:
            self.button_box.button(
                QtWidgets.QDialogButtonBox.Ok).setEnabled(False)

    @pyqtSlot(bool)  # prevents actions being handled twice
    def help_toggled(self, flag):
        """Show or hide the help tab in the stacked widget.

        :param flag: Flag indicating whether help should be shown or hidden.
        :type flag: bool
        """
        if flag:
            self.help_button.setText(self.tr('Hide Help'))
            self.show_help()
        else:
            self.help_button.setText(self.tr('Show Help'))
            self.hide_help()

    def hide_help(self):
        """Hide the usage info from the user."""
        self.main_stacked_widget.setCurrentIndex(1)

    def show_help(self):
        """Show usage info to the user."""
        # Read the header and footer html snippets
        self.main_stacked_widget.setCurrentIndex(0)
        header = html_header()
        footer = html_footer()

        string = header

        message = multi_buffer_help()

        string += message.to_html()
        string += footer

        self.help_web_view.setHtml(string)

    def launch_keyword_wizard(self):
        """Launch keyword creation wizard."""
        # make sure selected layer is the output layer
        if self.iface.activeLayer() != self.output_layer:
            return

        # launch wizard dialog
        self.keyword_wizard = WizardDialog(self.iface.mainWindow(), self.iface,
                                           self.dock_widget)
        self.keyword_wizard.set_keywords_creation_mode(self.output_layer)
        self.keyword_wizard.exec_()  # modal
Ejemplo n.º 12
0
    def run_task(self, task_item, status_item, count=0, index=''):
        """Run a single task.

        :param task_item: Table task_item containing task name / details.
        :type task_item: QTableWidgetItem

        :param status_item: Table task_item that holds the task status.
        :type status_item: QTableWidgetItem

        :param count: Count of scenarios that have been run already.
        :type count:

        :param index: The index for the table item that will be run.
        :type index: int

        :returns: Flag indicating if the task succeeded or not.
        :rtype: bool
        """
        self.enable_busy_cursor()
        for layer_group in self.layer_group_container:
            layer_group.setItemVisibilityChecked(False)

        # set status to 'running'
        status_item.setText(self.tr('Running'))

        # .. see also:: :func:`appendRow` to understand the next 2 lines
        variant = task_item.data(QtCore.Qt.UserRole)
        value = variant[0]
        result = True

        if isinstance(value, str):
            filename = value
            # run script
            try:
                self.run_script(filename)
                # set status to 'OK'
                status_item.setText(self.tr('Script OK'))
            except Exception as e:  # pylint: disable=W0703
                # set status to 'fail'
                status_item.setText(self.tr('Script Fail'))

                LOGGER.exception(
                    'Running macro failed. The exception: ' + str(e))
                result = False
        elif isinstance(value, dict):
            # start in new project if toggle is active
            if self.start_in_new_project:
                self.iface.newProject()
            # create layer group
            group_name = value['scenario_name']
            self.layer_group = self.root.addGroup(group_name)
            self.layer_group_container.append(self.layer_group)

            # Its a dict containing files for a scenario
            success, parameters = self.prepare_task(value)
            if not success:
                # set status to 'running'
                status_item.setText(self.tr('Please update scenario'))
                self.disable_busy_cursor()
                return False

            directory = self.output_directory.text()
            if self.scenario_directory_radio.isChecked():
                directory = self.source_directory.text()

            output_directory = os.path.join(directory, group_name)
            if not os.path.exists(output_directory):
                os.makedirs(output_directory)

            # If impact function parameters loaded successfully, initiate IF.
            impact_function = ImpactFunction()
            impact_function.datastore = Folder(output_directory)
            impact_function.datastore.default_vector_format = "geojson"
            impact_function.hazard = parameters[layer_purpose_hazard['key']]
            impact_function.exposure = (
                parameters[layer_purpose_exposure['key']])
            if parameters[layer_purpose_aggregation['key']]:
                impact_function.aggregation = (
                    parameters[layer_purpose_aggregation['key']])
            elif parameters['extent']:
                impact_function.requested_extent = parameters['extent']
                impact_function.crs = parameters['crs']
            prepare_status, prepare_message = impact_function.prepare()
            if prepare_status == PREPARE_SUCCESS:
                LOGGER.info('Impact function ready')
                status, message = impact_function.run()
                if status == ANALYSIS_SUCCESS:
                    status_item.setText(self.tr('Analysis Success'))
                    impact_layer = impact_function.impact
                    if impact_layer.isValid():
                        layer_list = [
                            impact_layer,
                            impact_function.analysis_impacted,
                            parameters[layer_purpose_hazard['key']],
                            parameters[layer_purpose_exposure['key']],
                            parameters[layer_purpose_aggregation['key']]]
                        QgsProject.instance().addMapLayers(
                            layer_list, False)
                        for layer in layer_list:
                            self.layer_group.addLayer(layer)
                        map_canvas = QgsProject.instance().mapLayers()
                        for layer in map_canvas:
                            # turn of layer visibility if not impact layer
                            if map_canvas[layer].id() == impact_layer.id():
                                self.set_layer_visible(
                                    map_canvas[layer], True)
                            else:
                                self.set_layer_visible(
                                    map_canvas[layer], False)

                        # we need to set analysis_impacted as an active layer
                        # because we need to get all qgis variables that we
                        # need from this layer for infographic.
                        if self.iface:
                            self.iface.setActiveLayer(
                                impact_function.analysis_impacted)

                        report_directory = os.path.join(
                            output_directory, 'output')

                        # generate map report and impact report
                        try:
                            error_code, message = (
                                impact_function.generate_report(
                                    all_default_report_components,
                                    report_directory))

                        except BaseException:
                            status_item.setText(
                                self.tr('Report failed to generate.'))
                    else:
                        LOGGER.info('Impact layer is invalid')

                elif status == ANALYSIS_FAILED_BAD_INPUT:
                    LOGGER.info('Bad input detected')

                elif status == ANALYSIS_FAILED_BAD_CODE:
                    LOGGER.info(
                        'Impact function encountered a bug: %s' % message)

            else:
                LOGGER.warning('Impact function not ready')
                send_error_message(self, prepare_message)

        else:
            LOGGER.exception('Data type not supported: "%s"' % value)
            result = False

        self.disable_busy_cursor()
        return result
Ejemplo n.º 13
0
class MultiBufferDialog(QtGui.QDialog, FORM_CLASS):
    """Dialog implementation class for the InaSAFE multi buffer tool."""

    def __init__(self, parent=None, iface=None, dock_widget=None):
        """Constructor for the multi buffer dialog.

        :param parent: Parent widget of this dialog.
        :type parent: QWidget
        """
        QtGui.QDialog.__init__(self, parent)
        self.setupUi(self)
        self.setWindowTitle(self.tr('InaSAFE Multi Buffer Tool'))
        self.parent = parent
        self.iface = iface
        self.dock_widget = dock_widget
        self.keyword_wizard = None

        # output file properties initialisation
        self.data_store = None
        self.output_directory = None
        self.output_filename = None
        self.output_extension = None
        self.output_layer = None
        self.classification = []

        # set icon
        self.add_class_button.setIcon(
            QIcon(resources_path('img', 'icons', 'add.svg')))
        self.remove_class_button.setIcon(
            QIcon(resources_path('img', 'icons', 'remove.svg')))

        # prepare dialog initialisation
        self.layer.setFilters(QgsMapLayerProxyModel.VectorLayer)
        self.directory_button_status()
        self.add_class_button_status()
        self.ok_button_status()
        self.output_form.setPlaceholderText(
            self.tr('[Create a temporary layer]'))
        self.keyword_wizard_checkbox.setChecked(True)

        # set signal
        self.layer.layerChanged.connect(self.directory_button_status)
        self.layer.layerChanged.connect(self.ok_button_status)
        self.output_form.textChanged.connect(self.ok_button_status)
        self.directory_button.clicked.connect(
            self.on_directory_button_tool_clicked)
        self.radius_form.valueChanged.connect(self.add_class_button_status)
        self.class_form.textChanged.connect(self.add_class_button_status)
        self.add_class_button.clicked.connect(
            self.populate_hazard_classification)
        self.add_class_button.clicked.connect(self.ok_button_status)
        self.remove_class_button.clicked.connect(
            self.remove_selected_classification)
        self.remove_class_button.clicked.connect(self.ok_button_status)

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

        # Fix for issue 1699 - cancel button does nothing
        cancel_button = self.button_box.button(QtGui.QDialogButtonBox.Cancel)
        cancel_button.clicked.connect(self.reject)
        # Fix ends
        ok_button = self.button_box.button(QtGui.QDialogButtonBox.Ok)
        ok_button.clicked.connect(self.accept)

    def accept(self):
        """Process the layer for multi buffering and generate a new layer.

        .. note:: This is called on OK click.
        """
        # set parameter from dialog
        input_layer = self.layer.currentLayer()
        output_path = self.output_form.text()
        radius = self.get_classification()
        # monkey patch keywords so layer works on multi buffering function
        input_layer.keywords = {'inasafe_fields': {}}

        # run multi buffering
        self.output_layer = multi_buffering(input_layer, radius)

        # save output layer to data store and check whether user
        # provide the output path.
        if output_path:
            self.output_directory, self.output_filename = (
                os.path.split(output_path))
            self.output_filename, self.output_extension = (
                os.path.splitext(self.output_filename))

        # if user do not provide the output path, create a temporary file.
        else:
            self.output_directory = temp_dir(sub_dir='work')
            self.output_filename = (
                unique_filename(
                    prefix='hazard_layer',
                    suffix='.geojson',
                    dir=self.output_directory))
            self.output_filename = os.path.split(self.output_filename)[1]
            self.output_filename, self.output_extension = (
                os.path.splitext(self.output_filename))

        self.data_store = Folder(self.output_directory)
        if self.output_extension == '.shp':
            self.data_store.default_vector_format = 'shp'
        elif self.output_extension == '.geojson':
            self.data_store.default_vector_format = 'geojson'
        self.data_store.add_layer(self.output_layer, self.output_filename)

        # add output layer to map canvas
        self.output_layer = self.data_store.layer(self.output_filename)

        QgsMapLayerRegistry.instance().addMapLayers(
            [self.output_layer])
        self.iface.setActiveLayer(self.output_layer)
        self.iface.zoomToActiveLayer()
        self.done(QtGui.QDialog.Accepted)

        if self.keyword_wizard_checkbox.isChecked():
            self.launch_keyword_wizard()

    @pyqtSignature('')  # prevents actions being handled twice
    def on_directory_button_tool_clicked(self):
        """Autoconnect slot activated when directory button is clicked."""
        # noinspection PyCallByClass,PyTypeChecker
        # set up parameter from dialog
        input_path = self.layer.currentLayer().source()
        input_directory, self.output_filename = os.path.split(input_path)
        file_extension = os.path.splitext(self.output_filename)[1]
        self.output_filename = os.path.splitext(self.output_filename)[0]
        # show Qt file directory dialog
        output_path = QFileDialog.getSaveFileName(
            self,
            self.tr('Output file'),
            '%s_multi_buffer%s' % (
                os.path.join(input_directory, self.output_filename),
                file_extension),
            'GeoJSON (*.geojson);;Shapefile (*.shp)')
        # set selected path to the dialog
        self.output_form.setText(output_path)

    def get_output_from_input(self):
        """Populate output form with default output path based on input layer.
        """
        input_path = self.layer.currentLayer().source()
        output_path = (
            os.path.splitext(input_path)[0] + '_multi_buffer' +
            os.path.splitext(input_path)[1])
        self.output_form.setText(output_path)

    def populate_hazard_classification(self):
        """Populate hazard classification on hazard class form."""
        new_class = {
            'value': self.radius_form.value(),
            'name': self.class_form.text()}
        self.classification.append(new_class)
        self.classification = sorted(
            self.classification, key=itemgetter('value'))

        self.hazard_class_form.clear()
        for item in self.classification:
            new_item = '{value} - {name}'.format(
                value=item['value'], name=item['name'])
            self.hazard_class_form.addItem(new_item)

        self.radius_form.setValue(0)
        self.class_form.clear()
        self.ok_button_status()

    def remove_selected_classification(self):
        """Remove selected item on hazard class form."""
        removed_classes = self.hazard_class_form.selectedItems()
        current_item = self.hazard_class_form.currentItem()
        removed_index = self.hazard_class_form.indexFromItem(current_item)
        del self.classification[removed_index.row()]
        for item in removed_classes:
            self.hazard_class_form.takeItem(
                self.hazard_class_form.row(item))

    def get_classification(self):
        """Get all hazard class created by user.

        :return: Hazard class definition created by user.
        :rtype: OrderedDict
        """
        classification_dictionary = {}
        for item in self.classification:
            classification_dictionary[item['value']] = item['name']

        classification_dictionary = OrderedDict(
            sorted(classification_dictionary.items()))

        return classification_dictionary

    def directory_button_status(self):
        """Function to enable or disable directory button."""
        if self.layer.currentLayer():
            self.directory_button.setEnabled(True)
        else:
            self.directory_button.setEnabled(False)

    def add_class_button_status(self):
        """Function to enable or disable add class button."""
        if self.class_form.text() and self.radius_form >= 0:
            self.add_class_button.setEnabled(True)
        else:
            self.add_class_button.setEnabled(False)

    def ok_button_status(self):
        """Function to enable or disable OK button."""
        if not self.layer.currentLayer():
            self.button_box.button(QtGui.QDialogButtonBox.Ok).setEnabled(False)
        elif (self.hazard_class_form.count() > 0 and
                self.layer.currentLayer().name() and
                    len(self.output_form.text()) >= 0):
            self.button_box.button(QtGui.QDialogButtonBox.Ok).setEnabled(True)
        else:
            self.button_box.button(QtGui.QDialogButtonBox.Ok).setEnabled(False)

    @pyqtSlot()
    @pyqtSignature('bool')  # prevents actions being handled twice
    def help_toggled(self, flag):
        """Show or hide the help tab in the stacked widget.

        :param flag: Flag indicating whether help should be shown or hidden.
        :type flag: bool
        """
        if flag:
            self.help_button.setText(self.tr('Hide Help'))
            self.show_help()
        else:
            self.help_button.setText(self.tr('Show Help'))
            self.hide_help()

    def hide_help(self):
        """Hide the usage info from the user."""
        self.main_stacked_widget.setCurrentIndex(1)

    def show_help(self):
        """Show usage info to the user."""
        # Read the header and footer html snippets
        self.main_stacked_widget.setCurrentIndex(0)
        header = html_header()
        footer = html_footer()

        string = header

        message = multi_buffer_help()

        string += message.to_html()
        string += footer

        self.help_web_view.setHtml(string)

    def launch_keyword_wizard(self):
        """Launch keyword creation wizard."""
        # make sure selected layer is the output layer
        if self.iface.activeLayer() != self.output_layer:
            return

        # launch wizard dialog
        self.keyword_wizard = WizardDialog(
            self.iface.mainWindow(),
            self.iface,
            self.dock_widget)
        self.keyword_wizard.set_keywords_creation_mode(self.output_layer)
        self.keyword_wizard.exec_()  # modal
Ejemplo n.º 14
0
    def test_folder_datastore(self):
        """Test if we can store shapefiles."""
        path = QDir(mkdtemp())
        data_store = Folder(path)
        self.assertTrue(data_store.is_writable())

        path = mkdtemp()
        data_store = Folder(path)

        # We do not have any layer yet.
        self.assertEqual(len(data_store.layers()), 0)

        # Let's add a vector layer.
        layer = load_test_vector_layer('hazard',
                                       'flood_multipart_polygons.shp')
        vector_layer_name = 'flood_test'

        result = data_store.add_layer(layer, vector_layer_name, True)
        self.assertTrue(result[0])
        self.assertEqual(result[1], vector_layer_name)

        # We try to add the layer twice with the same name.
        result = data_store.add_layer(layer, vector_layer_name)
        self.assertFalse(result[0])

        # We have imported one layer.
        self.assertEqual(len(data_store.layers()), 1)

        # Check if we have the correct URI.
        # self.assertIsNone(data_store.layer_uri(layer_name))
        expected = str(
            normcase(normpath(join(path, vector_layer_name + '.shp'))))
        self.assertEqual(
            normcase(normpath(data_store.layer_uri(vector_layer_name))),
            expected)

        # The style must be there
        expected = unicode(
            normcase(normpath(join(path, vector_layer_name + '.qml'))))
        self.assertTrue(exists(expected))
        self.assertTrue(isfile(expected))

        # This layer do not exist
        self.assertIsNone(data_store.layer_uri('fake_layer'))

        # Let's add a raster layer.
        layer = load_test_raster_layer('hazard', 'classified_hazard.tif')
        result = data_store.add_layer(layer, vector_layer_name)
        self.assertFalse(result[0])

        raster_layer_name = 'flood_raster'
        result = data_store.add_layer(layer, raster_layer_name, False)
        self.assertTrue(result[0])

        # The style must not be there
        expected = unicode(
            normcase(normpath(join(path, raster_layer_name + '.qml'))))
        self.assertFalse(exists(expected))
        self.assertFalse(isfile(expected))

        # The datastore should have two layers.
        self.assertEqual(len(data_store.layers()), 2)

        # Check the URI for the raster layer.
        expected = normpath(normpath(join(path, raster_layer_name)))
        self.assertEqual(
            normcase(normpath(data_store.layer_uri(raster_layer_name))),
            expected + '.tif')

        # Check keywords files
        data_store.uri.setNameFilters(['*.xml'])
        files = data_store.uri.entryList()
        data_store.uri.setNameFilters([])
        self.assertIn(raster_layer_name + '.xml', files)
        self.assertIn(vector_layer_name + '.xml', files)

        # Test layer without geometry
        layer = load_test_vector_layer('gisv4', 'impacts',
                                       'exposure_summary_table.csv')
        tabular_layer_name = 'breakdown'
        result = data_store.add_layer(layer, tabular_layer_name)
        self.assertTrue(result[0])

        self.assertIsNotNone(
            data_store.layer_keyword('layer_purpose', 'hazard'))
Ejemplo n.º 15
0
    def test_folder_datastore(self):
        """Test if we can store shapefiles."""
        path = QDir(mkdtemp())
        data_store = Folder(path)
        self.assertTrue(data_store.is_writable())

        path = mkdtemp()
        data_store = Folder(path)

        # We do not have any layer yet.
        self.assertEqual(len(data_store.layers()), 0)

        # Let's add a vector layer.
        layer = load_test_vector_layer(
            'hazard', 'flood_multipart_polygons.shp')
        vector_layer_name = 'flood_test'

        result = data_store.add_layer(layer, vector_layer_name)
        self.assertTrue(result[0])
        self.assertEqual(result[1], vector_layer_name)

        # We try to add the layer twice with the same name.
        result = data_store.add_layer(layer, vector_layer_name)
        self.assertFalse(result[0])

        # We have imported one layer.
        self.assertEqual(len(data_store.layers()), 1)

        # Check if we have the correct URI.
        # self.assertIsNone(data_store.layer_uri(layer_name))
        expected = unicode(
            normcase(normpath(join(path, vector_layer_name + '.shp'))))
        self.assertEquals(
            normcase(normpath(
                data_store.layer_uri(vector_layer_name))), expected)

        # This layer do not exist
        self.assertIsNone(data_store.layer_uri('fake_layer'))

        # Let's add a raster layer.
        layer = load_test_raster_layer('hazard', 'classified_hazard.tif')
        result = data_store.add_layer(layer, vector_layer_name)
        self.assertFalse(result[0])

        raster_layer_name = 'flood_raster'
        result = data_store.add_layer(layer, raster_layer_name)
        self.assertTrue(result[0])

        # The datastore should have two layers.
        self.assertEqual(len(data_store.layers()), 2)

        # Check the URI for the raster layer.
        expected = normpath(normpath(join(path, raster_layer_name)))
        self.assertEqual(
            normcase(normpath(data_store.layer_uri(raster_layer_name))),
            expected + '.tif')

        # Check keywords files
        data_store.uri.setNameFilters('*.xml')
        files = data_store.uri.entryList()
        data_store.uri.setNameFilters('')
        self.assertIn(raster_layer_name + '.xml', files)
        self.assertIn(vector_layer_name + '.xml', files)

        # Test layer without geometry
        layer = load_test_vector_layer(
            'gisv4', 'impacts', 'exposure_summary_table.csv')
        tabular_layer_name = 'breakdown'
        result = data_store.add_layer(layer, tabular_layer_name)
        self.assertTrue(result[0])

        self.assertIsNotNone(
            data_store.layer_keyword('layer_purpose', 'hazard')
        )
Ejemplo n.º 16
0
def rasterize_vector_layer(layer, width, height, extent):
    """Rasterize a vector layer to the grid given by extent and width/height.

    :param layer: The vector layer.
    :type layer: QgsVectorLayer

    :param width: The width of the output.
    :type width: int

    :param height: The height of the output.
    :type height: int

    :param extent: The extent to use.
    :type extent: QgsRectangle

    :return: The new raster layer.
    :rtype: QgsRasterLayer
    """
    name = rasterize_steps['gdal_layer_name']
    output_filename = unique_filename(prefix=name, suffix='.tif')

    extent_str = '%f,%f,%f,%f' % (
        extent.xMinimum(),
        extent.xMaximum(),
        extent.yMinimum(),
        extent.yMaximum())

    keywords = dict(layer.keywords)

    # The layer is in memory, we need to save it to a file for Processing.
    data_store = Folder(mkdtemp())
    data_store.default_vector_format = 'geojson'
    result = data_store.add_layer(layer, 'vector_layer')
    layer = data_store.layer(result[1])
    assert layer.isValid()

    field = layer.keywords['inasafe_fields'][aggregation_id_field['key']]

    # ET 21/02/17. I got some issues using rasterize algorithm from Processing.
    # I keep it in case of we need it later. Let's use gdal command line.
    use_gdal_command_line = True

    if use_gdal_command_line:
        startupinfo = None
        if sys.platform == 'win32':
            # On windows, we don't want to display the bash shell.
            # https://github.com/inasafe/inasafe/issues/3980
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        commands = [which('gdal_rasterize')[0]]
        commands += ['-a', field]
        commands += ['-ts', str(width), str(height)]
        commands += ['-ot', 'Int16']
        commands += ['-a_nodata', "'-1'"]
        commands += [layer.source(), output_filename]

        LOGGER.info(' '.join(commands))
        result = subprocess.check_call(commands, startupinfo=startupinfo)
        LOGGER.info('Result : %s' % result)
    else:
        parameters = dict()
        parameters['INPUT'] = layer
        parameters['FIELD'] = field
        parameters['DIMENSIONS'] = 0  # output size is given in pixels
        parameters['WIDTH'] = width
        parameters['HEIGHT'] = height
        parameters['RASTER_EXT'] = extent_str
        parameters['TFW'] = False  # force generation of ESRI TFW
        parameters['RTYPE'] = 1  # raster type: Int16
        parameters['NO_DATA'] = '-1'   # nodata value
        parameters['COMPRESS'] = 4  # GeoTIFF compression: DEFLATE
        parameters['JPEGCOMPRESSION'] = 75  # JPEG compression level: 75
        parameters['ZLEVEL'] = 6  # DEFLATE compression level
        parameters['PREDICTOR'] = 1  # predictor for JPEG/DEFLATE
        parameters['TILED'] = False  # Tiled GeoTIFF?
        parameters['BIGTIFF'] = 0  # whether to make big TIFF
        parameters['EXTRA'] = ''  # additional creation parameters
        parameters['OUTPUT'] = output_filename

        result = runalg('gdalogr:rasterize', parameters)
        if result is None:
            # Let's try be removing a new parameter added between 2.14 and 2.16
            del parameters['RASTER_EXT']

        result = runalg('gdalogr:rasterize', parameters)
        assert result is not None

    layer_aligned = QgsRasterLayer(output_filename, name, 'gdal')
    assert layer_aligned.isValid()

    layer_aligned.keywords = keywords
    layer_aligned.keywords['title'] = (
        rasterize_steps['output_layer_name'] % 'aggregation')
    layer_aligned.keywords['layer_purpose'] = (
        layer_purpose_aggregation_summary['key'])
    del layer_aligned.keywords['inasafe_fields']

    check_layer(layer_aligned)
    return layer_aligned