Beispiel #1
0
class DinfFlowDirOp(Operation):
    """ Compute Slope and Aspect from a DEM """

    title = 'D-infinity Flow Direction'
    name = 'dinf-flow-direction'
    output_types = [OutputType('slope', 'tif'), OutputType('aspect', 'tif')]

    def run(self, elevation_ds):
        subprocess.call([
            'dinfflowdir', '-fel', elevation_ds.loc.path, '-slp',
            self.locs['slope'].path, '-ang', self.locs['aspect'].path
        ])
Beispiel #2
0
class DownloadOpenDASOp(Operation):

    title = 'Download OpenDAS data using ncks'
    name = 'download-opendas'
    output_types = [OutputType('chunks', 'nc'), OutputType('data', 'nc')]

    def run(self, ds, vars, bbox, start, end, chunk=None):
        # Split large data sets into chunks
        if chunk:
            dates = pd.date_range(start, end, freq=chunk)
        else:
            dates = [start, end]
        print(self.locs['chunks'].filename)
        self.locs['chunks'] = DatetimeLoc(datetimes=dates,
                                          template=self.locs['chunks'])
        self.locs['chunks'].configure(self.cfg)

        # Download
        nco = Nco()
        for i, (start_date, end_date) in enumerate(zip(dates[:-1], dates[1:])):
            info = {'year': start_date.year}
            logging.debug(ds.loc.url.format(**info))
            logging.debug(self.locs['chunks'].locs[i].filename)
            nco.ncks(
                input=ds.loc.url.format(**info),
                output=self.locs['chunks'].locs[i].path,
                options=[
                    '--mk_rec_dmn time', '-v ' + ','.join(vars),
                    '-d ' + ','.join(
                        ['time',
                         start_date.isoformat(),
                         end_date.isoformat()]),
                    '-d ' + ','.join(
                        ['lon', str(bbox.min.lon),
                         str(bbox.max.lon)]),
                    '-d ' + ','.join(
                        ['lat', str(bbox.min.lat),
                         str(bbox.max.lat)])
                ])

        # Merge chunks
        cat_args = [
            'SKIP_SAME_TIME=1', 'cdo', 'mergetime',
            os.path.join(self.locs['chunks'].dirname, '*'),
            self.locs['data'].path
        ]
        logging.info('Calling process %s', ' '.join(cat_args))
        cat_process = subprocess.Popen(cat_args,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.STDOUT)
        cat_output, _ = cat_process.communicate()
Beispiel #3
0
class DownloadThreddsOp(Operation):

    title = 'Download Thredds data'
    name = 'download-thredds'
    output_types = [OutputType('chunks', 'nc'), OutputType('data', 'nc')]

    def run(self, ds, vars, bbox, start, end, chunk=None):
        # Split large data sets into chunks
        if chunk:
            dates = pd.date_range(start, end, freq=chunk)
        else:
            dates = [start, end]
        self.locs['chunks'] = DatetimeLoc(datetimes=dates,
                                          template=self.locs['chunks'])
        self.locs['chunks'].configure(self.cfg)

        # Download
        for i, (start_date, end_date) in enumerate(zip(dates[:-1], dates[1:])):
            info = {'year': start_date.year}

            params = {
                'var': vars,
                'north': bbox.urc.lat,
                'west': bbox.llc.lon,
                'east': bbox.urc.lon,
                'south': bbox.llc.lat,
                'horizStride': 1,
                'time_start': start_date,
                'time_end': end_date,
                'timeStride': 1,
                'accept': 'netcdf'
            }
            r = requests.get(ds.loc.url.format(**info),
                             params=params,
                             stream=True)
            with open(self.locs['chunks'].locs[i].path, 'wb') as file:
                for chunk in r.iter_content(chunk_size=128):
                    file.write(chunk)

        # Merge chunks
        cat_args = [
            'SKIP_SAME_TIME=1', 'cdo', 'mergetime',
            os.path.join(self.locs['chunks'].dirname, '*'),
            self.locs['data'].path
        ]
        logging.info('Calling process %s', ' '.join(cat_args))
        cat_process = subprocess.Popen(cat_args,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.STDOUT)
        cat_output, _ = cat_process.communicate()
Beispiel #4
0
class FlowDirectionOp(Operation):

    title = 'Flow Direction'
    name = 'flow-direction'
    output_types = [
        OutputType('flow-direction', 'tif'),
        OutputType('slope', 'tif')
    ]

    def run(self, dem_ds):
        subprocess.call([
            'd8flowdir', '-fel', dem_ds.loc.path, '-p',
            self.locs['flow-direction'].path, '-sd8', self.locs['slope'].path
        ])
Beispiel #5
0
class PeukerDouglasStreamDefinitionOp(Operation):
    """ Run the TauDEM Peuker Douglas Stream Definition Command """

    title = 'Peuker Douglas Stream Definition'
    name = 'stream-def-pd'
    output_types = [
        OutputType('ssa', 'tif'),
        OutputType('drop-analysis', 'txt'),
        OutputType('stream-definition', 'tif')
    ]

    def run(self, no_sinks_ds, flow_dir_ds, source_area_ds, outlet_ds):
        # The threshold range should be selected based on the raster size
        # Something like 10th to 99th percentile of flow accumulation?

        ## This is a three-step process - first compute the D8 source area
        subprocess.call([
            'aread8', '-p', flow_dir_ds.loc.path, '-o', outlet_ds.loc.path,
            '-ad8', self.locs['ssa'].path
        ])

        ## Next perform the drop analysis
        # This selects a sensible flow accumulation threshold value
        thresh_min = np.percentile(source_area_ds.array, 95)
        thresh_max = np.max(source_area_ds.array)

        subprocess.call([
            'dropanalysis', '-p', flow_dir_ds.loc.path, '-fel',
            no_sinks_ds.loc.path, '-ad8', source_area_ds.loc.path, '-o',
            outlet_ds.loc.path, '-ssa', self.locs['ssa'].path, '-drp',
            self.locs['drop-analysis'].path, '-par',
            str(thresh_min),
            str(thresh_max), '40', '0'
        ])

        ## Finally define the stream
        # Extract the threshold from the first row with drop statistic t < 2
        with open(self.locs['drop-analysis'].path, 'r') as drop_file:
            # Get optimum threshold value from last line
            for line in drop_file:
                pass
            last = line
            thresh_re = re.compile(r'([\.\d]*)$')
            threshold = thresh_re.search(last).group(1)

        subprocess.call([
            'threshold', '-ssa', self.locs['ssa'].path, '-thresh', threshold,
            '-src', self.locs['stream-definition'].path
        ])
Beispiel #6
0
class LabelGagesOp(Operation):
    """ Add a sequential id field to each outlet in a shapefile """

    title = 'Labeled Gages'
    name = 'label-outlet'
    output_types = [OutputType('labelled-outlet', 'shp')]

    def run(self, outlet_ds):
        ## Fix this - modify dataset at the new location, not the old
        outlet_ds.chmod(True)
        outlet_path = outlet_ds.loc

        layer = outlet_ds.dataset.GetLayer()
        id_field = ogr.FieldDefn('id', ogr.OFTInteger)
        layer.CreateField(id_field)

        feature = layer.GetNextFeature()
        gage_id = 1

        while feature:
            feature.SetField("id", gage_id)
            layer.SetFeature(feature)
            feature = layer.GetNextFeature()
            gage_id += 1

        # Clean up and copy to correct location
        outlet_ds.dataset.Destroy()
        for a_file in glob.glob(r'{}.*'.format(outlet_path.no_ext)):
            shutil.copyfile(
                a_file, self.locs['labelled-outlet'].ext(
                    os.path.splitext(a_file)[1][1:]))
class ReprojectVectorOp(Operation):

    title = 'Reproject Vector Dataset'
    name = 'reproject-vector'
    output_types = [OutputType('reprojected', 'shp')]

    def run(self, input_ds, srs):
        reproj_ds = BoundaryDataset(self.locs['reprojected'],
                                    update=True).new()

        out_srs = osr.SpatialReference()
        if not srs.startswith('EPSG:'):
            raise ValueError('SRS definition must start with "EPSG:"')
        out_srs.ImportFromEPSG(int(srs[5:]))

        for layer in input_ds.layers:
            # SRS transform
            in_srs = layer.GetSpatialRef()
            transform = osr.CoordinateTransformation(in_srs, out_srs)

            # create the output layer
            reproj_layer = reproj_ds.dataset.CreateLayer(
                layer.GetName(), srs=out_srs, geom_type=layer.GetGeomType())

            # add fields
            layer_defn = layer.GetLayerDefn()
            for i in range(0, layer_defn.GetFieldCount()):
                field_defn = layer_defn.GetFieldDefn(i)
                reproj_layer.CreateField(field_defn)

            # loop through the input features
            reproj_layer_defn = reproj_layer.GetLayerDefn()
            feature = layer.GetNextFeature()
            while feature:
                # reproject the input geometry
                geom = feature.GetGeometryRef()
                geom.Transform(transform)

                # create a new feature with same geometry and attributes
                reproj_feature = ogr.Feature(reproj_layer_defn)
                reproj_feature.SetGeometry(geom)
                for j in range(0, reproj_layer_defn.GetFieldCount()):
                    reproj_feature.SetField(
                        reproj_layer_defn.GetFieldDefn(j).GetNameRef(),
                        feature.GetField(j))

                # add the feature to the shapefile
                reproj_layer.CreateFeature(reproj_feature)

                # dereference the features and get the next input feature
                reproj_feature = None
                feature = layer.GetNextFeature()

        # Save and close the shapefiles
        input_ds.close()
        reproj_ds.close()
Beispiel #8
0
class StreamNetworkOp(Operation):
    """ Run the TauDEM Stream Reach and Watershed Command """

    title = 'Stream Network'
    name = 'stream-network'
    output_types = [
        OutputType('order', 'tif'),
        OutputType('tree', 'tsv'),
        OutputType('coord', 'tsv'),
        OutputType('network', 'shp'),
        OutputType('watershed', 'tif')
    ]

    tree_colnames = [
        'link_no', 'start_coord', 'end_coord', 'next_link', 'previous_link',
        'order', 'monitoring_point_id', 'network_magnitude'
    ]

    coord_colnames = [
        'x', 'y', 'distance_to_terminus', 'elevation', 'contributing_area'
    ]

    def run(self, no_sinks_ds, flow_dir_ds, source_area_ds, pd_stream_def_ds,
            outlet_ds):

        subprocess.call([
            'streamnet', '-p', flow_dir_ds.loc.path, '-fel',
            no_sinks_ds.loc.path, '-ad8', source_area_ds.loc.path, '-src',
            pd_stream_def_ds.loc.path, '-o', outlet_ds.loc.path, '-ord',
            self.locs['order'].path, '-tree', self.locs['tree'].path, '-coord',
            self.locs['coord'].path, '-net', self.locs['network'].path, '-w',
            self.locs['watershed'].path
        ])
        self.locs['tree'].csvargs.update({
            'delimiter': '\t',
            'header': None,
            'names': self.tree_colnames
        })
        self.locs['coord'].csvargs.update({
            'delimiter': '\t',
            'header': None,
            'names': self.coord_colnames
        })
class SoilType(Operation):
    """ Soil texture category from percent clay/sand/slit """

    title = "Soil Type"
    name = 'soil-type'
    output_types = [OutputType('type', 'gtif')]

    textures = {
        'Sand': 1,
        'Loamy sand': 2,
        'Sandy loam': 3,
        'Silty loam': 4,
        'Silt': 5,
        'Loam': 6,
        'Sandy clay loam': 7,
        'Silty clay loam': 8,
        'Clay loam': 9,
        'Sandy clay': 10,
        'Silty clay': 11,
        'Clay': 12,
        'Loamy sand': 13,
    }

    def run(self, texture_ds):
        types = {ds.meta['type']: ds for ds in texture_ds}
        clay = types['clay'].array
        sand = types['sand'].array
        silt = types['silt'].array
        t = 0 * np.ones(clay.shape)

        t = np.where((clay > 40) & (silt > 40), self.textures['Silty clay'], t)
        t = np.where((t == 0) & (clay > 40) & (sand < 56),
                     self.textures['Clay'], t)
        t = np.where((t == 0) & (clay > 28) & (sand < 20),
                     self.textures['Silty clay loam'], t)
        t = np.where((t == 0) & (clay > 28) & (sand < 44),
                     self.textures['Silty clay loam'], t)
        t = np.where((t == 0) & (clay > 36), self.textures['Sandy clay'], t)
        t = np.where((t == 0) & (clay > 20) & (silt < 28),
                     self.textures['Sandy clay'], t)
        t = np.where((t == 0) & (clay < 12) & (silt > 80),
                     self.textures['Silt'], t)
        t = np.where((t == 0) & (silt > 50), self.textures['Silty loam'], t)
        t = np.where((t == 0) & (clay > 8) & (sand < 52),
                     self.textures['Loam'], t)
        t = np.where((t == 0) & (sand - clay < 70),
                     self.textures['Sandy loam'], t)
        t = np.where((t == 0) & (sand - clay / 4. < 87.5),
                     self.textures['Loamy sand'], t)
        t = np.where(t == 0, self.textures['Sand'], t)

        texture_ds = GDALDataset(self.locs['type'], template=types['clay'])
        texture_ds.array = t
Beispiel #10
0
class DownloadPolarisOp(Operation):
    """ Download POLARIS data """

    title = 'Download POLARIS data'
    name = 'download-polaris'
    output_types = [OutputType('polaris', 'tif')]

    def run(self, input_ds):
        if not input_ds.loc.exists:
            req = requests.get(input_ds.loc.url, stream=True)
            with open(input_ds.loc.path, 'wb') as file:
                shutil.copyfileobj(req.raw, file)
Beispiel #11
0
class StreamDefinitionByThresholdOp(Operation):
    """ Run the TauDEM Stream Definition By Threshold Command """

    title = 'Stream Definition By Threshold'
    name = 'stream-definition-threshold'
    output_types = [OutputType('stream-raster', 'tif')]

    def run(self, source_area_ds):
        threshold = np.percentile(source_area_ds.array, 98)
        subprocess.call([
            'threshold', '-ssa', source_area_ds.loc.path, '-thresh',
            '{:.1f}'.format(threshold), '-src', self.locs['stream-raster'].path
        ])
Beispiel #12
0
class SourceAreaOp(Operation):

    title = 'Source Area/Flow Accumulation'
    name = 'source-area'
    output_types = [OutputType('source-area', 'tif')]

    def run(self, flow_dir_ds):
        subprocess.call([
            'aread8', '-p', flow_dir_ds.loc.path, '-ad8',
            self.locs['source-area'].path, '-nc'
        ])
        source_area_ds = GDALDataset(self.locs['source-area'])
        source_area_ds.nodata = 0
class ClipOp(Operation):

    title = 'Raster clipped to boundary'
    name = 'clip'
    output_types = [OutputType('clipped_raster', 'tif')]

    def run(self, input_ds, boundary_ds, algorithm='bilinear'):
        clip_warp_options = gdal.WarpOptions(format='GTiff',
                                             cutlineDSName=boundary_ds.loc.shp,
                                             cutlineBlend=.5,
                                             dstNodata=input_ds.nodata,
                                             resampleAlg=algorithm)
        gdal.Warp(path, input_ds.dataset, options=clip_warp_options)
Beispiel #14
0
class RemoveSinksOp(Operation):

    title = 'Sinks Removed'
    name = 'remove-sinks'
    output_types = [OutputType('no-sinks', 'tif')]

    def run(self, input_ds):
        # Remove Pits / Fill sinks
        proc = subprocess.call([
            'pitremove', '-z', input_ds.loc.path, '-fel',
            self.locs['no-sinks'].path
        ],
                               stdout=sys.stdout)
Beispiel #15
0
class MoveOutletsToStreamOp(Operation):
    """ Run the TauDEM Move Outlets to Streams Command """

    title = 'Move Outlets to Streams'
    name = 'snap-outlet'
    output_types = [
        OutputType('outlet-on-stream-nosrs', 'shp'),
        OutputType('outlet-on-stream', 'shp')
    ]

    def run(self, flow_dir_ds, stream_ds, outlet_ds):
        subprocess.call([
            'moveoutletstostrm', '-p', flow_dir_ds.loc.path, '-src',
            stream_ds.loc.path, '-o', outlet_ds.loc.path, '-om',
            self.locs['outlet-on-stream-nosrs'].path, '-omlyr', 'outlet'
        ])

        # Copy spatial reference from original outlet
        in_ds = BoundaryDataset(self.locs['outlet-on-stream-nosrs'])
        out_ds = BoundaryDataset(self.locs['outlet-on-stream'],
                                 update=True).new()

        # Create layer with outlet srs
        in_lyr = in_ds.dataset.GetLayer()
        outlet_lyr = outlet_ds.dataset.GetLayer()
        srs = outlet_lyr.GetSpatialRef()
        geom_type = in_lyr.GetLayerDefn().GetGeomType()
        out_lyr = out_ds.dataset.CreateLayer('outlet', srs, geom_type)

        # Copy outlet in correct srs
        outlet = in_lyr.GetFeature(0)
        out_feature = ogr.Feature(out_lyr.GetLayerDefn())
        out_feature.SetGeometry(outlet.GetGeometryRef().Clone())
        out_lyr.CreateFeature(out_feature)

        # Clean up
        out_ds.dataset.Destroy()
        in_ds.dataset.Destroy()
class SkyviewOp(Operation):
    """ Compute skyview files from a DEM """

    title = 'Skyview'
    name = 'skyview'
    output_types = [OutputType('skyview', 'nc')]

    def run(self,
            elevation_ds,
            elevation_epsg,
            time_zone,
            dt=1,
            n_directions=8,
            year=2000):
        standard_meridian_srs = osr.SpatialReference()
        standard_meridian_srs.ImportFromEPSG(4326)
        elevation_srs = osr.SpatialReference()
        elevation_srs.ImportFromEPSG(int(elevation_epsg[5:]))
        dem_to_latlon = osr.CoordinateTransformation(elevation_srs,
                                                     standard_meridian_srs)
        center = CoordProperty(*dem_to_latlon.TransformPoint(
            elevation_ds.center.lon, elevation_ds.center.lat)[:-1])

        # Skyview
        n_rows = str(elevation_ds.size.y)
        n_cols = str(elevation_ds.size.x)
        cell_size = str(int(elevation_ds.res.x))
        longitude = str(center.lon)
        latitude = str(center.lat)
        standard_meridian_lon = str(time_zone * 15.0)
        x_origin = str(elevation_ds.ul.x)
        y_origin = str(elevation_ds.ul.y)
        n_directions = str(n_directions)
        year = str(year)
        day = '15'
        steps_per_day = str(int(24 / dt))
        dt = str(dt)

        skyview_args = [
            self.scripts['skyview'], elevation_ds.loc.path,
            self.locs['skyview'].path, n_directions, n_rows, n_cols, cell_size,
            x_origin, y_origin
        ]
        logging.info('Calling process %s', ' '.join(skyview_args))
        skyview_process = subprocess.Popen(skyview_args,
                                           stdout=subprocess.PIPE,
                                           stderr=subprocess.STDOUT)
        skyview_output, _ = skyview_process.communicate()
        logging.info(skyview_output)
Beispiel #17
0
class ConvertOp(Operation):

    output_types = [OutputType('flow-direction', 'gtif')]

    def run(self, flow_dir_ds):
        converted_ds = GDALDataset(self.locs['flow-direction'],
                                   template=flow_dir_ds)
        convert_array = np.vectorize(self.convert)

        input_array = np.ma.masked_where(
            flow_dir_ds.array == flow_dir_ds.nodata, flow_dir_ds.array)
        input_array.set_fill_value(flow_dir_ds.nodata)

        converted = convert_array(input_array)
        converted_ds.array = converted.filled()
class MatchRasterOp(Operation):

    title = 'Warp dataset to match a template raster'
    name = 'match-raster'
    output_types = [OutputType('matched', 'tif')]

    def run(self, input_ds, template_ds=None, algorithm='bilinear'):
        agg_warp_options = gdal.WarpOptions(
            outputBounds=template_ds.rev_warp_output_bounds,
            width=template_ds.size.x,
            height=template_ds.size.y,
            resampleAlg=algorithm,
        )
        gdal.Warp(self.locs['matched'].path,
                  input_ds.dataset,
                  options=agg_warp_options)
Beispiel #19
0
class GageWatershedOp(Operation):
    """
    Run the TauDEM Gage Watershed Command

    Raster labeling each point by which gage it drains to directly
    """

    title = 'Gage Watershed'
    name = 'gage-watershed'
    output_types = [OutputType('gage-watershed', 'tif')]

    def run(self, flow_dir_ds, outlet_ds):
        subprocess.call([
            'gagewatershed', '-p', flow_dir_ds.loc.path, '-o',
            outlet_ds.loc.path, '-gw', self.locs['gage-watershed'].path
        ])
class SoilDepthOp(Operation):
    """ Compute soil depth from slope, elevation, and source area"""

    title = 'Soil Depth'
    name = 'soil-depth'
    output_types = [OutputType('soil-depth', 'gtif')]

    def run(self,
            slope_ds,
            source_ds,
            elev_ds,
            min_depth,
            max_depth,
            wt_slope=0.7,
            wt_source=0.0,
            wt_elev=0.3,
            max_slope=30.0,
            max_source=100000.0,
            max_elev=1500.0,
            pow_slope=0.25,
            pow_source=1.0,
            pow_elev=0.75):
        wt_total = float(wt_slope + wt_source + wt_elev)

        if not wt_total == 1.0:
            logging.warning(
                'Soil depth weights do not add up to 1.0 - scaling')
            wt_slope = wt_slope / wt_total
            wt_source = wt_source / wt_total
            wt_elev = wt_elev / wt_total

        # Scale sources: [min, max] -> [min/max, 1]
        slope_arr = np.clip(slope_ds.array / max_slope, None, 1)
        source_arr = np.clip(source_ds.array / max_source, None, 1)
        elev_arr = np.clip(elev_ds.array / max_elev, None, 1)

        # Calculate soil depth
        soil_depth_arr = min_depth + \
                (max_depth - min_depth) * (
                    wt_slope  * (1.0 - np.power(slope_arr, pow_slope)) +
                    wt_source * np.power(source_arr,       pow_source) +
                    wt_elev   * (1.0 - np.power(elev_arr,  pow_elev))
                )

        # Save in a dataset matching the DEM
        soil_depth_ds = GDALDataset(self.locs['soil-depth'], template=elev_ds)
        soil_depth_ds.array = soil_depth_arr
class ConvertFileType(Operation):

    title = 'Convert Filetype'
    name = 'convert-filetype'
    output_types = [OutputType('converted', '')]

    def run(self, input_ds, filetype, compress=None):
        self.locs['converted'].default_ext = filetype
        coptions = []
        if compress:
            coptions.append('COMPRESS=' + compress)
        convert_translate_options = gdal.TranslateOptions(
            format=input_ds.filetypes[filetype], creationOptions=coptions)
        gdal.Translate(self.locs['converted'].path,
                       input_ds.dataset,
                       options=convert_translate_options,
                       format=input_ds.filetypes[filetype])
class CropOp(Operation):

    title = 'Crop Raster Dataset'
    name = 'crop'
    output_types = [OutputType('cropped', 'tif')]

    def run(self,
            input_ds,
            template_ds=None,
            bbox=None,
            padding=CoordProperty(x=0, y=0),
            algorithm='bilinear',
            ignore_srs=False):
        if template_ds:
            bbox = copy.copy(template_ds.bbox)
            # SRS needs to match for output bounds and input dataset
            if not ignore_srs:
                if not template_ds.srs == input_ds.srs:
                    transform = osr.CoordinateTransformation(
                        template_ds.srs, input_ds.srs)
                    logging.debug('Bounding Box: %s', bbox)
                    logging.debug('Coordinate transformation: %s', transform)
                    logging.debug(template_ds.srs)
                    logging.debug(input_ds.srs)
                    bbox = BBox(llc=CoordProperty(*transform.TransformPoint(
                        bbox.llc.x, bbox.llc.y)[:-1]),
                                urc=CoordProperty(*transform.TransformPoint(
                                    bbox.urc.x, bbox.urc.y)[:-1]))

        grid_box = [
            bbox.llc.x - padding.x, bbox.llc.y - padding.y,
            bbox.urc.x + padding.x, bbox.urc.y + padding.y
        ]

        agg_warp_options = gdal.WarpOptions(
            outputBounds=grid_box,
            resampleAlg=algorithm,
        )

        gdal.UseExceptions()
        out = gdal.Warp(self.locs['cropped'].path,
                        input_ds.dataset,
                        options=agg_warp_options)
        logging.debug('GDAL output: %s', out)
class MosaicOp(Operation):

    title = 'Mosaic Rasters with gdal_merge'
    name = 'mosaic'
    output_types = [OutputType('merged', 'tif')]

    def run(self, input_ds):
        if not hasattr(input_ds, '__iter__'):
            input_ds = [input_ds]
        merge_args = [
            'gdal_merge.py', '-o', self.locs['merged'].path,
            *[ds.loc.path for ds in input_ds]
        ]
        logging.info('Calling process %s', ' '.join(merge_args))
        merge_process = subprocess.Popen(merge_args,
                                         stdout=subprocess.PIPE,
                                         stderr=subprocess.STDOUT)
        merge_output, _ = merge_process.communicate()
        logging.info(merge_output)
class AverageLayers(Operation):
    """ Weighted average of layers by depth """

    title = "Average Layers"
    name = 'average-layers'
    output_types = [OutputType('average', 'gtif')]

    def run(self, layered_ds):
        diff = []
        for layer in layered_ds:
            diff.append(layer.meta['layer_max'] - layer.meta['layer_min'])
        weights = [d / sum(diff) for d in diff]

        average = 0 * np.ones(layered_ds[0].array.shape)
        for layer, weight in zip(layered_ds, weights):
            average += layer.array * weight

        average_ds = GDALDataset(self.locs['average'], template=layered_ds[0])
        average_ds.array = average
class MergeOp(Operation):

    title = 'Merge rasters into a virtual raster'
    name = 'merge'
    output_types = [OutputType('merged', 'vrt')]

    def run(self, input_ds):
        if not hasattr(input_ds, '__iter__'):
            input_ds = [input_ds]
        vrt_args = [
            'gdalbuildvrt', self.locs['merged'].path,
            *[ds.loc.path for ds in input_ds]
        ]
        logging.info('Calling process %s', ' '.join(vrt_args))
        vrt_process = subprocess.Popen(vrt_args,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.STDOUT)
        vrt_output, _ = vrt_process.communicate()
        logging.info(vrt_output)
Beispiel #26
0
class FlowDistanceOp(Operation):

    output_types = [OutputType('flow-distance', 'gtif')]

    def dir2dist(self, lat, lon, direction, res, nodata):
        distance = direction.astype(np.float64)
        np.copyto(distance,
                  self.distance(lon - res.lon / 2, lat, lon + res.lon / 2,
                                lat),
                  where=np.isin(direction, [1, 5]))
        np.copyto(distance,
                  self.distance(lon, lat - res.lat / 2, lon,
                                lat + res.lat / 2),
                  where=np.isin(direction, [3, 7]))
        np.copyto(distance,
                  self.distance(lon - res.lon / 2, lat - res.lat / 2,
                                lon + res.lon / 2, lat + res.lat / 2),
                  where=np.isin(direction, [2, 4, 6, 8]))
        return distance

    def run(self, flow_dir_ds):
        direction = flow_dir_ds.array
        lon, lat = np.meshgrid(flow_dir_ds.cgrid.lon, flow_dir_ds.cgrid.lat)
        res = flow_dir_ds.resolution

        direction = np.ma.masked_where(direction == flow_dir_ds.nodata,
                                       direction)
        lat = np.ma.masked_where(direction == flow_dir_ds.nodata, lat)
        lon = np.ma.masked_where(direction == flow_dir_ds.nodata, lon)

        distance = self.dir2dist(lat, lon, direction, res, flow_dir_ds.nodata)

        flow_dist_ds = GDALDataset(self.locs['flow-distance'],
                                   template=flow_dir_ds)

        # Fix masking side-effects - not sure why this needs to be done
        flow_dist_ds.nodata = -9999
        distance = distance.filled(flow_dist_ds.nodata)
        distance[distance == None] = flow_dist_ds.nodata
        distance = distance.astype(np.float32)
        flow_dist_ds.array = distance
class ReprojectRasterOp(Operation):

    title = 'Reproject raster'
    name = 'reproject-raster'
    output_types = [OutputType('reprojected', '')]

    def run(self,
            input_ds,
            template_ds=None,
            srs=None,
            algorithm='bilinear',
            extent=None,
            format='tif'):
        if template_ds:
            srs = template_ds.srs
        self.locs['reprojected'].default_ext = format
        agg_warp_options = gdal.WarpOptions(dstSRS=srs,
                                            resampleAlg=algorithm,
                                            outputBounds=extent)
        gdal.Warp(self.locs['reprojected'].path,
                  input_ds.dataset,
                  options=agg_warp_options)
class GridAlignOp(Operation):

    title = 'Align Dataset'
    name = 'grid-align'
    output_types = [OutputType('aligned', 'ti')]

    def run(self,
            input_ds,
            template_ds=None,
            bbox=None,
            resolution=CoordProperty(x=1 / 240., y=1 / 240.),
            grid_res=None,
            padding=CoordProperty(x=0, y=0),
            algorithm='bilinear'):
        if not grid_res:
            grid_res = resolution
        if template_ds:
            resolution = template_ds.resolution
            bbox = copy.copy(template_ds.bbox)
        if bbox:
            grid_box = [
                bbox.llc.x - padding.x, bbox.llc.y - padding.y,
                bbox.urc.x + padding.x, bbox.urc.y + padding.y
            ]
        else:
            grid_box = input_ds.gridcorners(grid_res,
                                            padding=padding).warp_output_bounds

        agg_warp_options = gdal.WarpOptions(
            xRes=resolution.x,
            yRes=resolution.y,
            outputBounds=grid_box,
            targetAlignedPixels=True,
            resampleAlg=algorithm,
        )
        gdal.Warp(self.locs['aligned'].path,
                  input_ds.dataset,
                  options=agg_warp_options)
class NLCDtoDHSVM(Operation):
    """
    Convert vegetation classes from the National Land Cover
    Database to corresponding DHSVM values
    """

    title = 'Remap NLCD to DHSVM'
    name = 'nlcd-to-dhsvm'
    output_types = [OutputType('veg-type', 'gtif')]

    remap_table = {
        3: 12,
        11: 14,
        12: 20,
        21: 10,
        22: 13,
        23: 13,
        24: 13,
        31: 12,
        41: 4,
        42: 1,
        43: 5,
        51: 9,
        52: 9,
        71: 10,
        72: 10,
        81: 10,
        82: 11,
        90: 17,
        95: 9,
    }

    def run(self, nlcd_ds, nodata=-99):
        veg_type_ds = GDALDataset(self.locs['veg-type'], template=nlcd_ds)
        array = copy.copy(nlcd_ds.array)
        for nlcd, dhsvm in self.remap_table.items():
            array[nlcd_ds.array == nlcd] = dhsvm
        veg_type_ds.array = array
class CropDataFrameOp(Operation):

    title = 'Merged Raster'
    name = 'merge'
    output_types = [OutputType('merged', 'tif')]

    def run(self, input_ds):
        out_file = 'processed/glc_swe_alldates.csv'
        if not os.path.exists(os.path.dirname(out_file)):
            raise FileNotFoundError('Directory does not exist: {}'.format(out_file))
        slide = pd.read_csv('../data/SLIDE_NASA_GLC/GLC20180821.csv',
                            index_col='OBJECTID')
        origen_x = -125
        origen_y = 32
        res = 0.05

         # Filter Landslides to study area and duration
        slide = slide[slide.latitude > 32]
        slide = slide[slide.latitude < 43]
        slide = slide[slide.longitude > -125]
        slide = slide[slide.longitude < -114]

        slide['event_date'] = pd.to_datetime(
                slide['event_date'], format='%Y/%m/%d %H:%M')
        slide['event_date'] = slide['event_date'].dt.normalize()
        slide = slide[slide.event_date >= '2004-01-01']
        slide = slide[slide.event_date < '2016-01-01']

        # Open swe files
        files = glob.glob('data/daymet/daymet_v3_swe_*_na.nc4')
        files.sort()
        arrays = [xr.open_dataset(file) for file in files]
        coords = arrays[0].isel(time=0).drop(['time'])

        # Filter coordinates
        coords = coords.where(
            (coords.lat < 43) & (coords.lat > 32) &
            (coords.lon > -125) & (coords.lon < -114), drop=True)

        # Build KD-Tree
        locs = list(zip(coords.lon.values.flatten(), coords.lat.values.flatten()))
        kdt = cKDTree(locs)
        xa, ya = np.meshgrid(coords.x.values, coords.y.values)
        xv = xa.flatten()
        yv = ya.flatten()

        # Add swe to dataframe
        event_dfs = []
        count = 0
        for index, row in slide.iterrows():
            print(count)
            count += 1
            # Pull out swe values for location
            loc_i = kdt.query((row.longitude, row.latitude))[1]
            event_swe = pd.concat([
                arr.sel(x=xv[loc_i], y=yv[loc_i])[['swe']].to_dataframe()
                for arr in arrays])

            # Add location identifier to the index
            event_swe['OBJECTID'] = index
            event_swe = event_swe.set_index(['OBJECTID'], append=True)
            event_dfs.append(event_swe)

        swe_df = pd.concat(event_dfs)

        swe_df.to_csv(out_file)
        print(swe_df.sort_index().head())