def _clipRasterLayer(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. Args: * theLayer - a valid QGIS raster layer in EPSG:4326 * theExtent 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.** * 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. Returns: Path to the output clipped layer (placed in the system temp dir). Raises: Exception 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 = extentToKml(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.copyKeywords(theLayer, myFilename, theExtraKeywords=theExtraKeywords) return myFilename # Filename of created file
def testWhich(self): """Test that the which command works as expected.""" myBinary = 'gdalwarp' myPath = which(myBinary) # Check we found at least one match assert len(myPath) > 0
def _clipRasterLayer(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. Args: * theLayer - a valid QGIS raster layer in EPSG:4326 * theExtent 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.** * 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. Returns: Path to the output clipped layer (placed in the system temp dir). Raises: Exception 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 = extentToKml(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.copyKeywords(theLayer, myFilename, theExtraKeywords=theExtraKeywords) return myFilename # Filename of created file