Source code for watershed_workflow.sources.manager_3dep
"""Manager for downloading 3DEP data."""
from typing import Tuple, Optional, List
import cftime
import logging
import shapely.geometry
import xarray as xr
import py3dep
import watershed_workflow.crs
from watershed_workflow.crs import CRS
from . import manager_dataset
[docs]
class Manager3DEP(manager_dataset.ManagerDataset):
    """3D Elevation Program (3DEP) data manager.
    
    Provides access to USGS 3DEP elevation and derived products through
    the py3dep library. Supports multiple resolution options and various
    topographic layers including DEM, slope, aspect, and hillshade products.
    """
    
    def __init__(self, resolution : int):
        """Downloads DEM data from the 3DEP.
        Parameters
        ----------
        resolution : int
            Resolution in meters. Valid resolutions are: 60, 30, or 10.
        """
        self._resolution = resolution
        resolution_in_degrees = 2 * resolution * 9e-6
        in_crs = CRS.from_epsg(4326)  # lat-long
        out_crs = CRS.from_epsg(5070)  # CONUS Albers Equal Area
        valid_variables = [
            'DEM', 'Hillshade Gray', 'Aspect Degrees', 'Aspect Map',
            'GreyHillshade_elevationFill', 'Hillshade Multidirectional', 
            'Slope Map', 'Slope Degrees', 'Hillshade Elevation Tinted',
            'Height Ellipsoidal', 'Contour 25', 'Contour Smoothed 25'
        ]
        default_variables = ['DEM']
        
        # Initialize base class with native properties
        super().__init__('3DEP', 'py3dep', resolution_in_degrees, in_crs, out_crs,
                         None, None, valid_variables, default_variables)
    def _requestDataset(self, request : manager_dataset.ManagerDataset.Request
                        ) -> manager_dataset.ManagerDataset.Request:
        """Request the data -- ready upon request."""
        request.is_ready = True
        return request
    def _fetchDataset(self, request : manager_dataset.ManagerDataset.Request) -> xr.Dataset:
        """Implementation of abstract method to get 3DEP data."""
        # Base class ensures these for multi-variable, time independent class
        assert request.variables is not None
        assert request.start is None
        assert request.end is None
        
        # Use instance resolution and native CRS
        logging.info(f'Getting DEM with map of area = {request.geometry.area}')
        result = py3dep.get_map(request.variables, request.geometry, self._resolution, 
                               geo_crs=self.native_crs_in, crs=self.native_crs_out)
        
        # py3dep returns DataArray for single layer, Dataset for multiple layers
        if isinstance(result, xr.DataArray):
            # Convert DataArray to Dataset
            result = result.to_dataset(name=request.variables[0].lower().replace(' ', '_'))
            
        return result