def ridge_without_flowin_cell(self): """Find the original ridge sources that have no flow-in cells.""" for row in range(self.nrows): for col in range(self.ncols): tempdir = self.flowdir_data[row][col] if MathClass.floatequal(tempdir, self.nodata_flow): self.rdgsrc_data[row][col] = DEFAULT_NODATA continue if self.flowmodel == 1: # Dinf flow model temp_coor = DinfUtil.downstream_index_dinf( tempdir, row, col) for temprow, tempcol in temp_coor: if 0 <= temprow < self.nrows and 0 <= tempcol < self.ncols: self.rdgsrc_data[temprow][tempcol] = DEFAULT_NODATA else: self.rdgsrc_data[row][col] = DEFAULT_NODATA else: # D8 flow model temprow, tempcol = D8Util.downstream_index( tempdir, row, col) if 0 <= temprow < self.nrows and 0 <= tempcol < self.ncols: self.rdgsrc_data[temprow][tempcol] = DEFAULT_NODATA else: self.rdgsrc_data[row][col] = DEFAULT_NODATA RasterUtilClass.write_gtiff_file(self.rdgorg, self.nrows, self.ncols, self.rdgsrc_data, self.geotrans, self.srs, DEFAULT_NODATA, 6)
def post_process_of_delineated_data(cfg): """Do some necessary transfer for subbasin, stream, and flow direction raster.""" # inputs stream_net_file = cfg.taudems.streamnet_shp subbasin_file = cfg.taudems.subbsn_m flow_dir_file_tau = cfg.taudems.d8flow_m stream_raster_file = cfg.taudems.stream_m # outputs # -- shapefile shp_dir = cfg.dirs.geoshp UtilClass.mkdir(shp_dir) # ---- outlet, copy from DirNameUtils.TauDEM FileClass.copy_files(cfg.taudems.outlet_m, cfg.vecs.outlet) # ---- reaches output_reach_file = cfg.vecs.reach # ---- subbasins subbasin_vector_file = cfg.vecs.subbsn # -- raster file output_subbasin_file = cfg.spatials.subbsn output_flow_dir_file = cfg.spatials.d8flow output_stream_link_file = cfg.spatials.stream_link output_hillslope_file = cfg.spatials.hillslope id_map = StreamnetUtil.serialize_streamnet(stream_net_file, output_reach_file) RasterUtilClass.raster_reclassify(subbasin_file, id_map, output_subbasin_file, GDT_Int32) StreamnetUtil.assign_stream_id_raster(stream_raster_file, output_subbasin_file, output_stream_link_file) # Convert D8 encoding rule to ArcGIS D8Util.convert_code(flow_dir_file_tau, output_flow_dir_file) # convert raster to shapefile (for subbasin and basin) print('Generating subbasin vector...') VectorUtilClass.raster2shp(output_subbasin_file, subbasin_vector_file, 'subbasin', FieldNames.subbasin_id) mask_file = cfg.spatials.mask basin_vector = cfg.vecs.bsn print('Generating basin vector...') VectorUtilClass.raster2shp(mask_file, basin_vector, 'basin', FieldNames.basin) # delineate hillslope DelineateHillslope.downstream_method_whitebox(output_stream_link_file, flow_dir_file_tau, output_hillslope_file)
def assign_sequenced_stream_ids(c_id, vrow, vcol, flowmodel="taudem"): """set sequenced stream IDs""" in_strm_num, in_coors = inflow_stream_number(vrow, vcol, flowmodel) if in_strm_num == 0: # it's a headwater location so start a downstream flowpath c_id += 1 tmp_row = vrow tmp_col = vcol sequenced_stream_d[tmp_row][tmp_col] = c_id searched_flag = True while searched_flag: # find the downslope neighbour tmpflowd8 = flowd8_data[tmp_row][tmp_col] if tmpflowd8 < 0 or tmpflowd8 == flowd8_nodata: if stream_data[tmp_row][tmp_col] > 0 \ and stream_data[tmp_row][tmp_col] != stream_nodata: # it is a valid stream cell and probably just has no downslope # neighbour (e.g. at the edge of the grid) sequenced_stream_d[tmp_row][tmp_col] = c_id break tmp_row, tmp_col = D8Util.downstream_index( tmpflowd8, tmp_row, tmp_col, flowmodel) if tmp_row < 0 or tmp_row >= nrows or tmp_col < 0 or tmp_col >= ncols: break if stream_data[tmp_row][tmp_col] <= 0: searched_flag = False # it is not a stream cell else: if sequenced_stream_d[tmp_row][tmp_col] > 0: # run into a larger stream, end the downstream search break # is it a confluence (conjunction node) in_strm_num, in_coors = inflow_stream_number( tmp_row, tmp_col, flowmodel) if in_strm_num >= 2: c_id += 1 sequenced_stream_d[tmp_row][tmp_col] = c_id return c_id
def assign_sequenced_stream_ids(c_id, vrow, vcol, flowmodel="taudem"): """set sequenced stream IDs""" in_strm_num, in_coors = inflow_stream_number(vrow, vcol, flowmodel) if in_strm_num == 0: # it's a headwater location so start a downstream flowpath c_id += 1 tmp_row = vrow tmp_col = vcol sequenced_stream_d[tmp_row][tmp_col] = c_id searched_flag = True while searched_flag: # find the downslope neighbour tmpflowd8 = flowd8_data[tmp_row][tmp_col] if tmpflowd8 < 0 or tmpflowd8 == flowd8_nodata: if stream_data[tmp_row][tmp_col] > 0 \ and stream_data[tmp_row][tmp_col] != stream_nodata: # it is a valid stream cell and probably just has no downslope # neighbour (e.g. at the edge of the grid) sequenced_stream_d[tmp_row][tmp_col] = c_id break tmp_row, tmp_col = D8Util.downstream_index(tmpflowd8, tmp_row, tmp_col, flowmodel) if tmp_row < 0 or tmp_row >= nrows or tmp_col < 0 or tmp_col >= ncols: break if stream_data[tmp_row][tmp_col] <= 0: searched_flag = False # it is not a stream cell else: if sequenced_stream_d[tmp_row][tmp_col] > 0: # run into a larger stream, end the downstream search break # is it a confluence (conjunction node) in_strm_num, in_coors = inflow_stream_number(tmp_row, tmp_col, flowmodel) if in_strm_num >= 2: c_id += 1 sequenced_stream_d[tmp_row][tmp_col] = c_id return c_id
def downstream_method_whitebox(stream_raster, flow_dir_raster, hillslope_out, d8alg="taudem", stream_value_method=-1): """Algorithm modified from Whitebox GAT v3.4.0. source code: https://github.com/jblindsay/whitebox-geospatial-analysis-tools/ blob/master/HydroTools/src/plugins/Hillslopes.java Args: stream_raster: Stream cell value greater than 0 is identified by stream The input stream are recommended sequenced as 1, 2, 3... flow_dir_raster: D8 flow direction in TauDEM code hillslope_out: With the sequenced stream IDs, the output hillslope will be numbered: - Header hillslope: MaxStreamID + (current_id - 1) * 3 + 1 - Right hillslope: MaxStreamID + (current_id - 1) * 3 + 2 - Left hillslope: MaxStreamID + (current_id - 1) * 3 + 3 d8alg: Currently, "TauDEM", "ArcGIS", and "Whitebox" are supported. stream_value_method: stream value assigned method, depend on this parameter, the output hillslope will be appended as follows: -1 - all the four files will be output. 0 - keep stream link code, which has the default file name 1 - Set to the value of right hillslope and head hillslope, <name>_r.tif 2 - Set to the value of left hillslope and head hillslope, <name>_l.tif 3 - Set stream cell to NoData, <name>_n.tif """ print('Delineating hillslopes (header, left, and right hillslope)...') streamr = RasterUtilClass.read_raster(stream_raster) stream_data = streamr.data stream_nodata = streamr.noDataValue geotrans = streamr.geotrans srs = streamr.srs nrows = streamr.nRows ncols = streamr.nCols datatype = streamr.dataType flowd8r = RasterUtilClass.read_raster(flow_dir_raster) flowd8_data = flowd8r.data flowd8_nodata = flowd8r.noDataValue if flowd8r.nRows != nrows or flowd8r.nCols != ncols: raise ValueError("The input extent of D8 flow direction is not " "consistent with stream data!") # definition of utility functions def inflow_stream_number(vrow, vcol, flowmodel="taudem"): """ Count the inflow stream cell number and coordinates of all inflow cells Args: vrow: row number vcol: col number flowmodel: D8 flow direction algorithm. Returns: neighb_stream_cell_num: inflow cells number that is stream cell_coors: inflow cell coordinates, the size() is equal or greater than neighb_stream_cell_num """ neighb_stream_cell_num = 0 cell_coors = [] for c in range(8): newrow = vrow + FlowModelConst.ccw_drow[c] newcol = vcol + FlowModelConst.ccw_dcol[c] if newrow < 0 or newrow >= nrows or newcol < 0 or newcol >= ncols: continue if flowd8_data[newrow][ newcol] == FlowModelConst.d8_inflows.get(flowmodel)[c]: cell_coors.append((newrow, newcol)) if stream_data[newrow][newcol] > 0 \ and stream_data[newrow][newcol] != stream_nodata: neighb_stream_cell_num += 1 return neighb_stream_cell_num, cell_coors def assign_sequenced_stream_ids(c_id, vrow, vcol, flowmodel="taudem"): """set sequenced stream IDs""" in_strm_num, in_coors = inflow_stream_number(vrow, vcol, flowmodel) if in_strm_num == 0: # it's a headwater location so start a downstream flowpath c_id += 1 tmp_row = vrow tmp_col = vcol sequenced_stream_d[tmp_row][tmp_col] = c_id searched_flag = True while searched_flag: # find the downslope neighbour tmpflowd8 = flowd8_data[tmp_row][tmp_col] if tmpflowd8 < 0 or tmpflowd8 == flowd8_nodata: if stream_data[tmp_row][tmp_col] > 0 \ and stream_data[tmp_row][tmp_col] != stream_nodata: # it is a valid stream cell and probably just has no downslope # neighbour (e.g. at the edge of the grid) sequenced_stream_d[tmp_row][tmp_col] = c_id break tmp_row, tmp_col = D8Util.downstream_index( tmpflowd8, tmp_row, tmp_col, flowmodel) if tmp_row < 0 or tmp_row >= nrows or tmp_col < 0 or tmp_col >= ncols: break if stream_data[tmp_row][tmp_col] <= 0: searched_flag = False # it is not a stream cell else: if sequenced_stream_d[tmp_row][tmp_col] > 0: # run into a larger stream, end the downstream search break # is it a confluence (conjunction node) in_strm_num, in_coors = inflow_stream_number( tmp_row, tmp_col, flowmodel) if in_strm_num >= 2: c_id += 1 sequenced_stream_d[tmp_row][tmp_col] = c_id return c_id def assign_hillslope_code_of_neighbors(vrow, vcol, flowmodel="taudem"): """set hillslope code for neighbors of current stream cell.""" stream_coors.append((vrow, vcol)) in_strm_num, in_coors = inflow_stream_number(vrow, vcol, flowmodel) strm_id = stream_data[vrow][vcol] # print('Assign hillslope code for stream cell, r: %d, c: %d, ID: %d' % (vrow, vcol, # int(strm_id))) # set hillslope IDs hillslp_ids = DelineateHillslope.cal_hs_codes(max_id, strm_id) cur_d8_value = flowd8_data[vrow][vcol] if in_strm_num == 0: # it is a one-order stream head headstream_coors.append((vrow, vcol)) for (in_nostrm_row, in_nostrm_col) in in_coors: hillslope_mtx[in_nostrm_row][in_nostrm_col] = hillslp_ids[ 0] else: # search the 3*3 neighbors by clockwise and counterclockwise separately if cur_d8_value <= 0 or cur_d8_value == flowd8_nodata: return dirv = int(cur_d8_value) # direction code d_idx = FlowModelConst.d8_dirs.get(flowmodel).index( dirv) # direction index # look to the right side, i.e. clockwise d_idx_r = d_idx while True and len(in_coors) > 0: d_idx_r -= 1 if d_idx_r > 7: d_idx_r = 0 if d_idx_r < 0: d_idx_r = 7 tmp_row = vrow + FlowModelConst.ccw_drow[d_idx_r] tmp_col = vcol + FlowModelConst.ccw_dcol[d_idx_r] if (tmp_row, tmp_col ) not in in_coors: # not inflow to this cell continue tmpstream = stream_data[tmp_row][tmp_col] in_coors.remove((tmp_row, tmp_col)) if tmpstream <= 0 or tmpstream == stream_nodata: hillslope_mtx[tmp_row][tmp_col] = hillslp_ids[ 1] # right hillslope else: # encounter another in flow stream break # look to the left side, i.e. counterclockwise d_idx_l = d_idx while True and len(in_coors) > 0: d_idx_l += 1 if d_idx_l > 7: d_idx_l = 0 if d_idx_l < 0: d_idx_l = 7 tmp_row = vrow + FlowModelConst.ccw_drow[d_idx_l] tmp_col = vcol + FlowModelConst.ccw_dcol[d_idx_l] if (tmp_row, tmp_col ) not in in_coors: # not inflow to this cell continue tmpstream = stream_data[tmp_row][tmp_col] in_coors.remove((tmp_row, tmp_col)) if tmpstream <= 0 or tmpstream == stream_nodata: hillslope_mtx[tmp_row][tmp_col] = hillslp_ids[ 2] # left hillslope else: # encounter another in flow stream break # if any inflow cells existed? if len(in_coors) > 0: for (tmp_row, tmp_col) in in_coors: tmpstream = stream_data[tmp_row][tmp_col] if tmpstream <= 0 or tmpstream == stream_nodata: hillslope_mtx[tmp_row][tmp_col] = hillslp_ids[0] # add the current cell as head stream headstream_coors.append((vrow, vcol)) def output_hillslope(method_id): """Output hillslope according different stream cell value method.""" for (tmp_row, tmp_col) in stream_coors: tmp_hillslp_ids = DelineateHillslope.cal_hs_codes( max_id, stream_data[tmp_row][tmp_col]) if 0 < method_id < 3: hillslope_mtx[tmp_row][tmp_col] = tmp_hillslp_ids[ method_id] # is head stream cell? if (tmp_row, tmp_col) in headstream_coors: hillslope_mtx[tmp_row][tmp_col] = tmp_hillslp_ids[0] elif method_id == 3: hillslope_mtx[tmp_row][tmp_col] = DEFAULT_NODATA # Output to raster file hillslope_out_new = hillslope_out dirpath = os.path.dirname(hillslope_out_new) + os.path.sep corename = FileClass.get_core_name_without_suffix( hillslope_out_new) if method_id == 1: hillslope_out_new = dirpath + corename + '_right.tif' elif method_id == 2: hillslope_out_new = dirpath + corename + '_left.tif' elif method_id == 3: hillslope_out_new = dirpath + corename + '_nodata.tif' RasterUtilClass.write_gtiff_file(hillslope_out_new, nrows, ncols, hillslope_mtx, geotrans, srs, DEFAULT_NODATA, datatype) # 1. assign a unique id to each link in the stream network if needed assign_stream_id = False tmp = numpy.where((stream_data > 0) & (stream_data != stream_nodata), stream_data, numpy.nan) max_id = int(numpy.nanmax(tmp)) # i.e., stream link number min_id = int(numpy.nanmin(tmp)) for i in range(min_id, max_id + 1): if i not in tmp: assign_stream_id = True break if max_id == min_id: assign_stream_id = True current_id = 0 if assign_stream_id: # calculate and output sequenced stream raster sequenced_stream_d = numpy.ones((nrows, ncols)) * DEFAULT_NODATA for row in range(nrows): for col in range(ncols): # if the cell is not a stream, or has been assigned an ID if stream_data[row][col] <= 0 or stream_data[row][col] == stream_nodata \ or sequenced_stream_d[row][col] > 0: continue current_id = assign_sequenced_stream_ids( current_id, row, col, d8alg) stream_data = numpy.copy(sequenced_stream_d) stream_nodata = DEFAULT_NODATA stream_core = FileClass.get_core_name_without_suffix(stream_raster) stream_seq_file = os.path.dirname( stream_raster) + os.path.sep + stream_core + '_seq.tif' RasterUtilClass.write_gtiff_file(stream_seq_file, nrows, ncols, sequenced_stream_d, geotrans, srs, DEFAULT_NODATA, datatype) max_id = current_id # 2. assign hillslope code according to the 3*3 neighbors of stream cells hillslope_mtx = numpy.copy(stream_data) hillslope_mtx[stream_data == stream_nodata] = DEFAULT_NODATA headstream_coors = [] # head stream cells stream_coors = [] # all stream cells, include head stream cells. for row in range(nrows): for col in range(ncols): # if not a stream cell, or hillslope code has been assigned if stream_data[row][col] <= 0 or stream_data[row][col] == stream_nodata \ or hillslope_mtx[row][col] < 0: continue assign_hillslope_code_of_neighbors(row, col, d8alg) # 3. From each cell, search downstream for not assigned hillslope for row in range(nrows): for col in range(ncols): if hillslope_mtx[row][col] > 0 or flowd8_data[row][ col] == flowd8_nodata: continue flag = False tmprow = row tmpcol = col tmpcoors = [(row, col)] hillslp_id = DEFAULT_NODATA while not flag: # find it's downslope neighbour curflowdir = flowd8_data[tmprow][tmpcol] if curflowdir <= 0 or curflowdir == flowd8_nodata: break curflowdir = int(curflowdir) tmprow, tmpcol = D8Util.downstream_index( curflowdir, tmprow, tmpcol, d8alg) if tmprow < 0 or tmprow >= nrows or tmpcol < 0 or tmpcol >= ncols: break # if the new cell already has a hillslope value, use that if hillslope_mtx[tmprow][tmpcol] > 0: hillslp_id = hillslope_mtx[tmprow][tmpcol] flag = True if not flag: tmpcoors.append((tmprow, tmpcol)) # set the source cells for (crow, ccol) in tmpcoors: hillslope_mtx[crow][ccol] = hillslp_id # 4. reassign stream cell's value according to stream_value_method, and output if stream_value_method < 0: # output output_hillslope(0) output_hillslope(1) output_hillslope(2) output_hillslope(3) else: output_hillslope(stream_value_method)
def downstream_method_whitebox(stream_raster, flow_dir_raster, hillslope_out, d8alg="taudem", stream_value_method=-1): """Algorithm modified from Whitebox GAT v3.4.0. source code: https://github.com/jblindsay/whitebox-geospatial-analysis-tools/ blob/master/HydroTools/src/plugins/Hillslopes.java Args: stream_raster: Stream cell value greater than 0 is identified by stream The input stream are recommended sequenced as 1, 2, 3... flow_dir_raster: D8 flow direction in TauDEM code hillslope_out: With the sequenced stream IDs, the output hillslope will be numbered: - Header hillslope: MaxStreamID + (current_id - 1) * 3 + 1 - Right hillslope: MaxStreamID + (current_id - 1) * 3 + 2 - Left hillslope: MaxStreamID + (current_id - 1) * 3 + 3 d8alg: Currently, "TauDEM", "ArcGIS", and "Whitebox" are supported. stream_value_method: stream value assigned method, depend on this parameter, the output hillslope will be appended as follows: -1 - all the four files will be output. 0 - keep stream link code, which has the default file name 1 - Set to the value of right hillslope and head hillslope, <name>_r.tif 2 - Set to the value of left hillslope and head hillslope, <name>_l.tif 3 - Set stream cell to NoData, <name>_n.tif """ print('Delineating hillslopes (header, left, and right hillslope)...') streamr = RasterUtilClass.read_raster(stream_raster) stream_data = streamr.data stream_nodata = streamr.noDataValue geotrans = streamr.geotrans srs = streamr.srs nrows = streamr.nRows ncols = streamr.nCols datatype = streamr.dataType flowd8r = RasterUtilClass.read_raster(flow_dir_raster) flowd8_data = flowd8r.data flowd8_nodata = flowd8r.noDataValue if flowd8r.nRows != nrows or flowd8r.nCols != ncols: raise ValueError("The input extent of D8 flow direction is not " "consistent with stream data!") # definition of utility functions def inflow_stream_number(vrow, vcol, flowmodel="taudem"): """ Count the inflow stream cell number and coordinates of all inflow cells Args: vrow: row number vcol: col number flowmodel: D8 flow direction algorithm. Returns: neighb_stream_cell_num: inflow cells number that is stream cell_coors: inflow cell coordinates, the size() is equal or greater than neighb_stream_cell_num """ neighb_stream_cell_num = 0 cell_coors = [] for c in range(8): newrow = vrow + FlowModelConst.ccw_drow[c] newcol = vcol + FlowModelConst.ccw_dcol[c] if newrow < 0 or newrow >= nrows or newcol < 0 or newcol >= ncols: continue if flowd8_data[newrow][newcol] == FlowModelConst.d8_inflows.get(flowmodel)[c]: cell_coors.append((newrow, newcol)) if stream_data[newrow][newcol] > 0 \ and stream_data[newrow][newcol] != stream_nodata: neighb_stream_cell_num += 1 return neighb_stream_cell_num, cell_coors def assign_sequenced_stream_ids(c_id, vrow, vcol, flowmodel="taudem"): """set sequenced stream IDs""" in_strm_num, in_coors = inflow_stream_number(vrow, vcol, flowmodel) if in_strm_num == 0: # it's a headwater location so start a downstream flowpath c_id += 1 tmp_row = vrow tmp_col = vcol sequenced_stream_d[tmp_row][tmp_col] = c_id searched_flag = True while searched_flag: # find the downslope neighbour tmpflowd8 = flowd8_data[tmp_row][tmp_col] if tmpflowd8 < 0 or tmpflowd8 == flowd8_nodata: if stream_data[tmp_row][tmp_col] > 0 \ and stream_data[tmp_row][tmp_col] != stream_nodata: # it is a valid stream cell and probably just has no downslope # neighbour (e.g. at the edge of the grid) sequenced_stream_d[tmp_row][tmp_col] = c_id break tmp_row, tmp_col = D8Util.downstream_index(tmpflowd8, tmp_row, tmp_col, flowmodel) if tmp_row < 0 or tmp_row >= nrows or tmp_col < 0 or tmp_col >= ncols: break if stream_data[tmp_row][tmp_col] <= 0: searched_flag = False # it is not a stream cell else: if sequenced_stream_d[tmp_row][tmp_col] > 0: # run into a larger stream, end the downstream search break # is it a confluence (conjunction node) in_strm_num, in_coors = inflow_stream_number(tmp_row, tmp_col, flowmodel) if in_strm_num >= 2: c_id += 1 sequenced_stream_d[tmp_row][tmp_col] = c_id return c_id def assign_hillslope_code_of_neighbors(vrow, vcol, flowmodel="taudem"): """set hillslope code for neighbors of current stream cell.""" stream_coors.append((vrow, vcol)) in_strm_num, in_coors = inflow_stream_number(vrow, vcol, flowmodel) strm_id = stream_data[vrow][vcol] # print('Assign hillslope code for stream cell, r: %d, c: %d, ID: %d' % (vrow, vcol, # int(strm_id))) # set hillslope IDs hillslp_ids = DelineateHillslope.cal_hs_codes(max_id, strm_id) cur_d8_value = flowd8_data[vrow][vcol] if in_strm_num == 0: # it is a one-order stream head headstream_coors.append((vrow, vcol)) for (in_nostrm_row, in_nostrm_col) in in_coors: hillslope_mtx[in_nostrm_row][in_nostrm_col] = hillslp_ids[0] else: # search the 3*3 neighbors by clockwise and counterclockwise separately if cur_d8_value <= 0 or cur_d8_value == flowd8_nodata: return dirv = int(cur_d8_value) # direction code d_idx = FlowModelConst.d8_dirs.get(flowmodel).index(dirv) # direction index # look to the right side, i.e. clockwise d_idx_r = d_idx while True and len(in_coors) > 0: d_idx_r -= 1 if d_idx_r > 7: d_idx_r = 0 if d_idx_r < 0: d_idx_r = 7 tmp_row = vrow + FlowModelConst.ccw_drow[d_idx_r] tmp_col = vcol + FlowModelConst.ccw_dcol[d_idx_r] if (tmp_row, tmp_col) not in in_coors: # not inflow to this cell continue tmpstream = stream_data[tmp_row][tmp_col] in_coors.remove((tmp_row, tmp_col)) if tmpstream <= 0 or tmpstream == stream_nodata: hillslope_mtx[tmp_row][tmp_col] = hillslp_ids[1] # right hillslope else: # encounter another in flow stream break # look to the left side, i.e. counterclockwise d_idx_l = d_idx while True and len(in_coors) > 0: d_idx_l += 1 if d_idx_l > 7: d_idx_l = 0 if d_idx_l < 0: d_idx_l = 7 tmp_row = vrow + FlowModelConst.ccw_drow[d_idx_l] tmp_col = vcol + FlowModelConst.ccw_dcol[d_idx_l] if (tmp_row, tmp_col) not in in_coors: # not inflow to this cell continue tmpstream = stream_data[tmp_row][tmp_col] in_coors.remove((tmp_row, tmp_col)) if tmpstream <= 0 or tmpstream == stream_nodata: hillslope_mtx[tmp_row][tmp_col] = hillslp_ids[2] # left hillslope else: # encounter another in flow stream break # if any inflow cells existed? if len(in_coors) > 0: for (tmp_row, tmp_col) in in_coors: tmpstream = stream_data[tmp_row][tmp_col] if tmpstream <= 0 or tmpstream == stream_nodata: hillslope_mtx[tmp_row][tmp_col] = hillslp_ids[0] # add the current cell as head stream headstream_coors.append((vrow, vcol)) def output_hillslope(method_id): """Output hillslope according different stream cell value method.""" for (tmp_row, tmp_col) in stream_coors: tmp_hillslp_ids = DelineateHillslope.cal_hs_codes(max_id, stream_data[tmp_row][tmp_col]) if 0 < method_id < 3: hillslope_mtx[tmp_row][tmp_col] = tmp_hillslp_ids[method_id] # is head stream cell? if (tmp_row, tmp_col) in headstream_coors: hillslope_mtx[tmp_row][tmp_col] = tmp_hillslp_ids[0] elif method_id == 3: hillslope_mtx[tmp_row][tmp_col] = DEFAULT_NODATA # Output to raster file hillslope_out_new = hillslope_out dirpath = os.path.dirname(hillslope_out_new) + os.path.sep corename = FileClass.get_core_name_without_suffix(hillslope_out_new) if method_id == 1: hillslope_out_new = dirpath + corename + '_right.tif' elif method_id == 2: hillslope_out_new = dirpath + corename + '_left.tif' elif method_id == 3: hillslope_out_new = dirpath + corename + '_nodata.tif' RasterUtilClass.write_gtiff_file(hillslope_out_new, nrows, ncols, hillslope_mtx, geotrans, srs, DEFAULT_NODATA, datatype) # 1. assign a unique id to each link in the stream network if needed assign_stream_id = False tmp = numpy.where((stream_data > 0) & (stream_data != stream_nodata), stream_data, numpy.nan) max_id = int(numpy.nanmax(tmp)) # i.e., stream link number min_id = int(numpy.nanmin(tmp)) for i in range(min_id, max_id + 1): if i not in tmp: assign_stream_id = True break if max_id == min_id: assign_stream_id = True current_id = 0 if assign_stream_id: # calculate and output sequenced stream raster sequenced_stream_d = numpy.ones((nrows, ncols)) * DEFAULT_NODATA for row in range(nrows): for col in range(ncols): # if the cell is not a stream, or has been assigned an ID if stream_data[row][col] <= 0 or stream_data[row][col] == stream_nodata \ or sequenced_stream_d[row][col] > 0: continue current_id = assign_sequenced_stream_ids(current_id, row, col, d8alg) stream_data = numpy.copy(sequenced_stream_d) stream_nodata = DEFAULT_NODATA stream_core = FileClass.get_core_name_without_suffix(stream_raster) stream_seq_file = os.path.dirname(stream_raster) + os.path.sep + stream_core + '_seq.tif' RasterUtilClass.write_gtiff_file(stream_seq_file, nrows, ncols, sequenced_stream_d, geotrans, srs, DEFAULT_NODATA, datatype) max_id = current_id # 2. assign hillslope code according to the 3*3 neighbors of stream cells hillslope_mtx = numpy.copy(stream_data) hillslope_mtx[stream_data == stream_nodata] = DEFAULT_NODATA headstream_coors = [] # head stream cells stream_coors = [] # all stream cells, include head stream cells. for row in range(nrows): for col in range(ncols): # if not a stream cell, or hillslope code has been assigned if stream_data[row][col] <= 0 or stream_data[row][col] == stream_nodata \ or hillslope_mtx[row][col] < 0: continue assign_hillslope_code_of_neighbors(row, col, d8alg) # 3. From each cell, search downstream for not assigned hillslope for row in range(nrows): for col in range(ncols): if hillslope_mtx[row][col] > 0 or flowd8_data[row][col] == flowd8_nodata: continue flag = False tmprow = row tmpcol = col tmpcoors = [(row, col)] hillslp_id = DEFAULT_NODATA while not flag: # find it's downslope neighbour curflowdir = flowd8_data[tmprow][tmpcol] if curflowdir <= 0 or curflowdir == flowd8_nodata: break curflowdir = int(curflowdir) tmprow, tmpcol = D8Util.downstream_index(curflowdir, tmprow, tmpcol, d8alg) if tmprow < 0 or tmprow >= nrows or tmpcol < 0 or tmpcol >= ncols: break # if the new cell already has a hillslope value, use that if hillslope_mtx[tmprow][tmpcol] > 0: hillslp_id = hillslope_mtx[tmprow][tmpcol] flag = True if not flag: tmpcoors.append((tmprow, tmpcol)) # set the source cells for (crow, ccol) in tmpcoors: hillslope_mtx[crow][ccol] = hillslp_id # 4. reassign stream cell's value according to stream_value_method, and output if stream_value_method < 0: # output output_hillslope(0) output_hillslope(1) output_hillslope(2) output_hillslope(3) else: output_hillslope(stream_value_method)