def test_stacktrace_html(self): """Stack traces can be caught and rendered as html """ # This is about general exception handling, so ok to use catch-all # pylint: disable=W0703 try: bbox_intersection('aoeu', 'oaeu', []) except Exception, e: # Display message and traceback message = get_error_message(e) # print message message = message.to_text() self.assertIn(str(e), message) self.assertIn('line', message) self.assertIn('file', message) message = get_error_message(e) message = message.to_html() assert str(e) in message message = message.decode('string_escape') control_file_path = test_data_path( 'control', 'files', 'test-stacktrace-html.txt') expected_results = open(control_file_path).read().replace('\n', '') self.assertIn(expected_results, message)
def test_issue157(self): """Verify that we get the error class name back - issue #157 .. seealso:: https://github.com/AIFDR/inasafe/issues/121 """ try: bbox_intersection('aoeu', 'oaeu', []) except BoundingBoxError, e: message = get_error_message(e) myString = 'BoundingBoxError' assert myString in message.to_text(), message myString = 'Western boundary' assert myString in message.to_text(), message
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 get_optimal_extent(hazard_geo_extent, exposure_geo_extent, viewport_geo_extent=None): """A helper function to determine what the optimal extent is. Optimal extent should be considered as the intersection between the three inputs. The inasafe library will perform various checks to ensure that the extent is tenable, includes data from both etc. This is a thin wrapper around safe.storage.utilities.bbox_intersection Typically the result of this function will be used to clip input layers to a common extent before processing. :param hazard_geo_extent: An array representing the hazard 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. :type hazard_geo_extent: list :param exposure_geo_extent: An array representing the exposure layer extents in the form [xmin, ymin, xmax, ymax]. It is assumed that the coordinates are in EPSG:4326 although currently no checks are made to enforce this. :type exposure_geo_extent: list :param viewport_geo_extent: (optional) An array representing the viewport 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. ..note:: We do minimal checking as the inasafe library takes care of it for us. :returns: An array containing an extent in the form [xmin, ymin, xmax, ymax] e.g.:: [100.03, -1.14, 100.81, -0.73] :rtype: list :raises: Any exceptions raised by the InaSAFE library will be propagated. """ message = tr( 'theHazardGeoExtent or theExposureGeoExtent cannot be None.Found: ' '/ntheHazardGeoExtent: %s /ntheExposureGeoExtent: %s' % (hazard_geo_extent, exposure_geo_extent)) if (hazard_geo_extent is None) or (exposure_geo_extent is None): raise BoundingBoxError(message) # .. note:: The bbox_intersection function below assumes that # all inputs are in EPSG:4326 optimal_extent = bbox_intersection(hazard_geo_extent, exposure_geo_extent, viewport_geo_extent) if optimal_extent is None: # Bounding boxes did not overlap message = tr( 'Bounding boxes of hazard data, exposure data and viewport ' 'did not overlap, so no computation was done. Please make ' 'sure you pan to where the data is and that hazard and ' 'exposure data overlaps.') raise InsufficientOverlapError(message) return optimal_extent
def get_optimal_extent( hazard_geo_extent, exposure_geo_extent, viewport_geo_extent=None): """A helper function to determine what the optimal extent is. Optimal extent should be considered as the intersection between the three inputs. The inasafe library will perform various checks to ensure that the extent is tenable, includes data from both etc. This is a thin wrapper around safe.storage.utilities.bbox_intersection Typically the result of this function will be used to clip input layers to a common extent before processing. :param hazard_geo_extent: An array representing the hazard 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. :type hazard_geo_extent: list :param exposure_geo_extent: An array representing the exposure layer extents in the form [xmin, ymin, xmax, ymax]. It is assumed that the coordinates are in EPSG:4326 although currently no checks are made to enforce this. :type exposure_geo_extent: list :param viewport_geo_extent: (optional) An array representing the viewport 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. ..note:: We do minimal checking as the inasafe library takes care of it for us. :returns: An array containing an extent in the form [xmin, ymin, xmax, ymax] e.g.:: [100.03, -1.14, 100.81, -0.73] :rtype: list :raises: Any exceptions raised by the InaSAFE library will be propagated. """ message = tr( 'theHazardGeoExtent or theExposureGeoExtent cannot be None.Found: ' '/ntheHazardGeoExtent: %s /ntheExposureGeoExtent: %s' % (hazard_geo_extent, exposure_geo_extent)) if (hazard_geo_extent is None) or (exposure_geo_extent is None): raise BoundingBoxError(message) # .. note:: The bbox_intersection function below assumes that # all inputs are in EPSG:4326 optimal_extent = bbox_intersection( hazard_geo_extent, exposure_geo_extent, viewport_geo_extent) if optimal_extent is None: # Bounding boxes did not overlap message = tr( 'Bounding boxes of hazard data, exposure data and viewport ' 'did not overlap, so no computation was done. Please make ' 'sure you pan to where the data is and that hazard and ' 'exposure data overlaps.') raise InsufficientOverlapError(message) return optimal_extent
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 }