Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
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 -  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_',
                                            getTempDir())
    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

    # Now run GDAL warp scottie...
    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)