def subcatch_order_a(ldd, oorder): """ Determines subcatchments using the catchment order This version uses the last cell BELOW order to derive the catchments. In general you want the _b version Input: - ldd - order - order to use Output: - map with catchment for the given streamorder """ outl = find_outlet(ldd) large = pcr.subcatchment(ldd, pcr.boolean(outl)) stt = pcr.streamorder(ldd) sttd = pcr.downstream(ldd, stt) pts = pcr.ifthen((pcr.scalar(sttd) - pcr.scalar(stt)) > 0.0, sttd) dif = pcr.upstream( ldd, pcr.cover( pcr.ifthen( large, pcr.uniqueid(pcr.boolean(pcr.ifthen(stt == pcr.ordinal(oorder), pts))), ), 0, ), ) dif = pcr.cover(pcr.scalar(outl), dif) # Add catchment outlet dif = pcr.ordinal(pcr.uniqueid(pcr.boolean(dif))) sc = pcr.subcatchment(ldd, dif) return sc, dif, stt
def derive_HAND(dem, ldd, accuThreshold, rivers=None, basin=None, up_area=None, neg_HAND=None): """ Function derives Height-Above-Nearest-Drain. See http://www.sciencedirect.com/science/article/pii/S003442570800120X Input: dem -- pcraster object float32, elevation data ldd -- pcraster object direction, local drain directions accuThreshold -- upstream amount of cells as threshold for river delineation rivers=None -- you can provide a rivers layer here. Pixels that are identified as river should have a value > 0, other pixels a value of zero. basin=None -- set a boolean pcraster map where areas with True are estimated using the nearest drain in ldd distance and areas with False by means of the nearest friction distance. Friction distance estimated using the upstream area as weight (i.e. drains with a bigger upstream area have a lower friction) the spreadzone operator is used in this case. up_area=None -- provide the upstream area (if not assigned a guesstimate is prepared, assuming the LDD covers a full catchment area) neg_HAND=None -- if set to 1, HAND maps can have negative values when elevation outside of stream is lower than stream (for example when there are natural embankments) Output: hand -- pcraster bject float32, height, normalised to nearest stream dist -- distance to nearest stream measured in cell lengths according to D8 directions """ if rivers is None: # prepare stream from a strahler threshold stream = pcr.ifthenelse(pcr.accuflux(ldd, 1) >= accuThreshold, pcr.boolean(1), pcr.boolean(0)) else: # convert stream network to boolean stream = pcr.boolean(pcr.cover(rivers, 0)) # determine height in river (in DEM*100 unit as ordinal) height_river = pcr.ifthenelse(stream, pcr.ordinal(dem*100), 0) if basin is None: up_elevation = pcr.scalar(pcr.subcatchment(ldd, height_river)) else: # use basin to allocate areas outside basin to the nearest stream. Nearest is weighted by upstream area if up_area is None: up_area = pcr.accuflux(ldd, 1) up_area = pcr.ifthen(stream, up_area) # mask areas outside streams friction = 1./pcr.scalar(pcr.spreadzone(pcr.cover(pcr.ordinal(up_area), 0), 0, 0)) # if basin, use nearest river within subcatchment, if outside basin, use weighted-nearest river up_elevation = pcr.ifthenelse(basin, pcr.scalar(pcr.subcatchment(ldd, height_river)), pcr.scalar(pcr.spreadzone(height_river, 0, friction))) # replace areas outside of basin by a spread zone calculation. # make negative HANDS also possible if neg_HAND == 1: hand = (pcr.scalar(pcr.ordinal(dem*100))-up_elevation)/100 # convert back to float in DEM units else: hand = pcr.max(pcr.scalar(pcr.ordinal(dem*100))-up_elevation, 0)/100 # convert back to float in DEM units dist = pcr.ldddist(ldd, stream, 1) # compute horizontal distance estimate return hand, dist
def subcatch_stream(ldd, stream, threshold): """ Derive catchments based upon strahler threshold Input: ldd -- pcraster object direction, local drain directions stream -- pcraster object direction, streamorder threshold -- integer, strahler threshold, subcatchments ge threshold are derived output: stream_ge -- pcraster object, streams of strahler order ge threshold subcatch -- pcraster object, subcatchments of strahler order ge threshold """ # derive stream order # stream = pcr.streamorder(ldd) stream_ge = pcr.ifthen(stream >= threshold, stream) stream_up_sum = pcr.ordinal(pcr.upstream(ldd, pcr.cover(pcr.scalar(stream_ge), 0))) # detect any transfer of strahler order, to a higher strahler order. transition_strahler = pcr.ifthenelse(pcr.downstream(ldd, stream_ge) != stream_ge, pcr.boolean(1), pcr.ifthenelse(pcr.nominal(ldd) == 5, pcr.boolean(1), pcr.ifthenelse(pcr.downstream(ldd, pcr.scalar(stream_up_sum)) > pcr.scalar(stream_ge), pcr.boolean(1), pcr.boolean(0)))) # make unique ids (write to file) transition_unique = pcr.ordinal(pcr.uniqueid(transition_strahler)) # derive upstream catchment areas (write to file) subcatch = pcr.nominal(pcr.subcatchment(ldd, transition_unique)) return stream_ge, subcatch
def derive_HAND(dem, ldd, accuThreshold, rivers=None, basin=None): """ Function derives Height-Above-Nearest-Drain. See http://www.sciencedirect.com/science/article/pii/S003442570800120X Input: dem -- pcraster object float32, elevation data ldd -- pcraster object direction, local drain directions accuThreshold -- upstream amount of cells as threshold for river delineation rivers=None -- you can provide a rivers layer here. Pixels that are identified as river should have a value > 0, other pixels a value of zero. basin=None -- set a boolean pcraster map where areas with True are estimated using the nearest drain in ldd distance and areas with False by means of the nearest friction distance. Friction distance estimated using the upstream area as weight (i.e. drains with a bigger upstream area have a lower friction) the spreadzone operator is used in this case. Output: hand -- pcraster bject float32, height, normalised to nearest stream dist -- distance to nearest stream measured in cell lengths according to D8 directions """ if rivers is None: stream = pcr.ifthenelse( pcr.accuflux(ldd, 1) >= accuThreshold, pcr.boolean(1), pcr.boolean(0) ) else: stream = pcr.boolean(pcr.cover(rivers, 0)) height_river = pcr.ifthenelse(stream, pcr.ordinal(dem * 100), 0) if basin is None: up_elevation = pcr.scalar(pcr.subcatchment(ldd, height_river)) else: drainage_surf = pcr.ifthen(rivers, pcr.accuflux(ldd, 1)) weight = 1.0 / pcr.scalar( pcr.spreadzone(pcr.cover(pcr.ordinal(drainage_surf), 0), 0, 0) ) up_elevation = pcr.ifthenelse( basin, pcr.scalar(pcr.subcatchment(ldd, height_river)), pcr.scalar(pcr.spreadzone(height_river, 0, weight)), ) # replace areas outside of basin by a spread zone calculation. hand = pcr.max(pcr.scalar(pcr.ordinal(dem * 100)) - up_elevation, 0) / 100 dist = pcr.ldddist(ldd, stream, 1) return hand, dist
def subcatch(ldd, outlet): """ Determines a subcatchment map using LDD and outlet(s). In the resulting subcatchment map the i's of the catchment are determiend by the id's of the outlets. Input: - ldd - Outlet - maps with points for each outlet. Output: - map of subcatchments """ subcatch = pcr.subcatchment(ldd, pcr.ordinal(outlet)) return subcatch
def pt_flow_in_river(ldd, river): """ Returns all points (True) that flow into the mak river (boolean map with river set to True) :param ldd: Drainage network :param river: Map of river (True River, False non-river) :return ifmap: map with infrlo points into the river (True) :return ctach: catchment of each of the inflow points """ dspts = pcr.downstream(ldd, pcr.cover(river, 0)) dspts = pcr.ifthenelse(pcr.cover(river, 0) == 1, 0, dspts) catch = pcr.subcatchment(ldd, pcr.nominal(pcr.uniqueid(dspts))) return dspts, catch
def getParameterFiles( self, currTimeStep, cellArea, ldd, initial_condition_dictionary=None ): # parameters for Water Bodies: fracWat # waterBodyIds # waterBodyOut # waterBodyArea # waterBodyTyp # waterBodyCap # cell surface area (m2) and ldd self.cellArea = cellArea ldd = pcr.ifthen(self.landmask, ldd) # date used for accessing/extracting water body information date_used = currTimeStep.fulldate year_used = currTimeStep.year if self.onlyNaturalWaterBodies == True: date_used = self.dateForNaturalCondition year_used = self.dateForNaturalCondition[0:4] # fracWat = fraction of surface water bodies (dimensionless) self.fracWat = pcr.scalar(0.0) if self.useNetCDF: self.fracWat = vos.netcdf2PCRobjClone( self.ncFileInp, "fracWaterInp", date_used, useDoy="yearly", cloneMapFileName=self.cloneMap, ) else: self.fracWat = vos.readPCRmapClone( self.fracWaterInp + str(year_used) + ".map", self.cloneMap, self.tmpDir, self.inputDir, ) self.fracWat = pcr.cover(self.fracWat, 0.0) self.fracWat = pcr.max(0.0, self.fracWat) self.fracWat = pcr.min(1.0, self.fracWat) self.waterBodyIds = pcr.nominal(0) # waterBody ids self.waterBodyOut = pcr.boolean(0) # waterBody outlets self.waterBodyArea = pcr.scalar(0.0) # waterBody surface areas # water body ids if self.useNetCDF: self.waterBodyIds = vos.netcdf2PCRobjClone( self.ncFileInp, "waterBodyIds", date_used, useDoy="yearly", cloneMapFileName=self.cloneMap, ) else: self.waterBodyIds = vos.readPCRmapClone( self.waterBodyIdsInp + str(year_used) + ".map", self.cloneMap, self.tmpDir, self.inputDir, False, None, True, ) # self.waterBodyIds = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0.0, pcr.nominal(self.waterBodyIds) ) # water body outlets (correcting outlet positions) wbCatchment = pcr.catchmenttotal(pcr.scalar(1), ldd) self.waterBodyOut = pcr.ifthen( wbCatchment == pcr.areamaximum(wbCatchment, self.waterBodyIds), self.waterBodyIds, ) # = outlet ids self.waterBodyOut = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0.0, self.waterBodyOut ) # TODO: Please also consider endorheic lakes! # correcting water body ids self.waterBodyIds = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0.0, pcr.subcatchment(ldd, self.waterBodyOut), ) # boolean map for water body outlets: self.waterBodyOut = pcr.ifthen( pcr.scalar(self.waterBodyOut) > 0.0, pcr.boolean(1) ) # reservoir surface area (m2): if self.useNetCDF: resSfArea = ( 1000.0 * 1000.0 * vos.netcdf2PCRobjClone( self.ncFileInp, "resSfAreaInp", date_used, useDoy="yearly", cloneMapFileName=self.cloneMap, ) ) else: resSfArea = ( 1000.0 * 1000.0 * vos.readPCRmapClone( self.resSfAreaInp + str(year_used) + ".map", self.cloneMap, self.tmpDir, self.inputDir, ) ) resSfArea = pcr.areaaverage(resSfArea, self.waterBodyIds) resSfArea = pcr.cover(resSfArea, 0.0) # water body surface area (m2): (lakes and reservoirs) self.waterBodyArea = pcr.max( pcr.areatotal( pcr.cover(self.fracWat * self.cellArea, 0.0), self.waterBodyIds ), pcr.areaaverage(pcr.cover(resSfArea, 0.0), self.waterBodyIds), ) self.waterBodyArea = pcr.ifthen(self.waterBodyArea > 0.0, self.waterBodyArea) # correcting water body ids and outlets (exclude all water bodies with surfaceArea = 0) self.waterBodyIds = pcr.ifthen(self.waterBodyArea > 0.0, self.waterBodyIds) self.waterBodyOut = pcr.ifthen( pcr.boolean(self.waterBodyIds), self.waterBodyOut ) # water body types: # - 2 = reservoirs (regulated discharge) # - 1 = lakes (weirFormula) # - 0 = non lakes or reservoirs (e.g. wetland) self.waterBodyTyp = pcr.nominal(0) if self.useNetCDF: self.waterBodyTyp = vos.netcdf2PCRobjClone( self.ncFileInp, "waterBodyTyp", date_used, useDoy="yearly", cloneMapFileName=self.cloneMap, ) else: self.waterBodyTyp = vos.readPCRmapClone( self.waterBodyTypInp + str(year_used) + ".map", self.cloneMap, self.tmpDir, self.inputDir, False, None, True, ) # excluding wetlands (waterBodyTyp = 0) in all functions related to lakes/reservoirs # self.waterBodyTyp = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0, pcr.nominal(self.waterBodyTyp) ) self.waterBodyTyp = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0, pcr.nominal(self.waterBodyTyp) ) self.waterBodyTyp = pcr.areamajority( self.waterBodyTyp, self.waterBodyIds ) # choose only one type: either lake or reservoir self.waterBodyTyp = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0, pcr.nominal(self.waterBodyTyp) ) self.waterBodyTyp = pcr.ifthen( pcr.boolean(self.waterBodyIds), self.waterBodyTyp ) # correcting lakes and reservoirs ids and outlets self.waterBodyIds = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0, self.waterBodyIds ) self.waterBodyOut = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0, self.waterBodyOut ) # reservoir maximum capacity (m3): self.resMaxCap = pcr.scalar(0.0) self.waterBodyCap = pcr.scalar(0.0) if self.useNetCDF: self.resMaxCap = ( 1000.0 * 1000.0 * vos.netcdf2PCRobjClone( self.ncFileInp, "resMaxCapInp", date_used, useDoy="yearly", cloneMapFileName=self.cloneMap, ) ) else: self.resMaxCap = ( 1000.0 * 1000.0 * vos.readPCRmapClone( self.resMaxCapInp + str(year_used) + ".map", self.cloneMap, self.tmpDir, self.inputDir, ) ) self.resMaxCap = pcr.ifthen(self.resMaxCap > 0, self.resMaxCap) self.resMaxCap = pcr.areaaverage(self.resMaxCap, self.waterBodyIds) # water body capacity (m3): (lakes and reservoirs) self.waterBodyCap = pcr.cover( self.resMaxCap, 0.0 ) # Note: Most of lakes have capacities > 0. self.waterBodyCap = pcr.ifthen( pcr.boolean(self.waterBodyIds), self.waterBodyCap ) # correcting water body types: # Reservoirs that have zero capacities will be assumed as lakes. self.waterBodyTyp = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0.0, self.waterBodyTyp ) self.waterBodyTyp = pcr.ifthenelse( self.waterBodyCap > 0.0, self.waterBodyTyp, pcr.ifthenelse( pcr.scalar(self.waterBodyTyp) == 2, pcr.nominal(1), self.waterBodyTyp ), ) # final corrections: self.waterBodyTyp = pcr.ifthen( self.waterBodyArea > 0.0, self.waterBodyTyp ) # make sure that all lakes and/or reservoirs have surface areas self.waterBodyTyp = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0.0, self.waterBodyTyp ) # make sure that only types 1 and 2 will be considered in lake/reservoir functions self.waterBodyIds = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0.0, self.waterBodyIds ) # make sure that all lakes and/or reservoirs have ids self.waterBodyOut = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0.0, self.waterBodyOut ) # make sure that all lakes and/or reservoirs have outlets # for a natural run (self.onlyNaturalWaterBodies == True) # which uses only the year 1900, assume all reservoirs are lakes if ( self.onlyNaturalWaterBodies == True and date_used == self.dateForNaturalCondition ): logger.info( "Using only natural water bodies identified in the year 1900. All reservoirs in 1900 are assumed as lakes." ) self.waterBodyTyp = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0.0, pcr.nominal(1) ) # check that all lakes and/or reservoirs have types, ids, surface areas and outlets: test = ( pcr.defined(self.waterBodyTyp) & pcr.defined(self.waterBodyArea) & pcr.defined(self.waterBodyIds) & pcr.boolean( pcr.areamaximum(pcr.scalar(self.waterBodyOut), self.waterBodyIds) ) ) a, b, c = vos.getMinMaxMean(pcr.cover(pcr.scalar(test), 1.0) - pcr.scalar(1.0)) threshold = 1e-3 if abs(a) > threshold or abs(b) > threshold: logger.warning("Missing information in some lakes and/or reservoirs.") # at the beginning of simulation period (timeStepPCR = 1) # - we have to define/get the initial conditions # if currTimeStep.timeStepPCR == 1: self.getICs(initial_condition_dictionary) # For each new reservoir (introduced at the beginning of the year) # initiating storage, average inflow and outflow # self.waterBodyStorage = pcr.cover(self.waterBodyStorage, 0.0) self.avgInflow = pcr.cover(self.avgInflow, 0.0) self.avgOutflow = pcr.cover(self.avgOutflow, 0.0) # cropping only in the landmask region: self.fracWat = pcr.ifthen(self.landmask, self.fracWat) self.waterBodyIds = pcr.ifthen(self.landmask, self.waterBodyIds) self.waterBodyOut = pcr.ifthen(self.landmask, self.waterBodyOut) self.waterBodyArea = pcr.ifthen(self.landmask, self.waterBodyArea) self.waterBodyTyp = pcr.ifthen(self.landmask, self.waterBodyTyp) self.waterBodyCap = pcr.ifthen(self.landmask, self.waterBodyCap) self.waterBodyStorage = pcr.ifthen(self.landmask, self.waterBodyStorage) self.avgInflow = pcr.ifthen(self.landmask, self.avgInflow) self.avgOutflow = pcr.ifthen(self.landmask, self.avgOutflow)
def main(): ### Read input arguments ##### parser = OptionParser() usage = "usage: %prog [options]" parser = OptionParser(usage=usage) parser.add_option('-q', '--quiet', dest='verbose', default=True, action='store_false', help='do not print status messages to stdout') pars er.add_option('-i', '--ini', dest='inifile', default='hand_contour_inun.ini', nargs=1, help='ini configuration file') parser.add_option('-f', '--flood_map', nargs=1, dest='flood_map', help='Flood map file (NetCDF point time series file') parser.add_option('-v', '--flood_variable', nargs=1, dest='flood_variable', default='water_level', help='variable name of flood water level') parser.add_option('-b', '--bankfull_map', dest='bankfull_map', default='', help='Map containing bank full level (is subtracted from flood map, in NetCDF)') parser.add_option('-c', '--catchment', dest='catchment_strahler', default=7, type='int', help='Strahler order threshold >= are selected as catchment boundaries') parser.add_option('-t', '--time', dest='time', default='', help='time in YYYYMMDDHHMMSS, overrides time in NetCDF input if set') # parser.add_option('-s', '--hand_strahler', # dest='hand_strahler', default=7, type='int', # help='Strahler order threshold >= selected as riverine') parser.add_option('-m', '--max_strahler', dest = 'max_strahler', default=1000, type='int', help='Maximum Strahler order to loop over') parser.add_option('-d', '--destination', dest='dest_path', default='inun', help='Destination path') parser.add_option('-H', '--hand_file_prefix', dest='hand_file_prefix', default='', help='optional HAND file prefix of already generated HAND files') parser.add_option('-n', '--neg_HAND', dest='neg_HAND', default=0, type='int', help='if set to 1, allow for negative HAND values in HAND maps') (options, args) = parser.parse_args() if not os.path.exists(options.inifile): print 'path to ini file cannot be found' sys.exit(1) options.dest_path = os.path.abspath(options.dest_path) if not(os.path.isdir(options.dest_path)): os.makedirs(options.dest_path) # set up the logger flood_name = os.path.split(options.flood_map)[1].split('.')[0] # case_name = 'inun_{:s}_hand_{:02d}_catch_{:02d}'.format(flood_name, options.hand_strahler, options.catchment_strahler) case_name = 'inun_{:s}_catch_{:02d}'.format(flood_name, options.catchment_strahler) logfilename = os.path.join(options.dest_path, 'hand_contour_inun.log') logger, ch = inun_lib.setlogger(logfilename, 'HAND_INUN', options.verbose) logger.info('$Id: $') logger.info('Flood map: {:s}'.format(options.flood_map)) logger.info('Bank full map: {:s}'.format(options.bankfull_map)) logger.info('Destination path: {:s}'.format(options.dest_path)) # read out ini file ### READ CONFIG FILE # open config-file config = inun_lib.open_conf(options.inifile) # read settings options.dem_file = inun_lib.configget(config, 'HighResMaps', 'dem_file', True) options.ldd_file = inun_lib.configget(config, 'HighResMaps', 'ldd_file', True) options.stream_file = inun_lib.configget(config, 'HighResMaps', 'stream_file', True) options.riv_length_fact_file = inun_lib.configget(config, 'wflowResMaps', 'riv_length_fact_file', True) options.ldd_wflow = inun_lib.configget(config, 'wflowResMaps', 'ldd_wflow', True) options.riv_width_file = inun_lib.configget(config, 'wflowResMaps', 'riv_width_file', True) options.file_format = inun_lib.configget(config, 'file_settings', 'file_format', 0, datatype='int') options.out_format = inun_lib.configget(config, 'file_settings', 'out_format', 0, datatype='int') options.latlon = inun_lib.configget(config, 'file_settings', 'latlon', 0, datatype='int') options.x_tile = inun_lib.configget(config, 'tiling', 'x_tile', 10000, datatype='int') options.y_tile = inun_lib.configget(config, 'tiling', 'y_tile', 10000, datatype='int') options.x_overlap = inun_lib.configget(config, 'tiling', 'x_overlap', 1000, datatype='int') options.y_overlap = inun_lib.configget(config, 'tiling', 'y_overlap', 1000, datatype='int') options.iterations = inun_lib.configget(config, 'inundation', 'iterations', 20, datatype='int') options.initial_level = inun_lib.configget(config, 'inundation', 'initial_level', 32., datatype='float') options.flood_volume_type = inun_lib.configget(config, 'inundation', 'flood_volume_type', 0, datatype='int') # options.area_multiplier = inun_lib.configget(config, 'inundation', # 'area_multiplier', 1., datatype='float') logger.info('DEM file: {:s}'.format(options.dem_file)) logger.info('LDD file: {:s}'.format(options.ldd_file)) logger.info('Columns per tile: {:d}'.format(options.x_tile)) logger.info('Rows per tile: {:d}'.format(options.y_tile)) logger.info('Columns overlap: {:d}'.format(options.x_overlap)) logger.info('Rows overlap: {:d}'.format(options.y_overlap)) metadata_global = {} # add metadata from the section [metadata] meta_keys = config.options('metadata_global') for key in meta_keys: metadata_global[key] = config.get('metadata_global', key) # add a number of metadata variables that are mandatory metadata_global['config_file'] = os.path.abspath(options.inifile) metadata_var = {} metadata_var['units'] = 'm' metadata_var['standard_name'] = 'water_surface_height_above_reference_datum' metadata_var['long_name'] = 'flooding' metadata_var['comment'] = 'water_surface_reference_datum_altitude is given in file {:s}'.format(options.dem_file) if not os.path.exists(options.dem_file): logger.error('path to dem file {:s} cannot be found'.format(options.dem_file)) sys.exit(1) if not os.path.exists(options.ldd_file): logger.error('path to ldd file {:s} cannot be found'.format(options.ldd_file)) sys.exit(1) # Read extent from a GDAL compatible file try: extent = inun_lib.get_gdal_extent(options.dem_file) except: msg = 'Input file {:s} not a gdal compatible file'.format(options.dem_file) inun_lib.close_with_error(logger, ch, msg) sys.exit(1) try: x, y = inun_lib.get_gdal_axes(options.dem_file, logging=logger) srs = inun_lib.get_gdal_projection(options.dem_file, logging=logger) except: msg = 'Input file {:s} not a gdal compatible file'.format(options.dem_file) inun_lib.close_with_error(logger, ch, msg) sys.exit(1) # read history from flood file if options.file_format == 0: a = nc.Dataset(options.flood_map, 'r') metadata_global['history'] = 'Created by: $Id: $, boundary conditions from {:s},\nhistory: {:s}'.format(os.path.abspath(options.flood_map), a.history) a.close() else: metadata_global['history'] = 'Created by: $Id: $, boundary conditions from {:s},\nhistory: {:s}'.format(os.path.abspath(options.flood_map), 'PCRaster file, no history') # first write subcatch maps and hand maps ############### TODO ###### # setup a HAND file for each strahler order max_s = inun_lib.define_max_strahler(options.stream_file, logging=logger) stream_max = np.minimum(max_s, options.max_strahler) for hand_strahler in range(options.catchment_strahler, stream_max + 1, 1): dem_name = os.path.split(options.dem_file)[1].split('.')[0] if os.path.isfile('{:s}_{:02d}.tif'.format(options.hand_file_prefix, hand_strahler)): hand_file = '{:s}_{:02d}.tif'.format(options.hand_file_prefix, hand_strahler) else: logger.info('No HAND files with HAND prefix were found, checking {:s}_hand_strahler_{:02d}.tif'.format(dem_name, hand_strahler)) hand_file = os.path.join(options.dest_path, '{:s}_hand_strahler_{:02d}.tif'.format(dem_name, hand_strahler)) if not(os.path.isfile(hand_file)): # hand file does not exist yet! Generate it, otherwise skip! logger.info('HAND file {:s} not found, start setting up...please wait...'.format(hand_file)) hand_file_tmp = os.path.join(options.dest_path, '{:s}_hand_strahler_{:02d}.tif.tmp'.format(dem_name, hand_strahler)) ds_hand, band_hand = inun_lib.prepare_gdal(hand_file_tmp, x, y, logging=logger, srs=srs) # band_hand = ds_hand.GetRasterBand(1) # Open terrain data for reading ds_dem, rasterband_dem = inun_lib.get_gdal_rasterband(options.dem_file) ds_ldd, rasterband_ldd = inun_lib.get_gdal_rasterband(options.ldd_file) ds_stream, rasterband_stream = inun_lib.get_gdal_rasterband(options.stream_file) n = 0 for x_loop in range(0, len(x), options.x_tile): x_start = np.maximum(x_loop, 0) x_end = np.minimum(x_loop + options.x_tile, len(x)) # determine actual overlap for cutting for y_loop in range(0, len(y), options.y_tile): x_overlap_min = x_start - np.maximum(x_start - options.x_overlap, 0) x_overlap_max = np.minimum(x_end + options.x_overlap, len(x)) - x_end n += 1 # print('tile {:001d}:'.format(n)) y_start = np.maximum(y_loop, 0) y_end = np.minimum(y_loop + options.y_tile, len(y)) y_overlap_min = y_start - np.maximum(y_start - options.y_overlap, 0) y_overlap_max = np.minimum(y_end + options.y_overlap, len(y)) - y_end # cut out DEM logger.debug('Computing HAND for xmin: {:d} xmax: {:d} ymin {:d} ymax {:d}'.format(x_start, x_end,y_start, y_end)) terrain = rasterband_dem.ReadAsArray(x_start - x_overlap_min, y_start - y_overlap_min, (x_end + x_overlap_max) - (x_start - x_overlap_min), (y_end + y_overlap_max) - (y_start - y_overlap_min) ) drainage = rasterband_ldd.ReadAsArray(x_start - x_overlap_min, y_start - y_overlap_min, (x_end + x_overlap_max) - (x_start - x_overlap_min), (y_end + y_overlap_max) - (y_start - y_overlap_min) ) stream = rasterband_stream.ReadAsArray(x_start - x_overlap_min, y_start - y_overlap_min, (x_end + x_overlap_max) - (x_start - x_overlap_min), (y_end + y_overlap_max) - (y_start - y_overlap_min) ) # write to temporary file terrain_temp_file = os.path.join(options.dest_path, 'terrain_temp.map') drainage_temp_file = os.path.join(options.dest_path, 'drainage_temp.map') stream_temp_file = os.path.join(options.dest_path, 'stream_temp.map') if rasterband_dem.GetNoDataValue() is not None: inun_lib.gdal_writemap(terrain_temp_file, 'PCRaster', np.arange(0, terrain.shape[1]), np.arange(0, terrain.shape[0]), terrain, rasterband_dem.GetNoDataValue(), gdal_type=gdal.GDT_Float32, logging=logger) else: # in case no nodata value is found logger.warning('No nodata value found in {:s}. assuming -9999'.format(options.dem_file)) inun_lib.gdal_writemap(terrain_temp_file, 'PCRaster', np.arange(0, terrain.shape[1]), np.arange(0, terrain.shape[0]), terrain, -9999., gdal_type=gdal.GDT_Float32, logging=logger) inun_lib.gdal_writemap(drainage_temp_file, 'PCRaster', np.arange(0, terrain.shape[1]), np.arange(0, terrain.shape[0]), drainage, rasterband_ldd.GetNoDataValue(), gdal_type=gdal.GDT_Int32, logging=logger) inun_lib.gdal_writemap(stream_temp_file, 'PCRaster', np.arange(0, terrain.shape[1]), np.arange(0, terrain.shape[0]), stream, rasterband_ldd.GetNoDataValue(), gdal_type=gdal.GDT_Int32, logging=logger) # read as pcr objects pcr.setclone(terrain_temp_file) terrain_pcr = pcr.readmap(terrain_temp_file) drainage_pcr = pcr.lddrepair(pcr.ldd(pcr.readmap(drainage_temp_file))) # convert to ldd type map stream_pcr = pcr.scalar(pcr.readmap(stream_temp_file)) # convert to ldd type map #check if the highest stream order of the tile is below the hand_strahler # if the highest stream order of the tile is smaller than hand_strahler, than DEM values are taken instead of HAND values. max_stream_tile = inun_lib.define_max_strahler(stream_temp_file, logging=logger) if max_stream_tile < hand_strahler: hand_pcr = terrain_pcr logger.info('For this tile, DEM values are used instead of HAND because there is no stream order larger than {:02d}'.format(hand_strahler)) else: # compute streams stream_ge, subcatch = inun_lib.subcatch_stream(drainage_pcr, hand_strahler, stream=stream_pcr) # generate streams # compute basins stream_ge_dummy, subcatch = inun_lib.subcatch_stream(drainage_pcr, options.catchment_strahler, stream=stream_pcr) # generate streams basin = pcr.boolean(subcatch) hand_pcr, dist_pcr = inun_lib.derive_HAND(terrain_pcr, drainage_pcr, 3000, rivers=pcr.boolean(stream_ge), basin=basin, neg_HAND=options.neg_HAND) # convert to numpy hand = pcr.pcr2numpy(hand_pcr, -9999.) # cut relevant part if y_overlap_max == 0: y_overlap_max = -hand.shape[0] if x_overlap_max == 0: x_overlap_max = -hand.shape[1] hand_cut = hand[0+y_overlap_min:-y_overlap_max, 0+x_overlap_min:-x_overlap_max] band_hand.WriteArray(hand_cut, x_start, y_start) os.unlink(terrain_temp_file) os.unlink(drainage_temp_file) os.unlink(stream_temp_file) band_hand.FlushCache() ds_dem = None ds_ldd = None ds_stream = None band_hand.SetNoDataValue(-9999.) ds_hand = None logger.info('Finalizing {:s}'.format(hand_file)) # rename temporary file to final hand file os.rename(hand_file_tmp, hand_file) else: logger.info('HAND file {:s} already exists...skipping...'.format(hand_file)) ##################################################################################### # HAND file has now been prepared, moving to flood mapping part # ##################################################################################### # set the clone pcr.setclone(options.ldd_wflow) # read wflow ldd as pcraster object ldd_pcr = pcr.readmap(options.ldd_wflow) xax, yax, riv_width, fill_value = inun_lib.gdal_readmap(options.riv_width_file, 'GTiff', logging=logger) # determine cell length in meters using ldd_pcr as clone (if latlon=True, values are converted to m2 x_res, y_res, reallength_wflow = pcrut.detRealCellLength(pcr.scalar(ldd_pcr), not(bool(options.latlon))) cell_surface_wflow = pcr.pcr2numpy(x_res * y_res, 0) if options.flood_volume_type == 0: # load the staticmaps needed to estimate volumes across all # xax, yax, riv_length, fill_value = inun_lib.gdal_readmap(options.riv_length_file, 'GTiff', logging=logger) # riv_length = np.ma.masked_where(riv_length==fill_value, riv_length) xax, yax, riv_width, fill_value = inun_lib.gdal_readmap(options.riv_width_file, 'GTiff', logging=logger) riv_width[riv_width == fill_value] = 0 # read river length factor file (multiplier) xax, yax, riv_length_fact, fill_value = inun_lib.gdal_readmap(options.riv_length_fact_file, 'GTiff', logging=logger) riv_length_fact = np.ma.masked_where(riv_length_fact==fill_value, riv_length_fact) drain_length = wflow_lib.detdrainlength(ldd_pcr, x_res, y_res) # compute river length in each cell riv_length = pcr.pcr2numpy(drain_length, 0) * riv_length_fact # riv_length_pcr = pcr.numpy2pcr(pcr.Scalar, riv_length, 0) flood_folder = os.path.join(options.dest_path, case_name) flood_vol_map = os.path.join(flood_folder, '{:s}_vol.tif'.format(os.path.split(options.flood_map)[1].split('.')[0])) if not(os.path.isdir(flood_folder)): os.makedirs(flood_folder) if options.out_format == 0: inun_file_tmp = os.path.join(flood_folder, '{:s}.tif.tmp'.format(case_name)) inun_file = os.path.join(flood_folder, '{:s}.tif'.format(case_name)) else: inun_file_tmp = os.path.join(flood_folder, '{:s}.nc.tmp'.format(case_name)) inun_file = os.path.join(flood_folder, '{:s}.nc'.format(case_name)) hand_temp_file = os.path.join(flood_folder, 'hand_temp.map') drainage_temp_file = os.path.join(flood_folder, 'drainage_temp.map') stream_temp_file = os.path.join(flood_folder, 'stream_temp.map') flood_vol_temp_file = os.path.join(flood_folder, 'flood_warp_temp.tif') # load the data with river levels and compute the volumes if options.file_format == 0: # assume we need the maximum value in a NetCDF time series grid logger.info('Reading flood from {:s} NetCDF file'.format(options.flood_map)) a = nc.Dataset(options.flood_map, 'r') if options.latlon == 0: xax = a.variables['x'][:] yax = a.variables['y'][:] else: xax = a.variables['lon'][:] yax = a.variables['lat'][:] if options.time == '': time_list = nc.num2date(a.variables['time'][:], units = a.variables['time'].units, calendar=a.variables['time'].calendar) time = [time_list[len(time_list)/2]] else: time = [dt.datetime.strptime(options.time, '%Y%m%d%H%M%S')] flood_series = a.variables[options.flood_variable][:] flood_data = flood_series.max(axis=0) if np.ma.is_masked(flood_data): flood = flood_data.data flood[flood_data.mask] = 0 if yax[-1] > yax[0]: yax = np.flipud(yax) flood = np.flipud(flood) a.close() elif options.file_format == 1: logger.info('Reading flood from {:s} PCRaster file'.format(options.flood_map)) xax, yax, flood, flood_fill_value = inun_lib.gdal_readmap(options.flood_map, 'PCRaster', logging=logger) flood = np.ma.masked_equal(flood, flood_fill_value) if options.time == '': options.time = '20000101000000' time = [dt.datetime.strptime(options.time, '%Y%m%d%H%M%S')] flood[flood==flood_fill_value] = 0. # load the bankfull depths if options.bankfull_map == '': bankfull = np.zeros(flood.shape) else: if options.file_format == 0: logger.info('Reading bankfull from {:s} NetCDF file'.format(options.bankfull_map)) a = nc.Dataset(options.bankfull_map, 'r') xax = a.variables['x'][:] yax = a.variables['y'][:] bankfull_series = a.variables[options.flood_variable][:] bankfull_data = bankfull_series.max(axis=0) if np.ma.is_masked(bankfull_data): bankfull = bankfull_data.data bankfull[bankfull_data.mask] = 0 if yax[-1] > yax[0]: yax = np.flipud(yax) bankfull = np.flipud(bankfull) a.close() elif options.file_format == 1: logger.info('Reading bankfull from {:s} PCRaster file'.format(options.bankfull_map)) xax, yax, bankfull, bankfull_fill_value = inun_lib.gdal_readmap(options.bankfull_map, 'PCRaster', logging=logger) bankfull = np.ma.masked_equal(bankfull, bankfull_fill_value) # flood = bankfull*2 # res_x = 2000 # res_y = 2000 # subtract the bankfull water level to get flood levels (above bankfull) flood_vol = np.maximum(flood-bankfull, 0) if options.flood_volume_type == 0: flood_vol_m = riv_length*riv_width*flood_vol/cell_surface_wflow # volume expressed in meters water disc flood_vol_m_pcr = pcr.numpy2pcr(pcr.Scalar, flood_vol_m, 0) else: flood_vol_m = flood_vol/cell_surface_wflow flood_vol_m_data = flood_vol_m.data flood_vol_m_data[flood_vol_m.mask] = -999. logger.info('Saving water layer map to {:s}'.format(flood_vol_map)) # write to a tiff file inun_lib.gdal_writemap(flood_vol_map, 'GTiff', xax, yax, np.maximum(flood_vol_m_data, 0), -999., logging=logger) # this is placed later in the hand loop # ds_hand, rasterband_hand = inun_lib.get_gdal_rasterband(hand_file) ds_ldd, rasterband_ldd = inun_lib.get_gdal_rasterband(options.ldd_file) ds_stream, rasterband_stream = inun_lib.get_gdal_rasterband(options.stream_file) logger.info('Preparing flood map in {:s} ...please wait...'.format(inun_file)) if options.out_format == 0: ds_inun, band_inun = inun_lib.prepare_gdal(inun_file_tmp, x, y, logging=logger, srs=srs) # band_inun = ds_inun.GetRasterBand(1) else: ds_inun, band_inun = inun_lib.prepare_nc(inun_file_tmp, time, x, np.flipud(y), metadata=metadata_global, metadata_var=metadata_var, logging=logger) # loop over all the tiles n = 0 for x_loop in range(0, len(x), options.x_tile): x_start = np.maximum(x_loop, 0) x_end = np.minimum(x_loop + options.x_tile, len(x)) # determine actual overlap for cutting for y_loop in range(0, len(y), options.y_tile): x_overlap_min = x_start - np.maximum(x_start - options.x_overlap, 0) x_overlap_max = np.minimum(x_end + options.x_overlap, len(x)) - x_end n += 1 # print('tile {:001d}:'.format(n)) y_start = np.maximum(y_loop, 0) y_end = np.minimum(y_loop + options.y_tile, len(y)) y_overlap_min = y_start - np.maximum(y_start - options.y_overlap, 0) y_overlap_max = np.minimum(y_end + options.y_overlap, len(y)) - y_end x_tile_ax = x[x_start - x_overlap_min:x_end + x_overlap_max] y_tile_ax = y[y_start - y_overlap_min:y_end + y_overlap_max] # cut out DEM logger.debug('handling xmin: {:d} xmax: {:d} ymin {:d} ymax {:d}'.format(x_start, x_end, y_start, y_end)) drainage = rasterband_ldd.ReadAsArray(x_start - x_overlap_min, y_start - y_overlap_min, (x_end + x_overlap_max) - (x_start - x_overlap_min), (y_end + y_overlap_max) - (y_start - y_overlap_min) ) stream = rasterband_stream.ReadAsArray(x_start - x_overlap_min, y_start - y_overlap_min, (x_end + x_overlap_max) - (x_start - x_overlap_min), (y_end + y_overlap_max) - (y_start - y_overlap_min) ) # stream_max = np.minimum(stream.max(), options.max_strahler) inun_lib.gdal_writemap(drainage_temp_file, 'PCRaster', x_tile_ax, y_tile_ax, drainage, rasterband_ldd.GetNoDataValue(), gdal_type=gdal.GDT_Int32, logging=logger) inun_lib.gdal_writemap(stream_temp_file, 'PCRaster', x_tile_ax, y_tile_ax, stream, rasterband_stream.GetNoDataValue(), gdal_type=gdal.GDT_Int32, logging=logger) # read as pcr objects pcr.setclone(stream_temp_file) drainage_pcr = pcr.lddrepair(pcr.ldd(pcr.readmap(drainage_temp_file))) # convert to ldd type map stream_pcr = pcr.scalar(pcr.readmap(stream_temp_file)) # convert to ldd type map # warp of flood volume to inundation resolution inun_lib.gdal_warp(flood_vol_map, stream_temp_file, flood_vol_temp_file, gdal_interp=gdalconst.GRA_NearestNeighbour) # , x_tile_ax, y_tile_ax, flood_meter, fill_value = inun_lib.gdal_readmap(flood_vol_temp_file, 'GTiff', logging=logger) # make sure that the option unittrue is on !! (if unitcell was is used in another function) x_res_tile, y_res_tile, reallength = pcrut.detRealCellLength(pcr.scalar(stream_pcr), not(bool(options.latlon))) cell_surface_tile = pcr.pcr2numpy(x_res_tile * y_res_tile, 0) # convert meter depth to volume [m3] flood_vol = pcr.numpy2pcr(pcr.Scalar, flood_meter*cell_surface_tile, fill_value) # first prepare a basin map, belonging to the lowest order we are looking at inundation_pcr = pcr.scalar(stream_pcr) * 0 for hand_strahler in range(options.catchment_strahler, stream_max + 1, 1): # hand_temp_file = os.path.join(flood_folder, 'hand_temp.map') if os.path.isfile(os.path.join(options.dest_path, '{:s}_hand_strahler_{:02d}.tif'.format(dem_name, hand_strahler))): hand_file = os.path.join(options.dest_path, '{:s}_hand_strahler_{:02d}.tif'.format(dem_name, hand_strahler)) else: hand_file = '{:s}_{:02d}.tif'.format(options.hand_file_prefix, hand_strahler) ds_hand, rasterband_hand = inun_lib.get_gdal_rasterband(hand_file) hand = rasterband_hand.ReadAsArray(x_start - x_overlap_min, y_start - y_overlap_min, (x_end + x_overlap_max) - (x_start - x_overlap_min), (y_end + y_overlap_max) - (y_start - y_overlap_min) ) print('len x-ax: {:d} len y-ax {:d} x-shape {:d} y-shape {:d}'.format(len(x_tile_ax), len(y_tile_ax), hand.shape[1], hand.shape[0])) inun_lib.gdal_writemap(hand_temp_file, 'PCRaster', x_tile_ax, y_tile_ax, hand, rasterband_hand.GetNoDataValue(), gdal_type=gdal.GDT_Float32, logging=logger) hand_pcr = pcr.readmap(hand_temp_file) stream_ge_hand, subcatch_hand = inun_lib.subcatch_stream(drainage_pcr, options.catchment_strahler, stream=stream_pcr) # stream_ge_hand, subcatch_hand = inun_lib.subcatch_stream(drainage_pcr, hand_strahler, stream=stream_pcr) stream_ge, subcatch = inun_lib.subcatch_stream(drainage_pcr, options.catchment_strahler, stream=stream_pcr, basin=pcr.boolean(pcr.cover(subcatch_hand, 0)), assign_existing=True, min_strahler=hand_strahler, max_strahler=hand_strahler) # generate subcatchments, only within basin for HAND flood_vol_strahler = pcr.ifthenelse(pcr.boolean(pcr.cover(subcatch, 0)), flood_vol, 0) # mask the flood volume map with the created subcatch map for strahler order = hand_strahler inundation_pcr_step = inun_lib.volume_spread(drainage_pcr, hand_pcr, pcr.subcatchment(drainage_pcr, subcatch), # to make sure backwater effects can occur from higher order rivers to lower order rivers flood_vol_strahler, volume_thres=0., iterations=options.iterations, cell_surface=pcr.numpy2pcr(pcr.Scalar, cell_surface_tile, -9999), logging=logger, order=hand_strahler, neg_HAND=options.neg_HAND) # 1166400000. # use maximum value of inundation_pcr_step and new inundation for higher strahler order inundation_pcr = pcr.max(inundation_pcr, inundation_pcr_step) inundation = pcr.pcr2numpy(inundation_pcr, -9999.) # cut relevant part if y_overlap_max == 0: y_overlap_max = -inundation.shape[0] if x_overlap_max == 0: x_overlap_max = -inundation.shape[1] inundation_cut = inundation[0+y_overlap_min:-y_overlap_max, 0+x_overlap_min:-x_overlap_max] # inundation_cut if options.out_format == 0: band_inun.WriteArray(inundation_cut, x_start, y_start) band_inun.FlushCache() else: # with netCDF, data is up-side-down. inun_lib.write_tile_nc(band_inun, inundation_cut, x_start, y_start) # clean up os.unlink(flood_vol_temp_file) os.unlink(drainage_temp_file) os.unlink(hand_temp_file) os.unlink(stream_temp_file) #also remove temp stream file from output folder # if n == 35: # band_inun.SetNoDataValue(-9999.) # ds_inun = None # sys.exit(0) # os.unlink(flood_vol_map) logger.info('Finalizing {:s}'.format(inun_file)) # add the metadata to the file and band # band_inun.SetNoDataValue(-9999.) # ds_inun.SetMetadata(metadata_global) # band_inun.SetMetadata(metadata_var) if options.out_format == 0: ds_inun = None ds_hand = None else: ds_inun.close() ds_ldd = None # rename temporary file to final hand file if os.path.isfile(inun_file): # remove an old result if available os.unlink(inun_file) os.rename(inun_file_tmp, inun_file) logger.info('Done! Thank you for using hand_contour_inun.py') logger, ch = inun_lib.closeLogger(logger, ch) del logger, ch sys.exit(0)
else: call(('gdal_translate', '-of', 'GTiff', '-stats', '-ot', 'Float32', catchments_map, catchments_tif)) wt.Raster2Pol(catchments_tif, catchshp, srs) riversid_map = workdir + 'riverid.map' drain_map = workdir + 'drain.map' ldd_mask = pcr.ifthen(river, ldd) upstream = pcr.upstream(ldd_mask, pcr.scalar(river)) downstream = pcr.downstream(ldd_mask, upstream) #pcr.report(downstream,'downstream.map') confluences = pcr.boolean(pcr.ifthen(downstream >= 2, pcr.boolean(1))) #pcr.report(confluences,'confluences.map') boundaries = pcr.boolean(pcr.ifthen(pcr.scalar(ldd_mask) == 5, pcr.boolean(1))) catch_points = pcr.nominal(pcr.uniqueid(pcr.cover(confluences, boundaries))) catchmentsid = pcr.nominal(pcr.subcatchment(ldd, catch_points)) drain = pcr.accuflux(ldd_mask, 1) riversid = pcr.ifthen(river, catchmentsid) if not keepall: riversid = pcr.nominal( pcr.ifthen( pcr.mapmaximum( pcr.areatotal( pcr.scalar(catchments) * 0 + 1, pcr.nominal(catchments))) == pcr.areatotal( pcr.scalar(catchments) * 0 + 1, pcr.nominal(catchments)), riversid)) pcr.report(riversid, riversid_map) pcr.report(drain, drain_map)
def main(): ### Read input arguments ##### logfilename = 'wtools_static_maps.log' parser = OptionParser() usage = "usage: %prog [options]" parser = OptionParser(usage=usage) parser.add_option('-q', '--quiet', dest='verbose', default=True, action='store_false', help='do not print status messages to stdout') parser.add_option('-i', '--ini', dest='inifile', default=None, help='ini file with settings for static_maps.exe') parser.add_option('-s', '--source', dest='source', default='wflow', help='Source folder containing clone (default=./wflow)') parser.add_option('-d', '--destination', dest='destination', default='staticmaps', help='Destination folder (default=./staticmaps)') parser.add_option('-r', '--river', dest='rivshp', default=None, help='river network polyline layer (ESRI Shapefile)') parser.add_option('-c', '--catchment', dest='catchshp', default=None, help='catchment polygon layer (ESRI Shapefile)') parser.add_option('-g', '--gauges', dest='gaugeshp', default=None, help='gauge point layer (ESRI Shapefile)') parser.add_option('-D', '--dem', dest='dem_in', default=None, help='digital elevation model (GeoTiff)') parser.add_option('-L', '--landuse', dest='landuse', default=None, help='land use / land cover layer (GeoTiff)') parser.add_option('-S', '--soiltype', dest='soil', default=None, help='soil type layer (GeoTiff)') parser.add_option( '-V', '--vegetation', dest='lai', default=None, help= 'vegetation LAI layer location (containing 12 GeoTiffs <LAI00000.XXX.tif>)' ) parser.add_option( '-O', '--other_maps', dest='other_maps', default=None, help= 'bracketed [] comma-separated list of paths to other maps that should be reprojected' ) parser.add_option( '-C', '--clean', dest='clean', default=False, action='store_true', help='Clean the .xml files from static maps folder when finished') parser.add_option( '-A', '--alltouch', dest='alltouch', default=False, action='store_true', help= 'option to burn catchments "all touching".\nUseful when catchment-size is small compared to cellsize' ) (options, args) = parser.parse_args() # parse other maps into an array options.other_maps = options.other_maps.replace(' ', '').replace( '[', '').replace(']', '').split(',') options.source = os.path.abspath(options.source) clone_map = os.path.join(options.source, 'mask.map') clone_shp = os.path.join(options.source, 'mask.shp') clone_prj = os.path.join(options.source, 'mask.prj') if None in (options.inifile, options.rivshp, options.catchshp, options.dem_in): msg = """The following files are compulsory: - ini file - DEM (raster) - river (shape) - catchment (shape) """ print(msg) parser.print_help() sys.exit(1) if not os.path.exists(options.inifile): print 'path to ini file cannot be found' sys.exit(1) if not os.path.exists(options.rivshp): print 'path to river shape cannot be found' sys.exit(1) if not os.path.exists(options.catchshp): print 'path to catchment shape cannot be found' sys.exit(1) if not os.path.exists(options.dem_in): print 'path to DEM cannot be found' sys.exit(1) # open a logger, dependent on verbose print to screen or not logger, ch = wtools_lib.setlogger(logfilename, 'WTOOLS', options.verbose) # create directories # TODO: check if workdir is still necessary, try to keep in memory as much as possible # delete old files (when the source and destination folder are different) if np.logical_and(os.path.isdir(options.destination), options.destination is not options.source): shutil.rmtree(options.destination) if options.destination is not options.source: os.makedirs(options.destination) # Read mask if not (os.path.exists(clone_map)): logger.error( 'Clone file {:s} not found. Please run create_grid first.'.format( clone_map)) sys.exit(1) else: # set clone pcr.setclone(clone_map) # get the extent from clone.tif xax, yax, clone, fill_value = gis.gdal_readmap(clone_map, 'GTiff') trans = wtools_lib.get_geotransform(clone_map) extent = wtools_lib.get_extent(clone_map) xmin, ymin, xmax, ymax = extent zeros = np.zeros(clone.shape) ones = pcr.numpy2pcr(pcr.Scalar, np.ones(clone.shape), -9999) # get the projection from clone.tif srs = wtools_lib.get_projection(clone_map) unit_clone = srs.GetAttrValue('UNIT').lower() ### READ CONFIG FILE # open config-file config = wtools_lib.OpenConf(options.inifile) # read settings snapgaugestoriver = wtools_lib.configget(config, 'settings', 'snapgaugestoriver', True, datatype='boolean') burnalltouching = wtools_lib.configget(config, 'settings', 'burncatchalltouching', True, datatype='boolean') burninorder = wtools_lib.configget(config, 'settings', 'burncatchalltouching', False, datatype='boolean') verticetollerance = wtools_lib.configget(config, 'settings', 'vertice_tollerance', 0.0001, datatype='float') ''' read parameters ''' burn_outlets = wtools_lib.configget(config, 'parameters', 'burn_outlets', 10000, datatype='int') burn_rivers = wtools_lib.configget(config, 'parameters', 'burn_rivers', 200, datatype='int') burn_connections = wtools_lib.configget(config, 'parameters', 'burn_connections', 100, datatype='int') burn_gauges = wtools_lib.configget(config, 'parameters', 'burn_gauges', 100, datatype='int') minorder = wtools_lib.configget(config, 'parameters', 'riverorder_min', 3, datatype='int') percentiles = np.array(config.get('parameters', 'statisticmaps', '0, 100').replace(' ', '').split(','), dtype='float') # read the parameters for generating a temporary very high resolution grid if unit_clone == 'degree': cellsize_hr = wtools_lib.configget(config, 'parameters', 'highres_degree', 0.0005, datatype='float') elif (unit_clone == 'metre') or (unit_clone == 'meter'): cellsize_hr = wtools_lib.configget(config, 'parameters', 'highres_metre', 50, datatype='float') cols_hr = int((float(xmax) - float(xmin)) / cellsize_hr + 2) rows_hr = int((float(ymax) - float(ymin)) / cellsize_hr + 2) hr_trans = (float(xmin), cellsize_hr, float(0), float(ymax), 0, -cellsize_hr) clone_hr = os.path.join(options.destination, 'clone_highres.tif') # make a highres clone as well! wtools_lib.CreateTif(clone_hr, rows_hr, cols_hr, hr_trans, srs, 0) # read staticmap locations catchment_map = wtools_lib.configget(config, 'staticmaps', 'catchment', 'wflow_catchment.map') dem_map = wtools_lib.configget(config, 'staticmaps', 'dem', 'wflow_dem.map') demmax_map = wtools_lib.configget(config, 'staticmaps', 'demmax', 'wflow_demmax.map') demmin_map = wtools_lib.configget(config, 'staticmaps', 'demmin', 'wflow_demmin.map') gauges_map = wtools_lib.configget(config, 'staticmaps', 'gauges', 'wflow_gauges.map') landuse_map = wtools_lib.configget(config, 'staticmaps', 'landuse', 'wflow_landuse.map') ldd_map = wtools_lib.configget(config, 'staticmaps', 'ldd', 'wflow_ldd.map') river_map = wtools_lib.configget(config, 'staticmaps', 'river', 'wflow_river.map') outlet_map = wtools_lib.configget(config, 'staticmaps', 'outlet', 'wflow_outlet.map') riverlength_fact_map = wtools_lib.configget(config, 'staticmaps', 'riverlength_fact', 'wflow_riverlength_fact.map') soil_map = wtools_lib.configget(config, 'staticmaps', 'soil', 'wflow_soil.map') streamorder_map = wtools_lib.configget(config, 'staticmaps', 'streamorder', 'wflow_streamorder.map') subcatch_map = wtools_lib.configget(config, 'staticmaps', 'subcatch', 'wflow_subcatch.map') # read mask location (optional) masklayer = wtools_lib.configget(config, 'mask', 'masklayer', options.catchshp) # ???? empty = pcr.ifthen(ones == 0, pcr.scalar(0)) # TODO: check if extents are correct this way # TODO: check what the role of missing values is in zeros and ones (l. 123 in old code) # first add a missing value to dem_in ds = gdal.Open(options.dem_in, gdal.GA_Update) RasterBand = ds.GetRasterBand(1) fill_val = RasterBand.GetNoDataValue() if fill_val is None: RasterBand.SetNoDataValue(-9999) ds = None # reproject to clone map: see http://stackoverflow.com/questions/10454316/how-to-project-and-resample-a-grid-to-match-another-grid-with-gdal-python # resample DEM logger.info('Resampling dem from {:s} to {:s}'.format( os.path.abspath(options.dem_in), os.path.join(options.destination, dem_map))) gis.gdal_warp(options.dem_in, clone_map, os.path.join(options.destination, dem_map), format='PCRaster', gdal_interp=gdalconst.GRA_Average) # retrieve amount of rows and columns from clone # TODO: make windowstats applicable to source/target with different projections. This does not work yet. # retrieve srs from DEM try: srs_dem = wtools_lib.get_projection(options.dem_in) except: logger.warning( 'No projection found in DEM, assuming WGS 1984 lat long') srs_dem = osr.SpatialReference() srs_dem.ImportFromEPSG(4326) clone2dem_transform = osr.CoordinateTransformation(srs, srs_dem) #if srs.ExportToProj4() == srs_dem.ExportToProj4(): for percentile in percentiles: if percentile >= 100: logger.info('computing window maximum') percentile_dem = os.path.join(options.destination, 'wflow_dem_max.map') elif percentile <= 0: logger.info('computing window minimum') percentile_dem = os.path.join(options.destination, 'wflow_dem_min.map') else: logger.info('computing window {:d} percentile'.format( int(percentile))) percentile_dem = os.path.join( options.destination, 'wflow_dem_{:03d}.map'.format(int(percentile))) percentile_dem = os.path.join( options.destination, 'wflow_dem_{:03d}.map'.format(int(percentile))) stats = wtools_lib.windowstats(options.dem_in, len(yax), len(xax), trans, srs, percentile_dem, percentile, transform=clone2dem_transform, logger=logger) # else: # logger.warning('Projections of DEM and clone are different. DEM statistics for different projections is not yet implemented') """ # burn in rivers # first convert and clip the river shapefile # retrieve river shape projection, if not available assume EPSG:4326 file_att = os.path.splitext(os.path.basename(options.rivshp))[0] ds = ogr.Open(options.rivshp) lyr = ds.GetLayerByName(file_att) extent = lyr.GetExtent() extent_in = [extent[0], extent[2], extent[1], extent[3]] try: # get spatial reference from shapefile srs_rivshp = lyr.GetSpatialRef() logger.info('Projection in river shapefile is {:s}'.format(srs_rivshp.ExportToProj4())) except: logger.warning('No projection found in {:s}, assuming WGS 1984 lat-lon'.format(options.rivshp)) srs_rivshp = osr.SpatialReference() srs_rivshp.ImportFromEPSG(4326) rivprojshp = os.path.join(options.destination, 'rivshp_proj.shp') logger.info('Projecting and clipping {:s} to {:s}'.format(options.rivshp, rivprojshp)) # TODO: Line below takes a very long time to process, the bigger the shapefile, the more time. How do we deal with this? call(('ogr2ogr','-s_srs', srs_rivshp.ExportToProj4(),'-t_srs', srs.ExportToProj4(), '-clipsrc', '{:f}'.format(xmin), '{:f}'.format(ymin), '{:f}'.format(xmax), '{:f}'.format(ymax), rivprojshp, options.rivshp)) """ # TODO: BURNING!! # project catchment layer to projection of clone file_att = os.path.splitext(os.path.basename(options.catchshp))[0] print options.catchshp ds = ogr.Open(options.catchshp) lyr = ds.GetLayerByName(file_att) extent = lyr.GetExtent() extent_in = [extent[0], extent[2], extent[1], extent[3]] try: # get spatial reference from shapefile srs_catchshp = lyr.GetSpatialRef() logger.info('Projection in catchment shapefile is {:s}'.format( srs_catchshp.ExportToProj4())) except: logger.warning( 'No projection found in {:s}, assuming WGS 1984 lat-lon'.format( options.catchshp)) srs_catchshp = osr.SpatialReference() srs_catchshp.ImportFromEPSG(4326) catchprojshp = os.path.join(options.destination, 'catchshp_proj.shp') logger.info('Projecting {:s} to {:s}'.format(options.catchshp, catchprojshp)) call(('ogr2ogr', '-s_srs', srs_catchshp.ExportToProj4(), '-t_srs', srs.ExportToProj4(), '-clipsrc', '{:f}'.format(xmin), '{:f}'.format(ymin), '{:f}'.format(xmax), '{:f}'.format(ymax), catchprojshp, options.catchshp)) # logger.info('Calculating ldd') ldddem = pcr.readmap(os.path.join(options.destination, dem_map)) ldd_select = pcr.lddcreate(ldddem, 1e35, 1e35, 1e35, 1e35) pcr.report(ldd_select, os.path.join(options.destination, 'wflow_ldd.map')) # compute stream order, identify river cells streamorder = pcr.ordinal(pcr.streamorder(ldd_select)) river = pcr.ifthen(streamorder >= pcr.ordinal(minorder), pcr.boolean(1)) # find the minimum value in the DEM and cover missing values with a river with this value. Effect is none!! so now left out! # mindem = int(np.min(pcr.pcr2numpy(pcr.ordinal(os.path.join(options.destination, dem_map)),9999999))) # dem_resample_map = pcr.cover(os.path.join(options.destination, dem_map), pcr.scalar(river)*0+mindem) # pcr.report(dem_resample_map, os.path.join(options.destination, dem_map)) pcr.report(streamorder, os.path.join(options.destination, streamorder_map)) pcr.report(river, os.path.join(options.destination, river_map)) # deal with your catchments if options.gaugeshp == None: logger.info('No gauges defined, using outlets instead') gauges = pcr.ordinal( pcr.uniqueid( pcr.boolean( pcr.ifthen(pcr.scalar(ldd_select) == 5, pcr.boolean(1))))) pcr.report(gauges, os.path.join(options.destination, gauges_map)) # TODO: Add the gauge shape code from StaticMaps.py (line 454-489) # TODO: add river length map (see SticMaps.py, line 492-499) # report river length # make a high resolution empty map dem_hr_file = os.path.join(options.destination, 'dem_highres.tif') burn_hr_file = os.path.join(options.destination, 'burn_highres.tif') demburn_hr_file = os.path.join(options.destination, 'demburn_highres.map') riv_hr_file = os.path.join(options.destination, 'riv_highres.map') gis.gdal_warp(options.dem_in, clone_hr, dem_hr_file) # wtools_lib.CreateTif(riv_hr, rows_hr, cols_hr, hr_trans, srs, 0) file_att = os.path.splitext(os.path.basename(options.rivshp))[0] # open the shape layer ds = ogr.Open(options.rivshp) lyr = ds.GetLayerByName(file_att) gis.ogr_burn(lyr, clone_hr, -100, file_out=burn_hr_file, format='GTiff', gdal_type=gdal.GDT_Float32, fill_value=0) # read dem and burn values and add xax_hr, yax_hr, burn_hr, fill = gis.gdal_readmap(burn_hr_file, 'GTiff') burn_hr[burn_hr == fill] = 0 xax_hr, yax_hr, dem_hr, fill = gis.gdal_readmap(dem_hr_file, 'GTiff') dem_hr[dem_hr == fill] = np.nan demburn_hr = dem_hr + burn_hr demburn_hr[np.isnan(demburn_hr)] = -9999 gis.gdal_writemap(demburn_hr_file, 'PCRaster', xax_hr, yax_hr, demburn_hr, -9999.) pcr.setclone(demburn_hr_file) demburn_hr = pcr.readmap(demburn_hr_file) ldd_hr = pcr.lddcreate(demburn_hr, 1e35, 1e35, 1e35, 1e35) pcr.report(ldd_hr, os.path.join(options.destination, 'ldd_hr.map')) pcr.setglobaloption('unitcell') riv_hr = pcr.scalar( pcr.streamorder(ldd_hr) >= minorder) * pcr.downstreamdist(ldd_hr) pcr.report(riv_hr, riv_hr_file) pcr.setglobaloption('unittrue') pcr.setclone(clone_map) logger.info('Computing river length') #riverlength = wt.windowstats(riv_hr,clone_rows,clone_columns,clone_trans,srs_clone,resultdir,'frac',clone2dem_transform) riverlength = wtools_lib.windowstats(riv_hr_file, len(yax), len(xax), trans, srs, os.path.join(options.destination, riverlength_fact_map), stat='fact', logger=logger) # TODO: nothing happends with the river lengths yet. Need to decide how to use these # report outlet map pcr.report(pcr.ifthen(pcr.ordinal(ldd_select) == 5, pcr.ordinal(1)), os.path.join(options.destination, outlet_map)) # report subcatchment map subcatchment = pcr.subcatchment(ldd_select, gauges) pcr.report(pcr.ordinal(subcatchment), os.path.join(options.destination, subcatch_map)) # Report land use map if options.landuse == None: logger.info( 'No land use map used. Preparing {:s} with only ones.'.format( os.path.join(options.destination, landuse_map))) pcr.report(pcr.nominal(ones), os.path.join(options.destination, landuse_map)) else: logger.info('Resampling land use from {:s} to {:s}'.format( os.path.abspath(options.landuse), os.path.join(options.destination, os.path.abspath(landuse_map)))) gis.gdal_warp(options.landuse, clone_map, os.path.join(options.destination, landuse_map), format='PCRaster', gdal_interp=gdalconst.GRA_Mode, gdal_type=gdalconst.GDT_Int32) # report soil map if options.soil == None: logger.info('No soil map used. Preparing {:s} with only ones.'.format( os.path.join(options.destination, soil_map))) pcr.report(pcr.nominal(ones), os.path.join(options.destination, soil_map)) else: logger.info('Resampling soil from {:s} to {:s}'.format( os.path.abspath(options.soil), os.path.join(options.destination, os.path.abspath(soil_map)))) gis.gdal_warp(options.soil, clone_map, os.path.join(options.destination, soil_map), format='PCRaster', gdal_interp=gdalconst.GRA_Mode, gdal_type=gdalconst.GDT_Int32) if options.lai == None: logger.info( 'No vegetation LAI maps used. Preparing default maps {:s} with only ones.' .format(os.path.join(options.destination, soil_map))) pcr.report(pcr.nominal(ones), os.path.join(options.destination, soil_map)) else: dest_lai = os.path.join(options.destination, 'clim') os.makedirs(dest_lai) for month in range(12): lai_in = os.path.join(options.lai, 'LAI00000.{:03d}'.format(month + 1)) lai_out = os.path.join(dest_lai, 'LAI00000.{:03d}'.format(month + 1)) logger.info('Resampling vegetation LAI from {:s} to {:s}'.format( os.path.abspath(lai_in), os.path.abspath(lai_out))) gis.gdal_warp(lai_in, clone_map, lai_out, format='PCRaster', gdal_interp=gdalconst.GRA_Bilinear, gdal_type=gdalconst.GDT_Float32) # report soil map if options.other_maps == None: logger.info('No other maps used. Skipping other maps.') else: logger.info('Resampling list of other maps...') for map_file in options.other_maps: map_name = os.path.split(map_file)[1] logger.info('Resampling a map from {:s} to {:s}'.format( os.path.abspath(map_file), os.path.join(options.destination, map_name))) gis.gdal_warp(map_file, clone_map, os.path.join(options.destination, map_name), format='PCRaster', gdal_interp=gdalconst.GRA_Mode, gdal_type=gdalconst.GDT_Float32) if options.clean: wtools_lib.DeleteList(glob.glob( os.path.join(options.destination, '*.xml')), logger=logger) wtools_lib.DeleteList(glob.glob( os.path.join(options.destination, 'clim', '*.xml')), logger=logger) wtools_lib.DeleteList(glob.glob( os.path.join(options.destination, '*highres*')), logger=logger)
def main(source, destination, inifile, dem_in, rivshp, catchshp, gaugeshp=None, landuse=None, soil=None, lai=None, other_maps=None, logfilename='wtools_static_maps.log', verbose=True, clean=True, alltouch=False, outlets=([], [])): # parse other maps into an array if not other_maps == None: if type(other_maps) == str: print other_maps other_maps = other_maps.replace(' ', '').replace('[', '').replace( ']', '').split(',') source = os.path.abspath(source) clone_map = os.path.join(source, 'mask.map') clone_shp = os.path.join(source, 'mask.shp') clone_prj = os.path.join(source, 'mask.prj') if None in (rivshp, catchshp, dem_in): msg = """The following files are compulsory: - DEM (raster) - river (shape) - catchment (shape) """ print(msg) parser.print_help() sys.exit(1) if (inifile is not None) and (not os.path.exists(inifile)): print 'path to ini file cannot be found' sys.exit(1) if not os.path.exists(rivshp): print 'path to river shape cannot be found' sys.exit(1) if not os.path.exists(catchshp): print 'path to catchment shape cannot be found' sys.exit(1) if not os.path.exists(dem_in): print 'path to DEM cannot be found' sys.exit(1) # open a logger, dependent on verbose print to screen or not logger, ch = wt.setlogger(logfilename, 'WTOOLS', verbose) # create directories # TODO: check if workdir is still necessary, try to # keep in memory as much as possible # delete old files (when the source and destination folder are different) if np.logical_and(os.path.isdir(destination), destination is not source): shutil.rmtree(destination) if destination is not source: os.makedirs(destination) # Read mask if not (os.path.exists(clone_map)): logger.error( 'Clone file {:s} not found. Please run create_grid first.'.format( clone_map)) sys.exit(1) else: # set clone pcr.setclone(clone_map) # get the extent from clone.tif xax, yax, clone, fill_value = wt.gdal_readmap(clone_map, 'GTiff') trans = wt.get_geotransform(clone_map) extent = wt.get_extent(clone_map) xmin, ymin, xmax, ymax = extent zeros = np.zeros(clone.shape) ones = pcr.numpy2pcr(pcr.Scalar, np.ones(clone.shape), -9999) # get the projection from clone.tif srs = wt.get_projection(clone_map) unit_clone = srs.GetAttrValue('UNIT').lower() # READ CONFIG FILE # open config-file if inifile is None: config = ConfigParser.SafeConfigParser() config.optionxform = str else: config = wt.OpenConf(inifile) # read settings snapgaugestoriver = wt.configget(config, 'settings', 'snapgaugestoriver', True, datatype='boolean') burnalltouching = wt.configget(config, 'settings', 'burncatchalltouching', True, datatype='boolean') burninorder = wt.configget(config, 'settings', 'burncatchalltouching', False, datatype='boolean') verticetollerance = wt.configget(config, 'settings', 'vertice_tollerance', 0.0001, datatype='float') ''' read parameters ''' burn_outlets = wt.configget(config, 'parameters', 'burn_outlets', 10000, datatype='int') burn_rivers = wt.configget(config, 'parameters', 'burn_rivers', 200, datatype='int') burn_connections = wt.configget(config, 'parameters', 'burn_connections', 100, datatype='int') burn_gauges = wt.configget(config, 'parameters', 'burn_gauges', 100, datatype='int') minorder = wt.configget(config, 'parameters', 'riverorder_min', 3, datatype='int') try: percentiles = np.array(config.get('parameters', 'statisticmaps', '0, 100').replace(' ', '').split(','), dtype='float') except ConfigParser.NoOptionError: percentiles = [0.0, 100.0] # read the parameters for generating a temporary very high resolution grid if unit_clone == 'degree': cellsize_hr = wt.configget(config, 'parameters', 'highres_degree', 0.0005, datatype='float') elif (unit_clone == 'metre') or (unit_clone == 'meter'): cellsize_hr = wt.configget(config, 'parameters', 'highres_metre', 50, datatype='float') cols_hr = int((float(xmax) - float(xmin)) / cellsize_hr + 2) rows_hr = int((float(ymax) - float(ymin)) / cellsize_hr + 2) hr_trans = (float(xmin), cellsize_hr, float(0), float(ymax), 0, -cellsize_hr) clone_hr = os.path.join(destination, 'clone_highres.tif') # make a highres clone as well! wt.CreateTif(clone_hr, rows_hr, cols_hr, hr_trans, srs, 0) # read staticmap locations catchment_map = wt.configget(config, 'staticmaps', 'catchment', 'wflow_catchment.map') dem_map = wt.configget(config, 'staticmaps', 'dem', 'wflow_dem.map') demmax_map = wt.configget(config, 'staticmaps', 'demmax', 'wflow_demmax.map') demmin_map = wt.configget(config, 'staticmaps', 'demmin', 'wflow_demmin.map') gauges_map = wt.configget(config, 'staticmaps', 'gauges', 'wflow_gauges.map') landuse_map = wt.configget(config, 'staticmaps', 'landuse', 'wflow_landuse.map') ldd_map = wt.configget(config, 'staticmaps', 'ldd', 'wflow_ldd.map') river_map = wt.configget(config, 'staticmaps', 'river', 'wflow_river.map') outlet_map = wt.configget(config, 'staticmaps', 'outlet', 'wflow_outlet.map') riverlength_fact_map = wt.configget(config, 'staticmaps', 'riverlength_fact', 'wflow_riverlength_fact.map') soil_map = wt.configget(config, 'staticmaps', 'soil', 'wflow_soil.map') streamorder_map = wt.configget(config, 'staticmaps', 'streamorder', 'wflow_streamorder.map') subcatch_map = wt.configget(config, 'staticmaps', 'subcatch', 'wflow_subcatch.map') # read mask location (optional) masklayer = wt.configget(config, 'mask', 'masklayer', catchshp) # ???? empty = pcr.ifthen(ones == 0, pcr.scalar(0)) # TODO: check if extents are correct this way # TODO: check what the role of missing values is in zeros and ones (l. 123 # in old code) # first add a missing value to dem_in ds = gdal.Open(dem_in, gdal.GA_Update) RasterBand = ds.GetRasterBand(1) fill_val = RasterBand.GetNoDataValue() if fill_val is None: RasterBand.SetNoDataValue(-9999) ds = None # reproject to clone map: see http://stackoverflow.com/questions/10454316/how-to-project-and-resample-a-grid-to-match-another-grid-with-gdal-python # resample DEM logger.info('Resampling dem from {:s} to {:s}'.format( os.path.abspath(dem_in), os.path.join(destination, dem_map))) wt.gdal_warp(dem_in, clone_map, os.path.join(destination, dem_map), format='PCRaster', gdal_interp=gdalconst.GRA_Average) # retrieve amount of rows and columns from clone # TODO: make windowstats applicable to source/target with different projections. This does not work yet. # retrieve srs from DEM try: srs_dem = wt.get_projection(dem_in) except: logger.warning( 'No projection found in DEM, assuming WGS 1984 lat long') srs_dem = osr.SpatialReference() srs_dem.ImportFromEPSG(4326) clone2dem_transform = osr.CoordinateTransformation(srs, srs_dem) # if srs.ExportToProj4() == srs_dem.ExportToProj4(): wt.windowstats(dem_in, len(yax), len(xax), trans, srs, destination, percentiles, transform=clone2dem_transform, logger=logger) ## read catchment shape-file to create catchment map src = rasterio.open(clone_map) shapefile = fiona.open(catchshp, "r") catchment_shapes = [feature["geometry"] for feature in shapefile] image = features.rasterize(catchment_shapes, out_shape=src.shape, all_touched=True, transform=src.transform) catchment_domain = pcr.numpy2pcr(pcr.Ordinal, image.copy(), 0) ## read river shape-file and create burn layer shapefile = fiona.open(rivshp, "r") river_shapes = [feature["geometry"] for feature in shapefile] image = features.rasterize(river_shapes, out_shape=src.shape, all_touched=False, transform=src.transform) rivers = pcr.numpy2pcr(pcr.Nominal, image.copy(), 0) riverdem = pcr.scalar(rivers) * pcr.readmap( os.path.join(destination, dem_map)) pcr.setglobaloption("lddin") riverldd = pcr.lddcreate(riverdem, 1e35, 1e35, 1e35, 1e35) riveroutlet = pcr.cover( pcr.ifthen(pcr.scalar(riverldd) == 5, pcr.scalar(1000)), 0) burn_layer = pcr.cover((pcr.scalar( pcr.ifthen(pcr.streamorder(riverldd) > 1, pcr.streamorder(riverldd))) - 1) * 1000 + riveroutlet, 0) outlets_x, outlets_y = outlets n_outlets = len(outlets_x) logger.info('Number of outlets: {}'.format(n_outlets)) if n_outlets >= 1: outlets_map_numbered = tr.points_to_map(pcr.scalar(0), outlets_x, outlets_y, 0.5) outlets_map = pcr.boolean(outlets_map_numbered) # snap outlets to closest river (max 1 cell closer to river) outlets_map = pcr.boolean( pcr.cover(tr.snaptomap(pcr.ordinal(outlets_map), rivers), 0)) ## create ldd per catchment logger.info('Calculating ldd') ldddem = pcr.scalar(clone_map) # per subcatchment, burn dem, then create modified dem that fits the ldd of the subcatchment # this ldd dem is merged over catchments, to create a global ldd that abides to the subcatchment boundaries for idx, shape in enumerate(catchment_shapes): logger.info('Computing ldd for catchment ' + str(idx + 1) + '/' + str(len(catchment_shapes))) image = features.rasterize([shape], out_shape=src.shape, all_touched=True, transform=src.transform) catchment = pcr.numpy2pcr(pcr.Scalar, image.copy(), 0) dem_burned_catchment = ( pcr.readmap(os.path.join(destination, dem_map)) * pcr.scalar(catchment_domain) * catchment) - burn_layer ldddem_catchment = pcr.lddcreatedem(dem_burned_catchment, 1e35, 1e35, 1e35, 1e35) ldddem = pcr.cover(ldddem, ldddem_catchment) wflow_ldd = pcr.lddcreate(ldddem, 1e35, 1e35, 1e35, 1e35) if n_outlets >= 1: # set outlets to pit wflow_ldd = pcr.ifthenelse(outlets_map, pcr.ldd(5), wflow_ldd) wflow_ldd = pcr.lddrepair(wflow_ldd) pcr.report(wflow_ldd, os.path.join(destination, 'wflow_ldd.map')) # compute stream order, identify river cells streamorder = pcr.ordinal(pcr.streamorder(wflow_ldd)) river = pcr.ifthen(streamorder >= pcr.ordinal(minorder), pcr.boolean(1)) # find the minimum value in the DEM and cover missing values with a river with this value. Effect is none!! so now left out! # mindem = int(np.min(pcr.pcr2numpy(pcr.ordinal(os.path.join(destination, dem_map)),9999999))) # dem_resample_map = pcr.cover(os.path.join(destination, dem_map), pcr.scalar(river)*0+mindem) # pcr.report(dem_resample_map, os.path.join(destination, dem_map)) pcr.report(streamorder, os.path.join(destination, streamorder_map)) pcr.report(river, os.path.join(destination, river_map)) # deal with your catchments if gaugeshp == None: logger.info('No gauges defined, using outlets instead') gauges = pcr.ordinal( pcr.uniqueid( pcr.boolean( pcr.ifthen(pcr.scalar(wflow_ldd) == 5, pcr.boolean(1))))) pcr.report(gauges, os.path.join(destination, gauges_map)) # TODO: Add the gauge shape code from StaticMaps.py (line 454-489) # TODO: add river length map (see SticMaps.py, line 492-499) # since the products here (river length fraction) are not yet used # this is disabled for now, as it also takes a lot of computation time if False: # report river length # make a high resolution empty map dem_hr_file = os.path.join(destination, 'dem_highres.tif') burn_hr_file = os.path.join(destination, 'burn_highres.tif') demburn_hr_file = os.path.join(destination, 'demburn_highres.map') riv_hr_file = os.path.join(destination, 'riv_highres.map') wt.gdal_warp(dem_in, clone_hr, dem_hr_file) # wt.CreateTif(riv_hr, rows_hr, cols_hr, hr_trans, srs, 0) # open the shape layer ds = ogr.Open(rivshp) lyr = ds.GetLayer(0) wt.ogr_burn(lyr, clone_hr, -100, file_out=burn_hr_file, format='GTiff', gdal_type=gdal.GDT_Float32, fill_value=0) # read dem and burn values and add xax_hr, yax_hr, burn_hr, fill = wt.gdal_readmap(burn_hr_file, 'GTiff') burn_hr[burn_hr == fill] = 0 xax_hr, yax_hr, dem_hr, fill = wt.gdal_readmap(dem_hr_file, 'GTiff') dem_hr[dem_hr == fill] = np.nan demburn_hr = dem_hr + burn_hr demburn_hr[np.isnan(demburn_hr)] = -9999 wt.gdal_writemap(demburn_hr_file, 'PCRaster', xax_hr, yax_hr, demburn_hr, -9999.) pcr.setclone(demburn_hr_file) demburn_hr = pcr.readmap(demburn_hr_file) logger.info('Calculating ldd to determine river length') ldd_hr = pcr.lddcreate(demburn_hr, 1e35, 1e35, 1e35, 1e35) pcr.report(ldd_hr, os.path.join(destination, 'ldd_hr.map')) pcr.setglobaloption('unitcell') riv_hr = pcr.scalar( pcr.streamorder(ldd_hr) >= minorder) * pcr.downstreamdist(ldd_hr) pcr.report(riv_hr, riv_hr_file) pcr.setglobaloption('unittrue') pcr.setclone(clone_map) logger.info('Computing river length') wt.windowstats(riv_hr_file, len(yax), len(xax), trans, srs, destination, stat='fact', transform=False, logger=logger) # TODO: nothing happens with the river lengths yet. Need to decide how to use these # report outlet map pcr.report(pcr.ifthen(pcr.ordinal(wflow_ldd) == 5, pcr.ordinal(1)), os.path.join(destination, outlet_map)) # report subcatchment map subcatchment = pcr.subcatchment(wflow_ldd, gauges) pcr.report(pcr.ordinal(subcatchment), os.path.join(destination, subcatch_map)) # Report land use map if landuse == None: logger.info( 'No land use map used. Preparing {:s} with only ones.'.format( os.path.join(destination, landuse_map))) pcr.report(pcr.nominal(ones), os.path.join(destination, landuse_map)) else: logger.info('Resampling land use from {:s} to {:s}'.format( os.path.abspath(landuse), os.path.join(destination, os.path.abspath(landuse_map)))) wt.gdal_warp(landuse, clone_map, os.path.join(destination, landuse_map), format='PCRaster', gdal_interp=gdalconst.GRA_Mode, gdal_type=gdalconst.GDT_Int32) # report soil map if soil == None: logger.info('No soil map used. Preparing {:s} with only ones.'.format( os.path.join(destination, soil_map))) pcr.report(pcr.nominal(ones), os.path.join(destination, soil_map)) else: logger.info('Resampling soil from {:s} to {:s}'.format( os.path.abspath(soil), os.path.join(destination, os.path.abspath(soil_map)))) wt.gdal_warp(soil, clone_map, os.path.join(destination, soil_map), format='PCRaster', gdal_interp=gdalconst.GRA_Mode, gdal_type=gdalconst.GDT_Int32) if lai == None: logger.info( 'No vegetation LAI maps used. Preparing default maps {:s} with only ones.' .format(os.path.join(destination, soil_map))) pcr.report(pcr.nominal(ones), os.path.join(destination, soil_map)) else: dest_lai = os.path.join(destination, 'clim') os.makedirs(dest_lai) for month in range(12): lai_in = os.path.join(lai, 'LAI00000.{:03d}'.format(month + 1)) lai_out = os.path.join(dest_lai, 'LAI00000.{:03d}'.format(month + 1)) logger.info('Resampling vegetation LAI from {:s} to {:s}'.format( os.path.abspath(lai_in), os.path.abspath(lai_out))) wt.gdal_warp(lai_in, clone_map, lai_out, format='PCRaster', gdal_interp=gdalconst.GRA_Bilinear, gdal_type=gdalconst.GDT_Float32) # report soil map if other_maps == None: logger.info('No other maps used. Skipping other maps.') else: logger.info('Resampling list of other maps...') for map_file in other_maps: map_name = os.path.split(map_file)[1] logger.info('Resampling a map from {:s} to {:s}'.format( os.path.abspath(map_file), os.path.join( destination, os.path.splitext(os.path.basename(map_file))[0] + '.map'))) wt.gdal_warp(map_file, clone_map, os.path.join( destination, os.path.splitext(os.path.basename(map_file))[0] + '.map'), format='PCRaster', gdal_interp=gdalconst.GRA_Mode, gdal_type=gdalconst.GDT_Float32) if clean: wt.DeleteList(glob.glob(os.path.join(destination, '*.xml')), logger=logger) wt.DeleteList(glob.glob(os.path.join(destination, 'clim', '*.xml')), logger=logger) wt.DeleteList(glob.glob(os.path.join(destination, '*highres*')), logger=logger)
def subcatch_stream(ldd, threshold, stream=None, min_strahler=-999, max_strahler=999, assign_edge=False, assign_existing=False, up_area=None, basin=None): """ Derive catchments based upon strahler threshold Input: ldd -- pcraster object direction, local drain directions threshold -- integer, strahler threshold, subcatchments ge threshold are derived stream=None -- pcraster object ordinal, stream order map (made with pcr.streamorder), if provided, stream order map is not generated on the fly but used from this map. Useful when a subdomain within a catchment is provided, which would cause edge effects in the stream order map min_strahler=-999 -- integer, minimum strahler threshold of river catchments to return max_strahler=999 -- integer, maximum strahler threshold of river catchments to return assign_unique=False -- if set to True, unassigned connected areas at the edges of the domain are assigned a unique id as well. If set to False, edges are not assigned assign_existing=False == if set to True, unassigned edges are assigned to existing basins with an upstream weighting. If set to False, edges are assigned to unique IDs, or not assigned output: stream_ge -- pcraster object, streams of strahler order ge threshold subcatch -- pcraster object, subcatchments of strahler order ge threshold """ # derive stream order if stream is None: stream = pcr.streamorder(ldd) stream_ge = pcr.ifthen(stream >= threshold, stream) stream_up_sum = pcr.ordinal(pcr.upstream(ldd, pcr.cover(pcr.scalar(stream_ge), 0))) # detect any transfer of strahler order, to a higher strahler order. transition_strahler = pcr.ifthenelse(pcr.downstream(ldd, stream_ge) != stream_ge, pcr.boolean(1), pcr.ifthenelse(pcr.nominal(ldd) == 5, pcr.boolean(1), pcr.ifthenelse(pcr.downstream(ldd, pcr.scalar(stream_up_sum)) > pcr.scalar(stream_ge), pcr.boolean(1), pcr.boolean(0)))) # make unique ids (write to file) transition_unique = pcr.ordinal(pcr.uniqueid(transition_strahler)) # derive upstream catchment areas (write to file) subcatch = pcr.nominal(pcr.subcatchment(ldd, transition_unique)) # mask out areas outside basin if basin is not None: subcatch = pcr.ifthen(basin, subcatch) if assign_edge: # fill unclassified areas (in pcraster equal to zero) with a unique id, above the maximum id assigned so far unique_edge = pcr.clump(pcr.ifthen(subcatch==0, pcr.ordinal(0))) subcatch = pcr.ifthenelse(subcatch==0, pcr.nominal(pcr.mapmaximum(pcr.scalar(subcatch)) + pcr.scalar(unique_edge)), pcr.nominal(subcatch)) elif assign_existing: # unaccounted areas are added to largest nearest draining basin if up_area is None: up_area = pcr.ifthen(pcr.boolean(pcr.cover(stream_ge, 0)), pcr.accuflux(ldd, 1)) riverid = pcr.ifthen(pcr.boolean(pcr.cover(stream_ge, 0)), subcatch) friction = 1./pcr.scalar(pcr.spreadzone(pcr.cover(pcr.ordinal(up_area), 0), 0, 0)) # *(pcr.scalar(ldd)*0+1) delta = pcr.ifthen(pcr.scalar(ldd)>=0, pcr.ifthen(pcr.cover(subcatch, 0)==0, pcr.spreadzone(pcr.cover(riverid, 0), 0, friction))) subcatch = pcr.ifthenelse(pcr.boolean(pcr.cover(subcatch, 0)), subcatch, delta) # finally, only keep basins with minimum and maximum river order flowing through them strahler_subcatch = pcr.areamaximum(stream, subcatch) subcatch = pcr.ifthen(pcr.ordinal(strahler_subcatch) >= min_strahler, pcr.ifthen(pcr.ordinal(strahler_subcatch) <= max_strahler, subcatch)) return stream_ge, pcr.ordinal(subcatch)
def getParameterFiles(self,currTimeStep,cellArea,ldd,\ initial_condition_dictionary = None): # parameters for Water Bodies: fracWat # waterBodyIds # waterBodyOut # waterBodyArea # waterBodyTyp # waterBodyCap # cell surface area (m2) and ldd self.cellArea = cellArea ldd = pcr.ifthen(self.landmask, ldd) # date used for accessing/extracting water body information date_used = currTimeStep.fulldate year_used = currTimeStep.year if self.onlyNaturalWaterBodies == True: date_used = self.dateForNaturalCondition year_used = self.dateForNaturalCondition[0:4] # fracWat = fraction of surface water bodies (dimensionless) self.fracWat = pcr.scalar(0.0) if self.useNetCDF: self.fracWat = vos.netcdf2PCRobjClone(self.ncFileInp,'fracWaterInp', \ date_used, useDoy = 'yearly',\ cloneMapFileName = self.cloneMap) else: self.fracWat = vos.readPCRmapClone(\ self.fracWaterInp+str(year_used)+".map", self.cloneMap,self.tmpDir,self.inputDir) self.fracWat = pcr.cover(self.fracWat, 0.0) self.fracWat = pcr.max(0.0, self.fracWat) self.fracWat = pcr.min(1.0, self.fracWat) self.waterBodyIds = pcr.nominal(0) # waterBody ids self.waterBodyOut = pcr.boolean(0) # waterBody outlets self.waterBodyArea = pcr.scalar(0.) # waterBody surface areas # water body ids if self.useNetCDF: self.waterBodyIds = vos.netcdf2PCRobjClone(self.ncFileInp,'waterBodyIds', \ date_used, useDoy = 'yearly',\ cloneMapFileName = self.cloneMap) else: self.waterBodyIds = vos.readPCRmapClone(\ self.waterBodyIdsInp+str(year_used)+".map",\ self.cloneMap,self.tmpDir,self.inputDir,False,None,True) # self.waterBodyIds = pcr.ifthen(\ pcr.scalar(self.waterBodyIds) > 0.,\ pcr.nominal(self.waterBodyIds)) # water body outlets (correcting outlet positions) wbCatchment = pcr.catchmenttotal(pcr.scalar(1), ldd) self.waterBodyOut = pcr.ifthen(wbCatchment ==\ pcr.areamaximum(wbCatchment, \ self.waterBodyIds),\ self.waterBodyIds) # = outlet ids self.waterBodyOut = pcr.ifthen(\ pcr.scalar(self.waterBodyIds) > 0.,\ self.waterBodyOut) # TODO: Please also consider endorheic lakes! # correcting water body ids self.waterBodyIds = pcr.ifthen(\ pcr.scalar(self.waterBodyIds) > 0.,\ pcr.subcatchment(ldd,self.waterBodyOut)) # boolean map for water body outlets: self.waterBodyOut = pcr.ifthen(\ pcr.scalar(self.waterBodyOut) > 0.,\ pcr.boolean(1)) # reservoir surface area (m2): if self.useNetCDF: resSfArea = 1000. * 1000. * \ vos.netcdf2PCRobjClone(self.ncFileInp,'resSfAreaInp', \ date_used, useDoy = 'yearly',\ cloneMapFileName = self.cloneMap) else: resSfArea = 1000. * 1000. * vos.readPCRmapClone( self.resSfAreaInp+str(year_used)+".map",\ self.cloneMap,self.tmpDir,self.inputDir) resSfArea = pcr.areaaverage(resSfArea, self.waterBodyIds) resSfArea = pcr.cover(resSfArea, 0.) # water body surface area (m2): (lakes and reservoirs) self.waterBodyArea = pcr.max(pcr.areatotal(\ pcr.cover(\ self.fracWat*self.cellArea, 0.0), self.waterBodyIds), pcr.areaaverage(\ pcr.cover(resSfArea, 0.0) , self.waterBodyIds)) self.waterBodyArea = pcr.ifthen(self.waterBodyArea > 0.,\ self.waterBodyArea) # correcting water body ids and outlets (exclude all water bodies with surfaceArea = 0) self.waterBodyIds = pcr.ifthen(self.waterBodyArea > 0., self.waterBodyIds) self.waterBodyOut = pcr.ifthen(pcr.boolean(self.waterBodyIds), self.waterBodyOut) # water body types: # - 2 = reservoirs (regulated discharge) # - 1 = lakes (weirFormula) # - 0 = non lakes or reservoirs (e.g. wetland) self.waterBodyTyp = pcr.nominal(0) if self.useNetCDF: self.waterBodyTyp = vos.netcdf2PCRobjClone(self.ncFileInp,'waterBodyTyp', \ date_used, useDoy = 'yearly',\ cloneMapFileName = self.cloneMap) else: self.waterBodyTyp = vos.readPCRmapClone( self.waterBodyTypInp+str(year_used)+".map",\ self.cloneMap,self.tmpDir,self.inputDir,False,None,True) # excluding wetlands (waterBodyTyp = 0) in all functions related to lakes/reservoirs # self.waterBodyTyp = pcr.ifthen(\ pcr.scalar(self.waterBodyTyp) > 0,\ pcr.nominal(self.waterBodyTyp)) self.waterBodyTyp = pcr.ifthen(\ pcr.scalar(self.waterBodyIds) > 0,\ pcr.nominal(self.waterBodyTyp)) self.waterBodyTyp = pcr.areamajority(self.waterBodyTyp,\ self.waterBodyIds) # choose only one type: either lake or reservoir self.waterBodyTyp = pcr.ifthen(\ pcr.scalar(self.waterBodyTyp) > 0,\ pcr.nominal(self.waterBodyTyp)) self.waterBodyTyp = pcr.ifthen(pcr.boolean(self.waterBodyIds), self.waterBodyTyp) # correcting lakes and reservoirs ids and outlets self.waterBodyIds = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0, self.waterBodyIds) self.waterBodyOut = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0, self.waterBodyOut) # reservoir maximum capacity (m3): self.resMaxCap = pcr.scalar(0.0) self.waterBodyCap = pcr.scalar(0.0) if self.useNetCDF: self.resMaxCap = 1000. * 1000. * \ vos.netcdf2PCRobjClone(self.ncFileInp,'resMaxCapInp', \ date_used, useDoy = 'yearly',\ cloneMapFileName = self.cloneMap) else: self.resMaxCap = 1000. * 1000. * vos.readPCRmapClone(\ self.resMaxCapInp+str(year_used)+".map", \ self.cloneMap,self.tmpDir,self.inputDir) self.resMaxCap = pcr.ifthen(self.resMaxCap > 0,\ self.resMaxCap) self.resMaxCap = pcr.areaaverage(self.resMaxCap,\ self.waterBodyIds) # water body capacity (m3): (lakes and reservoirs) self.waterBodyCap = pcr.cover( self.resMaxCap, 0.0) # Note: Most of lakes have capacities > 0. self.waterBodyCap = pcr.ifthen(pcr.boolean(self.waterBodyIds), self.waterBodyCap) # correcting water body types: # Reservoirs that have zero capacities will be assumed as lakes. self.waterBodyTyp = \ pcr.ifthen(pcr.scalar(self.waterBodyTyp) > 0.,\ self.waterBodyTyp) self.waterBodyTyp = pcr.ifthenelse(self.waterBodyCap > 0.,\ self.waterBodyTyp,\ pcr.ifthenelse(pcr.scalar(self.waterBodyTyp) == 2,\ pcr.nominal(1),\ self.waterBodyTyp)) # final corrections: self.waterBodyTyp = pcr.ifthen(self.waterBodyArea > 0.,\ self.waterBodyTyp) # make sure that all lakes and/or reservoirs have surface areas self.waterBodyTyp = \ pcr.ifthen(pcr.scalar(self.waterBodyTyp) > 0.,\ self.waterBodyTyp) # make sure that only types 1 and 2 will be considered in lake/reservoir functions self.waterBodyIds = pcr.ifthen(pcr.scalar(self.waterBodyTyp) > 0.,\ self.waterBodyIds) # make sure that all lakes and/or reservoirs have ids self.waterBodyOut = pcr.ifthen(pcr.scalar(self.waterBodyIds) > 0.,\ self.waterBodyOut) # make sure that all lakes and/or reservoirs have outlets # for a natural run (self.onlyNaturalWaterBodies == True) # which uses only the year 1900, assume all reservoirs are lakes if self.onlyNaturalWaterBodies == True and date_used == self.dateForNaturalCondition: logger.info( "Using only natural water bodies identified in the year 1900. All reservoirs in 1900 are assumed as lakes." ) self.waterBodyTyp = \ pcr.ifthen(pcr.scalar(self.waterBodyTyp) > 0.,\ pcr.nominal(1)) # check that all lakes and/or reservoirs have types, ids, surface areas and outlets: test = pcr.defined(self.waterBodyTyp) & pcr.defined(self.waterBodyArea) &\ pcr.defined(self.waterBodyIds) & pcr.boolean(pcr.areamaximum(pcr.scalar(self.waterBodyOut), self.waterBodyIds)) a, b, c = vos.getMinMaxMean( pcr.cover(pcr.scalar(test), 1.0) - pcr.scalar(1.0)) threshold = 1e-3 if abs(a) > threshold or abs(b) > threshold: logger.warning( "Missing information in some lakes and/or reservoirs.") # at the beginning of simulation period (timeStepPCR = 1) # - we have to define/get the initial conditions # if currTimeStep.timeStepPCR == 1: self.getICs(initial_condition_dictionary) # For each new reservoir (introduced at the beginning of the year) # initiating storage, average inflow and outflow # self.waterBodyStorage = pcr.cover(self.waterBodyStorage, 0.0) self.avgInflow = pcr.cover(self.avgInflow, 0.0) self.avgOutflow = pcr.cover(self.avgOutflow, 0.0) # cropping only in the landmask region: self.fracWat = pcr.ifthen(self.landmask, self.fracWat) self.waterBodyIds = pcr.ifthen(self.landmask, self.waterBodyIds) self.waterBodyOut = pcr.ifthen(self.landmask, self.waterBodyOut) self.waterBodyArea = pcr.ifthen(self.landmask, self.waterBodyArea) self.waterBodyTyp = pcr.ifthen(self.landmask, self.waterBodyTyp) self.waterBodyCap = pcr.ifthen(self.landmask, self.waterBodyCap) self.waterBodyStorage = pcr.ifthen(self.landmask, self.waterBodyStorage) self.avgInflow = pcr.ifthen(self.landmask, self.avgInflow) self.avgOutflow = pcr.ifthen(self.landmask, self.avgOutflow)
def generate_hydro_datasets(path, output_dir, step): print(path) file_name = os.path.splitext(os.path.basename(path))[0] map_path = output_dir + "/" + file_name + ".map" path_prefix = map_path[:-14] if step == "ldd": cmd = u"gdal_translate -a_nodata -9999 -of PCRaster -ot Float32 " + path + " " + map_path print(cmd) subprocess.call(cmd, shell=True) # slope = pcr.slope(dem) # pcr.report(slope, path_prefix + '_slope.map') # pcr.setglobaloption("lddin") if step == "ldd": dem = pcr.readmap(map_path) print("Computing LDD ...") # enable pit filling ldd = pcr.lddcreate(dem, 9999999, 9999999, 9999999, 9999999) pcr.report(ldd, path_prefix + "_ldd.map") return elif step == "ldddem": dem = pcr.readmap(map_path) print("Computing LDD DEM ...") dem_pitfilled = pcr.lddcreatedem(dem, 9999999, 9999999, 9999999, 9999999) dem_diff = dem_pitfilled - dem pcr.report(dem_diff, path_prefix + "_dem_pits_diff.map") return # print("Computing LDD without pit filling ...") # ldd_pits = pcr.lddcreate(dem, 0, 0, 0, 0) # pcr.report(ldd_pits, path_prefix + '_ldd_with_pits.map') # print("Computing pits ...") # pits = pcr.pit(ldd_pits) # pcr.report(pits, path_prefix + '_pits.map') if step == "fa": ldd = pcr.readmap(path_prefix + "_ldd.map") print("Computing flow accumulation ...") fa = pcr.accuflux(ldd, 1) pcr.report(fa, path_prefix + "_fa.map") return if step == "catchments": ldd = pcr.readmap(path_prefix + "_ldd.map") print("Delineating catchments ...") catchments = pcr.catchment(ldd, pcr.pit(ldd)) pcr.report(catchments, path_prefix + "_catchments.map") return if step == "stream_order": ldd = pcr.readmap(path_prefix + "_ldd.map") print("Computing stream order ...") stream_order = pcr.streamorder(ldd) pcr.report(stream_order, path_prefix + "_streamorder.map") return if step == "stream": ldd = pcr.readmap(path_prefix + "_ldd.map") accuThreshold = 100 print("Computing stream ...") stream = pcr.ifthenelse(pcr.accuflux(ldd, 1) >= accuThreshold, pcr.boolean(1), pcr.boolean(0)) pcr.report(stream, path_prefix + "_stream.map") return if step == "height_river": print("Computing heigh_river ...") stream = pcr.readmap(path_prefix + "_stream.map") dem = pcr.readmap(map_path) height_river = pcr.ifthenelse(stream, pcr.ordinal(dem), 0) pcr.report(height_river, path_prefix + "_height_river.map") return if step == "up_elevation": print("Computing up_elevation ...") height_river = pcr.readmap(path_prefix + "_height_river.map") ldd = pcr.readmap(path_prefix + "_ldd.map") up_elevation = pcr.scalar(pcr.subcatchment(ldd, height_river)) pcr.report(up_elevation, path_prefix + "_up_elevation.map") return if step == "hand": print("Computing HAND ...") dem = pcr.readmap(map_path) up_elevation = pcr.readmap(path_prefix + "_up_elevation.map") hand = pcr.max(dem - up_elevation, 0) pcr.report(hand, path_prefix + "_hand.map") return if step == "dand": print("Computing DAND ...") ldd = pcr.readmap(path_prefix + "_ldd.map") stream = pcr.readmap(path_prefix + "_stream.map") dist = pcr.ldddist(ldd, stream, 1) pcr.report(dist, path_prefix + "_dist.map") return if step == "fa_river": print("Computing FA river ...") fa = pcr.readmap(path_prefix + "_fa.map") stream = pcr.readmap(path_prefix + "_stream.map") fa_river = pcr.ifthenelse(stream, pcr.ordinal(fa), 0) pcr.report(fa_river, path_prefix + "_fa_river.map") return if step == "faand": print("Computing FAAND ...") fa_river = pcr.readmap(path_prefix + "_fa_river.map") ldd = pcr.readmap(path_prefix + "_ldd.map") up_fa = pcr.scalar(pcr.subcatchment(ldd, fa_river)) pcr.report(up_fa, path_prefix + "_faand.map") return
def subcatch_order_b( ldd, oorder, sizelimit=0, fill=False, fillcomplete=False, stoporder=0 ): """ Determines subcatchments using the catchment order This version tries to keep the number op upstream/downstream catchment the small by first dederivingatchment connected to the major river(the order) given, and fill up from there. Input: - ldd - oorder - order to use - sizelimit - smallest catchments to include, default is all (sizelimit=0) in number of cells - if fill is set to True the higer order catchment are filled also - if fillcomplete is set to True the whole ldd is filled with catchments. :returns sc, dif, nldd; Subcatchment, Points, subcatchldd """ # outl = find_outlet(ldd) # large = pcr.subcatchment(ldd,pcr.boolean(outl)) if stoporder == 0: stoporder = oorder stt = pcr.streamorder(ldd) sttd = pcr.downstream(ldd, stt) pts = pcr.ifthen((pcr.scalar(sttd) - pcr.scalar(stt)) > 0.0, sttd) maxorder = pcraster.framework.getCellValue(pcr.mapmaximum(stt), 1, 1) dif = pcr.uniqueid(pcr.boolean(pcr.ifthen(stt == pcr.ordinal(oorder), pts))) if fill: for order in range(oorder, maxorder): m_pts = pcr.ifthen((pcr.scalar(sttd) - pcr.scalar(order)) > 0.0, sttd) m_dif = pcr.uniqueid( pcr.boolean(pcr.ifthen(stt == pcr.ordinal(order), m_pts)) ) dif = pcr.uniqueid(pcr.boolean(pcr.cover(m_dif, dif))) for myorder in range(oorder - 1, stoporder, -1): sc = pcr.subcatchment(ldd, pcr.nominal(dif)) m_pts = pcr.ifthen((pcr.scalar(sttd) - pcr.scalar(stt)) > 0.0, sttd) m_dif = pcr.uniqueid( pcr.boolean(pcr.ifthen(stt == pcr.ordinal(myorder - 1), m_pts)) ) dif = pcr.uniqueid( pcr.boolean(pcr.cover(pcr.ifthen(pcr.scalar(sc) == 0, m_dif), dif)) ) if fillcomplete: sc = pcr.subcatchment(ldd, pcr.nominal(dif)) cs, m_dif, stt = subcatch_order_a(ldd, stoporder) dif = pcr.uniqueid( pcr.boolean( pcr.cover( pcr.ifthen(pcr.scalar(sc) == 0, pcr.ordinal(m_dif)), pcr.ordinal(dif), ) ) ) scsize = pcr.catchmenttotal(1, ldd) dif = pcr.ordinal(pcr.uniqueid(pcr.boolean(pcr.ifthen(scsize >= sizelimit, dif)))) sc = pcr.subcatchment(ldd, dif) # Make pit ldd nldd = pcr.lddrepair(pcr.ifthenelse(pcr.cover(dif, 0) > 0, 5, ldd)) return sc, dif, nldd
def main(): ### Read input arguments ##### parser = OptionParser() usage = "usage: %prog [options]" parser = OptionParser(usage=usage) parser.add_option('-q', '--quiet', dest='verbose', default=True, action='store_false', help='do not print status messages to stdout') parser.add_option('-i', '--ini', dest='inifile', default='hand_contour_inun.ini', nargs=1, help='ini configuration file') parser.add_option('-f', '--flood_map', nargs=1, dest='flood_map', help='Flood map file (NetCDF point time series file') parser.add_option('-v', '--flood_variable', nargs=1, dest='flood_variable', default='water_level', help='variable name of flood water level') parser.add_option('-b', '--bankfull_map', dest='bankfull_map', default='', help='Map containing bank full level (is subtracted from flood map, in NetCDF)') parser.add_option('-c', '--catchment', dest='catchment_strahler', default=7, type='int', help='Strahler order threshold >= are selected as catchment boundaries') parser.add_option('-t', '--time', dest='time', default='', help='time in YYYYMMDDHHMMSS, overrides time in NetCDF input if set') # parser.add_option('-s', '--hand_strahler', # dest='hand_strahler', default=7, type='int', # help='Strahler order threshold >= selected as riverine') parser.add_option('-m', '--max_strahler', dest = 'max_strahler', default=1000, type='int', help='Maximum Strahler order to loop over') parser.add_option('-d', '--destination', dest='dest_path', default='inun', help='Destination path') parser.add_option('-H', '--hand_file_prefix', dest='hand_file_prefix', default='', help='optional HAND file prefix of already generated HAND files') parser.add_option('-n', '--neg_HAND', dest='neg_HAND', default=0, type='int', help='if set to 1, allow for negative HAND values in HAND maps') (options, args) = parser.parse_args() if not os.path.exists(options.inifile): print 'path to ini file cannot be found' sys.exit(1) options.dest_path = os.path.abspath(options.dest_path) if not(os.path.isdir(options.dest_path)): os.makedirs(options.dest_path) # set up the logger flood_name = os.path.split(options.flood_map)[1].split('.')[0] # case_name = 'inun_{:s}_hand_{:02d}_catch_{:02d}'.format(flood_name, options.hand_strahler, options.catchment_strahler) case_name = 'inun_{:s}_catch_{:02d}'.format(flood_name, options.catchment_strahler) logfilename = os.path.join(options.dest_path, 'hand_contour_inun.log') logger, ch = inun_lib.setlogger(logfilename, 'HAND_INUN', options.verbose) logger.info('$Id: $') logger.info('Flood map: {:s}'.format(options.flood_map)) logger.info('Bank full map: {:s}'.format(options.bankfull_map)) logger.info('Destination path: {:s}'.format(options.dest_path)) # read out ini file ### READ CONFIG FILE # open config-file config = inun_lib.open_conf(options.inifile) # read settings options.dem_file = inun_lib.configget(config, 'HighResMaps', 'dem_file', True) options.ldd_file = inun_lib.configget(config, 'HighResMaps', 'ldd_file', True) options.stream_file = inun_lib.configget(config, 'HighResMaps', 'stream_file', True) options.riv_length_fact_file = inun_lib.configget(config, 'wflowResMaps', 'riv_length_fact_file', True) options.ldd_wflow = inun_lib.configget(config, 'wflowResMaps', 'ldd_wflow', True) options.riv_width_file = inun_lib.configget(config, 'wflowResMaps', 'riv_width_file', True) options.file_format = inun_lib.configget(config, 'file_settings', 'file_format', 0, datatype='int') options.out_format = inun_lib.configget(config, 'file_settings', 'out_format', 0, datatype='int') options.latlon = inun_lib.configget(config, 'file_settings', 'latlon', 0, datatype='int') options.x_tile = inun_lib.configget(config, 'tiling', 'x_tile', 10000, datatype='int') options.y_tile = inun_lib.configget(config, 'tiling', 'y_tile', 10000, datatype='int') options.x_overlap = inun_lib.configget(config, 'tiling', 'x_overlap', 1000, datatype='int') options.y_overlap = inun_lib.configget(config, 'tiling', 'y_overlap', 1000, datatype='int') options.iterations = inun_lib.configget(config, 'inundation', 'iterations', 20, datatype='int') options.initial_level = inun_lib.configget(config, 'inundation', 'initial_level', 32., datatype='float') options.flood_volume_type = inun_lib.configget(config, 'inundation', 'flood_volume_type', 0, datatype='int') # options.area_multiplier = inun_lib.configget(config, 'inundation', # 'area_multiplier', 1., datatype='float') logger.info('DEM file: {:s}'.format(options.dem_file)) logger.info('LDD file: {:s}'.format(options.ldd_file)) logger.info('Columns per tile: {:d}'.format(options.x_tile)) logger.info('Rows per tile: {:d}'.format(options.y_tile)) logger.info('Columns overlap: {:d}'.format(options.x_overlap)) logger.info('Rows overlap: {:d}'.format(options.y_overlap)) metadata_global = {} # add metadata from the section [metadata] meta_keys = config.options('metadata_global') for key in meta_keys: metadata_global[key] = config.get('metadata_global', key) # add a number of metadata variables that are mandatory metadata_global['config_file'] = os.path.abspath(options.inifile) metadata_var = {} metadata_var['units'] = 'm' metadata_var['standard_name'] = 'water_surface_height_above_reference_datum' metadata_var['long_name'] = 'flooding' metadata_var['comment'] = 'water_surface_reference_datum_altitude is given in file {:s}'.format(options.dem_file) if not os.path.exists(options.dem_file): logger.error('path to dem file {:s} cannot be found'.format(options.dem_file)) sys.exit(1) if not os.path.exists(options.ldd_file): logger.error('path to ldd file {:s} cannot be found'.format(options.ldd_file)) sys.exit(1) # Read extent from a GDAL compatible file try: extent = inun_lib.get_gdal_extent(options.dem_file) except: msg = 'Input file {:s} not a gdal compatible file'.format(options.dem_file) inun_lib.close_with_error(logger, ch, msg) sys.exit(1) try: x, y = inun_lib.get_gdal_axes(options.dem_file, logging=logger) srs = inun_lib.get_gdal_projection(options.dem_file, logging=logger) except: msg = 'Input file {:s} not a gdal compatible file'.format(options.dem_file) inun_lib.close_with_error(logger, ch, msg) sys.exit(1) # read history from flood file if options.file_format == 0: a = nc.Dataset(options.flood_map, 'r') metadata_global['history'] = 'Created by: $Id: $, boundary conditions from {:s},\nhistory: {:s}'.format(os.path.abspath(options.flood_map), a.history) a.close() else: metadata_global['history'] = 'Created by: $Id: $, boundary conditions from {:s},\nhistory: {:s}'.format(os.path.abspath(options.flood_map), 'PCRaster file, no history') # first write subcatch maps and hand maps ############### TODO ###### # setup a HAND file for each strahler order max_s = inun_lib.define_max_strahler(options.stream_file, logging=logger) stream_max = np.minimum(max_s, options.max_strahler) for hand_strahler in range(options.catchment_strahler, stream_max + 1, 1): dem_name = os.path.split(options.dem_file)[1].split('.')[0] if os.path.isfile('{:s}_{:02d}.tif'.format(options.hand_file_prefix, hand_strahler)): hand_file = '{:s}_{:02d}.tif'.format(options.hand_file_prefix, hand_strahler) else: logger.info('No HAND files with HAND prefix were found, checking {:s}_hand_strahler_{:02d}.tif'.format(dem_name, hand_strahler)) hand_file = os.path.join(options.dest_path, '{:s}_hand_strahler_{:02d}.tif'.format(dem_name, hand_strahler)) if not(os.path.isfile(hand_file)): # hand file does not exist yet! Generate it, otherwise skip! logger.info('HAND file {:s} not found, start setting up...please wait...'.format(hand_file)) hand_file_tmp = os.path.join(options.dest_path, '{:s}_hand_strahler_{:02d}.tif.tmp'.format(dem_name, hand_strahler)) ds_hand, band_hand = inun_lib.prepare_gdal(hand_file_tmp, x, y, logging=logger, srs=srs) # band_hand = ds_hand.GetRasterBand(1) # Open terrain data for reading ds_dem, rasterband_dem = inun_lib.get_gdal_rasterband(options.dem_file) ds_ldd, rasterband_ldd = inun_lib.get_gdal_rasterband(options.ldd_file) ds_stream, rasterband_stream = inun_lib.get_gdal_rasterband(options.stream_file) n = 0 for x_loop in range(0, len(x), options.x_tile): x_start = np.maximum(x_loop, 0) x_end = np.minimum(x_loop + options.x_tile, len(x)) # determine actual overlap for cutting for y_loop in range(0, len(y), options.y_tile): x_overlap_min = x_start - np.maximum(x_start - options.x_overlap, 0) x_overlap_max = np.minimum(x_end + options.x_overlap, len(x)) - x_end n += 1 # print('tile {:001d}:'.format(n)) y_start = np.maximum(y_loop, 0) y_end = np.minimum(y_loop + options.y_tile, len(y)) y_overlap_min = y_start - np.maximum(y_start - options.y_overlap, 0) y_overlap_max = np.minimum(y_end + options.y_overlap, len(y)) - y_end # cut out DEM logger.debug('Computing HAND for xmin: {:d} xmax: {:d} ymin {:d} ymax {:d}'.format(x_start, x_end,y_start, y_end)) terrain = rasterband_dem.ReadAsArray(x_start - x_overlap_min, y_start - y_overlap_min, (x_end + x_overlap_max) - (x_start - x_overlap_min), (y_end + y_overlap_max) - (y_start - y_overlap_min) ) drainage = rasterband_ldd.ReadAsArray(x_start - x_overlap_min, y_start - y_overlap_min, (x_end + x_overlap_max) - (x_start - x_overlap_min), (y_end + y_overlap_max) - (y_start - y_overlap_min) ) stream = rasterband_stream.ReadAsArray(x_start - x_overlap_min, y_start - y_overlap_min, (x_end + x_overlap_max) - (x_start - x_overlap_min), (y_end + y_overlap_max) - (y_start - y_overlap_min) ) # write to temporary file terrain_temp_file = os.path.join(options.dest_path, 'terrain_temp.map') drainage_temp_file = os.path.join(options.dest_path, 'drainage_temp.map') stream_temp_file = os.path.join(options.dest_path, 'stream_temp.map') if rasterband_dem.GetNoDataValue() is not None: inun_lib.gdal_writemap(terrain_temp_file, 'PCRaster', np.arange(0, terrain.shape[1]), np.arange(0, terrain.shape[0]), terrain, rasterband_dem.GetNoDataValue(), gdal_type=gdal.GDT_Float32, logging=logger) else: # in case no nodata value is found logger.warning('No nodata value found in {:s}. assuming -9999'.format(options.dem_file)) inun_lib.gdal_writemap(terrain_temp_file, 'PCRaster', np.arange(0, terrain.shape[1]), np.arange(0, terrain.shape[0]), terrain, -9999., gdal_type=gdal.GDT_Float32, logging=logger) inun_lib.gdal_writemap(drainage_temp_file, 'PCRaster', np.arange(0, terrain.shape[1]), np.arange(0, terrain.shape[0]), drainage, rasterband_ldd.GetNoDataValue(), gdal_type=gdal.GDT_Int32, logging=logger) inun_lib.gdal_writemap(stream_temp_file, 'PCRaster', np.arange(0, terrain.shape[1]), np.arange(0, terrain.shape[0]), stream, rasterband_ldd.GetNoDataValue(), gdal_type=gdal.GDT_Int32, logging=logger) # read as pcr objects pcr.setclone(terrain_temp_file) terrain_pcr = pcr.readmap(terrain_temp_file) drainage_pcr = pcr.lddrepair(pcr.ldd(pcr.readmap(drainage_temp_file))) # convert to ldd type map stream_pcr = pcr.scalar(pcr.readmap(stream_temp_file)) # convert to ldd type map #check if the highest stream order of the tile is below the hand_strahler # if the highest stream order of the tile is smaller than hand_strahler, than DEM values are taken instead of HAND values. max_stream_tile = inun_lib.define_max_strahler(stream_temp_file, logging=logger) if max_stream_tile < hand_strahler: hand_pcr = terrain_pcr logger.info('For this tile, DEM values are used instead of HAND because there is no stream order larger than {:02d}'.format(hand_strahler)) else: # compute streams stream_ge, subcatch = inun_lib.subcatch_stream(drainage_pcr, hand_strahler, stream=stream_pcr) # generate streams # compute basins stream_ge_dummy, subcatch = inun_lib.subcatch_stream(drainage_pcr, options.catchment_strahler, stream=stream_pcr) # generate streams basin = pcr.boolean(subcatch) hand_pcr, dist_pcr = inun_lib.derive_HAND(terrain_pcr, drainage_pcr, 3000, rivers=pcr.boolean(stream_ge), basin=basin, neg_HAND=options.neg_HAND) # convert to numpy hand = pcr.pcr2numpy(hand_pcr, -9999.) # cut relevant part if y_overlap_max == 0: y_overlap_max = -hand.shape[0] if x_overlap_max == 0: x_overlap_max = -hand.shape[1] hand_cut = hand[0+y_overlap_min:-y_overlap_max, 0+x_overlap_min:-x_overlap_max] band_hand.WriteArray(hand_cut, x_start, y_start) os.unlink(terrain_temp_file) os.unlink(drainage_temp_file) os.unlink(stream_temp_file) band_hand.FlushCache() ds_dem = None ds_ldd = None ds_stream = None band_hand.SetNoDataValue(-9999.) ds_hand = None logger.info('Finalizing {:s}'.format(hand_file)) # rename temporary file to final hand file os.rename(hand_file_tmp, hand_file) else: logger.info('HAND file {:s} already exists...skipping...'.format(hand_file)) ##################################################################################### # HAND file has now been prepared, moving to flood mapping part # ##################################################################################### # set the clone pcr.setclone(options.ldd_wflow) # read wflow ldd as pcraster object ldd_pcr = pcr.readmap(options.ldd_wflow) xax, yax, riv_width, fill_value = inun_lib.gdal_readmap(options.riv_width_file, 'GTiff', logging=logger) # determine cell length in meters using ldd_pcr as clone (if latlon=True, values are converted to m2 x_res, y_res, reallength_wflow = pcrut.detRealCellLength(pcr.scalar(ldd_pcr), not(bool(options.latlon))) cell_surface_wflow = pcr.pcr2numpy(x_res * y_res, 0) if options.flood_volume_type == 0: # load the staticmaps needed to estimate volumes across all # xax, yax, riv_length, fill_value = inun_lib.gdal_readmap(options.riv_length_file, 'GTiff', logging=logger) # riv_length = np.ma.masked_where(riv_length==fill_value, riv_length) xax, yax, riv_width, fill_value = inun_lib.gdal_readmap(options.riv_width_file, 'GTiff', logging=logger) riv_width[riv_width == fill_value] = 0 # read river length factor file (multiplier) xax, yax, riv_length_fact, fill_value = inun_lib.gdal_readmap(options.riv_length_fact_file, 'GTiff', logging=logger) riv_length_fact = np.ma.masked_where(riv_length_fact==fill_value, riv_length_fact) drain_length = wflow_lib.detdrainlength(ldd_pcr, x_res, y_res) # compute river length in each cell riv_length = pcr.pcr2numpy(drain_length, 0) * riv_length_fact # riv_length_pcr = pcr.numpy2pcr(pcr.Scalar, riv_length, 0) flood_folder = os.path.join(options.dest_path, case_name) flood_vol_map = os.path.join(flood_folder, '{:s}_vol.tif'.format(os.path.split(options.flood_map)[1].split('.')[0])) if not(os.path.isdir(flood_folder)): os.makedirs(flood_folder) if options.out_format == 0: inun_file_tmp = os.path.join(flood_folder, '{:s}.tif.tmp'.format(case_name)) inun_file = os.path.join(flood_folder, '{:s}.tif'.format(case_name)) else: inun_file_tmp = os.path.join(flood_folder, '{:s}.nc.tmp'.format(case_name)) inun_file = os.path.join(flood_folder, '{:s}.nc'.format(case_name)) hand_temp_file = os.path.join(flood_folder, 'hand_temp.map') drainage_temp_file = os.path.join(flood_folder, 'drainage_temp.map') stream_temp_file = os.path.join(flood_folder, 'stream_temp.map') flood_vol_temp_file = os.path.join(flood_folder, 'flood_warp_temp.tif') # load the data with river levels and compute the volumes if options.file_format == 0: # assume we need the maximum value in a NetCDF time series grid logger.info('Reading flood from {:s} NetCDF file'.format(options.flood_map)) a = nc.Dataset(options.flood_map, 'r') if options.latlon == 0: xax = a.variables['x'][:] yax = a.variables['y'][:] else: xax = a.variables['lon'][:] yax = a.variables['lat'][:] if options.time == '': time_list = nc.num2date(a.variables['time'][:], units = a.variables['time'].units, calendar=a.variables['time'].calendar) time = [time_list[len(time_list)/2]] else: time = [dt.datetime.strptime(options.time, '%Y%m%d%H%M%S')] flood_series = a.variables[options.flood_variable][:] flood_data = flood_series.max(axis=0) if np.ma.is_masked(flood_data): flood = flood_data.data flood[flood_data.mask] = 0 if yax[-1] > yax[0]: yax = np.flipud(yax) flood = np.flipud(flood) a.close() elif options.file_format == 1: logger.info('Reading flood from {:s} PCRaster file'.format(options.flood_map)) xax, yax, flood, flood_fill_value = inun_lib.gdal_readmap(options.flood_map, 'PCRaster', logging=logger) flood = np.ma.masked_equal(flood, flood_fill_value) if options.time == '': options.time = '20000101000000' time = [dt.datetime.strptime(options.time, '%Y%m%d%H%M%S')] flood[flood==flood_fill_value] = 0. # load the bankfull depths if options.bankfull_map == '': bankfull = np.zeros(flood.shape) else: if options.file_format == 0: logger.info('Reading bankfull from {:s} NetCDF file'.format(options.bankfull_map)) a = nc.Dataset(options.bankfull_map, 'r') xax = a.variables['x'][:] yax = a.variables['y'][:] bankfull_series = a.variables[options.flood_variable][:] bankfull_data = bankfull_series.max(axis=0) if np.ma.is_masked(bankfull_data): bankfull = bankfull_data.data bankfull[bankfull_data.mask] = 0 if yax[-1] > yax[0]: yax = np.flipud(yax) bankfull = np.flipud(bankfull) a.close() elif options.file_format == 1: logger.info('Reading bankfull from {:s} PCRaster file'.format(options.bankfull_map)) xax, yax, bankfull, bankfull_fill_value = inun_lib.gdal_readmap(options.bankfull_map, 'PCRaster', logging=logger) bankfull = np.ma.masked_equal(bankfull, bankfull_fill_value) # flood = bankfull*2 # res_x = 2000 # res_y = 2000 # subtract the bankfull water level to get flood levels (above bankfull) flood_vol = np.maximum(flood-bankfull, 0) if options.flood_volume_type == 0: flood_vol_m = riv_length*riv_width*flood_vol/cell_surface_wflow # volume expressed in meters water disc flood_vol_m_pcr = pcr.numpy2pcr(pcr.Scalar, flood_vol_m, 0) else: flood_vol_m = flood_vol/cell_surface_wflow flood_vol_m_data = flood_vol_m.data flood_vol_m_data[flood_vol_m.mask] = -999. logger.info('Saving water layer map to {:s}'.format(flood_vol_map)) # write to a tiff file inun_lib.gdal_writemap(flood_vol_map, 'GTiff', xax, yax, np.maximum(flood_vol_m_data, 0), -999., logging=logger) # this is placed later in the hand loop # ds_hand, rasterband_hand = inun_lib.get_gdal_rasterband(hand_file) ds_ldd, rasterband_ldd = inun_lib.get_gdal_rasterband(options.ldd_file) ds_stream, rasterband_stream = inun_lib.get_gdal_rasterband(options.stream_file) logger.info('Preparing flood map in {:s} ...please wait...'.format(inun_file)) if options.out_format == 0: ds_inun, band_inun = inun_lib.prepare_gdal(inun_file_tmp, x, y, logging=logger, srs=srs) # band_inun = ds_inun.GetRasterBand(1) else: ds_inun, band_inun = inun_lib.prepare_nc(inun_file_tmp, time, x, np.flipud(y), metadata=metadata_global, metadata_var=metadata_var, logging=logger) # loop over all the tiles n = 0 for x_loop in range(0, len(x), options.x_tile): x_start = np.maximum(x_loop, 0) x_end = np.minimum(x_loop + options.x_tile, len(x)) # determine actual overlap for cutting for y_loop in range(0, len(y), options.y_tile): x_overlap_min = x_start - np.maximum(x_start - options.x_overlap, 0) x_overlap_max = np.minimum(x_end + options.x_overlap, len(x)) - x_end n += 1 # print('tile {:001d}:'.format(n)) y_start = np.maximum(y_loop, 0) y_end = np.minimum(y_loop + options.y_tile, len(y)) y_overlap_min = y_start - np.maximum(y_start - options.y_overlap, 0) y_overlap_max = np.minimum(y_end + options.y_overlap, len(y)) - y_end x_tile_ax = x[x_start - x_overlap_min:x_end + x_overlap_max] y_tile_ax = y[y_start - y_overlap_min:y_end + y_overlap_max] # cut out DEM logger.debug('handling xmin: {:d} xmax: {:d} ymin {:d} ymax {:d}'.format(x_start, x_end, y_start, y_end)) drainage = rasterband_ldd.ReadAsArray(x_start - x_overlap_min, y_start - y_overlap_min, (x_end + x_overlap_max) - (x_start - x_overlap_min), (y_end + y_overlap_max) - (y_start - y_overlap_min) ) stream = rasterband_stream.ReadAsArray(x_start - x_overlap_min, y_start - y_overlap_min, (x_end + x_overlap_max) - (x_start - x_overlap_min), (y_end + y_overlap_max) - (y_start - y_overlap_min) ) # stream_max = np.minimum(stream.max(), options.max_strahler) inun_lib.gdal_writemap(drainage_temp_file, 'PCRaster', x_tile_ax, y_tile_ax, drainage, rasterband_ldd.GetNoDataValue(), gdal_type=gdal.GDT_Int32, logging=logger) inun_lib.gdal_writemap(stream_temp_file, 'PCRaster', x_tile_ax, y_tile_ax, stream, rasterband_stream.GetNoDataValue(), gdal_type=gdal.GDT_Int32, logging=logger) # read as pcr objects pcr.setclone(stream_temp_file) drainage_pcr = pcr.lddrepair(pcr.ldd(pcr.readmap(drainage_temp_file))) # convert to ldd type map stream_pcr = pcr.scalar(pcr.readmap(stream_temp_file)) # convert to ldd type map # warp of flood volume to inundation resolution inun_lib.gdal_warp(flood_vol_map, stream_temp_file, flood_vol_temp_file, gdal_interp=gdalconst.GRA_NearestNeighbour) # , x_tile_ax, y_tile_ax, flood_meter, fill_value = inun_lib.gdal_readmap(flood_vol_temp_file, 'GTiff', logging=logger) # make sure that the option unittrue is on !! (if unitcell was is used in another function) x_res_tile, y_res_tile, reallength = pcrut.detRealCellLength(pcr.scalar(stream_pcr), not(bool(options.latlon))) cell_surface_tile = pcr.pcr2numpy(x_res_tile * y_res_tile, 0) # convert meter depth to volume [m3] flood_vol = pcr.numpy2pcr(pcr.Scalar, flood_meter*cell_surface_tile, fill_value) # first prepare a basin map, belonging to the lowest order we are looking at inundation_pcr = pcr.scalar(stream_pcr) * 0 for hand_strahler in range(options.catchment_strahler, stream_max + 1, 1): # hand_temp_file = os.path.join(flood_folder, 'hand_temp.map') if os.path.isfile(os.path.join(options.dest_path, '{:s}_hand_strahler_{:02d}.tif'.format(dem_name, hand_strahler))): hand_file = os.path.join(options.dest_path, '{:s}_hand_strahler_{:02d}.tif'.format(dem_name, hand_strahler)) else: hand_file = '{:s}_{:02d}.tif'.format(options.hand_file_prefix, hand_strahler) ds_hand, rasterband_hand = inun_lib.get_gdal_rasterband(hand_file) hand = rasterband_hand.ReadAsArray(x_start - x_overlap_min, y_start - y_overlap_min, (x_end + x_overlap_max) - (x_start - x_overlap_min), (y_end + y_overlap_max) - (y_start - y_overlap_min) ) print('len x-ax: {:d} len y-ax {:d} x-shape {:d} y-shape {:d}'.format(len(x_tile_ax), len(y_tile_ax), hand.shape[1], hand.shape[0])) inun_lib.gdal_writemap(hand_temp_file, 'PCRaster', x_tile_ax, y_tile_ax, hand, rasterband_hand.GetNoDataValue(), gdal_type=gdal.GDT_Float32, logging=logger) hand_pcr = pcr.readmap(hand_temp_file) stream_ge_hand, subcatch_hand = inun_lib.subcatch_stream(drainage_pcr, options.catchment_strahler, stream=stream_pcr) # stream_ge_hand, subcatch_hand = inun_lib.subcatch_stream(drainage_pcr, hand_strahler, stream=stream_pcr) stream_ge, subcatch = inun_lib.subcatch_stream(drainage_pcr, options.catchment_strahler, stream=stream_pcr, basin=pcr.boolean(pcr.cover(subcatch_hand, 0)), assign_existing=True, min_strahler=hand_strahler, max_strahler=hand_strahler) # generate subcatchments, only within basin for HAND flood_vol_strahler = pcr.ifthenelse(pcr.boolean(pcr.cover(subcatch, 0)), flood_vol, 0) # mask the flood volume map with the created subcatch map for strahler order = hand_strahler inundation_pcr_step = inun_lib.volume_spread(drainage_pcr, hand_pcr, pcr.subcatchment(drainage_pcr, subcatch), # to make sure backwater effects can occur from higher order rivers to lower order rivers flood_vol_strahler, volume_thres=0., iterations=options.iterations, cell_surface=pcr.numpy2pcr(pcr.Scalar, cell_surface_tile, -9999), logging=logger, order=hand_strahler, neg_HAND=options.neg_HAND) # 1166400000. # use maximum value of inundation_pcr_step and new inundation for higher strahler order inundation_pcr = pcr.max(inundation_pcr, inundation_pcr_step) inundation = pcr.pcr2numpy(inundation_pcr, -9999.) # cut relevant part if y_overlap_max == 0: y_overlap_max = -inundation.shape[0] if x_overlap_max == 0: x_overlap_max = -inundation.shape[1] inundation_cut = inundation[0+y_overlap_min:-y_overlap_max, 0+x_overlap_min:-x_overlap_max] # inundation_cut if options.out_format == 0: band_inun.WriteArray(inundation_cut, x_start, y_start) band_inun.FlushCache() else: # with netCDF, data is up-side-down. inun_lib.write_tile_nc(band_inun, inundation_cut, x_start, y_start) # clean up os.unlink(flood_vol_temp_file) os.unlink(drainage_temp_file) os.unlink(hand_temp_file) os.unlink(stream_temp_file) #also remove temp stream file from output folder # if n == 35: # band_inun.SetNoDataValue(-9999.) # ds_inun = None # sys.exit(0) # os.unlink(flood_vol_map) logger.info('Finalizing {:s}'.format(inun_file)) # add the metadata to the file and band # band_inun.SetNoDataValue(-9999.) # ds_inun.SetMetadata(metadata_global) # band_inun.SetMetadata(metadata_var) if options.out_format == 0: ds_inun = None ds_hand = None else: ds_inun.close() ds_ldd = None # rename temporary file to final hand file if os.path.isfile(inun_file): # remove an old result if available os.unlink(inun_file) os.rename(inun_file_tmp, inun_file) logger.info('Done! Thank you for using hand_contour_inun.py') logger, ch = inun_lib.closeLogger(logger, ch) del logger, ch sys.exit(0)
def subcatch_stream( ldd, threshold, min_strahler=-999, max_strahler=999, assign_edge=False, assign_existing=False, up_area=None, ): """ (From Deltares Hydrotools) Derive catchments based upon strahler threshold Input: ldd -- pcraster object direction, local drain directions threshold -- integer, strahler threshold, subcatchments ge threshold are derived min_strahler -- integer, minimum strahler threshold of river catchments to return max_strahler -- integer, maximum strahler threshold of river catchments to return assign_unique=False -- if set to True, unassigned connected areas at the edges of the domain are assigned a unique id as well. If set to False, edges are not assigned assign_existing=False == if set to True, unassigned edges are assigned to existing basins with an upstream weighting. If set to False, edges are assigned to unique IDs, or not assigned output: stream_ge -- pcraster object, streams of strahler order ge threshold subcatch -- pcraster object, subcatchments of strahler order ge threshold """ # derive stream order stream = pcr.streamorder(ldd) stream_ge = pcr.ifthen(stream >= threshold, stream) stream_up_sum = pcr.ordinal( pcr.upstream(ldd, pcr.cover(pcr.scalar(stream_ge), 0))) # detect any transfer of strahler order, to a higher strahler order. transition_strahler = pcr.ifthenelse( pcr.downstream(ldd, stream_ge) != stream_ge, pcr.boolean(1), pcr.ifthenelse( pcr.nominal(ldd) == 5, pcr.boolean(1), pcr.ifthenelse( pcr.downstream(ldd, pcr.scalar(stream_up_sum)) > pcr.scalar(stream_ge), pcr.boolean(1), pcr.boolean(0), ), ), ) # make unique ids (write to file) transition_unique = pcr.ordinal(pcr.uniqueid(transition_strahler)) # derive upstream catchment areas (write to file) subcatch = pcr.nominal(pcr.subcatchment(ldd, transition_unique)) if assign_edge: # fill unclassified areas (in pcraster equal to zero) with a unique id, above the maximum id assigned so far unique_edge = pcr.clump(pcr.ifthen(subcatch == 0, pcr.ordinal(0))) subcatch = pcr.ifthenelse( subcatch == 0, pcr.nominal( pcr.mapmaximum(pcr.scalar(subcatch)) + pcr.scalar(unique_edge)), pcr.nominal(subcatch), ) elif assign_existing: # unaccounted areas are added to largest nearest draining basin if up_area is None: up_area = pcr.ifthen(pcr.boolean(pcr.cover(stream_ge, 0)), pcr.accuflux(ldd, 1)) riverid = pcr.ifthen(pcr.boolean(pcr.cover(stream_ge, 0)), subcatch) friction = 1.0 / pcr.scalar( pcr.spreadzone(pcr.cover(pcr.ordinal(up_area), 0), 0, 0)) # *(pcr.scalar(ldd)*0+1) delta = pcr.ifthen( pcr.scalar(ldd) >= 0, pcr.ifthen( pcr.cover(subcatch, 0) == 0, pcr.spreadzone(pcr.cover(riverid, 0), 0, friction), ), ) subcatch = pcr.ifthenelse(pcr.boolean(pcr.cover(subcatch, 0)), subcatch, delta) # finally, only keep basins with minimum and maximum river order flowing through them strahler_subcatch = pcr.areamaximum(stream, subcatch) subcatch = pcr.ifthen( pcr.ordinal(strahler_subcatch) >= min_strahler, pcr.ifthen(pcr.ordinal(strahler_subcatch) <= max_strahler, subcatch), ) return stream_ge, pcr.ordinal(subcatch)
def subcatch_order_b(ldd, oorder, sizelimit=0, fill=False, fillcomplete=False, stoporder=0): """ Determines subcatchments using the catchment order This version tries to keep the number op upstream/downstream catchment the small by first dederivingatchment connected to the major river(the order) given, and fill up from there. Input: - ldd - oorder - order to use - sizelimit - smallest catchments to include, default is all (sizelimit=0) in number of cells - if fill is set to True the higer order catchment are filled also - if fillcomplete is set to True the whole ldd is filled with catchments. :returns sc, dif, nldd; Subcatchment, Points, subcatchldd """ # outl = find_outlet(ldd) # large = pcr.subcatchment(ldd,pcr.boolean(outl)) if stoporder == 0: stoporder = oorder stt = pcr.streamorder(ldd) sttd = pcr.downstream(ldd, stt) pts = pcr.ifthen((pcr.scalar(sttd) - pcr.scalar(stt)) > 0.0, sttd) maxorder = pcraster.framework.getCellValue(pcr.mapmaximum(stt), 1, 1) dif = pcr.uniqueid(pcr.boolean(pcr.ifthen(stt == pcr.ordinal(oorder), pts))) if fill: for order in range(oorder, maxorder): m_pts = pcr.ifthen((pcr.scalar(sttd) - pcr.scalar(order)) > 0.0, sttd) m_dif = pcr.uniqueid( pcr.boolean(pcr.ifthen(stt == pcr.ordinal(order), m_pts))) dif = pcr.uniqueid(pcr.boolean(pcr.cover(m_dif, dif))) for myorder in range(oorder - 1, stoporder, -1): sc = pcr.subcatchment(ldd, pcr.nominal(dif)) m_pts = pcr.ifthen((pcr.scalar(sttd) - pcr.scalar(stt)) > 0.0, sttd) m_dif = pcr.uniqueid( pcr.boolean(pcr.ifthen(stt == pcr.ordinal(myorder - 1), m_pts))) dif = pcr.uniqueid( pcr.boolean( pcr.cover(pcr.ifthen(pcr.scalar(sc) == 0, m_dif), dif))) if fillcomplete: sc = pcr.subcatchment(ldd, pcr.nominal(dif)) cs, m_dif, stt = subcatch_order_a(ldd, stoporder) dif = pcr.uniqueid( pcr.boolean( pcr.cover( pcr.ifthen(pcr.scalar(sc) == 0, pcr.ordinal(m_dif)), pcr.ordinal(dif), ))) scsize = pcr.catchmenttotal(1, ldd) dif = pcr.ordinal( pcr.uniqueid(pcr.boolean(pcr.ifthen(scsize >= sizelimit, dif)))) sc = pcr.subcatchment(ldd, dif) # Make pit ldd nldd = pcr.lddrepair(pcr.ifthenelse(pcr.cover(dif, 0) > 0, 5, ldd)) return sc, dif, nldd
msg = "Redefining the basin map (so that it is consistent with the ldd map used in PCR-GLOBWB):" logger.info(msg) # - calculate the upstream area of every pixel: upstream_area = pcr.catchmenttotal(cell_area, ldd) # - calculate the catchment area of every basin: upstream_area_maximum = pcr.areamaximum(upstream_area, basin_map) # - identify the outlet of every basin (in order to rederive the basin so that it is consistent with the ldd) outlet = pcr.nominal(pcr.uniqueid(pcr.ifthen(upstream_area == upstream_area_maximum, pcr.boolean(1.0)))) # - ignoring outlets with small upstream areas threshold = 50. * 1000. * 1000. # unit: m2 outlet = pcr.ifthen(upstream_area_maximum > threshold, outlet) #~ pcr.aguila(outlet) outlet = pcr.cover(outlet, pcr.nominal(0.0)) # - recalculate the basin basin_map = pcr.nominal(pcr.subcatchment(ldd, outlet)) basin_map = pcr.clump(basin_map) basin_map = pcr.ifthen(landmask, basin_map) pcr.report(basin_map , "basin_map.map") #~ pcr.aguila(basin_map) # - calculate the basin area basin_area = pcr.areatotal(cell_area, basin_map) pcr.report(basin_area, "basin_area.map") #~ pcr.aguila(basin_area) # finding the month that give the maximum discharge (from the climatology time series) msg = "Identifying the month with peak discharge (from climatology time series):" logger.info(msg) # - read the maximum monthly discharge for every basin maximum_discharge = vos.netcdf2PCRobjClone(input_files['maximumClimatologyDischargeMonthAvg'], \
def static_maps( source, # source folder containing clone destination, # destination folder inifile, # ini file with various settings dem_in, # path to digital elevation model (raster) rivshp, # path to river network (line vector) catchshp, # path to catchment polygon (polygon vector) gaugeshp=None, # path to gauge point (point vector) landuse=None, # path to land use / land cover (raster) soil=None, # path to soil type (raster) lai=None, # path to vegetation LAI (containing 12 GeoTiffs LAI00000.XXX.tif) other_maps=None, # bracketed [] comma-separated list of paths to other maps that should be reprojected logfilename="wtools_static_maps.log", # log file name verbose=True, clean=True, # Clean the .xml files from static maps folder when finished alltouch=False, # option to burn catchments "all touching".\nUseful when catchment-size is small compared to cellsize outlets=([], []), ): # parse other maps into an array if not other_maps == None: if type(other_maps) == str: print(other_maps) other_maps = ( other_maps.replace(" ", "").replace("[", "").replace("]", "").split(",") ) source = os.path.abspath(source) clone_tif = os.path.join(source, "mask.tif") clone_map = os.path.join(source, "mask.map") clone_shp = os.path.join(source, "mask.shp") clone_prj = os.path.join(source, "mask.prj") # open a logger, dependent on verbose print to screen or not logger, ch = wt.setlogger(logfilename, "WTOOLS", verbose) # create directories # TODO: check if workdir is still necessary, try to # keep in memory as much as possible # delete old files (when the source and destination folder are different) if np.logical_and(os.path.isdir(destination), destination is not source): shutil.rmtree(destination) if destination is not source: os.makedirs(destination) # Read mask if not (os.path.exists(clone_map)): logger.error( "Clone file {:s} not found. Please run create_grid first.".format(clone_map) ) sys.exit(1) else: # set clone pcr.setclone(clone_map) # get the extent from clone.tif xax, yax, clone, fill_value = wt.gdal_readmap(clone_tif, "GTiff") trans = wt.get_geotransform(clone_tif) extent = wt.get_extent(clone_tif) xmin, ymin, xmax, ymax = extent zeros = np.zeros(clone.shape) ones = pcr.numpy2pcr(pcr.Scalar, np.ones(clone.shape), -9999) # get the projection from clone.tif srs = wt.get_projection(clone_tif) unit_clone = srs.GetAttrValue("UNIT").lower() # READ CONFIG FILE # open config-file if inifile is None: config = configparser.ConfigParser() config.optionxform = str else: config = wt.OpenConf(inifile) # read settings """ read parameters """ minorder = wt.configget(config, "parameters", "riverorder_min", 3, datatype="int") try: percentiles_str = wt.configget( config, "parameters", "statisticmaps", "0, 100", datatype="str" ) percentiles_split = percentiles_str.replace(" ", "").split(",") percentiles = np.array(percentiles_split, dtype="float") except configparser.NoOptionError: percentiles = [0.0, 100.0] # read the parameters for generating a temporary very high resolution grid if unit_clone == "degree": cellsize_hr = wt.configget( config, "parameters", "highres_degree", 0.0005, datatype="float" ) elif (unit_clone == "metre") or (unit_clone == "meter"): cellsize_hr = wt.configget( config, "parameters", "highres_metre", 50, datatype="float" ) cols_hr = int((float(xmax) - float(xmin)) / cellsize_hr + 2) rows_hr = int((float(ymax) - float(ymin)) / cellsize_hr + 2) hr_trans = (float(xmin), cellsize_hr, float(0), float(ymax), 0, -cellsize_hr) clone_hr = os.path.join(destination, "clone_highres.tif") # make a highres clone as well! wt.CreateTif(clone_hr, rows_hr, cols_hr, hr_trans, srs, 0) # read staticmap locations dem_map = wt.configget(config, "staticmaps", "dem", "wflow_dem.map") gauges_map = wt.configget(config, "staticmaps", "gauges", "wflow_gauges.map") landuse_map = wt.configget(config, "staticmaps", "landuse", "wflow_landuse.map") river_map = wt.configget(config, "staticmaps", "river", "wflow_river.map") outlet_map = wt.configget(config, "staticmaps", "outlet", "wflow_outlet.map") soil_map = wt.configget(config, "staticmaps", "soil", "wflow_soil.map") streamorder_map = wt.configget( config, "staticmaps", "streamorder", "wflow_streamorder.map" ) subcatch_map = wt.configget(config, "staticmaps", "subcatch", "wflow_subcatch.map") # first add a missing value to dem_in ds = gdal.Open(dem_in, gdal.GA_Update) RasterBand = ds.GetRasterBand(1) fill_val = RasterBand.GetNoDataValue() if fill_val is None: RasterBand.SetNoDataValue(-9999) ds = None # reproject to clone map: see http://stackoverflow.com/questions/10454316/how-to-project-and-resample-a-grid-to-match-another-grid-with-gdal-python # resample DEM logger.info( "Resampling dem from {:s} to {:s}".format( os.path.abspath(dem_in), os.path.join(destination, dem_map) ) ) wt.gdal_warp( dem_in, clone_map, os.path.join(destination, dem_map), format="PCRaster", gdal_interp=gdalconst.GRA_Average, ) # retrieve amount of rows and columns from clone # TODO: make windowstats applicable to source/target with different projections. This does not work yet. # retrieve srs from DEM try: srs_dem = wt.get_projection(dem_in) except: logger.warning("No projection found in DEM, assuming WGS 1984 lat long") srs_dem = osr.SpatialReference() srs_dem.ImportFromEPSG(4326) clone2dem_transform = osr.CoordinateTransformation(srs, srs_dem) # if srs.ExportToProj4() == srs_dem.ExportToProj4(): wt.windowstats( dem_in, len(yax), len(xax), trans, srs, destination, percentiles, transform=clone2dem_transform, logger=logger, ) ## read catchment shape-file to create catchment map src = rasterio.open(clone_tif) shapefile = fiona.open(catchshp, "r") catchment_shapes = [feature["geometry"] for feature in shapefile] image = features.rasterize( catchment_shapes, out_shape=src.shape, all_touched=True, transform=src.transform ) catchment_domain = pcr.numpy2pcr(pcr.Ordinal, image.copy(), 0) ## read river shape-file and create burn layer shapefile = fiona.open(rivshp, "r") river_shapes = [feature["geometry"] for feature in shapefile] image = features.rasterize( river_shapes, out_shape=src.shape, all_touched=False, transform=src.transform ) rivers = pcr.numpy2pcr(pcr.Nominal, image.copy(), 0) riverdem = pcr.scalar(rivers) * pcr.readmap(os.path.join(destination, dem_map)) pcr.setglobaloption("lddin") riverldd = pcr.lddcreate(riverdem, 1e35, 1e35, 1e35, 1e35) riveroutlet = pcr.cover(pcr.ifthen(pcr.scalar(riverldd) == 5, pcr.scalar(1000)), 0) burn_layer = pcr.cover( ( pcr.scalar( pcr.ifthen(pcr.streamorder(riverldd) > 1, pcr.streamorder(riverldd)) ) - 1 ) * 1000 + riveroutlet, 0, ) outlets_x, outlets_y = outlets n_outlets = len(outlets_x) logger.info("Number of outlets: {}".format(n_outlets)) if n_outlets >= 1: outlets_map_numbered = points_to_map(pcr.scalar(0), outlets_x, outlets_y, 0.5) outlets_map = pcr.boolean(outlets_map_numbered) # snap outlets to closest river (max 1 cell closer to river) outlets_map = pcr.boolean( pcr.cover(snaptomap(pcr.ordinal(outlets_map), rivers), 0) ) ## create ldd per catchment logger.info("Calculating ldd") ldddem = pcr.scalar(clone_map) # per subcatchment, burn dem, then create modified dem that fits the ldd of the subcatchment # this ldd dem is merged over catchments, to create a global ldd that abides to the subcatchment boundaries for idx, shape in enumerate(catchment_shapes): logger.info( "Computing ldd for catchment " + str(idx + 1) + "/" + str(len(catchment_shapes)) ) image = features.rasterize( [shape], out_shape=src.shape, all_touched=True, transform=src.transform ) catchment = pcr.numpy2pcr(pcr.Scalar, image.copy(), 0) dem_burned_catchment = ( pcr.readmap(os.path.join(destination, dem_map)) * pcr.scalar(catchment_domain) * catchment ) - burn_layer ldddem = pcr.cover(ldddem, dem_burned_catchment) wflow_ldd = pcr.lddcreate(ldddem, 1e35, 1e35, 1e35, 1e35) if n_outlets >= 1: # set outlets to pit wflow_ldd = pcr.ifthenelse(outlets_map, pcr.ldd(5), wflow_ldd) wflow_ldd = pcr.lddrepair(wflow_ldd) pcr.report(wflow_ldd, os.path.join(destination, "wflow_ldd.map")) # compute stream order, identify river cells streamorder = pcr.ordinal(pcr.streamorder(wflow_ldd)) river = pcr.ifthen(streamorder >= pcr.ordinal(minorder), pcr.boolean(1)) # find the minimum value in the DEM and cover missing values with a river with this value. Effect is none!! so now left out! # mindem = int(np.min(pcr.pcr2numpy(pcr.ordinal(os.path.join(destination, dem_map)),9999999))) # dem_resample_map = pcr.cover(os.path.join(destination, dem_map), pcr.scalar(river)*0+mindem) # pcr.report(dem_resample_map, os.path.join(destination, dem_map)) pcr.report(streamorder, os.path.join(destination, streamorder_map)) pcr.report(river, os.path.join(destination, river_map)) # deal with your catchments if gaugeshp == None: logger.info("No gauges defined, using outlets instead") gauges = pcr.ordinal( pcr.uniqueid( pcr.boolean(pcr.ifthen(pcr.scalar(wflow_ldd) == 5, pcr.boolean(1))) ) ) pcr.report(gauges, os.path.join(destination, gauges_map)) # TODO: Add the gauge shape code from StaticMaps.py (line 454-489) # TODO: add river length map (see SticMaps.py, line 492-499) # since the products here (river length fraction) are not yet used # this is disabled for now, as it also takes a lot of computation time if False: # report river length # make a high resolution empty map dem_hr_file = os.path.join(destination, "dem_highres.tif") burn_hr_file = os.path.join(destination, "burn_highres.tif") demburn_hr_file = os.path.join(destination, "demburn_highres.map") riv_hr_file = os.path.join(destination, "riv_highres.map") wt.gdal_warp(dem_in, clone_hr, dem_hr_file) # wt.CreateTif(riv_hr, rows_hr, cols_hr, hr_trans, srs, 0) # open the shape layer ds = ogr.Open(rivshp) lyr = ds.GetLayer(0) wt.ogr_burn( lyr, clone_hr, -100, file_out=burn_hr_file, format="GTiff", gdal_type=gdal.GDT_Float32, fill_value=0, ) # read dem and burn values and add xax_hr, yax_hr, burn_hr, fill = wt.gdal_readmap(burn_hr_file, "GTiff") burn_hr[burn_hr == fill] = 0 xax_hr, yax_hr, dem_hr, fill = wt.gdal_readmap(dem_hr_file, "GTiff") dem_hr[dem_hr == fill] = np.nan demburn_hr = dem_hr + burn_hr demburn_hr[np.isnan(demburn_hr)] = -9999 wt.gdal_writemap( demburn_hr_file, "PCRaster", xax_hr, yax_hr, demburn_hr, -9999.0 ) pcr.setclone(demburn_hr_file) demburn_hr = pcr.readmap(demburn_hr_file) logger.info("Calculating ldd to determine river length") ldd_hr = pcr.lddcreate(demburn_hr, 1e35, 1e35, 1e35, 1e35) pcr.report(ldd_hr, os.path.join(destination, "ldd_hr.map")) pcr.setglobaloption("unitcell") riv_hr = pcr.scalar(pcr.streamorder(ldd_hr) >= minorder) * pcr.downstreamdist( ldd_hr ) pcr.report(riv_hr, riv_hr_file) pcr.setglobaloption("unittrue") pcr.setclone(clone_map) logger.info("Computing river length") wt.windowstats( riv_hr_file, len(yax), len(xax), trans, srs, destination, stat="fact", transform=False, logger=logger, ) # TODO: nothing happens with the river lengths yet. Need to decide how to use these # report outlet map pcr.report( pcr.ifthen(pcr.ordinal(wflow_ldd) == 5, pcr.ordinal(1)), os.path.join(destination, outlet_map), ) # report subcatchment map subcatchment = pcr.subcatchment(wflow_ldd, gauges) pcr.report(pcr.ordinal(subcatchment), os.path.join(destination, subcatch_map)) # Report land use map if landuse == None: logger.info( "No land use map used. Preparing {:s} with only ones.".format( os.path.join(destination, landuse_map) ) ) pcr.report(pcr.nominal(ones), os.path.join(destination, landuse_map)) else: logger.info( "Resampling land use from {:s} to {:s}".format( os.path.abspath(landuse), os.path.join(destination, os.path.abspath(landuse_map)), ) ) wt.gdal_warp( landuse, clone_map, os.path.join(destination, landuse_map), format="PCRaster", gdal_interp=gdalconst.GRA_Mode, gdal_type=gdalconst.GDT_Int32, ) # report soil map if soil == None: logger.info( "No soil map used. Preparing {:s} with only ones.".format( os.path.join(destination, soil_map) ) ) pcr.report(pcr.nominal(ones), os.path.join(destination, soil_map)) else: logger.info( "Resampling soil from {:s} to {:s}".format( os.path.abspath(soil), os.path.join(destination, os.path.abspath(soil_map)), ) ) wt.gdal_warp( soil, clone_map, os.path.join(destination, soil_map), format="PCRaster", gdal_interp=gdalconst.GRA_Mode, gdal_type=gdalconst.GDT_Int32, ) if lai == None: logger.info( "No vegetation LAI maps used. Preparing default maps {:s} with only ones.".format( os.path.join(destination, soil_map) ) ) pcr.report(pcr.nominal(ones), os.path.join(destination, soil_map)) else: dest_lai = os.path.join(destination, "clim") os.makedirs(dest_lai) for month in range(12): lai_in = os.path.join(lai, "LAI00000.{:03d}".format(month + 1)) lai_out = os.path.join(dest_lai, "LAI00000.{:03d}".format(month + 1)) logger.info( "Resampling vegetation LAI from {:s} to {:s}".format( os.path.abspath(lai_in), os.path.abspath(lai_out) ) ) wt.gdal_warp( lai_in, clone_map, lai_out, format="PCRaster", gdal_interp=gdalconst.GRA_Bilinear, gdal_type=gdalconst.GDT_Float32, ) # report soil map if other_maps == None: logger.info("No other maps used. Skipping other maps.") else: logger.info("Resampling list of other maps...") for map_file in other_maps: logger.info( "Resampling a map from {:s} to {:s}".format( os.path.abspath(map_file), os.path.join( destination, os.path.splitext(os.path.basename(map_file))[0] + ".map", ), ) ) wt.gdal_warp( map_file, clone_map, os.path.join( destination, os.path.splitext(os.path.basename(map_file))[0] + ".map", ), format="PCRaster", gdal_interp=gdalconst.GRA_Mode, gdal_type=gdalconst.GDT_Float32, ) if clean: wt.DeleteList(glob.glob(os.path.join(destination, "*.xml")), logger=logger) wt.DeleteList( glob.glob(os.path.join(destination, "clim", "*.xml")), logger=logger ) wt.DeleteList(glob.glob(os.path.join(destination, "*highres*")), logger=logger)