Пример #1
0
 def setUp(self):
     self.keyword_io = KeywordIO()
     uri = QgsDataSourceURI()
     uri.setDatabase(os.path.join(TESTDATA, 'jk.sqlite'))
     uri.setDataSource('', 'osm_buildings', 'Geometry')
     self.sqlite_layer = QgsVectorLayer(
         uri.uri(), 'OSM Buildings', 'spatialite')
     hazard_path = os.path.join(HAZDATA, 'Shakemap_Padang_2009.asc')
     self.raster_layer, layer_type = load_layer(
         hazard_path, directory=None)
     del layer_type
     self.vector_layer, layer_type = load_layer('Padang_WGS84.shp')
     del layer_type
     self.expected_sqlite_keywords = {
         'category': 'exposure',
         'datatype': 'OSM',
         'subcategory': 'building'}
     self.expected_vector_keywords = {
         'category': 'exposure',
         'datatype': 'itb',
         'subcategory': 'structure',
         'title': 'Padang WGS84'}
     self.expected_raster_keywords = {
         'category': 'hazard',
         'source': 'USGS',
         'subcategory': 'earthquake',
         'unit': 'MMI',
         'title': ('An earthquake in Padang '
                   'like in 2009')}
Пример #2
0
    def __init__(self, iface, dock=None, parent=None):
        """Constructor for the dialog.

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

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

        :param dock: Optional dock widget instance that we can notify of
            changes to the keywords.
        :type dock: Dock
        """

        QtGui.QDialog.__init__(self, parent)
        self.setupUi(self)
        self.setWindowTitle(self.tr('InaSAFE %s Options' % get_version()))
        # Save reference to the QGIS interface and parent
        self.iface = iface
        self.parent = parent
        self.dock = dock
        self.helpDialog = None
        self.keywordIO = KeywordIO()
        # Set up things for context help
        myButton = self.buttonBox.button(QtGui.QDialogButtonBox.Help)
        QtCore.QObject.connect(myButton, QtCore.SIGNAL('clicked()'),
                               self.show_help)
        self.grpNotImplemented.hide()
        self.adjustSize()
        self.restore_state()
        # hack prevent showing use thread visible and set it false see #557
        self.cbxUseThread.setChecked(True)
        self.cbxUseThread.setVisible(False)
Пример #3
0
    def show_keywords_editor(self):
        """Show the keywords editor."""
        # import here only so that it is AFTER i18n set up
        from safe_qgis.tools.keywords_dialog import KeywordsDialog

        # Next block is a fix for #776
        if self.iface.activeLayer() is None:
            return

        try:
            keyword_io = KeywordIO()
            keyword_io.read_keywords(self.iface.activeLayer())
        except UnsupportedProviderError:
            # noinspection PyUnresolvedReferences,PyCallByClass
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(
                None, self.tr('Unsupported layer type'),
                self.tr('The layer you have selected cannot be used for '
                        'analysis because its data type is unsupported.'))
            return
        # End of fix for #776
        # Fix for #793
        except NoKeywordsFoundError:
            # we will create them from scratch in the dialog
            pass
        # End of fix for #793
        # Fix for filtered-layer
        except InvalidParameterError, e:
            # noinspection PyTypeChecker,PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, self.tr('Invalid Layer'), e.message)
            return
Пример #4
0
    def setUp(self):
        """Fixture run before all tests"""

        self.maxDiff = None  # show full diff for assert errors

        os.environ['LANG'] = 'en'
        DOCK.show_only_visible_layers_flag = True
        load_standard_layers()
        DOCK.cboHazard.setCurrentIndex(0)
        DOCK.cboExposure.setCurrentIndex(0)
        DOCK.cboFunction.setCurrentIndex(0)
        DOCK.run_in_thread_flag = False
        DOCK.show_only_visible_layers_flag = False
        DOCK.set_layer_from_title_flag = False
        DOCK.zoom_to_impact_flag = False
        DOCK.hide_exposure_flag = False
        DOCK.show_intermediate_layers = False
        set_jakarta_extent()

        self._keywordIO = KeywordIO()
        self._defaults = breakdown_defaults()

        # Set extent as Jakarta extent
        geo_crs = QgsCoordinateReferenceSystem()
        geo_crs.createFromSrid(4326)
        self.extent = extent_to_geo_array(CANVAS.extent(), geo_crs)
Пример #5
0
    def __init__(self, iface):
        """Constructor for the Map class.

        :param iface: Reference to the QGIS iface object.
        :type iface: QgsAppInterface
        """
        LOGGER.debug('InaSAFE Map class initialised')
        self.iface = iface
        self.layer = iface.activeLayer()
        self.keyword_io = KeywordIO()
        self.printer = None
        self.composition = None
        self.legend = None
        self.logo = ':/plugins/inasafe/bnpb_logo.png'
        self.template = ':/plugins/inasafe/inasafe.qpt'
        #self.page_width = 210  # width in mm
        #self.page_height = 297  # height in mm
        self.page_width = 0  # width in mm
        self.page_height = 0  # height in mm
        self.page_dpi = 300.0
        #self.page_margin = 10  # margin in mm
        self.show_frames = False  # intended for debugging use only
        self.page_margin = None
        #vertical spacing between elements
        self.vertical_spacing = None
        self.map_height = None
        self.mapWidth = None
Пример #6
0
 def test_printImpactTable(self):
     """Test that we can render html from impact table keywords."""
     LOGGER.debug('InaSAFE HtmlRenderer testing printImpactTable')
     myFilename = 'test_floodimpact.tif'
     myLayer, _ = load_layer(myFilename)
     myMessage = 'Layer is not valid: %s' % myFilename
     assert myLayer.isValid(), myMessage
     myPageDpi = 300
     myHtmlRenderer = HtmlRenderer(myPageDpi)
     myPath = unique_filename(prefix='impactTable',
                              suffix='.pdf',
                              dir=temp_dir('test'))
     myKeywordIO = KeywordIO()
     myKeywords = myKeywordIO.read_keywords(myLayer)
     myPath = myHtmlRenderer.print_impact_table(myKeywords,
                                              filename=myPath)
     myMessage = 'Rendered output does not exist: %s' % myPath
     assert os.path.exists(myPath), myMessage
     # pdf rendering is non deterministic so we can't do a hash check
     # test_renderComposition renders just the image instead of pdf
     # so we hash check there and here we just do a basic minimum file
     # size check.
     mySize = os.stat(myPath).st_size
     myExpectedSizes = [20936,  # as rendered on linux ub 12.04 64
                        21523,  # as rendered on linux ub 12.10 64
                        20605,  # as rendered on linux ub 13.04 64
                        21527,  # as rendered on Jenkins post 22 June 2013
                        377191,  # as rendered on OSX
                        252699L,  # as rendered on Windows 7 64 bit
                        251782L,  # as rendered on Windows 8 64 bit amd
                        21491,  # as rendered on Slackware64 14.0
                        ]
     print 'Output pdf to %s' % myPath
     self.assertIn(mySize, myExpectedSizes)
Пример #7
0
    def show_keywords_editor(self):
        """Show the keywords editor."""
        # import here only so that it is AFTER i18n set up
        from safe_qgis.tools.keywords_dialog import KeywordsDialog

        # Next block is a fix for #776
        if self.iface.activeLayer() is None:
            return

        try:
            keyword_io = KeywordIO()
            keyword_io.read_keywords(self.iface.activeLayer())
        except UnsupportedProviderError:
            # noinspection PyUnresolvedReferences,PyCallByClass
            QMessageBox.warning(
                None, self.tr('Unsupported layer type'),
                self.tr('The layer you have selected cannot be used for '
                        'analysis because its data type is unsupported.'))
            return
        # End of fix for #776
        # Fix for #793
        except NoKeywordsFoundError:
            # we will create them from scratch in the dialog
            pass
        # End of fix for #793

        dialog = KeywordsDialog(self.iface.mainWindow(), self.iface,
                                self.dock_widget)
        dialog.exec_()  # modal
Пример #8
0
    def __init__(self, iface, dock):
        """Constructor for the class."""
        QDialog.__init__(self)
        # Class Member
        self.iface = iface
        self.dock = dock
        self.output_directory = None
        self.exposure_layer = None
        self.hazard_layer = None
        self.aggregation_layer = None
        self.function_id = None
        self.keyword_io = KeywordIO()

        # Calling some init methods
        self.restore_state()
Пример #9
0
    def __init__(self, layer, name=None):
        """Create the wrapper

        :param layer:       Qgis layer
        :type layer:        QgsMapLayer

        :param name:        A layer's name
        :type name:         Basestring or None
        """

        self.data = layer
        self.keyword_io = KeywordIO()
        self.keywords = self.keyword_io.read_keywords(layer)
        if name is None:
            try:
                self.name = self.get_keywords(key='title')
            except KeywordNotFoundError:
                pass
Пример #10
0
    def __init__(self, aggregator):
        """Director for aggregation based operations.

        :param aggregator: Aggregator that will be used in conjunction with
            postprocessors.
        :type aggregator: Aggregator
        """

        super(PostprocessorManager, self).__init__()

        # Aggregation / post processing related items
        self.output = {}
        self.keyword_io = KeywordIO()
        self.error_message = None

        self.aggregator = aggregator
        self.current_output_postprocessor = None
        self.attribute_title = None
Пример #11
0
 def test_print_impact_table(self):
     """Test that we can render html from impact table keywords."""
     LOGGER.debug('InaSAFE HtmlRenderer testing printImpactTable')
     file_name = 'test_floodimpact.tif'
     layer, _ = load_layer(file_name)
     message = 'Layer is not valid: %s' % file_name
     self.assertTrue(layer.isValid(), message)
     page_dpi = 300
     html_renderer = HtmlRenderer(page_dpi)
     path = unique_filename(prefix='impact_table',
                            suffix='.pdf',
                            dir=temp_dir('test'))
     keyword_io = KeywordIO()
     keywords = keyword_io.read_keywords(layer)
     path = html_renderer.print_impact_table(keywords, filename=path)
     message = 'Rendered output does not exist: %s' % path
     self.assertTrue(os.path.exists(path), message)
     # pdf rendering is non deterministic so we can't do a hash check
     # test_renderComposition renders just the image instead of pdf
     # so we hash check there and here we just do a basic minimum file
     # size check.
     size = os.stat(path).st_size
     expected_sizes = [
         20936,  # as rendered on linux ub 12.04 64
         21523,  # as rendered on linux ub 12.10 64
         20605,  # as rendered on linux ub 13.04 64
         13965,  # as rendered on linux ub 13.10 64
         14220,  # as rendered on linux ub 13.04 64 MB
         11085,  # as rendered on linux ub 14.04 64 AG
         17306,  # as rendered on linux ub 14.04_64 TS
         17127,  # as rendered on linux ub 14.04_64 MB
         17295,  # as rendered on linux ub 14.04_64 IS
         18665,  # as rendered on Jenkins per 19 June 2014
         377191,  # as rendered on OSX
         17556,  # as rendered on Windows 7_32
         16163L,  # as rendered on Windows 7 64 bit Ultimate i3
         251782L,  # as rendered on Windows 8 64 bit amd
         21491,  # as rendered on Slackware64 14.0
         18667,  # as rendered on Linux Mint 14_64
     ]
     print 'Output pdf to %s' % path
     self.assertIn(size, expected_sizes)
Пример #12
0
    def __init__(self, iface, dock=None, parent=None):
        """Constructor for the dialog.

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

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

        :param dock: Optional dock widget instance that we can notify of
            changes to the keywords.
        :type dock: Dock
        """

        QtGui.QDialog.__init__(self, parent)
        self.setupUi(self)
        self.setWindowTitle(self.tr('InaSAFE %s Options' % get_version()))
        # Save reference to the QGIS interface and parent
        self.iface = iface
        self.parent = parent
        self.dock = dock
        self.keyword_io = KeywordIO()
        self.defaults = get_defaults()

        # Set up things for context help
        button = self.buttonBox.button(QtGui.QDialogButtonBox.Help)
        button.clicked.connect(self.show_help)
        self.grpNotImplemented.hide()
        self.adjustSize()
        self.restore_state()
        # hack prevent showing use thread visible and set it false see #557
        self.cbxUseThread.setChecked(True)
        self.cbxUseThread.setVisible(False)

        # Set up listener for various UI
        self.custom_org_logo_checkbox.toggled.connect(
            self.set_organisation_logo)
        self.custom_north_arrow_checkbox.toggled.connect(self.set_north_arrow)
        self.custom_templates_dir_checkbox.toggled.connect(
            self.set_templates_dir)
        self.custom_org_disclaimer_checkbox.toggled.connect(
            self.set_org_disclaimer)
Пример #13
0
    def __init__(self, theAggregator):
        """Director for aggregation based operations.
        Args:
          theAggregationLayer: QgsMapLayer representing clipped
              aggregation. This will be converted to a memory layer inside
              this class. see self.aggregator.layer
        Returns:
           not applicable
        Raises:
           no exceptions explicitly raised
        """

        super(PostprocessorManager, self).__init__()

        # Aggregation / post processing related items
        self.postProcessingOutput = {}
        self.keywordIO = KeywordIO()
        self.errorMessage = None

        self.aggregator = theAggregator
Пример #14
0
    def setUp(self):
        """Fixture run before all tests"""

        self.maxDiff = None  # show full diff for assert errors

        os.environ['LANG'] = 'en'
        DOCK.showOnlyVisibleLayersFlag = True
        load_standard_layers()
        DOCK.cboHazard.setCurrentIndex(0)
        DOCK.cboExposure.setCurrentIndex(0)
        DOCK.cboFunction.setCurrentIndex(0)
        DOCK.runInThreadFlag = False
        DOCK.showOnlyVisibleLayersFlag = False
        DOCK.setLayerNameFromTitleFlag = False
        DOCK.zoomToImpactFlag = False
        DOCK.hideExposureFlag = False
        DOCK.showIntermediateLayers = False
        set_jakarta_extent()

        self.keywordIO = KeywordIO()
        self.defaults = defaults()
Пример #15
0
    def setUp(self):
        """Fixture run before all tests"""

        self.maxDiff = None  # show full diff for assert errors

        os.environ['LANG'] = 'en'
        DOCK.show_only_visible_layers_flag = True
        load_standard_layers()
        DOCK.cboHazard.setCurrentIndex(0)
        DOCK.cboExposure.setCurrentIndex(0)
        DOCK.cboFunction.setCurrentIndex(0)
        DOCK.run_in_thread_flag = False
        DOCK.show_only_visible_layers_flag = False
        DOCK.set_layer_from_title_flag = False
        DOCK.zoom_to_impact_flag = False
        DOCK.hide_exposure_flag = False
        DOCK.show_intermediate_layers = False
        set_jakarta_extent()

        self.keywordIO = KeywordIO()
        self.defaults = breakdown_defaults()
Пример #16
0
    def __init__(self, iface):
        """Constructor for the Map class.

        :param iface: Reference to the QGIS iface object.
        :type iface: QgsAppInterface
        """
        LOGGER.debug('InaSAFE Map class initialised')
        self.iface = iface
        self.layer = iface.activeLayer()
        self.keyword_io = KeywordIO()
        self.printer = None
        self.composition = None
        self.extent = iface.mapCanvas().extent()
        self.safe_logo = ':/plugins/inasafe/inasafe-logo-url.svg'
        self.north_arrow = ':/plugins/inasafe/simple_north_arrow.png'
        self.org_logo = ':/plugins/inasafe/supporters.png'
        self.template = ':/plugins/inasafe/inasafe-portrait-a4.qpt'
        self.disclaimer = disclaimer()
        self.page_width = 0  # width in mm
        self.page_height = 0  # height in mm
        self.page_dpi = 300.0
        self.show_frames = False  # intended for debugging use only
Пример #17
0
    def __init__(self,
                 layer,
                 dpi=300,
                 legend_title=None,
                 legend_notes=None,
                 legend_units=None):
        """Constructor for the Map Legend class.

        :param layer: Layer that the legend should be generated for.
        :type layer: QgsMapLayer, QgsVectorLayer

        :param dpi: DPI for the generated legend image. Defaults to 300 if
            not specified.
        :type dpi: int

        :param legend_title: Title for the legend.
        :type legend_title: str

        :param legend_notes: Notes to display under the title.
        :type legend_notes: str

        :param legend_units: Units for the legend.
        :type legend_units: str
        """
        LOGGER.debug('InaSAFE Map class initialised')
        self.legend_image = None
        self.layer = layer
        # how high each row of the legend should be
        self.legend_increment = 42
        self.keyword_io = KeywordIO()
        self.legend_font_size = 8
        self.legend_width = 900
        self.dpi = dpi
        if legend_title is None:
            self.legend_title = self.tr('Legend')
        else:
            self.legend_title = legend_title
        self.legend_notes = legend_notes
        self.legend_units = legend_units
Пример #18
0
    def __init__(self, iface):
        """Constructor for the Map class.

        :param iface: Reference to the QGIS iface object.
        :type iface: QgsAppInterface
        """
        LOGGER.debug('InaSAFE Map class initialised')
        self.iface = iface
        self.layer = iface.activeLayer()
        self.keywordIO = KeywordIO()
        self.printer = None
        self.composition = None
        self.legend = None
        self.pageWidth = 210  # width in mm
        self.pageHeight = 297  # height in mm
        self.pageDpi = 300.0
        self.pageMargin = 10  # margin in mm
        self.verticalSpacing = 1  # vertical spacing between elements
        self.showFramesFlag = False  # intended for debugging use only
        # make a square map where width = height = page width
        self.mapHeight = self.pageWidth - (self.pageMargin * 2)
        self.mapWidth = self.mapHeight
        self.disclaimer = self.tr('InaSAFE has been jointly developed by'
                                  ' BNPB, AusAid & the World Bank')
Пример #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

        if layer is None:
            self.layer = 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')), ('Not Set', self.tr('Not Set'))
        ])

        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)

        # set some initial ui state:
        self.defaults = breakdown_defaults()
        self.pbnAdvanced.setChecked(False)
        self.radPredefined.setChecked(True)
        self.dsbFemaleRatioDefault.blockSignals(True)
        self.dsbFemaleRatioDefault.setValue(self.defaults['FEM_RATIO'])
        self.dsbFemaleRatioDefault.blockSignals(False)

        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.grpAdvanced.setVisible(False)
        self.resize_dialog()
Пример #20
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:
        myMessage = tr('Layer or Extent passed to clip is None.')
        raise InvalidParameterError(myMessage)

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

    #myHandle, myFilename = tempfile.mkstemp('.sqlite', 'clip_',
    #    temp_dir())
    myHandle, myFilename = 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(myHandle)
    os.remove(myFilename)

    # Get the clip extents in the layer's native CRS
    myGeoCrs = QgsCoordinateReferenceSystem()
    myGeoCrs.createFromId(4326, QgsCoordinateReferenceSystem.EpsgCrsId)
    myXForm = QgsCoordinateTransform(myGeoCrs, layer.crs())
    myAllowedClipTypes = [QGis.WKBPolygon, QGis.WKBPolygon25D]
    if type(extent) is list:
        myRect = QgsRectangle(
            extent[0], extent[1],
            extent[2], extent[3])
        # noinspection PyCallByClass
        myClipPolygon = QgsGeometry.fromRect(myRect)
    elif (type(extent) is QgsGeometry and
          extent.wkbType in myAllowedClipTypes):
        myRect = extent.boundingBox().toRectF()
        myClipPolygon = extent
    else:
        raise InvalidClipGeometryError(
            tr(
                'Clip geometry must be an extent or a single part'
                'polygon based geometry.'))

    myProjectedExtent = myXForm.transformBoundingBox(myRect)

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

    # 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
    myAttributes = myProvider.attributeIndexes()
    myFetchGeometryFlag = True
    myUseIntersectFlag = True
    myProvider.select(
        myAttributes,
        myProjectedExtent,
        myFetchGeometryFlag,
        myUseIntersectFlag)

    myFieldList = myProvider.fields()

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

    # Reverse the coordinate xform now so that we can convert
    # geometries from layer crs to geocrs.
    myXForm = QgsCoordinateTransform(layer.crs(), myGeoCrs)
    # Retrieve every feature with its geometry and attributes
    myFeature = QgsFeature()
    myCount = 0
    myHasMultipart = False

    if explode_attribute is not None:
        theExplodeAttributeIndex = myProvider.fieldNameIndex(
            explode_attribute)

    while myProvider.nextFeature(myFeature):
        myGeometry = myFeature.geometry()
        if explode_attribute is not None:
            myAttrs = myFeature.attributeMap()
        # Loop through the parts adding them to the output file
        # we write out single part features unless explode_flag is False
        if explode_flag:
            myGeometryList = explode_multipart_geometry(myGeometry)
        else:
            myGeometryList = [myGeometry]

        for myPartIndex, myPart in enumerate(myGeometryList):
            myPart.transform(myXForm)
            if hard_clip_flag:
                # Remove any dangling bits so only intersecting area is
                # kept.
                myPart = clip_geometry(myClipPolygon, myPart)
            if myPart is None:
                continue

            myFeature.setGeometry(myPart)
            # There are multiple parts and we want to show it in the
            # explode_attribute
            if myPartIndex > 0 and explode_attribute is not None:
                myHasMultipart = True
                myPartAttr = QVariant(
                    '%s #%s' % (myAttrs[theExplodeAttributeIndex].toString(),
                                myPartIndex))
                myFeature.changeAttribute(theExplodeAttributeIndex, myPartAttr)

            myWriter.addFeature(myFeature)
        myCount += 1
    del myWriter  # Flush to disk

    if myCount < 1:
        myMessage = 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(myMessage)

    myKeywordIO = KeywordIO()
    if extra_keywords is None:
        extra_keywords = {}
    extra_keywords['HAD_MULTIPART_POLY'] = myHasMultipart
    myKeywordIO.copy_keywords(
        layer, myFilename, extra_keywords=extra_keywords)
    myBaseName = '%s clipped' % layer.name()
    myLayer = QgsVectorLayer(myFilename, myBaseName, 'ogr')

    return myLayer
Пример #21
0
def _clip_raster_layer(
        theLayer, theExtent, theCellSize=None, theExtraKeywords=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 theLayer: A valid QGIS raster layer in EPSG:4326
    :type theLayer: QgsRasterLayer

    :param theExtent:  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 theExtent: list(float), QgsGeometry

    :param theCellSize: 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 theCellSize: 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 theLayer or not theExtent:
        myMessage = tr('Layer or Extent passed to clip is None.')
        raise InvalidParameterError(myMessage)

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

    myWorkingLayer = str(theLayer.source())

    # Check for existence of keywords file
    myKeywordsPath = myWorkingLayer[:-4] + '.keywords'
    myMessage = tr(
        'Input file to be clipped "%s" does not have the '
        'expected keywords file %s' % (
            myWorkingLayer, myKeywordsPath))
    verify(os.path.isfile(myKeywordsPath), myMessage)

    # 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.
    myKeywords = readKeywordsFromFile(myKeywordsPath)
    if 'datatype' in myKeywords and myKeywords['datatype'] == 'density':
        if str(theLayer.crs().authid()) != 'EPSG:4326':

            # This layer is not WGS84 geographic
            myMessage = ('Layer %s represents density but has spatial '
                         'reference "%s". Density 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'
                         % (myWorkingLayer, theLayer.crs().toProj4()))
            raise InvalidProjectionError(myMessage)

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

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

    # If no cell size is specified, we need to run gdalwarp without
    # specifying the output pixel size to ensure the raster dims
    # remain consistent.
    myBinaryList = which('gdalwarp')
    LOGGER.debug('Path for gdalwarp: %s' % myBinaryList)
    if len(myBinaryList) < 1:
        raise CallGDALError(
            tr('gdalwarp could not be found on your computer'))
    # Use the first matching gdalwarp found
    myBinary = myBinaryList[0]
    if theCellSize is None:
        myCommand = ('%s -q -t_srs EPSG:4326 -r near '
                     '-cutline %s -crop_to_cutline -of GTiff '
                     '"%s" "%s"' % (myBinary,
                                    myClipKml,
                                    myWorkingLayer,
                                    myFilename))
    else:
        myCommand = ('%s -q -t_srs EPSG:4326 -r near -tr %f %f '
                     '-cutline %s -crop_to_cutline -of GTiff '
                     '"%s" "%s"' % (myBinary,
                                    theCellSize,
                                    theCellSize,
                                    myClipKml,
                                    myWorkingLayer,
                                    myFilename))

    LOGGER.debug(myCommand)
    myResult = QProcess().execute(myCommand)

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

    # .. todo:: Check the result of the shell call is ok
    myKeywordIO = KeywordIO()
    myKeywordIO.copy_keywords(
        theLayer, myFilename, extra_keywords=theExtraKeywords)
    myBaseName = '%s clipped' % theLayer.name()
    myLayer = QgsRasterLayer(myFilename, myBaseName)

    return myLayer
Пример #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 = ':/plugins/inasafe/merged_report.qpt'

        # Safe Logo Path
        self.safe_logo_path = ':/plugins/inasafe/inasafe-logo-url.png'

        # Organisation Logo Path
        self.organisation_logo_path = ':/plugins/inasafe/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()
Пример #23
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 = metadata.global_default_attribute['name']
        self.do_not_use_string = metadata.do_not_use_attribute['name']
        self.global_default_data = metadata.global_default_attribute['id']
        self.do_not_use_data = metadata.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
Пример #24
0
    def test_keywords_creation_wizard(self):
        """Test how the widgets work."""
        expected_category_count = 3
        expected_categories = ['exposure', 'hazard', 'aggregation']
        chosen_category = 'hazard'

        expected_subcategory_count = 4
        expected_subcategories = ['volcano', 'earthquake', 'flood', 'tsunami']
        chosen_subcategory = "tsunami"

        expected_unit_count = 3
        expected_units = ['wetdry', 'metres_depth', 'feet_depth']
        expected_chosen_unit = 'feet_depth'

        expected_field_count = 5
        expected_fields = [
            'OBJECTID', 'GRIDCODE', 'Shape_Leng', 'Shape_Area', 'Category'
        ]
        expected_chosen_field = 'GRIDCODE'

        expected_keywords = {
            'category': 'hazard',
            'subcategory': 'tsunami',
            'unit': 'feet_depth',
            'field': 'GRIDCODE',
            'source': 'some source',
            'title': 'some title'
        }

        layer = clone_shp_layer(name='tsunami_polygon')

        # check the environment first
        message = 'Test layer is not readable. Check environment variables.'
        self.assertIsNotNone(layer.dataProvider(), message)

        # Initialize dialog
        # noinspection PyTypeChecker
        dialog = WizardDialog(layer=layer)

        # step 1 of 7 - select category
        count = dialog.lstCategories.count()
        message = ('Invalid category count! There should be %d while there '
                   'were: %d') % (expected_category_count, count)
        self.assertEqual(count, expected_category_count, message)

        # Get all the categories given by wizards and save the 'hazard' index
        categories = []
        hazard_index = -1
        for i in range(expected_category_count):
            category_name = eval(
                dialog.lstCategories.item(i).data(Qt.UserRole))['id']
            categories.append(category_name)
            if category_name == chosen_category:
                hazard_index = i
        # Check if categories is the same with expected_categories
        message = 'Invalid categories! It should be "%s" while it was %s' % (
            expected_categories, categories)
        self.assertEqual(set(categories), set(expected_categories), message)
        # Check if the Next button state is on the right state
        message = ('Invalid Next button state in step 1! Enabled while '
                   'there\'s nothing selected yet')
        self.assertTrue(not dialog.pbnNext.isEnabled(), message)
        # Select hazard one
        dialog.lstCategories.setCurrentRow(hazard_index)
        message = ('Invalid Next button state in step 1! Still disabled after '
                   'an item selected')
        self.assertTrue(dialog.pbnNext.isEnabled(), message)
        # Click Next
        dialog.pbnNext.click()

        # step 2 of 7 - select subcategory
        # Check the number of sub categories
        count = dialog.lstSubcategories.count()
        message = ('Invalid subcategory count! There should be %d and there '
                   'were: %d') % (expected_subcategory_count, count)
        self.assertEqual(count, expected_subcategory_count, message)

        # Get all the subcategories given and save the 'tsunami' index
        subcategories = []
        tsunami_index = -1
        for i in range(expected_subcategory_count):
            subcategory_name = eval(
                dialog.lstSubcategories.item(i).data(Qt.UserRole))['id']
            subcategories.append(subcategory_name)
            if subcategory_name == chosen_subcategory:
                tsunami_index = i
        # Check if subcategories is the same with expected_subcategories
        message = ('Invalid sub categories! It should be "%s" while it was '
                   '%s') % (expected_subcategories, subcategories)
        self.assertEqual(set(subcategories), set(expected_subcategories),
                         message)
        # The Next button should be on disabled state first
        self.assertTrue(
            not dialog.pbnNext.isEnabled(), 'Invalid Next button'
            ' state in step 2! Enabled while there\'s nothing selected yet')
        # Set to tsunami subcategories
        dialog.lstSubcategories.setCurrentRow(tsunami_index)
        message = ('Invalid Next button state in step 2! Still disabled after '
                   'an item selected')
        self.assertTrue(dialog.pbnNext.isEnabled(), message)
        # Click next button
        dialog.pbnNext.click()

        # step 3 of 7 - select tsunami units
        # Check if the number of unit for tsunami is 3
        count = dialog.lstUnits.count()
        message = ('Invalid unit count! There should be %d while there were: '
                   '%d') % (expected_unit_count, count)
        self.assertEqual(count, expected_unit_count, message)
        # Get all the units given and save the 'feet_depth' index
        units = []
        feet_unit_index = -1
        for i in range(expected_unit_count):
            unit_name = eval(dialog.lstUnits.item(i).data(Qt.UserRole))['id']
            units.append(unit_name)
            if unit_name == expected_chosen_unit:
                feet_unit_index = i
        # Check if units is the same with expected_units
        message = ('Invalid units! It should be "%s" while it was '
                   '%s') % (expected_units, units)
        self.assertEqual(set(expected_units), set(units), message)
        # The button should be on disabled state first
        message = ('Invalid Next button state in step 3! Enabled while '
                   'there\'s nothing selected yet')
        self.assertTrue(not dialog.pbnNext.isEnabled(), message)
        dialog.lstUnits.setCurrentRow(feet_unit_index)
        message = ('Invalid Next button state in step 3! Enabled while '
                   'there\'s nothing selected yet')
        self.assertTrue(dialog.pbnNext.isEnabled(), message)

        dialog.pbnNext.click()

        # step 4 of 7 - select data field for tsunami feet
        count = dialog.lstFields.count()
        message = ('Invalid field count! There should be %d while there were: '
                   '%d') % (expected_field_count, count)
        self.assertEqual(count, expected_field_count, message)
        # Get all the fields given and save the 'GRIDCODE' index
        fields = []
        gridcode_index = -1
        for i in range(expected_field_count):
            field_name = dialog.lstFields.item(i).text()
            fields.append(field_name)
            if field_name == expected_chosen_field:
                gridcode_index = i
        # Check if fields is the same with expected_fields
        message = ('Invalid fields! It should be "%s" while it was '
                   '%s') % (expected_fields, fields)
        self.assertEqual(set(expected_fields), set(fields), message)
        # The button should be on disabled first
        message = ('Invalid Next button state in step 4! Enabled while '
                   'there\'s nothing selected yet')
        self.assertTrue(not dialog.pbnNext.isEnabled(), message)
        dialog.lstFields.setCurrentRow(gridcode_index)
        message = ('Invalid Next button state in step 4! Still disabled after '
                   'an item selected')
        self.assertTrue(dialog.pbnNext.isEnabled(), message)
        # Click next
        dialog.pbnNext.click()

        # step 6 of 7 - enter source
        message = ('Invalid Next button state in step 6! Disabled while '
                   'source is optional')
        self.assertTrue(dialog.pbnNext.isEnabled(), message)
        dialog.leSource.setText('some source')
        dialog.pbnNext.click()

        # step 7 of 7 - enter title
        dialog.leTitle.setText('some title')
        message = ('Invalid Next button state in step 7! Still disabled '
                   'after a text entered')
        self.assertTrue(dialog.pbnNext.isEnabled(), message)
        dialog.pbnNext.click()

        # test the resulting keywords
        keyword_io = KeywordIO()
        # noinspection PyTypeChecker
        keywords = keyword_io.read_keywords(layer)

        message = 'Invalid metadata!\n Was: %s\n Should be: %s' % (
            unicode(keywords), unicode(expected_keywords))

        self.assertEqual(keywords, expected_keywords, message)