def show(raster): from IPython.display import Image import grass.script as gs region = gs.region() gs.run_command('d.erase') gs.use_temp_region() gs.run_command('g.region', align='ortho', n=region['n'], s=region['s'], e=region['e'], w=region['w']) gs.run_command('d.rast', map='ortho') gs.run_command('d.rast', map=raster, values=0, flags='i') gs.run_command('d.vect', map='NHDFlowline', where="FCODE >= 46006", color='30:144:255') gs.run_command('d.vect', map='roads', where="FULLNAME is not NULL", color='165:159:159', width=2) gs.run_command('d.barscale', at=[38.0, 97.0], flags='n', style='both_ticks', segment=5, color='255:255:255', bgcolor='none') gs.del_temp_region() return Image("map.png")
def convert_map(output, variable): """Convert imported raster map unit and format.""" # prepare for unit conversion if flags['c'] and variable in ['tmin', 'tmax', 'tmean']: grass.message("Converting {} to degree Celcius...".format(output)) a = 0.1 b = 0 elif flags['k'] and variable in ['tmin', 'tmax', 'tmean']: grass.message("Converting {} to Kelvin...".format(output)) a = 0.1 b = 273.15 elif flags['y'] and variable == 'prec': grass.message("Converting {} to meter per year...".format(output)) a = 0.012 b = 0 elif flags['f']: grass.message("Converting {} to floating-point...".format(output)) a = 1 b = 0 else: a = None b = None # convert unit and format if a or b: grass.use_temp_region() grass.run_command('g.region', rast=output) grass.mapcalc('$output=float($output*$a+$b)', a=a, b=b, output=output, overwrite=True) grass.del_temp_region()
def main(): # process command options input = options['input'] if not gs.find_file(input)['file']: gs.fatal(_("Raster map <%s> not found") % input) output = options['output'] if gs.find_file(output)['file'] and not gs.overwrite(): gs.fatal(_("Output map <%s> already exists") % output) # set aside region for internal use gs.use_temp_region() # subset input if desired region = options.get('region') if region: if not gs.find_file(region)['file']: gs.fatal(_("Raster map <%s> not found") % region) gs.message("Setting region to %s" % region, flag='i') gs.run_command('g.region', rast=region, align=input) else: gs.message("Using existing GRASS region", flag='i') gs.debug('='*50) gs.debug('\n'.join(gs.parse_command('g.region', 'p').keys())) gs.debug('='*50) calculate_noise(input, output) # restore original region gs.del_temp_region() return None
def convert_map(output, field): """Convert imported raster map unit and format.""" # prepare for unit conversion if flags['c'] and field in ['tmin', 'tmax', 'tmean']: grass.message("Converting <%s> to degree Celcius..." % output) a = 0.1 b = 0 elif flags['k'] and field in ['tmin', 'tmax', 'tmean']: grass.message("Converting <%s> to Kelvin..." % output) a = 0.1 b = 273.15 elif flags['y'] and field == 'prec': grass.message("Converting <%s> to meter per year..." % output) a = 0.012 b = 0 elif flags['f']: grass.message("Converting <%s> to floating-point..." % output) a = 1 b = 0 else: a = None b = None # convert unit and format if a or b: grass.use_temp_region() grass.run_command('g.region', rast=output) grass.mapcalc('$output=float($output*%s+%s)' % (a, b), output=output) grass.del_temp_region()
def main(): #create a list for parallel mapcalc modules and a mapcalc module to act as template mapcalc_list = [] mapcalc = Module("r.mapcalc", overwrite=True, run_=False) #get number of tiles in each row and col from arguments tile_rows = int(sys.argv[1]) tile_cols = int(sys.argv[2]) #Create que for parallel processes queue = ParallelModuleQueue(nprocs=sys.argv[3]) #Use temporary region that will be reset after execution of this script gscript.use_temp_region() #Input raster (can be grass raster dataset or externally linked dataset such as tiff vrt etc.) input="input_raster" #Read raster boundaries and resolution into numeric variables info = gscript.raster_info(input) no = float(info['north']) so = float(info['south']) we = float(info['west']) ea = float(info['east']) ro = int(info['rows']) co = int(info['cols']) ewr = int(info['ewres']) nsr = int(info['nsres']) #Start mapcalc module for each tile k = 0 for i in xrange(tile_rows): for j in xrange(tile_cols): #Set processing region to specific part of the raster (column+row) gscript.run_command('g.region', n=so+(i+1)*ro/tile_rows*nsr, s=so+i*ro/tile_rows*nsr, e=we+(1+j)*co/tile_cols*ewr, w=we+j*co/tile_cols*ewr, rows=ro/tile_rows, cols=co/tile_cols, nsres=nsr, ewres=ewr) #Create a copy of mapcalc template, give it mapcalc expression and put it into parallel que where it will be executed when a process becomes available. new_mapcalc = copy.deepcopy(mapcalc) mapcalc_list.append(new_mapcalc) m = new_mapcalc(expression="test_pygrass_%i = %s * (%i+1)"%(k,input, k)) queue.put(m) k+=1 #wait for all mapcalc modules to have finished execution queue.wait() #print mapcalc returncodes to check that everything went as expected for mapcalc in mapcalc_list: print(mapcalc.popen.returncode) #delete temporary region to restore the region that was in use at the start of the script gscript.del_temp_region()
def cleanup(self): """Remove temporary region and mask. """ msgr.debug("Reset mask and region") if self.raster_mask_id: msgr.debug("Remove temp MASK...") self.del_temp_mask() if self.region_id: msgr.debug("Remove temp region...") gscript.del_temp_region() self.raster_writer_queue.put(None) self.raster_writer_thread.join()
def main(): # start messenger msgr = Messenger() # Use temporary GRASS region grass.use_temp_region() # reads CLI options rast_n_file_name = options['friction'] rast_dem_name = options['dem'] rast_start_file_name = options['start_file'] rast_bc_name = options['bc'] rast_bcval_name = options['bcval'] rast_user_name = options['user_inflow'] # Load *.par file par = Par(msgr, options['par_file']) par.read() # Write DEM file par.write_dem(rast_dem_name, grass.overwrite()) # set computational region to match DEM par.set_region_from_map(rast_dem_name) # Write friction par.write_n_map(rast_n_file_name, grass.overwrite()) # Write start file par.write_start_h(rast_start_file_name, grass.overwrite()) # boundary conditions bc = BoundaryConditions(msgr, sim_time=par.sim_time, region=par.region) if par.bci_file: bci_full_path = os.path.join(par.directory, par.bci_file) bc.read_bci(bci_full_path) if par.bdy_file: bdy_full_path = os.path.join(par.directory, par.bdy_file) bc.read_bdy(bdy_full_path) # create STDS bc.create_stds(stds_name=rast_user_name, overwrite=grass.overwrite()) bc.create_stds(rast_bcval_name, overwrite=grass.overwrite()) # Write maps en register them in STDS bc.populate_user_flow_stds(rast_quser_name=rast_user_name, overwrite=grass.overwrite()) bc.populate_bc_stds(rast_bcval_name, grass.overwrite()) # write Boundary condition type map bc.write_bctype(rast_bc_name, grass.overwrite()) # Restore original region grass.del_temp_region() return 0
def patch_tiles(mt, out, vari, bc=None, mnth=None): """Set region to tiles, and run r.patch""" bc = (str(bc) if bc else '') mnth = (str(mnth) if mnth else '') grass.message("Patching the tiles for {}{}{} to {}" .format(vari, bc, mnth, out)) if len(mt) > 1: grass.use_temp_region() grass.run_command("g.region", raster=mt) grass.run_command("r.patch", input=mt, output=out) grass.run_command("g.remove", type="raster", name=mt, flags="f", quiet=True) grass.del_temp_region() else: # simply rename if there is only one tile grass.run_command("g.rename", raster=[mt, out])
def run_benchmark(resolution_list, runs, testdict, profile): regions = [] for resolution in resolution_list: core.use_temp_region() core.run_command('g.region', e=50, w=-50, n=50, s=-50, res=resolution, flags='p') # Adjust the computational region for this process region = libgis.Cell_head() libraster.Rast_get_window(ctypes.byref(region)) region.e = 50 region.w = -50 region.n = 50 region.s = -50 region.ew_res = resolution region.ns_res = resolution libgis.G_adjust_Cell_head(ctypes.byref(region), 0, 0) libraster.Rast_set_window(ctypes.byref(region)) libgis.G_set_window(ctypes.byref(region)) # Create two raster maps with random numbers core.mapcalc("test_a = rand(0, 100)", quite=True, overwrite=True) core.mapcalc("test_b = rand(0.0, 1.0)", quite=True, overwrite=True) result = collections.OrderedDict() result['res'] = resolution result['cols'] = region.cols result['rows'] = region.rows result['cells'] = region.rows * region.cols result['results'] = copy.deepcopy(testdict) for execmode, operation in result['results'].items(): print(execmode) for oper, operdict in operation.items(): operdict['time'], operdict['times'] = mytimer(operdict['func'],runs) if profile: filename = '{}_{}_{}'.format(execmode, oper, profile) cProfile.runctx(operdict['func'].__name__ + '()', globals(), locals(), filename = filename) print((' {0}: {1: 40.6f}s'.format(oper, operdict['time']))) del(operdict['func']) regions.append(result) core.del_temp_region() return regions
def main(): # process command options input = options['input'] if not gs.find_file(input)['file']: gs.fatal(_("Raster map <%s> not found") % input) smooth = options['output'] if gs.find_file(smooth)['file'] and not gs.overwrite(): gs.fatal(_("Output map <%s> already exists") % smooth) sd = options['sd'] try: sd = float(sd) except ValueError: if not gs.find_file(sd)['file']: gs.fatal(_("Raster map <%s> not found") % sd) alpha = float(options['alpha']) # set aside region for internal use gs.use_temp_region() # subset input if desired region = options.get('region') if region: if not gs.find_file(region)['file']: gs.fatal(_("Raster map <%s> not found") % region) gs.message("Setting region to %s" % region, flag='i') gs.run_command('g.region', rast=region, align=input) else: gs.message("Using existing GRASS region", flag='i') gs.debug('='*50) gs.debug('\n'.join(gs.parse_command('g.region', 'p').keys())) gs.debug('='*50) multiscalesmooth(input, smooth, sd, alpha) # restore original region gs.del_temp_region() return None
def main(): global acq_time, esd """1st, get input, output, options and flags""" spectral_bands = options['band'].split(',') outputsuffix = options['outputsuffix'] utc = options['utc'] doy = options['doy'] sea = options['sea'] radiance = flags['r'] keep_region = flags['k'] # mapset = grass.gisenv()['MAPSET'] # Current Mapset? # imglst = [spectral_bands] # images = {} # for img in imglst: # Retrieving Image Info # images[img] = Info(img, mapset) # images[img].read() # ----------------------------------------------------------------------- # Temporary Region and Files # ----------------------------------------------------------------------- if not keep_region: grass.use_temp_region() # to safely modify the region tmpfile = grass.tempfile() # Temporary file - replace with os.getpid? tmp = "tmp." + grass.basename(tmpfile) # use its basename # ----------------------------------------------------------------------- # Global Metadata: Earth-Sun distance, Sun Zenith Angle # ----------------------------------------------------------------------- # Earth-Sun distance if doy: esd = jd_to_esd(int(doy)) elif utc: acq_utc = AcquisitionTime(utc) # will hold esd (earth-sun distance) acq_dat = datetime(acq_utc.year, acq_utc.month, acq_utc.day) esd = acq_utc.esd else: grass.fatal(_("Either the UTC string or " "the Day-of-Year (doy) are required!")) sza = 90 - float(sea) # Sun Zenith Angle based on Sun Elevation Angle # ----------------------------------------------------------------------- # Loop processing over all bands # ----------------------------------------------------------------------- for band in spectral_bands: global tmp_rad g.message("|* Processing the %s spectral band" % band, flags='i') if not keep_region: g.message("\n|! Matching region to %s" % band) # set region run('g.region', rast=band) # ## FixMe # ------------------------------------------------------------------- # Converting to Spectral Radiance # ------------------------------------------------------------------- msg = "\n|> Converting to Spectral Radiance: " \ # "L(λ) = 10^4 x DN(λ) / CalCoef(λ) x Bandwidth(λ)" # Unicode? ## g.message(msg) # ------------------------------------------------------------------- # Band dependent metadata for Spectral Radiance # ------------------------------------------------------------------- # Why is this necessary? Any function to remove the mapsets name? if '@' in band: band_key = (band.split('@')[0]) else: band_key = band # get coefficients if acq_dat < cc_update: g.message("\n|! Using Pre-2001 Calibration Coefficient values", flags='i') cc = float(CC[band_key][0]) else: cc = float(CC[band_key][1]) # get bandwidth bw = float(CC[band_key][2]) # inform msg = " [Calibration Coefficient=%d, Bandwidth=%.1f]" \ % (cc, bw) g.message(msg) # convert tmp_rad = "%s.Radiance" % tmp # Temporary Map rad = "%s = 10^4 * %s / %f * %f" \ % (tmp_rad, band, cc, bw) grass.mapcalc(rad, overwrite=True) # string for metadata history_rad = rad history_rad += "Calibration Coefficient=%d; Effective Bandwidth=%.1f" \ % (cc, bw) title_rad = "%s band (Spectral Radiance)" % band units_rad = "W / m2 / μm / ster" description_rad = "At-sensor %s band spectral Radiance (W/m2/μm/sr)" \ % band if not radiance: # --------------------------------------------------------------- # Converting to Top-of-Atmosphere Reflectance # --------------------------------------------------------------- global tmp_toar msg = "\n|> Converting to Top-of-Atmosphere Reflectance" \ # "ρ(p) = π x L(λ) x d^2 / ESUN(λ) x cos(θ(S))" # Unicode? g.message(msg) # --------------------------------------------------------------- # Band dependent metadata for Spectral Radiance # --------------------------------------------------------------- # get esun esun = CC[band_key][3] # inform msg = " [Earth-Sun distane=%f, Mean solar exoatmospheric " \ "irradiance=%.1f]" % (esd, esun) g.message(msg) # convert tmp_toar = "%s.Reflectance" % tmp # Spectral Reflectance toar = "%s = %f * %s * %f^2 / %f * cos(%f)" \ % (tmp_toar, math.pi, tmp_rad, esd, esun, sza) grass.mapcalc(toar, overwrite=True) # strings for output's metadata history_toar = toar history_toar += "ESD=%f; BAND_Esun=%f; SZA=%f" % (esd, esun, sza) title_toar = "%s band (Top of Atmosphere Reflectance)" % band units_toar = "Unitless planetary reflectance" description_toar = "Top of Atmosphere `echo ${BAND}` band spectral" " Reflectance (unitless)" if tmp_toar: # history entry run("r.support", map=tmp_toar, title=title_toar, units=units_toar, description=description_toar, source1=source1_toar, source2=source2_toar, history=history_toar) # add suffix to basename & rename end product toar_name = ("%s.%s" % (band.split('@')[0], outputsuffix)) run("g.rename", rast=(tmp_toar, toar_name)) elif tmp_rad: # history entry run("r.support", map=tmp_rad, title=title_rad, units=units_rad, description=description_rad, source1=source1_rad, source2=source2_rad, history=history_rad) # add suffix to basename & rename end product rad_name = ("%s.%s" % (band.split('@')[0], outputsuffix)) run("g.rename", rast=(tmp_rad, rad_name)) # visualising-related information if not keep_region: grass.del_temp_region() # restoring previous region settings g.message("\n|! Region's resolution restored!") g.message("\n>>> Hint: rebalancing colors " "(i.colors.enhance) may improve appearance of RGB composites!", flags='i')
def main(): """ Main program """ # Temporary filenames # The following three are meant for a test step-by-step cwv estimation, see # unused functions! # tmp_ti_mean = tmp_map_name('ti_mean') # for cwv # tmp_tj_mean = tmp_map_name('tj_mean') # for cwv # tmp_ratio = tmp_map_name('ratio') # for cwv tmp_avg_lse = tmp_map_name('avg_lse') tmp_delta_lse = tmp_map_name('delta_lse') tmp_cwv = tmp_map_name('cwv') #tmp_lst = tmp_map_name('lst') # basic equation for mapcalc global equation, citation_lst equation = "{result} = {expression}" # user input mtl_file = options['mtl'] if not options['prefix']: b10 = options['b10'] b11 = options['b11'] t10 = options['t10'] t11 = options['t11'] if not options['clouds']: qab = options['qab'] cloud_map = False else: qab = False cloud_map = options['clouds'] elif options['prefix']: prefix = options['prefix'] b10 = prefix + '10' b11 = prefix + '11' if not options['clouds']: qab = prefix + 'QA' cloud_map = False else: cloud_map = options['clouds'] qab = False qapixel = options['qapixel'] lst_output = options['lst'] # save Brightness Temperature maps? global brightness_temperature_prefix if options['prefix_bt']: brightness_temperature_prefix = options['prefix_bt'] else: brightness_temperature_prefix = None global cwv_output cwv_window_size = int(options['window']) assertion_for_cwv_window_size_msg = ('A spatial window of size 5^2 or less is not ' 'recommended. Please select a larger window. ' 'Refer to the manual\'s notes for details.') assert cwv_window_size >= 7, assertion_for_cwv_window_size_msg cwv_output = options['cwv'] # optional maps average_emissivity_map = options['emissivity'] delta_emissivity_map = options['delta_emissivity'] # output for in-between maps? global emissivity_output, delta_emissivity_output emissivity_output = options['emissivity_out'] delta_emissivity_output = options['delta_emissivity_out'] global landcover_map, emissivity_class landcover_map = options['landcover'] emissivity_class = options['emissivity_class'] # flags global info, null info = flags['i'] scene_extent = flags['e'] timestamping = flags['t'] null = flags['n'] global rounding rounding = flags['r'] global celsius celsius = flags['c'] # ToDo: # shell = flags['g'] # # Pre-production actions # # Set Region if scene_extent: grass.use_temp_region() # safely modify the region msg = "\n|! Matching region extent to map {name}" # ToDo: check if extent-B10 == extent-B11? Unnecessary? # Improve below! if b10: run('g.region', rast=b10, align=b10) msg = msg.format(name=b10) elif t10: run('g.region', rast=t10, align=t10) msg = msg.format(name=t10) g.message(msg) elif scene_extent: grass.warning(_('Operating on current region')) # # 1. Mask clouds # if cloud_map: # user-fed cloud map? msg = '\n|i Using {cmap} as a MASK'.format(cmap=cloud_map) g.message(msg) r.mask(raster=cloud_map, flags='i', overwrite=True) else: # using the quality assessment band and a "QA" pixel value mask_clouds(qab, qapixel) # # 2. TIRS > Brightness Temperatures # if mtl_file: # if MTL and b10 given, use it to compute at-satellite temperature t10 if b10: # convert DNs to at-satellite temperatures t10 = tirs_to_at_satellite_temperature(b10, mtl_file) # likewise for b11 -> t11 if b11: # convert DNs to at-satellite temperatures t11 = tirs_to_at_satellite_temperature(b11, mtl_file) # # Initialise a SplitWindowLST object # split_window_lst = SplitWindowLST(emissivity_class) citation_lst = split_window_lst.citation # # 3. Land Surface Emissivities # # use given fixed class? if emissivity_class: if split_window_lst.landcover_class is False: # replace with meaningful error g.warning('Unknown land cover class string! Note, this string ' 'input option is case sensitive.') if emissivity_class == 'Random': msg = "\n|! Random emissivity class selected > " + \ split_window_lst.landcover_class + ' ' else: msg = '\n|! Retrieving average emissivities *only* for {eclass} ' if info: msg += '| Average emissivities (channels 10, 11): ' msg += str(split_window_lst.emissivity_t10) + ', ' + \ str(split_window_lst.emissivity_t11) msg = msg.format(eclass=split_window_lst.landcover_class) g.message(msg) # use the FROM-GLC map elif landcover_map: if average_emissivity_map: tmp_avg_lse = average_emissivity_map if not average_emissivity_map: determine_average_emissivity(tmp_avg_lse, landcover_map, split_window_lst.average_lse_mapcalc) if options['emissivity_out']: tmp_avg_lse = options['emissivity_out'] if delta_emissivity_map: tmp_delta_lse = delta_emissivity_map if not delta_emissivity_map: determine_delta_emissivity(tmp_delta_lse, landcover_map, split_window_lst.delta_lse_mapcalc) if options['delta_emissivity_out']: tmp_delta_lse = options['delta_emissivity_out'] # # 4. Modified Split-Window Variance-Covariance Matrix > Column Water Vapor # if info: msg = '\n|i Spatial window of size {n} for Column Water Vapor estimation: ' msg = msg.format(n=cwv_window_size) g.message(msg) cwv = Column_Water_Vapor(cwv_window_size, t10, t11) citation_cwv = cwv.citation estimate_cwv_big_expression(tmp_cwv, t10, t11, cwv._big_cwv_expression()) if cwv_output: tmp_cwv = cwv_output # # 5. Estimate Land Surface Temperature # if info and emissivity_class == 'Random': msg = '\n|* Will pick a random emissivity class!' grass.verbose(msg) estimate_lst(lst_output, t10, t11, tmp_avg_lse, tmp_delta_lse, tmp_cwv, split_window_lst.sw_lst_mapcalc) # # Post-production actions # # remove MASK r.mask(flags='r', verbose=True) # time-stamping if timestamping: add_timestamp(mtl_file, lst_output) if cwv_output: add_timestamp(mtl_file, cwv_output) # Apply color table if celsius: run('r.colors', map=lst_output, color='celsius') else: # color table for kelvin run('r.colors', map=lst_output, color='kelvin') # ToDo: helper function for r.support # strings for metadata history_lst = '\n' + citation_lst history_lst += '\n\n' + citation_cwv history_lst += '\n\nSplit-Window model: ' history_lst += split_window_lst._equation # :wsw_lst_mapcalc description_lst = ('Land Surface Temperature derived from a split-window algorithm. ') if celsius: title_lst = 'Land Surface Temperature (C)' units_lst = 'Celsius' else: title_lst = 'Land Surface Temperature (K)' units_lst = 'Kelvin' landsat8_metadata = Landsat8_MTL(mtl_file) source1_lst = landsat8_metadata.scene_id source2_lst = landsat8_metadata.origin # history entry run("r.support", map=lst_output, title=title_lst, units=units_lst, description=description_lst, source1=source1_lst, source2=source2_lst, history=history_lst) # (re)name the LST product #run("g.rename", rast=(tmp_lst, lst_output)) # restore region if scene_extent: grass.del_temp_region() # restoring previous region settings g.message("|! Original Region restored") # print citation if info: print '\nSource: ' + citation_lst
def main(): pan = options['pan'] msxlst = options['msx'].split(',') outputsuffix = options['suffix'] custom_ratio = options['ratio'] center = options['center'] center2 = options['center2'] modulation = options['modulation'] modulation2 = options['modulation2'] if options['trim']: trimming_factor = float(options['trim']) else: trimming_factor = False histogram_match = flags['l'] second_pass = flags['2'] color_match = flags['c'] # # Check & warn user about "ns == ew" resolution of current region ====== # region = grass.region() # nsr = region['nsres'] # ewr = region['ewres'] # # if nsr != ewr: # msg = ('>>> Region's North:South ({ns}) and East:West ({ew}) ' # 'resolutions do not match!') # msg = msg.format(ns=nsr, ew=ewr) # g.message(msg, flags='w') mapset = grass.gisenv()['MAPSET'] # Current Mapset? region = grass.region() # and region settings # List images and their properties imglst = [pan] imglst.extend(msxlst) # List of input imagery images = {} for img in imglst: # Retrieving Image Info images[img] = Info(img, mapset) images[img].read() panres = images[pan].nsres # Panchromatic resolution grass.use_temp_region() # to safely modify the region run('g.region', res=panres) # Respect extent, change resolution g.message("|! Region's resolution matched to Pan's ({p})".format(p=panres)) # Loop Algorithm over Multi-Spectral images for msx in msxlst: g.message("\nProcessing image: {m}".format(m=msx)) # Tracking command history -- Why don't do this all r.* modules? cmd_history = [] # # 1. Compute Ratio # g.message("\n|1 Determining ratio of low to high resolution") # Custom Ratio? Skip standard computation method. if custom_ratio: ratio = float(custom_ratio) g.message('Using custom ratio, overriding standard method!', flags='w') # Multi-Spectral resolution(s), multiple else: # Image resolutions g.message(" > Retrieving image resolutions") msxres = images[msx].nsres # check if panres == msxres: msg = ("The Panchromatic's image resolution ({pr}) " "equals to the Multi-Spectral's one ({mr}). " "Something is probably not right! " "Please check your input images.") msg = msg.format(pr=panres, mr=msxres) grass.fatal(_(msg)) # compute ratio ratio = msxres / panres msg_ratio = (' >> Resolution ratio ' 'low ({m:.{dec}f}) to high ({p:.{dec}f}): {r:.1f}') msg_ratio = msg_ratio.format(m=msxres, p=panres, r=ratio, dec=3) g.message(msg_ratio) # 2nd Pass requested, yet Ratio < 5.5 if second_pass and ratio < 5.5: g.message(" >>> Resolution ratio < 5.5, skipping 2nd pass.\n" " >>> If you insist, force it via the <ratio> option!", flags='i') second_pass = bool(0) # # 2. High Pass Filtering # g.message('\n|2 High Pass Filtering the Panchromatic Image') tmpfile = grass.tempfile() # Temporary file - replace with os.getpid? tmp = 'tmp.' + grass.basename(tmpfile) # use its basenam tmp_pan_hpf = '{tmp}_pan_hpf'.format(tmp=tmp) # HPF image tmp_msx_blnr = '{tmp}_msx_blnr'.format(tmp=tmp) # Upsampled MSx tmp_msx_hpf = '{tmp}_msx_hpf'.format(tmp=tmp) # Fused image tmp_hpf_matrix = grass.tempfile() # ASCII filter # Construct and apply Filter hpf = get_high_pass_filter(ratio, center) hpf_ascii(center, hpf, tmp_hpf_matrix, second_pass) run('r.mfilter', input=pan, filter=tmp_hpf_matrix, output=tmp_pan_hpf, title='High Pass Filtered Panchromatic image', overwrite=True) # 2nd pass if second_pass and ratio > 5.5: # Temporary files tmp_pan_hpf_2 = '{tmp}_pan_hpf_2'.format(tmp=tmp) # 2nd Pass HPF image tmp_hpf_matrix_2 = grass.tempfile() # 2nd Pass ASCII filter # Construct and apply 2nd Filter hpf_2 = get_high_pass_filter(ratio, center2) hpf_ascii(center2, hpf_2, tmp_hpf_matrix_2, second_pass) run('r.mfilter', input=pan, filter=tmp_hpf_matrix_2, output=tmp_pan_hpf_2, title='2-High-Pass Filtered Panchromatic Image', overwrite=True) # # 3. Upsampling low resolution image # g.message("\n|3 Upsampling (bilinearly) low resolution image") run('r.resamp.interp', method='bilinear', input=msx, output=tmp_msx_blnr, overwrite=True) # # 4. Weighting the High Pass Filtered image(s) # g.message("\n|4 Weighting the High-Pass-Filtered image (HPFi)") # Compute (1st Pass) Weighting msg_w = " > Weighting = StdDev(MSx) / StdDev(HPFi) * " \ "Modulating Factor" g.message(msg_w) # StdDev of Multi-Spectral Image(s) msx_avg = avg(msx) msx_sd = stddev(msx) g.message(" >> StdDev of <{m}>: {sd:.3f}".format(m=msx, sd=msx_sd)) # StdDev of HPF Image hpf_sd = stddev(tmp_pan_hpf) g.message(" >> StdDev of HPFi: {sd:.3f}".format(sd=hpf_sd)) # Modulating factor modulator = get_modulator_factor(modulation, ratio) g.message(" >> Modulating Factor: {m:.2f}".format(m=modulator)) # weighting HPFi weighting = hpf_weight(msx_sd, hpf_sd, modulator, 1) # # 5. Adding weighted HPF image to upsampled Multi-Spectral band # g.message("\n|5 Adding weighted HPFi to upsampled image") fusion = '{hpf} = {msx} + {pan} * {wgt}' fusion = fusion.format(hpf=tmp_msx_hpf, msx=tmp_msx_blnr, pan=tmp_pan_hpf, wgt=weighting) grass.mapcalc(fusion) # command history hst = 'Weigthing applied: {msd:.3f} / {hsd:.3f} * {mod:.3f}' cmd_history.append(hst.format(msd=msx_sd, hsd=hpf_sd, mod=modulator)) if second_pass and ratio > 5.5: # # 4+ 2nd Pass Weighting the High Pass Filtered image # g.message("\n|4+ 2nd Pass Weighting the HPFi") # StdDev of HPF Image #2 hpf_2_sd = stddev(tmp_pan_hpf_2) g.message(" >> StdDev of 2nd HPFi: {h:.3f}".format(h=hpf_2_sd)) # Modulating factor #2 modulator_2 = get_modulator_factor2(modulation2) msg = ' >> 2nd Pass Modulating Factor: {m:.2f}' g.message(msg.format(m=modulator_2)) # 2nd Pass weighting weighting_2 = hpf_weight(msx_sd, hpf_2_sd, modulator_2, 2) # # 5+ Adding weighted HPF image to upsampled Multi-Spectral band # g.message("\n|5+ Adding small-kernel-based weighted 2nd HPFi " "back to fused image") add_back = '{final} = {msx_hpf} + {pan_hpf} * {wgt}' add_back = add_back.format(final=tmp_msx_hpf, msx_hpf=tmp_msx_hpf, pan_hpf=tmp_pan_hpf_2, wgt=weighting_2) grass.mapcalc(add_back) # 2nd Pass history entry hst = "2nd Pass Weighting: {m:.3f} / {h:.3f} * {mod:.3f}" cmd_history.append(hst.format(m=msx_sd, h=hpf_2_sd, mod=modulator_2)) if color_match: g.message("\n|* Matching output to input color table") run('r.colors', map=tmp_msx_hpf, raster=msx) # # 6. Stretching linearly the HPF-Sharpened image(s) to match the Mean # and Standard Deviation of the input Multi-Sectral image(s) # if histogram_match: # adapt output StdDev and Mean to the input(ted) ones g.message("\n|+ Matching histogram of Pansharpened image " "to %s" % (msx), flags='v') # Collect stats for linear histogram matching msx_hpf_avg = avg(tmp_msx_hpf) msx_hpf_sd = stddev(tmp_msx_hpf) # expression for mapcalc lhm = '{out} = ({hpf} - {hpfavg}) / {hpfsd} * {msxsd} + {msxavg}' lhm = lhm.format(out=tmp_msx_hpf, hpf=tmp_msx_hpf, hpfavg=msx_hpf_avg, hpfsd=msx_hpf_sd, msxsd=msx_sd, msxavg=msx_avg) # compute grass.mapcalc(lhm, quiet=True, overwrite=True) # update history string cmd_history.append("Linear Histogram Matching: %s" % lhm) # # Optional. Trim to remove black border effect (rectangular only) # if trimming_factor: tf = trimming_factor # communicate msg = '\n|* Trimming output image border pixels by ' msg += '{factor} times the low resolution\n'.format(factor=tf) nsew = ' > Input extent: n: {n}, s: {s}, e: {e}, w: {w}' nsew = nsew.format(n=region.n, s=region.s, e=region.e, w=region.w) msg += nsew g.message(msg) # re-set borders region.n -= tf * images[msx].nsres region.s += tf * images[msx].nsres region.e -= tf * images[msx].ewres region.w += tf * images[msx].ewres # communicate and act msg = ' > Output extent: n: {n}, s: {s}, e: {e}, w: {w}' msg = msg.format(n=region.n, s=region.s, e=region.e, w=region.w) g.message(msg) # modify only the extent run('g.region', n=region.n, s=region.s, e=region.e, w=region.w) trim = "{out} = {input}".format(out=tmp_msx_hpf, input=tmp_msx_hpf) grass.mapcalc(trim) # # End of Algorithm # history entry run("r.support", map=tmp_msx_hpf, history="\n".join(cmd_history)) # add suffix to basename & rename end product msx_name = "{base}.{suffix}" msx_name = msx_name.format(base=msx.split('@')[0], suffix=outputsuffix) run("g.rename", raster=(tmp_msx_hpf, msx_name)) # remove temporary files cleanup() # visualising-related information grass.del_temp_region() # restoring previous region settings g.message("\n|! Original Region restored") g.message("\n>>> Hint, rebalancing colors (via i.colors.enhance) " "may improve appearance of RGB composites!", flags='i')
def main(): global acq_time, esd """1st, get input, output, options and flags""" spectral_bands = options['band'].split(',') outputsuffix = options['outputsuffix'] utc = options['utc'] doy = options['doy'] sea = options['sea'] radiance = flags['r'] if radiance and outputsuffix == 'toar': outputsuffix = 'rad' g.message("Output-suffix set to %s" % outputsuffix) keep_region = flags['k'] info = flags['i'] # ----------------------------------------------------------------------- # Equations # ----------------------------------------------------------------------- if info: # conversion to Radiance based on (1) msg = "|i Spectral Radiance = K * DN / Effective Bandwidth | " \ "Reflectance = ( Pi * Radiance * ESD^2 ) / BAND_Esun * cos(SZA)" g.message(msg) # ----------------------------------------------------------------------- # List images and their properties # ----------------------------------------------------------------------- mapset = grass.gisenv()['MAPSET'] # Current Mapset? # imglst = [pan] # imglst.extend(msxlst) # List of input imagery images = {} for img in spectral_bands: # Retrieving Image Info images[img] = Info(img, mapset) images[img].read() # ----------------------------------------------------------------------- # Temporary Region and Files # ----------------------------------------------------------------------- if not keep_region: grass.use_temp_region() # to safely modify the region tmpfile = grass.tempfile() # Temporary file - replace with os.getpid? tmp = "tmp." + grass.basename(tmpfile) # use its basename # ----------------------------------------------------------------------- # Global Metadata: Earth-Sun distance, Sun Zenith Angle # ----------------------------------------------------------------------- # Earth-Sun distance if doy: g.message("|! Using Day of Year to calculate Earth-Sun distance.") esd = jd_to_esd(int(doy)) elif (not doy) and utc: acq_utc = AcquisitionTime(utc) # will hold esd (earth-sun distance) esd = acq_utc.esd else: grass.fatal(_("Either the UTC string or " "the Day-of-Year (doy) are required!")) sza = 90 - float(sea) # Sun Zenith Angle based on Sun Elevation Angle # ----------------------------------------------------------------------- # Loop processing over all bands # ----------------------------------------------------------------------- for band in spectral_bands: global tmp_rad # ------------------------------------------------------------------- # Match bands region if... ? # ------------------------------------------------------------------- if not keep_region: run('g.region', rast=band) # ## FixMe? msg = "\n|! Region matching the %s spectral band" % band g.message(msg) elif keep_region: msg = "|! Operating on current region" g.message(msg) # ------------------------------------------------------------------- # Band dependent metadata for Spectral Radiance # ------------------------------------------------------------------- g.message("\n|* Processing the %s band" % band, flags='i') # Why is this necessary? Any function to remove the mapsets name? if '@' in band: band = (band.split('@')[0]) # get absolute calibration factor acf = float(CF_BW_ESUN[band][2]) acf_msg = "K=" + str(acf) # effective bandwidth bw = float(CF_BW_ESUN[band][0]) # ------------------------------------------------------------------- # Converting to Spectral Radiance # ------------------------------------------------------------------- msg = "\n|> Converting to Spectral Radiance " \ "| Conversion Factor %s, Bandwidth=%.3f" % (acf_msg, bw) g.message(msg) # convert tmp_rad = "%s.Radiance" % tmp # Temporary Map rad = "%s = %f * %s / %f" \ % (tmp_rad, acf, band, bw) # Attention: 32-bit calculations requ. grass.mapcalc(rad, overwrite=True) # strings for metadata history_rad = rad history_rad += "Conversion Factor=%f; Effective Bandwidth=%.3f" \ % (acf, bw) title_rad = "" description_rad = "Top-of-Atmosphere %s band spectral Radiance " \ "[W/m^2/sr/μm]" % band units_rad = "W / sq.m. / μm / ster" if not radiance: # --------------------------------------------------------------- # Converting to Top-of-Atmosphere Reflectance # --------------------------------------------------------------- global tmp_toar msg = "\n|> Converting to Top-of-Atmosphere Reflectance" g.message(msg) esun = float(CF_BW_ESUN[band][1]) msg = " %s band mean solar exoatmospheric irradiance=%.2f" \ % (band, esun) g.message(msg) # convert tmp_toar = "%s.Reflectance" % tmp # Spectral Reflectance toar = "%s = %f * %s * %f^2 / %f * cos(%f)" \ % (tmp_toar, math.pi, tmp_rad, esd, esun, sza) grass.mapcalc(toar, overwrite=True) # report range? Using a flag and skip actual conversion? # todo? # strings for metadata title_toar = "%s band (Top of Atmosphere Reflectance)" % band description_toar = "Top of Atmosphere %s band spectral Reflectance" \ % band units_toar = "Unitless planetary reflectance" history_toar = "K=%f; Bandwidth=%.1f; ESD=%f; Esun=%.2f; SZA=%.1f" \ % (acf, bw, esd, esun, sza) if tmp_toar: # history entry run("r.support", map=tmp_toar, title=title_toar, units=units_toar, description=description_toar, source1=source1_toar, source2=source2_toar, history=history_toar) # add suffix to basename & rename end product toar_name = ("%s.%s" % (band.split('@')[0], outputsuffix)) run("g.rename", rast=(tmp_toar, toar_name)) elif tmp_rad: # history entry run("r.support", map=tmp_rad, title=title_rad, units=units_rad, description=description_rad, source1=source1_rad, source2=source2_rad, history=history_rad) # add suffix to basename & rename end product rad_name = ("%s.%s" % (band.split('@')[0], outputsuffix)) run("g.rename", rast=(tmp_rad, rad_name)) # visualising-related information if not keep_region: grass.del_temp_region() # restoring previous region settings g.message("\n|! Region's resolution restored!") g.message("\n>>> Hint: rebalancing colors " "(i.colors.enhance) may improve appearance of RGB composites!", flags='i')
def main(): global TMPLOC, SRCGISRC, GISDBASE, TMP_REG_NAME GDALdatasource = options['input'] output = options['output'] method = options['resample'] memory = options['memory'] bands = options['band'] tgtres = options['resolution'] title = options["title"] if options['resolution_value']: if tgtres != 'value': grass.fatal(_("To set custom resolution value, select 'value' in resolution option")) tgtres_value = float(options['resolution_value']) if tgtres_value <= 0: grass.fatal(_("Resolution value can't be smaller than 0")) elif tgtres == 'value': grass.fatal(_("Please provide the resolution for the imported dataset or change to 'estimated' resolution")) grassenv = grass.gisenv() tgtloc = grassenv['LOCATION_NAME'] tgtmapset = grassenv['MAPSET'] GISDBASE = grassenv['GISDBASE'] tgtgisrc = os.environ['GISRC'] SRCGISRC = grass.tempfile() TMPLOC = 'temp_import_location_' + str(os.getpid()) f = open(SRCGISRC, 'w') f.write('MAPSET: PERMANENT\n') f.write('GISDBASE: %s\n' % GISDBASE) f.write('LOCATION_NAME: %s\n' % TMPLOC) f.write('GUI: text\n') f.close() tgtsrs = grass.read_command('g.proj', flags='j', quiet=True) # create temp location from input without import grass.verbose(_("Creating temporary location for <%s>...") % GDALdatasource) parameters = dict(input=GDALdatasource, output=output, memory=memory, flags='c', title=title, location=TMPLOC, quiet=True) if bands: parameters['band'] = bands try: grass.run_command('r.in.gdal', **parameters) except CalledModuleError: grass.fatal(_("Unable to read GDAL dataset <%s>") % GDALdatasource) # switch to temp location os.environ['GISRC'] = str(SRCGISRC) # switch to target location os.environ['GISRC'] = str(tgtgisrc) # try r.in.gdal directly first additional_flags = 'l' if flags['l'] else '' if flags['o']: additional_flags += 'o' if flags['o'] or grass.run_command('r.in.gdal', input=GDALdatasource, flags='j', errors='status', quiet=True) == 0: parameters = dict(input=GDALdatasource, output=output, memory=memory, flags='k' + additional_flags) if bands: parameters['band'] = bands try: grass.run_command('r.in.gdal', **parameters) grass.verbose(_("Input <%s> successfully imported without reprojection") % GDALdatasource) return 0 except CalledModuleError as e: grass.fatal(_("Unable to import GDAL dataset <%s>") % GDALdatasource) # make sure target is not xy if grass.parse_command('g.proj', flags='g')['name'] == 'xy_location_unprojected': grass.fatal(_("Coordinate reference system not available for current location <%s>") % tgtloc) # switch to temp location os.environ['GISRC'] = str(SRCGISRC) # make sure input is not xy if grass.parse_command('g.proj', flags='g')['name'] == 'xy_location_unprojected': grass.fatal(_("Coordinate reference system not available for input <%s>") % GDALdatasource) # import into temp location grass.verbose(_("Importing <%s> to temporary location...") % GDALdatasource) parameters = dict(input=GDALdatasource, output=output, memory=memory, flags='k' + additional_flags) if bands: parameters['band'] = bands try: grass.run_command('r.in.gdal', **parameters) except CalledModuleError: grass.fatal(_("Unable to import GDAL dataset <%s>") % GDALdatasource) outfiles = grass.list_grouped('raster')['PERMANENT'] # is output a group? group = False path = os.path.join(GISDBASE, TMPLOC, 'group', output) if os.path.exists(path): group = True path = os.path.join(GISDBASE, TMPLOC, 'group', output, 'POINTS') if os.path.exists(path): grass.fatal(_("Input contains GCPs, rectification is required")) # switch to target location os.environ['GISRC'] = str(tgtgisrc) region = grass.region() rflags = None if flags['n']: rflags = 'n' for outfile in outfiles: n = region['n'] s = region['s'] e = region['e'] w = region['w'] grass.use_temp_region() if options['extent'] == 'input': # r.proj -g try: tgtextents = grass.read_command('r.proj', location=TMPLOC, mapset='PERMANENT', input=outfile, flags='g', memory=memory, quiet=True) except CalledModuleError: grass.fatal(_("Unable to get reprojected map extent")) try: srcregion = grass.parse_key_val(tgtextents, val_type=float, vsep=' ') n = srcregion['n'] s = srcregion['s'] e = srcregion['e'] w = srcregion['w'] except ValueError: # import into latlong, expect 53:39:06.894826N srcregion = grass.parse_key_val(tgtextents, vsep=' ') n = grass.float_or_dms(srcregion['n'][:-1]) * \ (-1 if srcregion['n'][-1] == 'S' else 1) s = grass.float_or_dms(srcregion['s'][:-1]) * \ (-1 if srcregion['s'][-1] == 'S' else 1) e = grass.float_or_dms(srcregion['e'][:-1]) * \ (-1 if srcregion['e'][-1] == 'W' else 1) w = grass.float_or_dms(srcregion['w'][:-1]) * \ (-1 if srcregion['w'][-1] == 'W' else 1) grass.run_command('g.region', n=n, s=s, e=e, w=w) # v.in.region in tgt vreg = TMP_REG_NAME = 'vreg_tmp_' + str(os.getpid()) grass.run_command('v.in.region', output=vreg, quiet=True) grass.del_temp_region() # reproject to src # switch to temp location os.environ['GISRC'] = str(SRCGISRC) try: grass.run_command('v.proj', input=vreg, output=vreg, location=tgtloc, mapset=tgtmapset, quiet=True) except CalledModuleError: grass.fatal(_("Unable to reproject to source location")) # set region from region vector grass.run_command('g.region', raster=outfile) grass.run_command('g.region', vector=vreg) # align to first band grass.run_command('g.region', align=outfile) # get number of cells cells = grass.region()['cells'] estres = math.sqrt((n - s) * (e - w) / cells) # remove from source location for multi bands import grass.run_command('g.remove', type='vector', name=vreg, flags='f', quiet=True) os.environ['GISRC'] = str(tgtgisrc) grass.run_command('g.remove', type='vector', name=vreg, flags='f', quiet=True) grass.message(_("Estimated target resolution for input band <{out}>: {res}").format(out=outfile, res=estres)) if flags['e']: continue if options['extent'] == 'input': grass.use_temp_region() grass.run_command('g.region', n=n, s=s, e=e, w=w) res = None if tgtres == 'estimated': res = estres elif tgtres == 'value': res = tgtres_value grass.message(_("Using given resolution for input band <{out}>: {res}").format(out=outfile, res=res)) # align to requested resolution grass.run_command('g.region', res=res, flags='a') else: curr_reg = grass.region() grass.message(_("Using current region resolution for input band " "<{out}>: nsres={ns}, ewres={ew}").format(out=outfile, ns=curr_reg['nsres'], ew=curr_reg['ewres'])) # r.proj grass.message(_("Reprojecting <%s>...") % outfile) try: grass.run_command('r.proj', location=TMPLOC, mapset='PERMANENT', input=outfile, method=method, resolution=res, memory=memory, flags=rflags, quiet=True) except CalledModuleError: grass.fatal(_("Unable to to reproject raster <%s>") % outfile) if grass.raster_info(outfile)['min'] is None: grass.fatal(_("The reprojected raster <%s> is empty") % outfile) if options['extent'] == 'input': grass.del_temp_region() if flags['e']: return 0 if group: grass.run_command('i.group', group=output, input=','.join(outfiles)) # TODO: write metadata with r.support return 0
def main(): global usermask, mapset, tmp_rmaps, tmp_vmaps input = options['input'] output = options['output'] tension = options['tension'] smooth = options['smooth'] method = options['method'] edge = int(options['edge']) segmax = int(options['segmax']) npmin = int(options['npmin']) quiet = True # FIXME mapset = grass.gisenv()['MAPSET'] unique = str(os.getpid()) # Shouldn't we use temp name? prefix = 'r_fillnulls_%s_' % unique failed_list = list() # a list of failed holes. Caused by issues with v.surf.rst. Connected with #1813 #check if input file exists if not grass.find_file(input)['file']: grass.fatal(_("Raster map <%s> not found") % input) # save original region reg_org = grass.region() # check if a MASK is already present # and remove it to not interfere with NULL lookup part # as we don't fill MASKed parts! if grass.find_file('MASK', mapset = mapset)['file']: usermask = "usermask_mask." + unique grass.message(_("A user raster mask (MASK) is present. Saving it...")) grass.run_command('g.rename', quiet = quiet, rast = ('MASK',usermask)) #check if method is rst to use v.surf.rst if method == 'rst': # idea: filter all NULLS and grow that area(s) by 3 pixel, then # interpolate from these surrounding 3 pixel edge filling = prefix + 'filled' grass.use_temp_region() grass.run_command('g.region', align = input, quiet = quiet) region = grass.region() ns_res = region['nsres'] ew_res = region['ewres'] grass.message(_("Using RST interpolation...")) grass.message(_("Locating and isolating NULL areas...")) # creating binary (0/1) map if usermask: grass.message(_("Skipping masked raster parts")) grass.mapcalc("$tmp1 = if(isnull($input) && !($mask == 0 || isnull($mask)),1,null())", tmp1 = prefix + 'nulls', input = input, mask = usermask) else: grass.mapcalc("$tmp1 = if(isnull($input),1,null())", tmp1 = prefix + 'nulls', input = input) tmp_rmaps.append(prefix + 'nulls') # restoring user's mask, if present # to ignore MASKed original values if usermask: grass.message(_("Restoring user mask (MASK)...")) if grass.run_command('g.rename', quiet = quiet, rast = (usermask, 'MASK')) != 0: grass.warning(_("Failed to restore user MASK!")) usermask = None # grow identified holes by X pixels grass.message(_("Growing NULL areas")) tmp_rmaps.append(prefix + 'grown') if grass.run_command('r.grow', input = prefix + 'nulls', radius = edge + 0.01, old = 1, new = 1, out = prefix + 'grown', quiet = quiet) != 0: grass.fatal(_("abandoned. Removing temporary map, restoring user mask if needed:")) # assign unique IDs to each hole or hole system (holes closer than edge distance) grass.message(_("Assigning IDs to NULL areas")) tmp_rmaps.append(prefix + 'clumped') if grass.run_command('r.clump', input = prefix + 'grown', output = prefix + 'clumped', quiet = quiet) != 0: grass.fatal(_("abandoned. Removing temporary map, restoring user mask if needed:")) # get a list of unique hole cat's grass.mapcalc("$out = if(isnull($inp), null(), $clumped)", out = prefix + 'holes', inp = prefix + 'nulls', clumped = prefix + 'clumped') tmp_rmaps.append(prefix + 'holes') # use new IDs to identify holes if grass.run_command('r.to.vect', flags = 'v', input = prefix + 'holes', output = prefix + 'holes', type = 'area', quiet = quiet) != 0: grass.fatal(_("abandoned. Removing temporary maps, restoring user mask if needed:")) tmp_vmaps.append(prefix + 'holes') # get a list of unique hole cat's cats_file_name = grass.tempfile(False) grass.run_command('v.db.select', flags = 'c', map = prefix + 'holes', columns = 'cat', file = cats_file_name, quiet = quiet) cat_list = list() cats_file = file(cats_file_name) for line in cats_file: cat_list.append(line.rstrip('\n')) cats_file.close() os.remove(cats_file_name) if len(cat_list) < 1: grass.fatal(_("Input map has no holes. Check region settings.")) # GTC Hole is NULL area in a raster map grass.message(_("Processing %d map holes") % len(cat_list)) first = True hole_n = 1 for cat in cat_list: holename = prefix + 'hole_' + cat # GTC Hole is a NULL area in a raster map grass.message(_("Filling hole %s of %s") % (hole_n, len(cat_list))) hole_n = hole_n + 1 # cut out only CAT hole for processing if grass.run_command('v.extract', input = prefix + 'holes', output = holename + '_pol', cats = cat, quiet = quiet) != 0: grass.fatal(_("abandoned. Removing temporary maps, restoring user mask if needed:")) tmp_vmaps.append(holename + '_pol') # zoom to specific hole with a buffer of two cells around the hole to remove rest of data if grass.run_command('g.region', vect = holename + '_pol', align = input, w = 'w-%d' % (edge * 2 * ew_res), e = 'e+%d' % (edge * 2 * ew_res), n = 'n+%d' % (edge * 2 * ns_res), s = 's-%d' % (edge * 2 * ns_res), quiet = quiet) != 0: grass.fatal(_("abandoned. Removing temporary maps, restoring user mask if needed:")) # remove temporary map to not overfill disk if grass.run_command('g.remove', flags = 'fb', type = 'vect', pattern = holename + '_pol', quiet = quiet) != 0: grass.fatal(_("abandoned. Removing temporary maps, restoring user mask if needed:")) tmp_vmaps.remove(holename + '_pol') # copy only data around hole grass.mapcalc("$out = if($inp == $catn, $inp, null())", out = holename, inp = prefix + 'holes', catn = cat) tmp_rmaps.append(holename) # If here loop is split into two, next part of loop can be run in parallel # (except final result patching) # Downside - on large maps such approach causes large disk usage # grow hole border to get it's edge area tmp_rmaps.append(holename + '_grown') if grass.run_command('r.grow', input = holename, radius = edge + 0.01, old = -1, out = holename + '_grown', quiet = quiet) != 0: grass.fatal(_("abandoned. Removing temporary map, restoring user mask if needed:")) # no idea why r.grow old=-1 doesn't replace existing values with NULL grass.mapcalc("$out = if($inp == -1, null(), $dem)", out = holename + '_edges', inp = holename + '_grown', dem = input) tmp_rmaps.append(holename + '_edges') # convert to points for interpolation tmp_vmaps.append(holename) if grass.run_command('r.to.vect', input = holename + '_edges', output = holename, type = 'point', flags = 'z', quiet = quiet) != 0: grass.fatal(_("abandoned. Removing temporary maps, restoring user mask if needed:")) # count number of points to control segmax parameter for interpolation: pointsnumber = grass.vector_info_topo(map = holename)['points'] grass.verbose(_("Interpolating %d points") % pointsnumber) if pointsnumber < 2: grass.verbose(_("No points to interpolate")) failed_list.append(holename) continue # Avoid v.surf.rst warnings if pointsnumber < segmax: npmin = pointsnumber + 1 segmax = pointsnumber # launch v.surf.rst tmp_rmaps.append(holename + '_dem') if grass.run_command('v.surf.rst', quiet = quiet, input = holename, elev = holename + '_dem', tension = tension, smooth = smooth, segmax = segmax, npmin = npmin) != 0: # GTC Hole is NULL area in a raster map grass.fatal(_("Failed to fill hole %s") % cat) # v.surf.rst sometimes fails with exit code 0 # related bug #1813 if not grass.find_file(holename + '_dem')['file']: try: tmp_rmaps.remove(holename) tmp_rmaps.remove(holename + '_grown') tmp_rmaps.remove(holename + '_edges') tmp_rmaps.remove(holename + '_dem') tmp_vmaps.remove(holename) except: pass grass.warning(_("Filling has failed silently. Leaving temporary maps with prefix <%s> for debugging.") % holename) failed_list.append(holename) continue # append hole result to interpolated version later used to patch into original DEM if first: tmp_rmaps.append(filling) grass.run_command('g.region', align = input, rast = holename + '_dem', quiet = quiet) grass.mapcalc("$out = if(isnull($inp), null(), $dem)", out = filling, inp = holename, dem = holename + '_dem') first = False else: tmp_rmaps.append(filling + '_tmp') grass.run_command('g.region', align = input, rast = (filling, holename + '_dem'), quiet = quiet) grass.mapcalc("$out = if(isnull($inp), if(isnull($fill), null(), $fill), $dem)", out = filling + '_tmp', inp = holename, dem = holename + '_dem', fill = filling) if grass.run_command('g.rename', rast = (filling + '_tmp', filling), overwrite = True, quiet = quiet) != 0: grass.fatal(_("abandoned. Removing temporary maps, restoring user mask if needed:")) tmp_rmaps.remove(filling + '_tmp') # this map has been removed. No need for later cleanup. # remove temporary maps to not overfill disk try: tmp_rmaps.remove(holename) tmp_rmaps.remove(holename + '_grown') tmp_rmaps.remove(holename + '_edges') tmp_rmaps.remove(holename + '_dem') except: pass if grass.run_command('g.remove', quiet = quiet, flags = 'fb', type = 'rast', pattern = (holename, holename + '_grown', holename + '_edges', holename + '_dem')) != 0: grass.fatal(_("abandoned. Removing temporary maps, restoring user mask if needed:")) try: tmp_vmaps.remove(holename) except: pass if grass.run_command('g.remove', quiet = quiet, flags = 'fb', type = 'vect', pattern = holename) != 0: grass.fatal(_("abandoned. Removing temporary maps, restoring user mask if needed:")) #check if method is different from rst to use r.resamp.bspline if method != 'rst': grass.message(_("Using %s bspline interpolation") % method) # clone current region grass.use_temp_region() grass.run_command('g.region', align = input) reg = grass.region() # launch r.resamp.bspline tmp_rmaps.append(prefix + 'filled') if usermask: grass.run_command('r.resamp.bspline', input = input, mask = usermask, output = prefix + 'filled', method = method, ew_step = 3 * reg['ewres'], ns_step = 3 * reg['nsres'], _lambda = 0.01, flags = 'n') else: grass.run_command('r.resamp.bspline', input = input, output = prefix + 'filled', method = method, ew_step = 3 * reg['ewres'], ns_step = 3 * reg['nsres'], _lambda = 0.01, flags = 'n') # restoring user's mask, if present: if usermask: grass.message(_("Restoring user mask (MASK)...")) if grass.run_command('g.rename', quiet = quiet, rast = (usermask, 'MASK')) != 0: grass.warning(_("Failed to restore user MASK!")) usermask = None # set region to original extents, align to input grass.run_command('g.region', n = reg_org['n'], s = reg_org['s'], e = reg_org['e'], w = reg_org['w'], align = input) # patch orig and fill map grass.message(_("Patching fill data into NULL areas...")) # we can use --o here as g.parser already checks on startup grass.run_command('r.patch', input = (input,prefix + 'filled'), output = output, overwrite = True) # restore the real region grass.del_temp_region() grass.message(_("Filled raster map is: %s") % output) # write cmd history: grass.raster_history(output) if len(failed_list) > 0: grass.warning(_("Following holes where not filled. Temporary maps with are left in place to allow examination of unfilled holes")) outlist = failed_list[0] for hole in failed_list[1:]: outlist = ', ' + outlist grass.message(outlist) grass.message(_("Done."))