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 test_clip_both(self): """Raster and Vector layers can be clipped.""" # Create a vector layer layer_name = 'padang' vector_layer = QgsVectorLayer(VECTOR_PATH, layer_name, 'ogr') message = ('Did not find layer "%s" in path "%s"' % (layer_name, VECTOR_PATH)) assert vector_layer.isValid(), message # Create a raster layer layer_name = 'shake' raster_layer = QgsRasterLayer(RASTERPATH, layer_name) message = ('Did not find layer "%s" in path "%s"' % (layer_name, RASTERPATH)) assert raster_layer.isValid(), message # Create a bounding box view_port_geo_extent = [99.53, -1.22, 101.20, -0.36] # Get the Hazard extents as an array in EPSG:4326 hazard_geo_extent = [ raster_layer.extent().xMinimum(), raster_layer.extent().yMinimum(), raster_layer.extent().xMaximum(), raster_layer.extent().yMaximum() ] # Get the Exposure extents as an array in EPSG:4326 exposure_geo_extent = [ vector_layer.extent().xMinimum(), vector_layer.extent().yMinimum(), vector_layer.extent().xMaximum(), vector_layer.extent().yMaximum() ] # 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. # Extent is returned as an array [xmin,ymin,xmax,ymax] geo_extent = get_optimal_extent(hazard_geo_extent, exposure_geo_extent, view_port_geo_extent) # Clip the vector to the bbox result = clip_layer(vector_layer, geo_extent) # Check the output is valid assert os.path.exists(result.source()) read_safe_layer(result.source()) # Clip the raster to the bbox result = clip_layer(raster_layer, geo_extent) # Check the output is valid assert os.path.exists(result.source()) read_safe_layer(result.source()) # ------------------------------- # Check the extra keywords option # ------------------------------- # Clip the vector to the bbox result = clip_layer(vector_layer, geo_extent, extra_keywords={'title': 'piggy'}) # Check the output is valid assert os.path.exists(result.source()) safe_layer = read_safe_layer(result.source()) keywords = safe_layer.get_keywords() # message = 'Extra keyword was not found in %s: %s' % (myResult, # keywords) assert keywords['title'] == 'piggy' # Clip the raster to the bbox result = clip_layer(raster_layer, geo_extent, extra_keywords={'email': 'animal'}) # Check the output is valid assert os.path.exists(result.source()) safe_layer = read_safe_layer(result.source()) keywords = safe_layer.get_keywords() message = ('Extra keyword was not found in %s: %s' % (result.source(), keywords)) assert keywords['email'] == 'animal', message
def test_clip_both(self): """Raster and Vector layers can be clipped.""" # Create a vector layer layer_name = 'padang' vector_layer = QgsVectorLayer(VECTOR_PATH, layer_name, 'ogr') message = ( 'Did not find layer "%s" in path "%s"' % (layer_name, VECTOR_PATH)) assert vector_layer.isValid(), message # Create a raster layer layer_name = 'shake' raster_layer = QgsRasterLayer(RASTERPATH, layer_name) message = ( 'Did not find layer "%s" in path "%s"' % (layer_name, RASTERPATH)) assert raster_layer.isValid(), message # Create a bounding box view_port_geo_extent = [99.53, -1.22, 101.20, -0.36] # Get the Hazard extents as an array in EPSG:4326 hazard_geo_extent = [ raster_layer.extent().xMinimum(), raster_layer.extent().yMinimum(), raster_layer.extent().xMaximum(), raster_layer.extent().yMaximum() ] # Get the Exposure extents as an array in EPSG:4326 exposure_geo_extent = [ vector_layer.extent().xMinimum(), vector_layer.extent().yMinimum(), vector_layer.extent().xMaximum(), vector_layer.extent().yMaximum() ] # 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. # Extent is returned as an array [xmin,ymin,xmax,ymax] geo_extent = get_optimal_extent( hazard_geo_extent, exposure_geo_extent, view_port_geo_extent) # Clip the vector to the bbox result = clip_layer(vector_layer, geo_extent) # Check the output is valid assert os.path.exists(result.source()) read_safe_layer(result.source()) # Clip the raster to the bbox result = clip_layer(raster_layer, geo_extent) # Check the output is valid assert os.path.exists(result.source()) read_safe_layer(result.source()) # ------------------------------- # Check the extra keywords option # ------------------------------- # Clip the vector to the bbox result = clip_layer( vector_layer, geo_extent, extra_keywords={'title': 'piggy'}) # Check the output is valid assert os.path.exists(result.source()) safe_layer = read_safe_layer(result.source()) keywords = safe_layer.get_keywords() # message = 'Extra keyword was not found in %s: %s' % (myResult, # keywords) assert keywords['title'] == 'piggy' # Clip the raster to the bbox result = clip_layer( raster_layer, geo_extent, extra_keywords={'email': 'animal'}) # Check the output is valid assert os.path.exists(result.source()) safe_layer = read_safe_layer(result.source()) keywords = safe_layer.get_keywords() message = ('Extra keyword was not found in %s: %s' % (result.source(), keywords)) assert keywords['email'] == 'animal', message
def test_get_optimal_extent(self): """Optimal extent is calculated correctly""" exposure_path = join(TESTDATA, 'Population_2010.asc') hazard_path = join(HAZDATA, 'Lembang_Earthquake_Scenario.asc') # Expected data haz_metadata = { 'bounding_box': ( 105.3000035, -8.3749994999999995, 110.2914705, -5.5667784999999999), 'resolution': ( 0.0083330000000000001, 0.0083330000000000001)} exp_metadata = { 'bounding_box': ( 94.972335000000001, -11.009721000000001, 141.0140016666665, 6.0736123333332639), 'resolution': ( 0.0083333333333333003, 0.0083333333333333003)} # Verify relevant metada is ok H = read_layer(hazard_path) E = read_layer(exposure_path) hazard_bbox = H.get_bounding_box() assert numpy.allclose(hazard_bbox, haz_metadata['bounding_box'], rtol=1.0e-12, atol=1.0e-12) exposure_bbox = E.get_bounding_box() assert numpy.allclose(exposure_bbox, exp_metadata['bounding_box'], rtol=1.0e-12, atol=1.0e-12) hazard_res = H.get_resolution() assert numpy.allclose(hazard_res, haz_metadata['resolution'], rtol=1.0e-12, atol=1.0e-12) exposure_res = E.get_resolution() assert numpy.allclose(exposure_res, exp_metadata['resolution'], rtol=1.0e-12, atol=1.0e-12) # First, do some examples that produce valid results ref_box = [105.3000035, -8.3749995, 110.2914705, -5.5667785] view_port = [94.972335, -11.009721, 141.014002, 6.073612] bbox = get_optimal_extent(hazard_bbox, exposure_bbox, view_port) assert numpy.allclose(bbox, ref_box, rtol=1.0e-12, atol=1.0e-12) # testing with viewport clipping disabled bbox = get_optimal_extent(hazard_bbox, exposure_bbox, None) assert numpy.allclose(bbox, ref_box, rtol=1.0e-12, atol=1.0e-12) view_port = [105.3000035, -8.3749994999999995, 110.2914705, -5.5667784999999999] bbox = get_optimal_extent(hazard_bbox, exposure_bbox, view_port) assert numpy.allclose(bbox, ref_box, rtol=1.0e-12, atol=1.0e-12) # Very small viewport fully inside other layers view_port = [106.0, -6.0, 108.0, -5.8] bbox = get_optimal_extent(hazard_bbox, exposure_bbox, view_port) assert numpy.allclose(bbox, view_port, rtol=1.0e-12, atol=1.0e-12) # viewport that intersects hazard layer view_port = [107.0, -6.0, 112.0, -3.0] ref_box = [107, -6, 110.2914705, -5.5667785] bbox = get_optimal_extent(hazard_bbox, exposure_bbox, view_port) assert numpy.allclose(bbox, ref_box, rtol=1.0e-12, atol=1.0e-12) # Then one where boxes don't overlap view_port = [105.3, -4.3, 110.29, -2.5] try: get_optimal_extent(hazard_bbox, exposure_bbox, view_port) except InsufficientOverlapError, e: message = 'Did not find expected error message in %s' % str(e) assert 'did not overlap' in str(e), message
# Then one where boxes don't overlap view_port = [105.3, -4.3, 110.29, -2.5] try: get_optimal_extent(hazard_bbox, exposure_bbox, view_port) except InsufficientOverlapError, e: message = 'Did not find expected error message in %s' % str(e) assert 'did not overlap' in str(e), message else: message = ('Non ovelapping bounding boxes should have raised ' 'an exception') raise Exception(message) # Try with wrong input data try: # noinspection PyTypeChecker get_optimal_extent(haz_metadata, exp_metadata, view_port) except BoundingBoxError: # good this was expected pass except InsufficientOverlapError, e: message = 'Did not find expected error message in %s' % str(e) assert 'Invalid' in str(e), message else: message = 'Wrong input data should have raised an exception' raise Exception(message) try: # noinspection PyTypeChecker get_optimal_extent(None, None, view_port) except BoundingBoxError, e: message = 'Did not find expected error message in %s' % str(e)
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 }