def copyKeywords(self, theSourceLayer, theDestinationFile, theExtraKeywords=None): """Helper to copy the keywords file from a source dataset to a destination dataset. e.g.:: copyKeywords('foo.shp', 'bar.shp') Will result in the foo.keywords file being copied to bar.keyword. Optional argument extraKeywords is a dictionary with additional keywords that will be added to the destination file e.g:: copyKeywords('foo.shp', 'bar.shp', {'resolution': 0.01}) Args: * theSourceLayer - A QGIS QgsMapLayer instance. * theDestinationFile - the output filename that should be used to store the keywords in. It can be a .shp or a .keywords for exampled since the suffix will always be replaced with .keywords. * theExtraKeywords - a dict containing all the extra keywords to be written for the layer. The written keywords will consist of any original keywords from the source layer's keywords file and and the extra keywords (which will replace the source layers keywords if the key is identical). Returns: None. Raises: None """ myKeywords = self.readKeywords(theSourceLayer) if theExtraKeywords is None: theExtraKeywords = {} myMessage = self.tr( 'Expected extraKeywords to be a dictionary. Got %s' % str(type(theExtraKeywords))[1:-1]) verify(isinstance(theExtraKeywords, dict), myMessage) # compute the output keywords file name myDestinationBase = os.path.splitext(theDestinationFile)[0] myNewDestination = myDestinationBase + '.keywords' # write the extra keywords into the source dict try: for key in theExtraKeywords: myKeywords[key] = theExtraKeywords[key] writeKeywordsToFile(myNewDestination, myKeywords) except Exception, e: myMessage = self.tr( 'Failed to copy keywords file from :' '\n%s\nto\%s: %s' % (theSourceLayer.source(), myNewDestination, str(e))) raise Exception(myMessage)
def copy_keywords( self, source_layer, destination_file, extra_keywords=None): """Helper to copy the keywords file from a source to a target dataset. e.g.:: copyKeywords('foo.shp', 'bar.shp') Will result in the foo.keywords file being copied to bar.keyword. Optional argument extraKeywords is a dictionary with additional keywords that will be added to the destination file e.g:: copyKeywords('foo.shp', 'bar.shp', {'resolution': 0.01}) :param source_layer: A QGIS QgsMapLayer instance. :type source_layer: QgsMapLayer :param destination_file: The output filename that should be used to store the keywords in. It can be a .shp or a .keywords for example since the suffix will always be replaced with .keywords. :type destination_file: str :param extra_keywords: A dict containing all the extra keywords to be written for the layer. The written keywords will consist of any original keywords from the source layer's keywords file and and the extra keywords (which will replace the source layers keywords if the key is identical). :type extra_keywords: dict """ keywords = self.read_keywords(source_layer) if extra_keywords is None: extra_keywords = {} message = self.tr( 'Expected extraKeywords to be a dictionary. Got ' '%s' % str(type(extra_keywords))[1:-1]) verify(isinstance(extra_keywords, dict), message) # compute the output keywords file name destination_base = os.path.splitext(destination_file)[0] new_destination = destination_base + '.keywords' # write the extra keywords into the source dict try: for key in extra_keywords: keywords[key] = extra_keywords[key] writeKeywordsToFile(new_destination, keywords) except Exception, e: message = self.tr( 'Failed to copy keywords file from : \n%s\nto\n%s: %s' % ( source_layer.source(), new_destination, str(e))) raise Exception(message)
def copy_keywords(self, source_layer, destination_file, extra_keywords=None): """Helper to copy the keywords file from a source to a target dataset. e.g.:: copyKeywords('foo.shp', 'bar.shp') Will result in the foo.keywords file being copied to bar.keyword. Optional argument extraKeywords is a dictionary with additional keywords that will be added to the destination file e.g:: copyKeywords('foo.shp', 'bar.shp', {'resolution': 0.01}) :param source_layer: A QGIS QgsMapLayer instance. :type source_layer: QgsMapLayer :param destination_file: The output filename that should be used to store the keywords in. It can be a .shp or a .keywords for example since the suffix will always be replaced with .keywords. :type destination_file: str :param extra_keywords: A dict containing all the extra keywords to be written for the layer. The written keywords will consist of any original keywords from the source layer's keywords file and and the extra keywords (which will replace the source layers keywords if the key is identical). :type extra_keywords: dict """ myKeywords = self.read_keywords(source_layer) if extra_keywords is None: extra_keywords = {} myMessage = self.tr('Expected extraKeywords to be a dictionary. Got ' '%s' % str(type(extra_keywords))[1:-1]) verify(isinstance(extra_keywords, dict), myMessage) # compute the output keywords file name myDestinationBase = os.path.splitext(destination_file)[0] myNewDestination = myDestinationBase + '.keywords' # write the extra keywords into the source dict try: for key in extra_keywords: myKeywords[key] = extra_keywords[key] writeKeywordsToFile(myNewDestination, myKeywords) except Exception, e: myMessage = self.tr( 'Failed to copy keywords file from : \n%s\nto\n%s: %s' % (source_layer.source(), myNewDestination, str(e))) raise Exception(myMessage)
def copyKeywords(self, theSourceLayer, theDestinationFile, theExtraKeywords=None): """Helper to copy the keywords file from a source dataset to a destination dataset. e.g.:: copyKeywords('foo.shp', 'bar.shp') Will result in the foo.keywords file being copied to bar.keyword. Optional argument extraKeywords is a dictionary with additional keywords that will be added to the destination file e.g:: copyKeywords('foo.shp', 'bar.shp', {'resolution': 0.01}) Args: * theSourceLayer - A QGIS QgsMapLayer instance. * theDestinationFile - the output filename that should be used to store the keywords in. It can be a .shp or a .keywords for exampled since the suffix will always be replaced with .keywords. * theExtraKeywords - a dict containing all the extra keywords to be written for the layer. The written keywords will consist of any original keywords from the source layer's keywords file and and the extra keywords (which will replace the source layers keywords if the key is identical). Returns: None. Raises: None """ myKeywords = self.readKeywords(theSourceLayer) if theExtraKeywords is None: theExtraKeywords = {} myMessage = self.tr('Expected extraKeywords to be a dictionary. Got %s' % str(type(theExtraKeywords))[1:-1]) verify(isinstance(theExtraKeywords, dict), myMessage) # compute the output keywords file name myDestinationBase = os.path.splitext(theDestinationFile)[0] myNewDestination = myDestinationBase + '.keywords' # write the extra keywords into the source dict try: for key in theExtraKeywords: myKeywords[key] = theExtraKeywords[key] writeKeywordsToFile(myNewDestination, myKeywords) except Exception, e: myMessage = self.tr('Failed to copy keywords file from :' '\n%s\nto\%s: %s' % (theSourceLayer.source(), myNewDestination, str(e))) raise Exception(myMessage)
def _clip_raster_layer( layer, extent, cell_size=None, extra_keywords=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 layer: A valid QGIS raster layer in EPSG:4326 :type layer: QgsRasterLayer :param 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. or: A QgsGeometry of type polygon. **Polygon clipping currently only supported for vector datasets.** :type extent: list(float), QgsGeometry :param cell_size: 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 cell_size: 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 layer or not extent: message = tr('Layer or Extent passed to clip is None.') raise InvalidParameterError(message) if layer.type() != QgsMapLayer.RasterLayer: message = tr( 'Expected a raster layer but received a %s.' % str(layer.type())) raise InvalidParameterError(message) working_layer = str(layer.source()) # Check for existence of keywords file base, _ = os.path.splitext(working_layer) keywords_path = base + '.keywords' message = tr( 'Input file to be clipped "%s" does not have the ' 'expected keywords file %s' % ( working_layer, keywords_path )) verify(os.path.isfile(keywords_path), message) # 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. keywords = read_file_keywords(keywords_path) if 'datatype' in keywords and keywords['datatype'] == 'density': if str(layer.crs().authid()) != 'EPSG:4326': # This layer is not WGS84 geographic message = ( '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' % ( working_layer, layer.crs().toProj4() )) raise InvalidProjectionError(message) # We need to provide gdalwarp with a dataset for the clip # because unline gdal_translate, it does not take projwin. clip_kml = extent_to_kml(extent) # Create a filename for the clipped, resampled and reprojected layer handle, filename = tempfile.mkstemp('.tif', 'clip_', temp_dir()) os.close(handle) os.remove(filename) # If no cell size is specified, we need to run gdalwarp without # specifying the output pixel size to ensure the raster dims # remain consistent. binary_list = which('gdalwarp') LOGGER.debug('Path for gdalwarp: %s' % binary_list) if len(binary_list) < 1: raise CallGDALError( tr('gdalwarp could not be found on your computer')) # Use the first matching gdalwarp found binary = binary_list[0] if cell_size is None: command = ( '"%s" -q -t_srs EPSG:4326 -r near -cutline %s -crop_to_cutline ' '-of GTiff "%s" "%s"' % ( binary, clip_kml, working_layer, filename)) else: command = ( '"%s" -q -t_srs EPSG:4326 -r near -tr %f %f -cutline %s ' '-crop_to_cutline -of GTiff "%s" "%s"' % ( binary, cell_size, cell_size, clip_kml, working_layer, filename)) LOGGER.debug(command) result = QProcess().execute(command) # For QProcess exit codes see # http://qt-project.org/doc/qt-4.8/qprocess.html#execute if result == -2: # cannot be started message_detail = tr('Process could not be started.') message = tr( '<p>Error while executing the following shell command:' '</p><pre>%s</pre><p>Error message: %s' % (command, message_detail)) raise CallGDALError(message) elif result == -1: # process crashed message_detail = tr('Process crashed.') message = tr('<p>Error while executing the following shell command:</p>' '<pre>%s</pre><p>Error message: %s' % (command, message_detail)) raise CallGDALError(message) # .. todo:: Check the result of the shell call is ok keyword_io = KeywordIO() keyword_io.copy_keywords(layer, filename, extra_keywords=extra_keywords) base_name = '%s clipped' % layer.name() layer = QgsRasterLayer(filename, base_name) return layer
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 - 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. * 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 InvalidParameterException(myMessage) if theLayer.type() != QgsMapLayer.RasterLayer: myMessage = tr('Expected a raster layer but received a %s.' % str(theLayer.type())) raise InvalidParameterException(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 theLayer.srs().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.srs().toProj4())) raise InvalidProjectionException(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. if theCellSize is None: myCommand = ('gdalwarp -q -t_srs EPSG:4326 -r near ' '-cutline %s -crop_to_cutline -of GTiff ' '"%s" "%s"' % (myClipKml, myWorkingLayer, myFilename)) else: myCommand = ('gdalwarp -q -t_srs EPSG:4326 -r near -tr %f %f ' '-cutline %s -crop_to_cutline -of GTiff ' '"%s" "%s"' % (theCellSize, theCellSize, myClipKml, myWorkingLayer, myFilename)) myExecutablePrefix = '' if sys.platform == 'darwin': # Mac OS X # .. todo:: FIXME - softcode gdal version in this path myExecutablePrefix = ('/Library/Frameworks/GDAL.framework/' 'Versions/1.9/Programs/') myCommand = myExecutablePrefix + myCommand 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 _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 _clip_raster_layer(layer, extent, cell_size=None, extra_keywords=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 layer: A valid QGIS raster layer in EPSG:4326 :type layer: QgsRasterLayer :param 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. or: A QgsGeometry of type polygon. **Polygon clipping currently only supported for vector datasets.** :type extent: list(float), QgsGeometry :param cell_size: 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 cell_size: 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 layer or not extent: message = tr('Layer or Extent passed to clip is None.') raise InvalidParameterError(message) if layer.type() != QgsMapLayer.RasterLayer: message = tr('Expected a raster layer but received a %s.' % str(layer.type())) raise InvalidParameterError(message) working_layer = str(layer.source()) # Check for existence of keywords file base, _ = os.path.splitext(working_layer) keywords_path = base + '.keywords' message = tr('Input file to be clipped "%s" does not have the ' 'expected keywords file %s' % (working_layer, keywords_path)) verify(os.path.isfile(keywords_path), message) # 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. keywords = read_file_keywords(keywords_path) if 'datatype' in keywords and keywords['datatype'] == 'density': if str(layer.crs().authid()) != 'EPSG:4326': # This layer is not WGS84 geographic message = ( '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' % (working_layer, layer.crs().toProj4())) raise InvalidProjectionError(message) # We need to provide gdalwarp with a dataset for the clip # because unline gdal_translate, it does not take projwin. clip_kml = extent_to_kml(extent) # Create a filename for the clipped, resampled and reprojected layer handle, filename = tempfile.mkstemp('.tif', 'clip_', temp_dir()) os.close(handle) os.remove(filename) # If no cell size is specified, we need to run gdalwarp without # specifying the output pixel size to ensure the raster dims # remain consistent. binary_list = which('gdalwarp') LOGGER.debug('Path for gdalwarp: %s' % binary_list) if len(binary_list) < 1: raise CallGDALError(tr('gdalwarp could not be found on your computer')) # Use the first matching gdalwarp found binary = binary_list[0] if cell_size is None: command = ( '"%s" -q -t_srs EPSG:4326 -r near -cutline %s -crop_to_cutline ' '-ot Float64 -of GTiff "%s" "%s"' % (binary, clip_kml, working_layer, filename)) else: command = ( '"%s" -q -t_srs EPSG:4326 -r near -tr %f %f -cutline %s ' '-crop_to_cutline -ot Float64 -of GTiff "%s" "%s"' % (binary, cell_size, cell_size, clip_kml, working_layer, filename)) LOGGER.debug(command) result = QProcess().execute(command) # For QProcess exit codes see # http://qt-project.org/doc/qt-4.8/qprocess.html#execute if result == -2: # cannot be started message_detail = tr('Process could not be started.') message = tr('<p>Error while executing the following shell command:' '</p><pre>%s</pre><p>Error message: %s' % (command, message_detail)) raise CallGDALError(message) elif result == -1: # process crashed message_detail = tr('Process crashed.') message = tr( '<p>Error while executing the following shell command:</p>' '<pre>%s</pre><p>Error message: %s' % (command, message_detail)) raise CallGDALError(message) # .. todo:: Check the result of the shell call is ok keyword_io = KeywordIO() keyword_io.copy_keywords(layer, filename, extra_keywords=extra_keywords) base_name = '%s clipped' % layer.name() layer = QgsRasterLayer(filename, base_name) return layer
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 - 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. * 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 InvalidParameterException(myMessage) if theLayer.type() != QgsMapLayer.RasterLayer: myMessage = tr('Expected a raster layer but received a %s.' % str(theLayer.type())) raise InvalidParameterException(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 theLayer.srs().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.srs().toProj4())) raise InvalidProjectionException(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. if theCellSize is None: myCommand = ('gdalwarp -q -t_srs EPSG:4326 -r near ' '-cutline %s -crop_to_cutline -of GTiff ' '"%s" "%s"' % (myClipKml, myWorkingLayer, myFilename)) else: myCommand = ( 'gdalwarp -q -t_srs EPSG:4326 -r near -tr %f %f ' '-cutline %s -crop_to_cutline -of GTiff ' '"%s" "%s"' % (theCellSize, theCellSize, myClipKml, myWorkingLayer, myFilename)) myExecutablePrefix = '' if sys.platform == 'darwin': # Mac OS X # .. todo:: FIXME - softcode gdal version in this path myExecutablePrefix = ('/Library/Frameworks/GDAL.framework/' 'Versions/1.9/Programs/') myCommand = myExecutablePrefix + myCommand # For debugging only # myCommand = myExecutablePrefix + myCommand # myFile = file('C:/temp/command.txt', 'wt') # myFile.write(myCommand) # myFile.close() # Now run GDAL warp scottie... LOGGER.debug(myCommand) try: myResult = call(myCommand, shell=True) del myResult except CalledProcessError, e: myMessage = tr('<p>Error while executing the following shell command:' '</p><pre>%s</pre><p>Error message: %s' % (myCommand, str(e))) # shameless hack - see https://github.com/AIFDR/inasafe/issues/141 if sys.platform == 'darwin': # Mac OS X if 'Errno 4' in str(e): # continue as the error seems to be non critical pass else: raise Exception(myMessage) else: raise Exception(myMessage)