Example #1
0
def getDEMForBoundingBox(config, outputDir, outFilename, bbox, srs, coverage=DEFAULT_COVERAGE, 
                         resx=None, resy=None, interpolation=DEFAULT_RASTER_RESAMPLE_METHOD, scale=1.0, overwrite=True,
                         verbose=False, outfp=sys.stdout):
    """ Fetch U.S. 1/3 arcsecond DEM data hosted by U.S. Geological Survey using OGC WCS 1.1.1 query.
    
        @note Adapted from code provided by [email protected].
    
        @param config A Python ConfigParser (not currently used)
        @param outputDir String representing the absolute/relative path of the directory into which output raster should be written
        @param outFilename String representing the name of the raster file to be written
        @param bbox Dict representing the lat/long coordinates and spatial reference of the bounding box area
            for which the raster is to be extracted.  The following keys must be specified: minX, minY, maxX, maxY, srs.
        @param srs String representing the spatial reference of the raster to be returned.
        @param coverage String representing the raster source from which to get the raster coverage.  Must be one of: NHDPlus_hydroDEM, NED
        @param resx Float representing the X resolution of the raster(s) to be returned
        @param resy Float representing the Y resolution of the raster(s) to be returned
        @param interpolation String representing interpolation method.  Must be one of RASTER_RESAMPLE_METHOD.  Defaults to DEFAULT_RASTER_RESAMPLE_METHOD.
        @param scale Float representing factor by which to scale elevation data.  Defaults to 1.0.
        @param overwrite Boolean value indicating whether or not the file indicated by filename should be overwritten.
            If False and filename exists, IOError exception will be thrown with errno.EEXIST
        @param verbose Boolean True if detailed output information should be printed to outfp
        @param outfp File-like object to which verbose output should be printed
    
        @raise IOError if outputDir is not a writable directory
        @raise IOError if outFilename already exists and overwrite is False (see above)
        @raise Exception if there was an error making the WCS request
    
        @return Tuple(True if raster data were fetched and False if not, URL of raster fetched)
    """
    dataFetched = False
    assert(coverage in COVERAGES.keys())
    assert('minX' in bbox)
    assert('minY' in bbox)
    assert('maxX' in bbox)
    assert('maxY' in bbox)
    assert('srs' in bbox)
    assert(scale > 0.0)
    
    if not os.path.isdir(outputDir):
        raise IOError(errno.ENOTDIR, "Output directory %s is not a directory" % (outputDir,))
    if not os.access(outputDir, os.W_OK):
        raise IOError(errno.EACCES, "Not allowed to write to output directory %s" % (outputDir,))
    outputDir = os.path.abspath(outputDir)
    
    outFilepath = os.path.join(outputDir, outFilename)
    
    deleteOldfile = False
    if os.path.exists(outFilepath):
        if overwrite: 
            deleteOldfile = True
        else:
            raise IOError(errno.EEXIST, "Raster file %s already exists" % outFilepath)
     
    cov = COVERAGES[coverage]
    grid_origin = cov['grid_origin']
    grid_offset = cov['grid_offset']
    grid_extent = cov['grid_extent']
    s_srs = cov['srs']
    
    if resx is None:
        resx = abs(grid_offset[0])
    if resy is None:
        resy = abs(grid_offset[1])
    t_srs = srs

    # For requests, grid cell centers are used. Need to add half the grid_offset to the grid_origin
    grid_origin_0 = grid_origin[0] + grid_offset[0] / 2.0
    grid_origin_1 = grid_origin[1] + grid_offset[1] / 2.0
    
    p = Proj(init=DEFAULT_SRS)
    (x1, y1) = p(bbox['minX'], bbox['minY'])
    (x2, y2) = p(bbox['maxX'], bbox['maxY'])
    # Pad the width of the bounding box as the Albers transform results in regions of interest
    # that are a bit narrower than I would like, which risks watershed boundaries lying beyond
    # the DEM boundary.
    len_x = x2 - x1
    del_x = len_x * 0.30
    x1 = x1 - del_x
    x2 = x2 + del_x
    bbox_srs = DEFAULT_SRS
 
    # Find the number of grid cells from the grid origin to each edge of the request.
    # Multiply by the grid_offset and add the grid origin to get to the request location.
    xi1 = floor((x1 - grid_origin_0) / grid_offset[0]) * grid_offset[0] + grid_origin_0
    xi2 = ceil((x2 - grid_origin_0) / grid_offset[0]) * grid_offset[0] + grid_origin_0
    yi1 = floor((y1 - grid_origin_1) / grid_offset[1]) * grid_offset[1] + grid_origin_1
    yi2 = ceil((y2 - grid_origin_1) / grid_offset[1]) * grid_offset[1] + grid_origin_1
 
    # coverage, crs, bbox, format. May have the following fields: response_srs, store, resx, resy, interpolation
    url = URL_PROTO.format(coverage=coverage, x1=xi1, y1=yi1, x2=xi2, y2=yi2, bbox_srs=bbox_srs,
                           xoffset=grid_offset[0], yoffset=grid_offset[1])
    #ORG urlFetched = "http://%s%s" % (HOST, url)
    urlFetched = "https://%s%s" % (HOST, url)

    if verbose:
        outfp.write("Acquiring DEM data from {0} ...\n".format(urlFetched))

    # Make initial request, which will return the URL of our clipped coverage
    r = requests.get(urlFetched)
    if r.status_code != 200:
        raise Exception("Error fetching {url}, HTTP status code was {code} {reason}".format(urlFetched,
                                                                                            r.status_code,
                                                                                            r.reason))
    usgs_dem_coverage_handler = USGSDEMCoverageHandler()
    xml.sax.parseString(r.text, usgs_dem_coverage_handler)
    coverage_url = usgs_dem_coverage_handler.coverage_url
    if coverage_url is None:
        raise Exception("Unable to deteremine coverage URL from WCS server response.  Response text was: {0}".format(r.text))
    parsed_coverage_url = urlparse.urlparse(coverage_url)
    
    if verbose:
        outfp.write("Downloading DEM coverage from {0} ...\n".format(coverage_url))
    
    # Download coverage to tempfile
    tmp_dir = tempfile.mkdtemp()
    tmp_cov_name = os.path.join(tmp_dir, 'usgswcsdemtmp')
    tmp_out = open(tmp_cov_name, mode='w+b')
    
    #ORG conn = httplib.HTTPConnection(parsed_coverage_url.netloc)
    conn = httplib.HTTPSConnection(parsed_coverage_url.netloc)
    try:
        conn.request('GET', parsed_coverage_url.path)
        res = conn.getresponse(buffering=True)
    except socket.error as e:
        msg = "Encountered the following error when trying to read raster from %s. Error: %s.  Please try again later or contact the developer." % \
            (urlFetched, str(e) )
        raise Exception(msg)
  
    if 200 != res.status:
        msg = "HTTP response %d %s encountered when querying %s.  Please try again later or contact the developer." % \
            (res.status, res.reason, urlFetched)
        raise Exception(msg) 
     
    contentType = res.getheader('Content-Type')
    
    mimeType = 'image/tiff'
    if contentType.startswith(mimeType):
        # The data returned were of the type expected, read the data
        data = res.read(_BUFF_LEN)
        if data: 
            dataFetched = True
            while data:
                tmp_out.write(data)
                data = res.read(_BUFF_LEN)
            tmp_out.close()
    elif contentType.startswith(CONTENT_TYPE_ERRORS):
        # Read the error and print to stderr
        msg = "The following error was encountered reading WCS coverage URL %s\n\n" %\
            (coverage_url, )
        data = res.read(_BUFF_LEN)
        while data:
            msg += data 
        raise Exception(msg)
    else:
        msg = "Query for raster from URL %s returned content type %s, was expecting type %s. " + \
              " Operation failed." % \
            (coverage_url, contentType, mimeType)
        raise Exception(msg)

    # Rescale raster values if requested        
    if scale != 1.0:
        # Rescale values in raster
        if verbose:
            outfp.write("Rescaling raster values by factor {0}".format(scale))
        rescale_out = os.path.basename("{0}_rescale".format(tmp_cov_name))
        rescaleRaster(config, tmp_dir, tmp_cov_name, rescale_out, scale)
        tmp_cov_name = os.path.join(tmp_dir, rescale_out)
    
    if deleteOldfile:
        deleteGeoTiff(outFilepath)
    
    if verbose:
        msg = "Resampling raster from {s_srs} to {t_srs} " + \
              "with X resolution {resx} and Y resolution {resy}\n"
        outfp.write(msg.format(s_srs=s_srs,
                               t_srs=t_srs,
                               resx=resx,
                               resy=resy))
    
    # Re-sample to target spatial reference and resolution
    resampleRaster(config, outputDir, tmp_cov_name,  outFilename,
                   s_srs, t_srs, resx, resy, interpolation)
    
    # Delete temp directory
    shutil.rmtree(tmp_dir)
        
    return ( dataFetched, urlFetched )
Example #2
0
else:
    sys.stdout.write("Reprojecting DEM from %s to %s, spatial resolution (%.2f, %.2f) to (%.2f, %.2f)..." % \
                     (s_srs, t_srs, inSpatialMetadata[0], inSpatialMetadata[1],
                      demResolutionX, demResolutionY) )
    sys.stdout.flush()
    if args.scale:
        resampleRaster(context.config, context.projectDir, inDEMPath,
                       demFilenameTemp, s_srs, t_srs, demResolutionX,
                       demResolutionY, args.resampleMethod)
    else:
        resampleRaster(context.config, context.projectDir, inDEMPath,
                       demFilename, s_srs, t_srs, demResolutionX,
                       demResolutionY, args.resampleMethod)

if args.scale:
    rescaleRaster(context.config, context.projectDir, demFilepathTemp,
                  demFilename, args.scale)
    os.unlink(demFilepathTemp)

sys.stdout.write('done\n')
# Get the bounding box for the DEM
bbox = getBoundingBoxForRaster(demFilepath)
# Write a shapefile for the bounding box
shpFilename = writeBboxPolygonToShapefile(bbox, context.projectDir,
                                          "studyarea")

# Write metadata
GenericMetadata.writeStudyAreaEntry(
    context, "bbox_wgs84",
    "%f %f %f %f" % (bbox['minX'], bbox['minY'], bbox['maxX'], bbox['maxY']))

# Get spatial metadata for DEM
Example #3
0
def getDEMForBoundingBox(config,
                         outputDir,
                         outFilename,
                         bbox,
                         srs,
                         coverage=DEFAULT_COVERAGE,
                         resx=None,
                         resy=None,
                         interpolation=DEFAULT_RASTER_RESAMPLE_METHOD,
                         scale=1.0,
                         overwrite=True,
                         verbose=False,
                         outfp=sys.stdout):
    """ Fetch U.S. 1/3 arcsecond DEM data hosted by U.S. Geological Survey using OGC WCS 1.1.1 query.
    
        @note Adapted from code provided by [email protected].
    
        @param config A Python ConfigParser (not currently used)
        @param outputDir String representing the absolute/relative path of the directory into which output raster should be written
        @param outFilename String representing the name of the raster file to be written
        @param bbox Dict representing the lat/long coordinates and spatial reference of the bounding box area
            for which the raster is to be extracted.  The following keys must be specified: minX, minY, maxX, maxY, srs.
        @param srs String representing the spatial reference of the raster to be returned.
        @param coverage String representing the raster source from which to get the raster coverage.  Must be one of: NHDPlus_hydroDEM, NED
        @param resx Float representing the X resolution of the raster(s) to be returned
        @param resy Float representing the Y resolution of the raster(s) to be returned
        @param interpolation String representing interpolation method.  Must be one of RASTER_RESAMPLE_METHOD.  Defaults to DEFAULT_RASTER_RESAMPLE_METHOD.
        @param scale Float representing factor by which to scale elevation data.  Defaults to 1.0.
        @param overwrite Boolean value indicating whether or not the file indicated by filename should be overwritten.
            If False and filename exists, IOError exception will be thrown with errno.EEXIST
        @param verbose Boolean True if detailed output information should be printed to outfp
        @param outfp File-like object to which verbose output should be printed
    
        @raise IOError if outputDir is not a writable directory
        @raise IOError if outFilename already exists and overwrite is False (see above)
        @raise Exception if there was an error making the WCS request
    
        @return Tuple(True if raster data were fetched and False if not, URL of raster fetched)
    """
    dataFetched = False
    assert (coverage in list(COVERAGES.keys()))
    assert ('minX' in bbox)
    assert ('minY' in bbox)
    assert ('maxX' in bbox)
    assert ('maxY' in bbox)
    assert ('srs' in bbox)
    assert (scale > 0.0)

    if not os.path.isdir(outputDir):
        raise IOError(errno.ENOTDIR,
                      "Output directory %s is not a directory" % (outputDir, ))
    if not os.access(outputDir, os.W_OK):
        raise IOError(
            errno.EACCES,
            "Not allowed to write to output directory %s" % (outputDir, ))
    outputDir = os.path.abspath(outputDir)

    outFilepath = os.path.join(outputDir, outFilename)

    deleteOldfile = False
    if os.path.exists(outFilepath):
        if overwrite:
            deleteOldfile = True
        else:
            raise IOError(errno.EEXIST,
                          "Raster file %s already exists" % outFilepath)

    cov = COVERAGES[coverage]
    grid_origin = cov['grid_origin']
    grid_offset = cov['grid_offset']
    grid_extent = cov['grid_extent']
    s_srs = cov['srs']

    if resx is None:
        resx = abs(grid_offset[0])
    if resy is None:
        resy = abs(grid_offset[1])
    t_srs = srs

    # For requests, grid cell centers are used. Need to add half the grid_offset to the grid_origin
    grid_origin_0 = grid_origin[0] + grid_offset[0] / 2.0
    grid_origin_1 = grid_origin[1] + grid_offset[1] / 2.0

    p = Proj(init=DEFAULT_SRS)
    (x1, y1) = p(bbox['minX'], bbox['minY'])
    (x2, y2) = p(bbox['maxX'], bbox['maxY'])
    # Pad the width of the bounding box as the Albers transform results in regions of interest
    # that are a bit narrower than I would like, which risks watershed boundaries lying beyond
    # the DEM boundary.
    len_x = x2 - x1
    del_x = len_x * 0.30
    x1 = x1 - del_x
    x2 = x2 + del_x
    bbox_srs = DEFAULT_SRS

    # Find the number of grid cells from the grid origin to each edge of the request.
    # Multiply by the grid_offset and add the grid origin to get to the request location.
    xi1 = floor(
        (x1 - grid_origin_0) / grid_offset[0]) * grid_offset[0] + grid_origin_0
    xi2 = ceil(
        (x2 - grid_origin_0) / grid_offset[0]) * grid_offset[0] + grid_origin_0
    yi1 = floor(
        (y1 - grid_origin_1) / grid_offset[1]) * grid_offset[1] + grid_origin_1
    yi2 = ceil(
        (y2 - grid_origin_1) / grid_offset[1]) * grid_offset[1] + grid_origin_1

    # coverage, crs, bbox, format. May have the following fields: response_srs, store, resx, resy, interpolation
    url = URL_PROTO.format(coverage=coverage,
                           x1=xi1,
                           y1=yi1,
                           x2=xi2,
                           y2=yi2,
                           bbox_srs=bbox_srs,
                           xoffset=grid_offset[0],
                           yoffset=grid_offset[1])
    #ORG urlFetched = "http://%s%s" % (HOST, url)
    urlFetched = "https://%s%s" % (HOST, url)

    if verbose:
        outfp.write("Acquiring DEM data from {0} ...\n".format(urlFetched))

    # Make initial request, which will return the URL of our clipped coverage
    r = requests.get(urlFetched)
    if r.status_code != 200:
        raise Exception(
            "Error fetching {url}, HTTP status code was {code} {reason}".
            format(urlFetched, r.status_code, r.reason))
    usgs_dem_coverage_handler = USGSDEMCoverageHandler()
    xml.sax.parseString(r.text, usgs_dem_coverage_handler)
    coverage_url = usgs_dem_coverage_handler.coverage_url
    if coverage_url is None:
        raise Exception(
            "Unable to deteremine coverage URL from WCS server response.  Response text was: {0}"
            .format(r.text))
    parsed_coverage_url = urllib.parse.urlparse(coverage_url)

    if verbose:
        outfp.write(
            "Downloading DEM coverage from {0} ...\n".format(coverage_url))

    # Download coverage to tempfile
    tmp_dir = tempfile.mkdtemp()
    tmp_cov_name = os.path.join(tmp_dir, 'usgswcsdemtmp')
    tmp_out = open(tmp_cov_name, mode='w+b')

    #ORG conn = httplib.HTTPConnection(parsed_coverage_url.netloc)
    conn = http.client.HTTPSConnection(parsed_coverage_url.netloc)
    try:
        conn.request('GET', parsed_coverage_url.path)
        res = conn.getresponse(buffering=True)
    except socket.error as e:
        msg = "Encountered the following error when trying to read raster from %s. Error: %s.  Please try again later or contact the developer." % \
            (urlFetched, str(e) )
        raise Exception(msg)

    if 200 != res.status:
        msg = "HTTP response %d %s encountered when querying %s.  Please try again later or contact the developer." % \
            (res.status, res.reason, urlFetched)
        raise Exception(msg)

    contentType = res.getheader('Content-Type')

    mimeType = 'image/tiff'
    if contentType.startswith(mimeType):
        # The data returned were of the type expected, read the data
        data = res.read(_BUFF_LEN)
        if data:
            dataFetched = True
            while data:
                tmp_out.write(data)
                data = res.read(_BUFF_LEN)
            tmp_out.close()
    elif contentType.startswith(CONTENT_TYPE_ERRORS):
        # Read the error and print to stderr
        msg = "The following error was encountered reading WCS coverage URL %s\n\n" %\
            (coverage_url, )
        data = res.read(_BUFF_LEN)
        while data:
            msg += data
        raise Exception(msg)
    else:
        msg = "Query for raster from URL %s returned content type %s, was expecting type %s. " + \
              " Operation failed." % \
            (coverage_url, contentType, mimeType)
        raise Exception(msg)

    # Rescale raster values if requested
    if scale != 1.0:
        # Rescale values in raster
        if verbose:
            outfp.write("Rescaling raster values by factor {0}".format(scale))
        rescale_out = os.path.basename("{0}_rescale".format(tmp_cov_name))
        rescaleRaster(config, tmp_dir, tmp_cov_name, rescale_out, scale)
        tmp_cov_name = os.path.join(tmp_dir, rescale_out)

    if deleteOldfile:
        deleteGeoTiff(outFilepath)

    if verbose:
        msg = "Resampling raster from {s_srs} to {t_srs} " + \
              "with X resolution {resx} and Y resolution {resy}\n"
        outfp.write(msg.format(s_srs=s_srs, t_srs=t_srs, resx=resx, resy=resy))

    # Re-sample to target spatial reference and resolution
    resampleRaster(config, outputDir, tmp_cov_name, outFilename, s_srs, t_srs,
                   resx, resy, interpolation)

    # Delete temp directory
    shutil.rmtree(tmp_dir)

    return (dataFetched, urlFetched)
    else:
        copyRasterToGeoTIFF(context.config, context.projectDir, inDEMPath, demFilename)
else:
    sys.stdout.write("Reprojecting DEM from %s to %s, spatial resolution (%.2f, %.2f) to (%.2f, %.2f)..." % \
                     (s_srs, t_srs, inSpatialMetadata[0], inSpatialMetadata[1],
                      demResolutionX, demResolutionY) )
    sys.stdout.flush()
    if args.scale:
        resampleRaster(context.config, context.projectDir, inDEMPath, demFilenameTemp,
                       s_srs, t_srs, demResolutionX, demResolutionY, args.resampleMethod)
    else:
        resampleRaster(context.config, context.projectDir, inDEMPath, demFilename,
                       s_srs, t_srs, demResolutionX, demResolutionY, args.resampleMethod)

if args.scale:
    rescaleRaster(context.config, context.projectDir, demFilepathTemp, demFilename, args.scale)
    os.unlink(demFilepathTemp)

sys.stdout.write('done\n')
# Get the bounding box for the DEM
bbox = getBoundingBoxForRaster(demFilepath)
# Write a shapefile for the bounding box
shpFilename = writeBboxPolygonToShapefile(bbox, context.projectDir, "studyarea")

# Write metadata
GenericMetadata.writeStudyAreaEntry(context, "bbox_wgs84", "%f %f %f %f" % (bbox['minX'], bbox['minY'], bbox['maxX'], bbox['maxY']))

# Get spatial metadata for DEM
demSpatialMetadata = getSpatialReferenceForRaster(demFilepath)
GenericMetadata.writeStudyAreaEntry(context, "dem_res_x", demSpatialMetadata[0])
GenericMetadata.writeStudyAreaEntry(context, "dem_res_y", demSpatialMetadata[1])