Ejemplo n.º 1
0
    def get_hucs(self, huc, level):
        """Loads HUCs from file."""
        huc = source_utils.huc_str(huc)
        huc_level = len(huc)

        # error checking on the levels, require file_level <= huc_level <= level <= lowest_level
        if self.lowest_level < level:
            raise ValueError("{}: files include HUs at max level {}.".format(
                self.name, self.lowest_level))
        if level < huc_level:
            raise ValueError(
                "{}: cannot ask for HUs at level {} contained in {}.".format(
                    self.name, level, huc_level))
        if huc_level < self.file_level:
            raise ValueError(
                "{}: files are organized at HUC level {}, so cannot ask for a larger HUC than that level."
                .format(self.name, self.file_level))

        # download the file
        filename = self._download(huc[0:self.file_level])
        logging.info('Using HUC file "{}"'.format(filename))

        # read the file
        layer = 'WBDHU{}'.format(level)
        logging.debug("{}: opening '{}' layer '{}' for HUCs in '{}'".format(
            self.name, filename, layer, huc))
        with fiona.open(filename, mode='r', layer=layer) as fid:
            hus = [
                hu for hu in fid
                if hu['properties']['HUC{:d}'.format(level)].startswith(huc)
            ]
            profile = fid.profile
        return profile, hus
Ejemplo n.º 2
0
    def get_hydro(self, huc, bounds=None, bounds_crs=None):
        """Get all reaches within a given HUC and/or coordinate bounds.

        Parameters
        ----------
        huc : int or str
          The USGS Hydrologic Unit Code
        bounds : [xmin, ymin, xmax, ymax], optional
          Coordinate bounds to filter reaches returned.  If this is provided,
          bounds_crs must also be provided.
        bounds_crs : CRS, optional
          CRS of the above bounds.

        Returns
        -------
        profile : dict
          The fiona shapefile profile (see Fiona documentation).
        reaches : list(dict)
          List of fiona shape objects representing the stream reaches.

        Note this finds and downloads files as needed.
        """
        if 'WBD' in self.name:
            raise RuntimeError(
                '{}: does not provide hydrographic data.'.format(self.name))

        huc = source_utils.huc_str(huc)
        hint_level = len(huc)

        # try to get bounds if not provided
        if bounds is None:
            # can we infer a bounds by getting the HUC?
            profile, hu = self.get_huc(huc)
            bounds = workflow.utils.bounds(hu)
            bounds_crs = workflow.crs.from_fiona(profile['crs'])

        # error checking on the levels, require file_level <= huc_level <= lowest_level
        if hint_level < self.file_level:
            raise ValueError(
                "{}: files are organized at HUC level {}, so cannot ask for a larger HUC than that level."
                .format(self.name, self.file_level))

        # download the file
        filename = self._download(huc[0:self.file_level])
        logging.info('Using Hydrography file "{}"'.format(filename))

        # find and open the hydrography layer
        filename = self.name_manager.file_name(huc[0:self.file_level])
        layer = 'NHDFlowline'
        logging.debug("{}: opening '{}' layer '{}' for streams in '{}'".format(
            self.name, filename, layer, bounds))
        with fiona.open(filename, mode='r', layer=layer) as fid:
            profile = fid.profile
            bounds = workflow.warp.bounds(
                bounds, bounds_crs, workflow.crs.from_fiona(profile['crs']))
            rivers = [r for (i, r) in fid.items(bbox=bounds)]
        return profile, rivers
Ejemplo n.º 3
0
    def get_hucs(self, huc, level, force_download=False):
        """Get all sub-catchments of a given HUC level within a given HUC.

        Parameters
        ----------
        huc : int or str
          The USGS Hydrologic Unit Code
        level : int
          Level of requested sub-catchments.  Must be larger or equal to the
          level of the input huc.
        force_download : bool
          Download or re-download the file if true.

        Returns
        -------
        profile : dict
          The fiona shapefile profile (see Fiona documentation).
        hus : list(dict)
          List of fiona shape objects representing the hydrologic units.

        Note this finds and downloads files as needed.
        """
        huc = source_utils.huc_str(huc)
        huc_level = len(huc)

        # error checking on the levels, require file_level <= huc_level <= level <= lowest_level
        if self.lowest_level < level:
            raise ValueError("{}: files include HUs at max level {}.".format(
                self.name, self.lowest_level))
        if level < huc_level:
            raise ValueError(
                "{}: cannot ask for HUs at level {} contained in {}.".format(
                    self.name, level, huc_level))
        if huc_level < self.file_level:
            raise ValueError(
                "{}: files are organized at HUC level {}, so cannot ask for a larger HUC than that level."
                .format(self.name, self.file_level))

        # download the file
        filename = self._download(huc[0:self.file_level], force=force_download)
        logging.info('Using HUC file "{}"'.format(filename))

        # read the file
        layer = 'WBDHU{}'.format(level)
        logging.debug("{}: opening '{}' layer '{}' for HUCs in '{}'".format(
            self.name, filename, layer, huc))

        with fiona.open(filename, mode='r', layer=layer) as fid:
            hus = [
                hu for hu in fid
                if source_utils.get_code(hu, level).startswith(huc)
            ]
            profile = fid.profile
        profile['always_xy'] = True
        return profile, hus
Ejemplo n.º 4
0
    def get_hucs(self, huc, level):
        huc = source_utils.huc_str(huc)
        if len(huc) > 2:
            return self.nhd_plus.get_hucs(huc, level)
        else:
            prof, wbd_hucs = self.wbd.get_hucs(huc, 4)
            contained = []
            for hu in wbd_hucs:
                print(list(hu['properties'].keys()))
                prof, subhucs = self.nhd_plus.get_hucs(hu['properties']['HUC4'], level)
                contained.extend(subhucs)
            return prof, contained

                
Ejemplo n.º 5
0
    def get_huc(self, huc, force_download=False):
        """Get the specified HUC in its native CRS.

        Parameters
        ----------
        huc : int or str
          The USGS Hydrologic Unit Code

        Returns
        -------
        profile : dict
          The fiona shapefile profile (see Fiona documentation).
        hu : dict
          Fiona shape object representing the hydrologic unit.

        Note this finds and downloads files as needed.
        """
        huc = source_utils.huc_str(huc)
        profile, hus = self.get_hucs(huc, len(huc), force_download)
        assert (len(hus) == 1)
        return profile, hus[0]
Ejemplo n.º 6
0
    def get_hydro(self, huc, bounds=None, bounds_crs=None):
        """Downloads and reads hydrography within these bounds and/or huc.

        Note this requires a HUC hint of at least a level 4 HUC which contains bounds.
        """
        if 'WBD' in self.name:
            raise RuntimeError(
                '{}: does not provide hydrographic data.'.format(self.name))

        huc = source_utils.huc_str(huc)
        hint_level = len(huc)

        # try to get bounds if not provided
        if bounds is None:
            # can we infer a bounds by getting the HUC?
            profile, hu = self.get_huc(huc)
            bounds = workflow.utils.bounds(hu)
            bounds_crs = profile['crs']

        # error checking on the levels, require file_level <= huc_level <= lowest_level
        if hint_level < self.file_level:
            raise ValueError(
                "{}: files are organized at HUC level {}, so cannot ask for a larger HUC than that level."
                .format(self.name, self.file_level))

        # download the file
        filename = self._download(huc[0:self.file_level])
        logging.info('Using Hydrography file "{}"'.format(filename))

        # find and open the hydrography layer
        filename = self.name_manager.file_name(huc[0:self.file_level])
        layer = 'NHDFlowline'
        logging.debug("{}: opening '{}' layer '{}' for streams in '{}'".format(
            self.name, filename, layer, bounds))
        with fiona.open(filename, mode='r', layer=layer) as fid:
            profile = fid.profile
            bounds = workflow.warp.warp_bounds(bounds, bounds_crs,
                                               profile['crs'])
            rivers = [r for (i, r) in fid.items(bbox=bounds)]
        return profile, rivers
Ejemplo n.º 7
0
 def get_huc(self, huc):
     huc = source_utils.huc_str(huc)
     profile, hus = self.get_hucs(huc, len(huc))
     assert (len(hus) == 1)
     return profile, hus[0]
Ejemplo n.º 8
0
 def get_huc(self, huc):
     huc = source_utils.huc_str(huc)
     if len(huc) > 2:
         return self.nhd_plus.get_huc(huc)
     else:
         return self.wbd.get_huc(huc)
Ejemplo n.º 9
0
    def get_hydro(self,
                  huc,
                  bounds=None,
                  bounds_crs=None,
                  in_network=True,
                  force_download=False):
        """Get all reaches within a given HUC and/or coordinate bounds.

        Parameters
        ----------
        huc : int or str
          The USGS Hydrologic Unit Code
        bounds : [xmin, ymin, xmax, ymax], optional
          Coordinate bounds to filter reaches returned.  If this is provided,
          bounds_crs must also be provided.
        bounds_crs : CRS, optional
          CRS of the above bounds.
        in_network : bool, optional
          If True (default), remove reaches that are not "in" the NHD network
        force_download : bool
          Download or re-download the file if true.

        Returns
        -------
        profile : dict
          The fiona shapefile profile (see Fiona documentation).
        reaches : list(dict)
          List of fiona shape objects representing the stream reaches.

        Note this finds and downloads files as needed.
        """
        if 'WBD' in self.name:
            raise RuntimeError(
                '{}: does not provide hydrographic data.'.format(self.name))

        huc = source_utils.huc_str(huc)
        hint_level = len(huc)

        # try to get bounds if not provided
        if bounds is None:
            # can we infer a bounds by getting the HUC?
            profile, hu = self.get_huc(huc)
            bounds = workflow.utils.bounds(hu)
            bounds_crs = workflow.crs.from_fiona(profile['crs'])

        # error checking on the levels, require file_level <= huc_level <= lowest_level
        if hint_level < self.file_level:
            raise ValueError(
                "{}: files are organized at HUC level {}, so cannot ask for a larger HUC than that level."
                .format(self.name, self.file_level))

        # download the file
        filename = self._download(huc[0:self.file_level], force=force_download)
        logging.info('  Using Hydrography file "{}"'.format(filename))

        # find and open the hydrography layer
        filename = self.name_manager.file_name(huc[0:self.file_level])
        layer = 'NHDFlowline'
        logging.info(
            "  {}: opening '{}' layer '{}' for streams in '{}'".format(
                self.name, filename, layer, bounds))
        with fiona.open(filename, mode='r', layer=layer) as fid:
            profile = fid.profile
            bounds = workflow.warp.bounds(
                bounds, bounds_crs, workflow.crs.from_fiona(profile['crs']))
            reaches = [r for (i, r) in fid.items(bbox=bounds)]

        # filter not in network
        if in_network:
            logging.info("  Filtering reaches not in-network".format(
                self.name, filename, layer, bounds))
            reaches = [
                r for r in reaches if 'InNetwork' not in r['properties']
                or r['properties']['InNetwork'] == 1
            ]

        # associate catchment areas with the reaches if NHDPlus
        if 'Plus' in self.name:
            layer = 'NHDPlusCatchment'
            logging.info(
                "  {}: opening '{}' layer '{}' for catchment areas in '{}'".
                format(self.name, filename, layer, bounds))
            with fiona.open(filename, mode='r', layer=layer) as fid:
                bounded_catchments = list(fid.items())  #.items(bbox=bounds))

            missing_catchments = 0
            for i, reach in enumerate(reaches):
                try:
                    catch = next(c for (i, c) in bounded_catchments
                                 if c['properties']['NHDPlusID'] ==
                                 reach['properties']['NHDPlusID'])
                except StopIteration:
                    logging.debug(
                        f"reach missing catchment: {reach['properties']['NHDPlusID']}"
                    )

                    if missing_catchments == i and missing_catchments > 10:
                        # give up fairly quickly, as this can be slow
                        break
                    missing_catchments += 1
                else:
                    reach['properties']['catchment'] = catch

        return profile, reaches