def clip_parameters(self): """Calculate the best extents to use for the assessment. :returns: A dictionary consisting of: * extra_exposure_keywords: dict - any additional keywords that should be written to the exposure layer. For example if rescaling is required for a raster, the original resolution can be added to the keywords file. * adjusted_geo_extent: list - [xmin, ymin, xmax, ymax] - the best extent that can be used given the input datasets and the current viewport extents. * cell_size: float - the cell size that is the best of the hazard and exposure rasters. :rtype: dict, QgsRectangle, float, QgsMapLayer, QgsRectangle, QgsMapLayer :raises: InsufficientOverlapError """ if self._clip_parameters is None: # Get the Hazard extents as an array in EPSG:4326 # noinspection PyTypeChecker hazard_geoextent = extent_to_array(self.hazard.extent(), self.hazard.crs()) # Get the Exposure extents as an array in EPSG:4326 # noinspection PyTypeChecker exposure_geoextent = extent_to_array(self.exposure.extent(), self.exposure.crs()) # Set the analysis extents based on user's desired behaviour settings = QSettings() mode_name = settings.value('inasafe/analysis_extents_mode', 'HazardExposureView') # Default to using canvas extents if no case below matches analysis_geoextent = self.viewport_extent if mode_name == 'HazardExposureView': analysis_geoextent = self.viewport_extent elif mode_name == 'HazardExposure': analysis_geoextent = None elif mode_name == 'HazardExposureBookmark' or \ mode_name == 'HazardExposureBoundingBox': if self.requested_extent is not None \ and self.requested_extent_crs is not None: # User has defined preferred extent, so use that analysis_geoextent = array_to_geo_array( self.requested_extent, self.requested_extent_crs) # Now work out the optimal extent between the two layers and # the current view extent. The optimal extent is the intersection # between the two layers and the viewport. try: # Extent is returned as an array [xmin,ymin,xmax,ymax] # We will convert it to a QgsRectangle afterwards. # If the user has defined a preferred analysis extent it will # always be used, otherwise the data will be clipped to # the viewport unless the user has deselected clip to viewport # in options. geo_extent = get_optimal_extent(hazard_geoextent, exposure_geoextent, analysis_geoextent) except InsufficientOverlapError, e: # noinspection PyTypeChecker message = generate_insufficient_overlap_message( e, exposure_geoextent, self.exposure.qgis_layer(), hazard_geoextent, self.hazard.qgis_layer(), analysis_geoextent) raise InsufficientOverlapError(message) # TODO: move this to its own function # Next work out the ideal spatial resolution for rasters # in the analysis. If layers are not native WGS84, we estimate # this based on the geographic extents # rather than the layers native extents so that we can pass # the ideal WGS84 cell size and extents to the layer prep routines # and do all preprocessing in a single operation. # All this is done in the function getWGS84resolution adjusted_geo_extent = geo_extent cell_size = None extra_exposure_keywords = {} if self.hazard.layer_type() == QgsMapLayer.RasterLayer: # Hazard layer is raster hazard_geo_cell_size, _ = get_wgs84_resolution( self.hazard.qgis_layer()) if self.exposure.layer_type() == QgsMapLayer.RasterLayer: # In case of two raster layers establish common resolution exposure_geo_cell_size, _ = get_wgs84_resolution( self.exposure.qgis_layer()) # See issue #1008 - the flag below is used to indicate # if the user wishes to prevent resampling of exposure data keywords = self.exposure.keywords allow_resampling_flag = True if 'allow_resampling' in keywords: resampling_lower = keywords['allow_resampling'].lower() allow_resampling_flag = resampling_lower == 'true' if hazard_geo_cell_size < exposure_geo_cell_size and \ allow_resampling_flag: cell_size = hazard_geo_cell_size # Adjust the geo extent to coincide with hazard grids # so gdalwarp can do clipping properly adjusted_geo_extent = adjust_clip_extent( geo_extent, get_wgs84_resolution(self.hazard.qgis_layer()), hazard_geoextent) else: cell_size = exposure_geo_cell_size # Adjust extent to coincide with exposure grids # so gdalwarp can do clipping properly adjusted_geo_extent = adjust_clip_extent( geo_extent, get_wgs84_resolution(self.exposure.qgis_layer()), exposure_geoextent) # Record native resolution to allow rescaling of exposure if not numpy.allclose(cell_size, exposure_geo_cell_size): extra_exposure_keywords['resolution'] = \ exposure_geo_cell_size else: if self.exposure.layer_type() != QgsMapLayer.VectorLayer: raise RuntimeError # In here we do not set cell_size so that in # _clip_raster_layer we can perform gdalwarp without # specifying cell size as we still want to have the # original pixel size. # Adjust the geo extent to be at the edge of the pixel in # so gdalwarp can do clipping properly adjusted_geo_extent = adjust_clip_extent( geo_extent, get_wgs84_resolution(self.hazard.qgis_layer()), hazard_geoextent) # If exposure is vector data grow hazard raster layer to # ensure there are enough pixels for points at the edge of # the view port to be interpolated correctly. This requires # resolution to be available adjusted_geo_extent = get_buffered_extent( adjusted_geo_extent, get_wgs84_resolution(self.hazard.qgis_layer())) else: # Hazard layer is vector # In case hazard data is a point data set, we will need to set # the geo_extent to the extent of exposure and the analysis # extent. We check the extent first if the point extent # intersects with geo_extent. if self.hazard.geometry_type() == QGis.Point: user_extent_enabled = (self.requested_extent is not None and self.requested_extent_crs is not None) if user_extent_enabled: # Get intersection between exposure and analysis extent geo_extent = bbox_intersection(exposure_geoextent, analysis_geoextent) # Check if the point is within geo_extent if bbox_intersection(geo_extent, exposure_geoextent) is None: raise InsufficientOverlapError else: geo_extent = exposure_geoextent adjusted_geo_extent = geo_extent if self.exposure.layer_type() == QgsMapLayer.RasterLayer: # Adjust the geo extent to be at the edge of the pixel in # so gdalwarp can do clipping properly adjusted_geo_extent = adjust_clip_extent( geo_extent, get_wgs84_resolution(self.exposure.qgis_layer()), exposure_geoextent) self._clip_parameters = { 'extra_exposure_keywords': extra_exposure_keywords, 'adjusted_geo_extent': adjusted_geo_extent, 'cell_size': cell_size }
def save_scenario(self, scenario_file_path=None): """Save current scenario to a text file. You can use the saved scenario with the batch runner. :param scenario_file_path: A path to the scenario file. :type scenario_file_path: str """ # Validate Input warning_title = self.tr('InaSAFE Save Scenario Warning') is_valid, warning_message = self.validate_input() if not is_valid: # noinspection PyCallByClass,PyTypeChecker,PyArgumentList QtGui.QMessageBox.warning(self, warning_title, warning_message) return # Make extent to look like: # 109.829170982, -8.13333290561, 111.005344795, -7.49226294379 # Added in 2.2 to support user defined analysis extents if self.dock.extent.user_extent is not None \ and self.dock.extent.user_extent_crs is not None: extent = extent_to_array( self.dock.extent.user_extent, self.dock.extent.user_extent_crs) else: extent = viewport_geo_array(self.iface.mapCanvas()) extent_string = ', '.join(('%f' % x) for x in extent) exposure_path = self.exposure_layer.publicSource() hazard_path = self.hazard_layer.publicSource() title = self.keyword_io.read_keywords(self.hazard_layer, 'title') title = self.tr(title) default_filename = title.replace( ' ', '_').replace('(', '').replace(')', '') # Popup a dialog to request the filename if scenario_file_path = None dialog_title = self.tr('Save Scenario') if scenario_file_path is None: # noinspection PyCallByClass,PyTypeChecker scenario_file_path = QFileDialog.getSaveFileName( self, dialog_title, os.path.join(self.output_directory, default_filename + '.txt'), "Text files (*.txt)") if scenario_file_path is None or scenario_file_path == '': return self.output_directory = os.path.dirname(scenario_file_path) # Get relative path for each layer relative_exposure_path = self.relative_path( scenario_file_path, exposure_path) relative_hazard_path = self.relative_path( scenario_file_path, hazard_path) # Write to file parser = ConfigParser() parser.add_section(title) parser.set(title, 'exposure', relative_exposure_path) parser.set(title, 'hazard', relative_hazard_path) parser.set(title, 'function', self.function_id) parser.set(title, 'extent', extent_string) if self.dock.extent.user_extent_crs is None: parser.set(title, 'extent_crs', 'EPSG:4326') else: parser.set( title, 'extent_crs', self.dock.extent.user_extent_crs.authid()) if self.aggregation_layer is not None: aggregation_path = self.aggregation_layer.publicSource() relative_aggregation_path = self.relative_path( scenario_file_path, aggregation_path) parser.set(title, 'aggregation', relative_aggregation_path) try: parser.write(open(scenario_file_path, 'a')) except IOError: # noinspection PyTypeChecker,PyCallByClass,PyArgumentList QtGui.QMessageBox.warning( self, self.tr('InaSAFE'), self.tr('Failed to save scenario to ' + scenario_file_path)) # Save State self.save_state()
def save_scenario(self, scenario_file_path=None): """Save current scenario to a text file. You can use the saved scenario with the batch runner. :param scenario_file_path: A path to the scenario file. :type scenario_file_path: str """ # Validate Input warning_title = tr('InaSAFE Save Scenario Warning') is_valid, warning_message = self.validate_input() if not is_valid: # noinspection PyCallByClass,PyTypeChecker,PyArgumentList QtGui.QMessageBox.warning(self, warning_title, warning_message) return # Make extent to look like: # 109.829170982, -8.13333290561, 111.005344795, -7.49226294379 # Added in 2.2 to support user defined analysis extents if self.dock.extent.user_extent is not None \ and self.dock.extent.crs is not None: # In V4.0, user_extent is QgsGeometry. user_extent = self.dock.extent.user_extent.boundingBox() extent = extent_to_array(user_extent, self.dock.extent.crs) else: extent = viewport_geo_array(self.iface.mapCanvas()) extent_string = ', '.join(('%f' % x) for x in extent) exposure_path = self.exposure_layer.publicSource() hazard_path = self.hazard_layer.publicSource() title = self.keyword_io.read_keywords(self.hazard_layer, 'title') title = tr(title) default_filename = title.replace(' ', '_').replace('(', '').replace(')', '') # Popup a dialog to request the filename if scenario_file_path = None dialog_title = tr('Save Scenario') if scenario_file_path is None: # noinspection PyCallByClass,PyTypeChecker scenario_file_path = QFileDialog.getSaveFileName( self, dialog_title, os.path.join(self.output_directory, default_filename + '.txt'), "Text files (*.txt)") if scenario_file_path is None or scenario_file_path == '': return self.output_directory = os.path.dirname(scenario_file_path) # Write to file parser = ConfigParser() parser.add_section(title) # Relative path is not recognized by the batch runner, so we use # absolute path. parser.set(title, 'exposure', exposure_path) parser.set(title, 'hazard', hazard_path) parser.set(title, 'extent', extent_string) if self.dock.extent.crs is None: parser.set(title, 'extent_crs', 'EPSG:4326') else: parser.set(title, 'extent_crs', self.dock.extent.crs.authid()) if self.aggregation_layer is not None: aggregation_path = self.aggregation_layer.publicSource() relative_aggregation_path = self.relative_path( scenario_file_path, aggregation_path) parser.set(title, 'aggregation', relative_aggregation_path) # noinspection PyBroadException try: parser.write(open(scenario_file_path, 'a')) except Exception as e: # noinspection PyTypeChecker,PyCallByClass,PyArgumentList QtGui.QMessageBox.warning( self, 'InaSAFE', tr('Failed to save scenario to {path}, exception ' '{exception}').format(path=scenario_file_path, exception=str(e))) # Save State self.save_state()
def clip_parameters(self): """Calculate the best extents to use for the assessment. :returns: A dictionary consisting of: * extra_exposure_keywords: dict - any additional keywords that should be written to the exposure layer. For example if rescaling is required for a raster, the original resolution can be added to the keywords file. * adjusted_geo_extent: list - [xmin, ymin, xmax, ymax] - the best extent that can be used given the input datasets and the current viewport extents. * cell_size: float - the cell size that is the best of the hazard and exposure rasters. :rtype: dict, QgsRectangle, float, QgsMapLayer, QgsRectangle, QgsMapLayer :raises: InsufficientOverlapError """ if self._clip_parameters is None: # Get the Hazard extents as an array in EPSG:4326 # noinspection PyTypeChecker hazard_geoextent = extent_to_array( self.hazard.extent(), self.hazard.crs()) # Get the Exposure extents as an array in EPSG:4326 # noinspection PyTypeChecker exposure_geoextent = extent_to_array( self.exposure.extent(), self.exposure.crs()) # Set the analysis extents based on user's desired behaviour settings = QSettings() mode_name = settings.value( 'inasafe/analysis_extents_mode', 'HazardExposureView') # Default to using canvas extents if no case below matches analysis_geoextent = self.viewport_extent if mode_name == 'HazardExposureView': analysis_geoextent = self.viewport_extent elif mode_name == 'HazardExposure': analysis_geoextent = None elif mode_name == 'HazardExposureBookmark' or \ mode_name == 'HazardExposureBoundingBox': if self.requested_extent is not None \ and self.requested_extent_crs is not None: # User has defined preferred extent, so use that analysis_geoextent = array_to_geo_array( self.requested_extent, self.requested_extent_crs) # Now work out the optimal extent between the two layers and # the current view extent. The optimal extent is the intersection # between the two layers and the viewport. try: # Extent is returned as an array [xmin,ymin,xmax,ymax] # We will convert it to a QgsRectangle afterwards. # If the user has defined a preferred analysis extent it will # always be used, otherwise the data will be clipped to # the viewport unless the user has deselected clip to viewport # in options. geo_extent = get_optimal_extent( hazard_geoextent, exposure_geoextent, analysis_geoextent) except InsufficientOverlapError, e: # noinspection PyTypeChecker message = generate_insufficient_overlap_message( e, exposure_geoextent, self.exposure.qgis_layer(), hazard_geoextent, self.hazard.qgis_layer(), analysis_geoextent) raise InsufficientOverlapError(message) # TODO: move this to its own function # Next work out the ideal spatial resolution for rasters # in the analysis. If layers are not native WGS84, we estimate # this based on the geographic extents # rather than the layers native extents so that we can pass # the ideal WGS84 cell size and extents to the layer prep routines # and do all preprocessing in a single operation. # All this is done in the function getWGS84resolution adjusted_geo_extent = geo_extent cell_size = None extra_exposure_keywords = {} if self.hazard.layer_type() == QgsMapLayer.RasterLayer: # Hazard layer is raster hazard_geo_cell_size, _ = get_wgs84_resolution( self.hazard.qgis_layer()) if self.exposure.layer_type() == QgsMapLayer.RasterLayer: # In case of two raster layers establish common resolution exposure_geo_cell_size, _ = get_wgs84_resolution( self.exposure.qgis_layer()) # See issue #1008 - the flag below is used to indicate # if the user wishes to prevent resampling of exposure data keywords = self.exposure.keywords allow_resampling_flag = True if 'allow_resampling' in keywords: resampling_lower = keywords['allow_resampling'].lower() allow_resampling_flag = resampling_lower == 'true' if hazard_geo_cell_size < exposure_geo_cell_size and \ allow_resampling_flag: cell_size = hazard_geo_cell_size # Adjust the geo extent to coincide with hazard grids # so gdalwarp can do clipping properly adjusted_geo_extent = adjust_clip_extent( geo_extent, get_wgs84_resolution(self.hazard.qgis_layer()), hazard_geoextent) else: cell_size = exposure_geo_cell_size # Adjust extent to coincide with exposure grids # so gdalwarp can do clipping properly adjusted_geo_extent = adjust_clip_extent( geo_extent, get_wgs84_resolution(self.exposure.qgis_layer()), exposure_geoextent) # Record native resolution to allow rescaling of exposure if not numpy.allclose(cell_size, exposure_geo_cell_size): extra_exposure_keywords['resolution'] = \ exposure_geo_cell_size else: if self.exposure.layer_type() != QgsMapLayer.VectorLayer: raise RuntimeError # In here we do not set cell_size so that in # _clip_raster_layer we can perform gdalwarp without # specifying cell size as we still want to have the # original pixel size. # Adjust the geo extent to be at the edge of the pixel in # so gdalwarp can do clipping properly adjusted_geo_extent = adjust_clip_extent( geo_extent, get_wgs84_resolution(self.hazard.qgis_layer()), hazard_geoextent) # If exposure is vector data grow hazard raster layer to # ensure there are enough pixels for points at the edge of # the view port to be interpolated correctly. This requires # resolution to be available adjusted_geo_extent = get_buffered_extent( adjusted_geo_extent, get_wgs84_resolution(self.hazard.qgis_layer())) else: # Hazard layer is vector # In case hazard data is a point data set, we will need to set # the geo_extent to the extent of exposure and the analysis # extent. We check the extent first if the point extent # intersects with geo_extent. if self.hazard.geometry_type() == QGis.Point: user_extent_enabled = ( self.requested_extent is not None and self.requested_extent_crs is not None) if user_extent_enabled: # Get intersection between exposure and analysis extent geo_extent = bbox_intersection( exposure_geoextent, analysis_geoextent) # Check if the point is within geo_extent if bbox_intersection( geo_extent, exposure_geoextent) is None: raise InsufficientOverlapError else: geo_extent = exposure_geoextent adjusted_geo_extent = geo_extent if self.exposure.layer_type() == QgsMapLayer.RasterLayer: # Adjust the geo extent to be at the edge of the pixel in # so gdalwarp can do clipping properly adjusted_geo_extent = adjust_clip_extent( geo_extent, get_wgs84_resolution(self.exposure.qgis_layer()), exposure_geoextent) self._clip_parameters = { 'extra_exposure_keywords': extra_exposure_keywords, 'adjusted_geo_extent': adjusted_geo_extent, 'cell_size': cell_size }