def brovey(pan, ms1, ms2, ms3, out, pid, sproc): grass.verbose(_("Using Brovey algorithm")) # pan/intensity histogram matching using linear regression grass.message(_("Pan channel/intensity histogram matching using linear regression")) outname = 'tmp%s_pan1' % pid panmatch1 = matchhist(pan, ms1, outname) outname = 'tmp%s_pan2' % pid panmatch2 = matchhist(pan, ms2, outname) outname = 'tmp%s_pan3' % pid panmatch3 = matchhist(pan, ms3, outname) outr = '%s_red' % out outg = '%s_green' % out outb = '%s_blue' % out # calculate brovey transformation grass.message(_("Calculating Brovey transformation...")) if sproc: # serial processing e = '''eval(k = "$ms1" + "$ms2" + "$ms3") "$outr" = 1 * round("$ms3" * "$panmatch3" / k) "$outg" = 1 * round("$ms2" * "$panmatch2" / k) "$outb" = 1 * round("$ms1" * "$panmatch1" / k)''' grass.mapcalc(e, outr=outr, outg=outg, outb=outb, panmatch1=panmatch1, panmatch2=panmatch2, panmatch3=panmatch3, ms1=ms1, ms2=ms2, ms3=ms3, overwrite=True) else: # parallel processing pb = grass.mapcalc_start('%s_blue = 1 * round((%s * %s) / (%s + %s + %s))' % (out, ms1, panmatch1, ms1, ms2, ms3), overwrite=True) pg = grass.mapcalc_start('%s_green = 1 * round((%s * %s) / (%s + %s + %s))' % (out, ms2, panmatch2, ms1, ms2, ms3), overwrite=True) pr = grass.mapcalc_start('%s_red = 1 * round((%s * %s) / (%s + %s + %s))' % (out, ms3, panmatch3, ms1, ms2, ms3), overwrite=True) pb.wait(), pg.wait(), pr.wait() try: pb.terminate(), pg.terminate(), pr.terminate() except: "" # Cleanup try: grass.run_command('g.remove', flags='f', quiet=True, type='raster', name='%s,%s,%s' % (panmatch1, panmatch2, panmatch3)) except: ""
def main(): if not hasNumPy: grass.fatal(_("Required dependency NumPy not found. Exiting.")) sharpen = options['method'] # sharpening algorithm ms1 = options['blue'] # blue channel ms2 = options['green'] # green channel ms3 = options['red'] # red channel pan = options['pan'] # high res pan channel out = options['output'] # prefix for output RGB maps bladjust = flags['l'] # adjust blue channel sproc = flags['s'] # serial processing outb = grass.core.find_file('%s_blue' % out) outg = grass.core.find_file('%s_green' % out) outr = grass.core.find_file('%s_red' % out) if (outb['name'] != '' or outg['name'] != '' or outr['name'] != '') and not grass.overwrite(): grass.warning(_('Maps with selected output prefix names already exist.' ' Delete them or use overwrite flag')) return pid = str(os.getpid()) # get PAN resolution: kv = grass.raster_info(map=pan) nsres = kv['nsres'] ewres = kv['ewres'] panres = (nsres + ewres) / 2 # clone current region grass.use_temp_region() grass.run_command('g.region', res=panres, align=pan) grass.message(_("Performing pan sharpening with hi res pan image: %f" % panres)) if sharpen == "brovey": grass.verbose(_("Using Brovey algorithm")) # pan/intensity histogram matching using linear regression outname = 'tmp%s_pan1' % pid panmatch1 = matchhist(pan, ms1, outname) outname = 'tmp%s_pan2' % pid panmatch2 = matchhist(pan, ms2, outname) outname = 'tmp%s_pan3' % pid panmatch3 = matchhist(pan, ms3, outname) outr = '%s_red' % out outg = '%s_green' % out outb = '%s_blue' % out # calculate brovey transformation grass.message(_("Calculating Brovey transformation...")) if sproc: # serial processing e = '''eval(k = "$ms1" + "$ms2" + "$ms3") "$outr" = 1.0 * "$ms3" * "$panmatch3" / k "$outg" = 1.0 * "$ms2" * "$panmatch2" / k "$outb" = 1.0 * "$ms1" * "$panmatch1" / k''' grass.mapcalc(e, outr=outr, outg=outg, outb=outb, panmatch1=panmatch1, panmatch2=panmatch2, panmatch3=panmatch3, ms1=ms1, ms2=ms2, ms3=ms3, overwrite=True) else: # parallel processing pb = grass.mapcalc_start('%s_blue = (1.0 * %s * %s) / (%s + %s + %s)' % (out, ms1, panmatch1, ms1, ms2, ms3), overwrite=True) pg = grass.mapcalc_start('%s_green = (1.0 * %s * %s) / (%s + %s + %s)' % (out, ms2, panmatch2, ms1, ms2, ms3), overwrite=True) pr = grass.mapcalc_start('%s_red = (1.0 * %s * %s) / (%s + %s + %s)' % (out, ms3, panmatch3, ms1, ms2, ms3), overwrite=True) pb.wait() pg.wait() pr.wait() # Cleanup grass.run_command('g.remove', flags='f', quiet=True, type='raster', name='%s,%s,%s' % (panmatch1, panmatch2, panmatch3)) elif sharpen == "ihs": grass.verbose(_("Using IHS<->RGB algorithm")) # transform RGB channels into IHS color space grass.message(_("Transforming to IHS color space...")) grass.run_command('i.rgb.his', overwrite=True, red=ms3, green=ms2, blue=ms1, hue="tmp%s_hue" % pid, intensity="tmp%s_int" % pid, saturation="tmp%s_sat" % pid) # pan/intensity histogram matching using linear regression target = "tmp%s_int" % pid outname = "tmp%s_pan_int" % pid panmatch = matchhist(pan, target, outname) # substitute pan for intensity channel and transform back to RGB color space grass.message(_("Transforming back to RGB color space and sharpening...")) grass.run_command('i.his.rgb', overwrite=True, hue="tmp%s_hue" % pid, intensity="%s" % panmatch, saturation="tmp%s_sat" % pid, red="%s_red" % out, green="%s_green" % out, blue="%s_blue" % out) # Cleanup grass.run_command('g.remove', flags='f', quiet=True, type='raster', name=panmatch) elif sharpen == "pca": grass.verbose(_("Using PCA/inverse PCA algorithm")) grass.message(_("Creating PCA images and calculating eigenvectors...")) # initial PCA with RGB channels pca_out = grass.read_command('i.pca', quiet=True, rescale='0,0', input='%s,%s,%s' % (ms1, ms2, ms3), output='tmp%s.pca' % pid) if len(pca_out) < 1: grass.fatal(_("Input has no data. Check region settings.")) b1evect = [] b2evect = [] b3evect = [] for l in pca_out.replace('(', ',').replace(')', ',').splitlines(): b1evect.append(float(l.split(',')[1])) b2evect.append(float(l.split(',')[2])) b3evect.append(float(l.split(',')[3])) # inverse PCA with hi res pan channel substituted for principal component 1 pca1 = 'tmp%s.pca.1' % pid pca2 = 'tmp%s.pca.2' % pid pca3 = 'tmp%s.pca.3' % pid b1evect1 = b1evect[0] b1evect2 = b1evect[1] b1evect3 = b1evect[2] b2evect1 = b2evect[0] b2evect2 = b2evect[1] b2evect3 = b2evect[2] b3evect1 = b3evect[0] b3evect2 = b3evect[1] b3evect3 = b3evect[2] outname = 'tmp%s_pan' % pid panmatch = matchhist(pan, ms1, outname) grass.message(_("Performing inverse PCA ...")) stats1 = grass.parse_command("r.univar", map=ms1, flags='g', parse=(grass.parse_key_val, {'sep': '='})) stats2 = grass.parse_command("r.univar", map=ms2, flags='g', parse=(grass.parse_key_val, {'sep': '='})) stats3 = grass.parse_command("r.univar", map=ms3, flags='g', parse=(grass.parse_key_val, {'sep': '='})) b1mean = float(stats1['mean']) b2mean = float(stats2['mean']) b3mean = float(stats3['mean']) if sproc: # serial processing e = '''eval(k = "$ms1" + "$ms2" + "$ms3") "$outr" = 1.0 * "$ms3" * "$panmatch3" / k "$outg" = 1.0 * "$ms2" * "$panmatch2" / k "$outb" = 1.0* "$ms1" * "$panmatch1" / k''' outr = '%s_red' % out outg = '%s_green' % out outb = '%s_blue' % out cmd1 = "$outb = (1.0 * $panmatch * $b1evect1) + ($pca2 * $b2evect1) + ($pca3 * $b3evect1) + $b1mean" cmd2 = "$outg = (1.0 * $panmatch * $b1evect2) + ($pca2 * $b2evect1) + ($pca3 * $b3evect2) + $b2mean" cmd3 = "$outr = (1.0 * $panmatch * $b1evect3) + ($pca2 * $b2evect3) + ($pca3 * $b3evect3) + $b3mean" cmd = '\n'.join([cmd1, cmd2, cmd3]) grass.mapcalc(cmd, outb=outb, outg=outg, outr=outr, panmatch=panmatch, pca2=pca2, pca3=pca3, b1evect1=b1evect1, b2evect1=b2evect1, b3evect1=b3evect1, b1evect2=b1evect2, b2evect2=b2evect2, b3evect2=b3evect2, b1evect3=b1evect3, b2evect3=b2evect3, b3evect3=b3evect3, b1mean=b1mean, b2mean=b2mean, b3mean=b3mean, overwrite=True) else: # parallel processing pb = grass.mapcalc_start('%s_blue = (%s * %f) + (%s * %f) + (%s * %f) + %f' % (out, panmatch, b1evect1, pca2, b2evect1, pca3, b3evect1, b1mean), overwrite=True) pg = grass.mapcalc_start('%s_green = (%s * %f) + (%s * %f) + (%s * %f) + %f' % (out, panmatch, b1evect2, pca2, b2evect2, pca3, b3evect2, b2mean), overwrite=True) pr = grass.mapcalc_start('%s_red = (%s * %f) + (%s * %f) + (%s * ''%f) + %f' % (out, panmatch, b1evect3, pca2, b2evect3, pca3, b3evect3, b3mean), overwrite=True) pr.wait() pg.wait() pb.wait() # Cleanup grass.run_command('g.remove', flags='f', quiet=True, type="raster", pattern='tmp%s*,%s' % (pid, panmatch)) # Could add other sharpening algorithms here, e.g. wavelet transformation grass.message(_("Assigning grey equalized color tables to output images...")) # equalized grey scales give best contrast for ch in ['red', 'green', 'blue']: grass.run_command('r.colors', quiet=True, map="%s_%s" % (out, ch), flags="e", color='grey') # Landsat too blue-ish because panchromatic band less sensitive to blue # light, so output blue channed can be modified if bladjust: grass.message(_("Adjusting blue channel color table...")) rules = grass.tempfile() colors = open(rules, 'w') colors.write('5 0 0 0\n20 200 200 200\n40 230 230 230\n67 255 255 255 \n') colors.close() grass.run_command('r.colors', map="%s_blue" % out, rules=rules) os.remove(rules) # output notice grass.verbose(_("The following pan-sharpened output maps have been generated:")) for ch in ['red', 'green', 'blue']: grass.verbose(_("%s_%s") % (out, ch)) grass.verbose(_("To visualize output, run: g.region -p raster=%s_red" % out)) grass.verbose(_("d.rgb r=%s_red g=%s_green b=%s_blue" % (out, out, out))) grass.verbose(_("If desired, combine channels into a single RGB map with 'r.composite'.")) grass.verbose(_("Channel colors can be rebalanced using i.colors.enhance.")) # write cmd history: for ch in ['red', 'green', 'blue']: grass.raster_history("%s_%s" % (out, ch)) # create a group with the three output grass.run_command('i.group', group=out, input="{n}_red,{n}_blue,{n}_green".format(n=out)) # Cleanup grass.run_command('g.remove', flags="f", type="raster", pattern="tmp%s*" % pid, quiet=True)
def main(): # User inputs friction = options['friction'] # Input friction raster points = options['points'] # Input point layer output = options['output'] # Output least cost path raster radius = int(options['radius']) # Point search radius n_closepoints = int(options['closepoints']) # Number of closest points netout = options['netout'] # Network output # Initiate PontLayer() object input_points = PointLayer(points) # Get process id (pid) and create temporary layer names pid = os.getpid() # Process ID, used for making (more or less) unique temporary filenames costmap1 = "tmp_cost_%d" % pid # Cost surface from onepoint1 costmap2 = "tmp_cost_%d_%i" % (pid, 2) # Cost surface from onepoint2; parallel process lcpmap1 = "tmp_lcp_%d" % pid # Least cost path map from costmap1 lcpmap2 = "tmp_lcp_%d_%i" % (pid, 2) # Least cost path map from costmap2; parallel process lcptemp = "tmp_lcptemp_%d" % pid # Temporary file for mapcalc # Create a a long string of all temporary layernames for easy deletion them later on tmpvars = costmap1 + "," + costmap2 + "," + lcpmap1 + "," + lcpmap2 + "," + lcptemp # Get coordinates of input point layer and also the total number of point features in the layer #all_coords, n_feats = pointCoords(points) #distdict = pointDistances(all_coords, n_feats) n_feats = input_points.featCount() # Initiate new Popen() object for multiprocessing mapcalc mapcalcproc = grass.Popen("") # Main loop that creates least cost paths for all points for feat in range(1, n_feats +1, 2): # Initiate new PointLayer() objects layer_point1 = PointLayer(points, feat) layer_point2 = PointLayer(points, feat+1) if radius > 0 and n_closepoints <= 0: drainpointlayer1 = None drainpointlayer2 = None drainpoints1 = layer_point1.pointsInRadius(radius, stringoutput=True) drainpoints2 = layer_point2.pointsInRadius(radius, stringoutput=True) elif radius > 0 and n_closepoints > 0: drainpointlayer1 = None drainpointlayer2 = None drainpoints1 = layer_point1.closePointsInRadius(n_closepoints, radius) drainpoints2 = layer_point2.closePointsInRadius(n_closepoints, radius) elif radius == 0 and n_closepoints > 0: drainpointlayer1 = None drainpointlayer2 = None drainpoints1 = layer_point1.closePoints(n_closepoints) drainpoints2 = layer_point2.closePoints(n_closepoints) else: drainpointlayer1 = points drainpointlayer2 = points drainpoints1 = None drainpoints2 = None try: lcpproc1 = grass.start_command('r.cost', flags="k", overwrite=True, input=friction, output=costmap1, start_coordinates=layer_point1.oneCoord()) lcpproc2 = grass.start_command('r.cost', flags="k", overwrite=True, input=friction, output=costmap2, start_coordinates=layer_point2.oneCoord()) # Least-cost paths from every other point to the current point lcpproc1.wait() lcpproc1 = grass.start_command('r.drain', overwrite=True, input=costmap1, output=lcpmap1, start_coordinates=drainpoints1, start_points=drainpointlayer1) lcpproc2.wait() lcpproc2 = grass.start_command('r.drain', overwrite=True, input=costmap2, output=lcpmap2, start_coordinates=drainpoints2, start_points=drainpointlayer2) lcpproc1.wait() lcpproc2.wait() except: cleanUp(tmpvars) grass.fatal("Problem with lcp creation") try: if feat == 1: mapcalcproc = grass.mapcalc_start("$outmap = if(isnull($tempmap),0,1) + if(isnull($tempmap2),0,1)", outmap = output, tempmap = lcpmap1, tempmap2 = lcpmap2, overwrite=True) else: # Wait for the mapcalc operation from previous iteration to finish mapcalcproc.wait() # Rename the cumulative lcp map from previous iteration so that mapcalc can use it (x=x+y doesn't work with mapcalc) grass.run_command('g.rename', rast = output + ',' + lcptemp, overwrite=True) # output = Previous LCP + Current LCP mapcalcproc = grass.start_command('r.mapcalc', "$outmap = $inmap + if(isnull($tempmap),0,1) + if(isnull($tempmap2),0,1)", inmap = lcptemp, outmap = output, tempmap = lcpmap1, tempmap2 = lcpmap2) except: cleanUp(tmpvars) grass.fatal("Problem with mapcalc") # Wait for last mapcalc to finish mapcalcproc.wait() # Make 0 values into NULLs nullproc = grass.start_command('r.null', map = output, setnull = "0") cleanUp(tmpvars) nullproc.wait() grass.message("All done")
def pca(pan, ms1, ms2, ms3, out, pid, sproc): grass.verbose(_("Using PCA/inverse PCA algorithm")) grass.message(_("Creating PCA images and calculating eigenvectors...")) # initial PCA with RGB channels pca_out = grass.read_command('i.pca', quiet=True, rescale='0,0', input='%s,%s,%s' % (ms1, ms2, ms3), output='tmp%s.pca' % pid) if len(pca_out) < 1: grass.fatal(_("Input has no data. Check region settings.")) b1evect = [] b2evect = [] b3evect = [] for l in pca_out.replace('(', ',').replace(')', ',').splitlines(): b1evect.append(float(l.split(',')[1])) b2evect.append(float(l.split(',')[2])) b3evect.append(float(l.split(',')[3])) # inverse PCA with hi res pan channel substituted for principal component 1 pca1 = 'tmp%s.pca.1' % pid pca2 = 'tmp%s.pca.2' % pid pca3 = 'tmp%s.pca.3' % pid b1evect1 = b1evect[0] b1evect2 = b1evect[1] b1evect3 = b1evect[2] b2evect1 = b2evect[0] b2evect2 = b2evect[1] b2evect3 = b2evect[2] b3evect1 = b3evect[0] b3evect2 = b3evect[1] b3evect3 = b3evect[2] # Histogram matching outname = 'tmp%s_pan1' % pid panmatch1 = matchhist(pan, ms1, outname) outname = 'tmp%s_pan2' % pid panmatch2 = matchhist(pan, ms2, outname) outname = 'tmp%s_pan3' % pid panmatch3 = matchhist(pan, ms3, outname) grass.message(_("Performing inverse PCA ...")) # Get mean value of each channel stats1 = grass.parse_command("r.univar", map=ms1, flags='g', parse=(grass.parse_key_val, { 'sep': '=' })) stats2 = grass.parse_command("r.univar", map=ms2, flags='g', parse=(grass.parse_key_val, { 'sep': '=' })) stats3 = grass.parse_command("r.univar", map=ms3, flags='g', parse=(grass.parse_key_val, { 'sep': '=' })) b1mean = float(stats1['mean']) b2mean = float(stats2['mean']) b3mean = float(stats3['mean']) if sproc: # serial processing outr = '%s_red' % out outg = '%s_green' % out outb = '%s_blue' % out cmd1 = "$outb = 1 * round(($panmatch1 * $b1evect1) + ($pca2 * $b1evect2) + ($pca3 * $b1evect3) + $b1mean)" cmd2 = "$outg = 1 * round(($panmatch2 * $b2evect1) + ($pca2 * $b2evect2) + ($pca3 * $b2evect3) + $b2mean)" cmd3 = "$outr = 1 * round(($panmatch3 * $b3evect1) + ($pca2 * $b3evect2) + ($pca3 * $b3evect3) + $b3mean)" cmd = '\n'.join([cmd1, cmd2, cmd3]) grass.mapcalc(cmd, outb=outb, outg=outg, outr=outr, panmatch1=panmatch1, panmatch2=panmatch2, panmatch3=panmatch3, pca2=pca2, pca3=pca3, b1evect1=b1evect1, b2evect1=b2evect1, b3evect1=b3evect1, b1evect2=b1evect2, b2evect2=b2evect2, b3evect2=b3evect2, b1evect3=b1evect3, b2evect3=b2evect3, b3evect3=b3evect3, b1mean=b1mean, b2mean=b2mean, b3mean=b3mean, overwrite=True) else: # parallel processing pb = grass.mapcalc_start( '%s_blue = 1 * round((%s * %f) + (%s * %f) + (%s * %f) + %f)' % (out, panmatch1, b1evect1, pca2, b1evect2, pca3, b1evect3, b1mean), overwrite=True) pg = grass.mapcalc_start( '%s_green = 1 * round((%s * %f) + (%s * %f) + (%s * %f) + %f)' % (out, panmatch2, b2evect1, pca2, b2evect2, pca3, b2evect3, b2mean), overwrite=True) pr = grass.mapcalc_start( '%s_red = 1 * round((%s * %f) + (%s * %f) + (%s * %f) + %f)' % (out, panmatch3, b3evect1, pca2, b3evect2, pca3, b3evect3, b3mean), overwrite=True) pb.wait(), pg.wait(), pr.wait() try: pb.terminate(), pg.terminate(), pr.terminate() except: "" # Cleanup grass.run_command('g.remove', flags='f', quiet=True, type='raster', name='%s,%s,%s' % (panmatch1, panmatch2, panmatch3))
def main(): tavg = options["tavg"] tmin = options["tmin"] tmax = options["tmax"] prec = options["precipitation"] outpre = options["output"] tinscale = options["tinscale"] toutscale = options["toutscale"] workers = int(options["workers"]) quartals = int(options["quartals"]) qstep = 12 / quartals mapset = grass.gisenv()["MAPSET"] # count input maps if len(tmin.split(",")) != 12: grass.fatal(_("12 maps with minimum temperatures are required")) if len(tmax.split(",")) != 12: grass.fatal(_("12 maps with maximum temperatures are required")) if tavg and len(tavg.split(",")) != 12: grass.fatal(_("12 maps with average temperatures are required")) if prec: if len(prec.split(",")) != 12: grass.fatal(_("12 maps with precipitation are required")) tinscale = int(tinscale) if tinscale <= 0: grass.fatal(_("Input temperature scale must be positive")) toutscale = int(toutscale) if toutscale <= 0: grass.fatal(_("Output temperature scale must be positive")) pid = os.getpid() # all temporary raster maps must follow this naming pattern tmp_pattern = "%s.*.%d" % (outpre, pid) tminl = tmin.split(",") tmaxl = tmax.split(",") ps = {} if not tavg: # calculate monthly averages from min and max grass.message(_("Calculating monthly averages from min and max")) e = "$ta = ($tmax + $tmin) / 2.0" if workers > 1: for i in range(0, 12, workers): jend = 12 - i if jend > workers: jend = workers for j in range(jend): ta = "%s.tavg%02d.%d" % (outpre, (i + j + 1), pid) ps[j] = grass.mapcalc_start( e, ta=ta, tmax=tmaxl[i + j], tmin=tminl[i + j] ) for j in range(jend): ps[j].wait() else: for i in range(12): ta = "%s.tavg%02d.%d" % (outpre, (i + 1), pid) grass.mapcalc(e, ta=ta, tmax=tmaxl[i], tmin=tminl[i]) tavg = grass.read_command( "g.list", quiet=True, type="raster", pattern="%s.tavg??.%d" % (outpre, pid), separator=",", mapset=".", ) tavg = tavg.strip("\n") tavgl = tavg.split(",") # BIO1 = Annual Mean Temperature grass.message(_("BIO1 = Annual Mean Temperature ...")) output = outpre + "bio01." + str(pid) grass.run_command("r.series", input=tavg, output=output, method="average") grass.mapcalc( "$bio = round(double($oscale) * $input / $iscale)", bio=outpre + "bio01", oscale=toutscale, input=output, iscale=tinscale, ) grass.run_command( "r.support", map=outpre + "bio01", description="BIOCLIM01: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio01", history=os.environ["CMDLINE"]) grass.run_command("g.remove", flags="f", type="raster", name=output, quiet=True) # BIO2 = Mean Diurnal Range (Mean of monthly (max temp - min temp)) grass.message(_("BIO2 = Mean Diurnal Range ...")) e = "$tr = $tmax - $tmin" if workers > 1: for i in range(0, 12, workers): jend = 12 - i if jend > workers: jend = workers for j in range(jend): tr = "%s.tr%02d.%d" % (outpre, (i + j + 1), pid) ps[j] = grass.mapcalc_start( e, tr=tr, tmax=tmaxl[i + j], tmin=tminl[i + j] ) for j in range(jend): ps[j].wait() else: for i in range(12): tr = "%s.tr%02d.%d" % (outpre, (i + 1), pid) grass.mapcalc(e, tr=tr, tmax=tmaxl[i], tmin=tminl[i]) tr = grass.read_command( "g.list", quiet=True, type="raster", pattern="%s.tr??.%d" % (outpre, pid), separator=",", mapset=".", ) tr = tr.strip("\n") output = outpre + "bio02." + str(pid) grass.run_command("r.series", input=tr, output=output, method="average") grass.mapcalc( "$bio = round(double($oscale) * $input / $iscale)", bio=outpre + "bio02", oscale=toutscale, input=output, iscale=tinscale, ) grass.run_command( "r.support", map=outpre + "bio02", description="BIOCLIM02: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio02", history=os.environ["CMDLINE"]) grass.run_command("g.remove", flags="f", type="raster", name=output, quiet=True) grass.run_command( "g.remove", flags="f", type="raster", pattern="%s.tr??.%d" % (outpre, pid), quiet=True, ) # BIO4 = Temperature Seasonality (standard deviation * 100) grass.message(_("BIO4 = Temperature Seasonality ...")) output = outpre + "bio04." + str(pid) grass.run_command("r.series", input=tavg, output=output, method="stddev") grass.mapcalc( "$bio = round(100.0 * $biotmp / $iscale * $oscale)", bio=outpre + "bio04", biotmp=output, iscale=tinscale, oscale=toutscale, ) grass.run_command( "r.support", map=outpre + "bio04", description="BIOCLIM04: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio04", history=os.environ["CMDLINE"]) grass.run_command("g.remove", flags="f", type="raster", name=output, quiet=True) # BIO5 = Max Temperature of Warmest Month grass.message(_("BIO5 = Max Temperature of Warmest Month ...")) output = outpre + "bio05." + str(pid) grass.run_command("r.series", input=tmax, output=output, method="maximum") grass.mapcalc( "$bio = round(double($oscale) * $biotmp / $iscale)", bio=outpre + "bio05", oscale=toutscale, iscale=tinscale, biotmp=output, ) grass.run_command( "r.support", map=outpre + "bio05", description="BIOCLIM05: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio05", history=os.environ["CMDLINE"]) grass.run_command("g.remove", flags="f", type="raster", name=output, quiet=True) # BIO6 = Min Temperature of Coldest Month grass.message(_("BIO6 = Min Temperature of Coldest Month ...")) output = outpre + "bio06." + str(pid) grass.run_command("r.series", input=tmin, output=output, method="minimum") grass.mapcalc( "$bio = round(double($oscale) * $biotmp / $iscale)", bio=outpre + "bio06", oscale=toutscale, biotmp=output, iscale=tinscale, ) grass.run_command( "r.support", map=outpre + "bio06", description="BIOCLIM06: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio06", history=os.environ["CMDLINE"]) grass.run_command("g.remove", flags="f", type="raster", name=output, quiet=True) # BIO7 = Temperature Annual Range (BIO5-BIO6) grass.message(_("BIO7 = Temperature Annual Range ...")) grass.mapcalc( "$bio = $bio5 - $bio6", bio=outpre + "bio07", bio5=outpre + "bio05", bio6=outpre + "bio06", ) grass.run_command( "r.support", map=outpre + "bio07", description="BIOCLIM07: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio07", history=os.environ["CMDLINE"]) # BIO3 = Isothermality (BIO2/BIO7) (* 100) grass.message(_("BIO3 = Isothermality (BIO2/BIO7) ...")) grass.mapcalc( "$bio = round(100.0 * $bio2 / $bio7)", bio=outpre + "bio03", bio2=outpre + "bio02", bio7=outpre + "bio07", ) grass.run_command( "r.support", map=outpre + "bio03", description="BIOCLIM03: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio03", history=os.environ["CMDLINE"]) # mean of mean for each quarter year grass.message(_("Mean temperature for each quarter year ...")) for i in range(quartals): tavgq = "%s.tavgq.%02d.%d" % (outpre, i, pid) m1 = int(i * qstep) m2 = m1 + 1 if m2 > 11: m2 = m2 - 12 m3 = m1 + 2 if m3 > 11: m3 = m3 - 12 grass.run_command( "r.series", input="%s,%s,%s" % (tavgl[m1], tavgl[m2], tavgl[m3]), output=tavgq, method="average", ) # BIO10 = Mean Temperature of Warmest Quarter # BIO11 = Mean Temperature of Coldest Quarter grass.message(_("BIO10 = Mean Temperature of Warmest Quarter,")) grass.message(_("BIO11 = Mean Temperature of Coldest Quarter ...")) tavgq = grass.read_command( "g.list", quiet=True, type="raster", pattern="%s.tavgq.??.%d" % (outpre, pid), separator=",", mapset=".", ) tavgq = tavgq.strip("\n") bio10 = outpre + "bio10." + str(pid) bio11 = outpre + "bio11." + str(pid) grass.run_command( "r.series", input=tavgq, output="%s,%s" % (bio10, bio11), method="maximum,minimum", ) grass.mapcalc( "$bio = round(double($oscale) * $biotmp / $iscale)", bio=outpre + "bio10", oscale=toutscale, biotmp=bio10, iscale=tinscale, ) grass.run_command( "r.support", map=outpre + "bio10", description="BIOCLIM10: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio10", history=os.environ["CMDLINE"]) grass.mapcalc( "$bio = round(double($oscale) * $biotmp / $iscale)", bio=outpre + "bio11", oscale=toutscale, biotmp=bio11, iscale=tinscale, ) grass.run_command( "r.support", map=outpre + "bio11", description="BIOCLIM11: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio11", history=os.environ["CMDLINE"]) grass.run_command( "g.remove", flags="f", type="raster", name="%s,%s" % (bio10, bio11), quiet=True ) if not prec: grass.run_command( "g.remove", flags="f", type="raster", pattern=tmp_pattern, quiet=True ) sys.exit(1) precl = prec.split(",") # sum for each quarter year grass.message(_("Precipitation for each quarter year ...")) for i in range(quartals): precq = "%s.precq.%02d.%d" % (outpre, i + 1, pid) m1 = int(i * qstep) m2 = m1 + 1 if m2 > 11: m2 = m2 - 12 m3 = m1 + 2 if m3 > 11: m3 = m3 - 12 grass.run_command( "r.series", input="%s,%s,%s" % (precl[m1], precl[m2], precl[m3]), output=precq, method="sum", ) precq = grass.read_command( "g.list", quiet=True, type="raster", pattern="%s.precq.??.%d" % (outpre, pid), separator=",", mapset=".", ) precq = precq.strip("\n") # warmest and coldest quarter warmestq = "%s.warmestq.%d" % (outpre, pid) coldestq = "%s.coldestq.%d" % (outpre, pid) grass.run_command( "r.series", input=tavgq, output="%s,%s" % (warmestq, coldestq), method="max_raster,min_raster", ) tavgql = tavgq.split(",") # wettest and driest quarter wettestq = "%s.wettestq.%d" % (outpre, pid) driestq = "%s.driestq.%d" % (outpre, pid) grass.run_command( "r.series", input=precq, output="%s,%s" % (wettestq, driestq), method="max_raster,min_raster", ) # BIO8 = Mean Temperature of Wettest Quarter grass.message(_("BIO8 = Mean Temperature of Wettest Quarter ...")) if quartals == 4: grass.mapcalc( "$bio = round(if($wettestq == 0, $tavgq0, \ if($wettestq == 1, $tavgq1, \ if($wettestq == 2, $tavgq2, \ if($wettestq == 3, $tavgq3, null())))) \ * $oscale / $iscale)", bio=outpre + "bio08", wettestq=wettestq, tavgq0=tavgql[0], tavgq1=tavgql[1], tavgq2=tavgql[2], tavgq3=tavgql[3], oscale=toutscale, iscale=tinscale, ) else: # quartals == 12 grass.mapcalc( "$bio = round(if($wettestq == 0, $tavgq0, \ if($wettestq == 1, $tavgq1, \ if($wettestq == 2, $tavgq2, \ if($wettestq == 3, $tavgq3, \ if($wettestq == 4, $tavgq4, \ if($wettestq == 5, $tavgq5, \ if($wettestq == 6, $tavgq6, \ if($wettestq == 7, $tavgq7, \ if($wettestq == 8, $tavgq8, \ if($wettestq == 9, $tavgq9, \ if($wettestq == 10, $tavgq10, \ if($wettestq == 11, $tavgq11, null())))))))))))) \ * $oscale / $iscale)", bio=outpre + "bio08", wettestq=wettestq, tavgq0=tavgql[0], tavgq1=tavgql[1], tavgq2=tavgql[2], tavgq3=tavgql[3], tavgq4=tavgql[4], tavgq5=tavgql[5], tavgq6=tavgql[6], tavgq7=tavgql[7], tavgq8=tavgql[8], tavgq9=tavgql[9], tavgq10=tavgql[10], tavgq11=tavgql[11], oscale=toutscale, iscale=tinscale, ) grass.run_command( "r.support", map=outpre + "bio08", description="BIOCLIM08: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio08", history=os.environ["CMDLINE"]) # BIO9 = Mean Temperature of Driest Quarter grass.message(_("BIO9 = Mean Temperature of Driest Quarter ...")) if quartals == 4: grass.mapcalc( "$bio = round(if($driestq == 0, $tavgq0, \ if($driestq == 1, $tavgq1, \ if($driestq == 2, $tavgq2, \ if($driestq == 3, $tavgq3, null())))) \ * $oscale / $iscale)", bio=outpre + "bio09", driestq=driestq, tavgq0=tavgql[0], tavgq1=tavgql[1], tavgq2=tavgql[2], tavgq3=tavgql[3], oscale=toutscale, iscale=tinscale, ) else: # quartals == 12 grass.mapcalc( "$bio = round(if($driestq == 0, $tavgq0, \ if($driestq == 1, $tavgq1, \ if($driestq == 2, $tavgq2, \ if($driestq == 3, $tavgq3, \ if($driestq == 4, $tavgq4, \ if($driestq == 5, $tavgq5, \ if($driestq == 6, $tavgq6, \ if($driestq == 7, $tavgq7, \ if($driestq == 8, $tavgq8, \ if($driestq == 9, $tavgq9, \ if($driestq == 10, $tavgq10, \ if($driestq == 11, $tavgq11, null())))))))))))) \ * $oscale / $iscale)", bio=outpre + "bio09", driestq=driestq, tavgq0=tavgql[0], tavgq1=tavgql[1], tavgq2=tavgql[2], tavgq3=tavgql[3], tavgq4=tavgql[4], tavgq5=tavgql[5], tavgq6=tavgql[6], tavgq7=tavgql[7], tavgq8=tavgql[8], tavgq9=tavgql[9], tavgq10=tavgql[10], tavgq11=tavgql[11], oscale=toutscale, iscale=tinscale, ) grass.run_command( "r.support", map=outpre + "bio09", description="BIOCLIM09: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio09", history=os.environ["CMDLINE"]) # BIO12 = Annual Precipitation grass.message(_("BIO12 = Annual Precipitation ...")) output = outpre + "bio12" grass.run_command("r.series", input=prec, output=output, method="sum") grass.run_command( "r.support", map=outpre + "bio12", description="BIOCLIM12: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio12", history=os.environ["CMDLINE"]) # BIO13 = Precipitation of Wettest Month # BIO14 = Precipitation of Driest Month grass.message(_("BIO13 = Precipitation of Wettest Month,")) grass.message(_("BIO14 = Precipitation of Driest Month ...")) bio13 = outpre + "bio13" bio14 = outpre + "bio14" grass.run_command( "r.series", input=prec, output="%s,%s" % (bio13, bio14), method="maximum,minimum", ) grass.run_command( "r.support", map=outpre + "bio13", description="BIOCLIM13: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio13", history=os.environ["CMDLINE"]) grass.run_command( "r.support", map=outpre + "bio14", description="BIOCLIM14: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio14", history=os.environ["CMDLINE"]) # BIO15 = Precipitation Seasonality (Coefficient of Variation) grass.message(_("BIO15 = Precipitation Seasonality ...")) precavg = "%s.precavg.%d" % (outpre, pid) precstddev = "%s.precstddev.%d" % (outpre, pid) grass.run_command( "r.series", input=prec, output="%s,%s" % (precavg, precstddev), method="average,stddev", ) grass.mapcalc( "$bio = if($precavg == 0, 0, round(100.0 * $precstddev / $precavg))", bio=outpre + "bio15", precstddev=precstddev, precavg=precavg, ) grass.run_command( "r.support", map=outpre + "bio15", description="BIOCLIM15: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio15", history=os.environ["CMDLINE"]) # BIO16 = Precipitation of Wettest Quarter # BIO17 = Precipitation of Driest Quarter grass.message(_("BIO16 = Precipitation of Wettest Quarter,")) grass.message(_("BIO17 = Precipitation of Driest Quarter ...")) bio16 = outpre + "bio16" bio17 = outpre + "bio17" grass.run_command( "r.series", input=precq, output="%s,%s" % (bio16, bio17), method="maximum,minimum", ) grass.run_command( "r.support", map=outpre + "bio16", description="BIOCLIM16: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio16", history=os.environ["CMDLINE"]) grass.run_command( "r.support", map=outpre + "bio17", description="BIOCLIM17: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio17", history=os.environ["CMDLINE"]) precql = precq.split(",") # BIO18 = Precipitation of Warmest Quarter grass.message(_("BIO18 = Precipitation of Warmest Quarter ...")) if quartals == 4: grass.mapcalc( "$bio = round(if($warmestq == 0, $precq0, \ if($warmestq == 1, $precq1, \ if($warmestq == 2, $precq2, \ if($warmestq == 3, $precq3, null())))))", bio=outpre + "bio18", warmestq=warmestq, precq0=precql[0], precq1=precql[1], precq2=precql[2], precq3=precql[3], ) else: # quartals == 12 grass.mapcalc( "$bio = round(if($warmestq == 0, $precq0, \ if($warmestq == 1, $precq1, \ if($warmestq == 2, $precq2, \ if($warmestq == 3, $precq3, \ if($warmestq == 4, $precq4, \ if($warmestq == 5, $precq5, \ if($warmestq == 6, $precq6, \ if($warmestq == 7, $precq7, \ if($warmestq == 8, $precq8, \ if($warmestq == 9, $precq9, \ if($warmestq == 10, $precq10, \ if($warmestq == 11, $precq11, null())))))))))))))", bio=outpre + "bio18", warmestq=warmestq, precq0=precql[0], precq1=precql[1], precq2=precql[2], precq3=precql[3], precq4=precql[4], precq5=precql[5], precq6=precql[6], precq7=precql[7], precq8=precql[8], precq9=precql[9], precq10=precql[10], precq11=precql[11], ) grass.run_command( "r.support", map=outpre + "bio18", description="BIOCLIM18: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio18", history=os.environ["CMDLINE"]) # BIO19 = Precipitation of Coldest Quarter grass.message(_("BIO19 = Precipitation of Coldest Quarter ...")) if quartals == 4: grass.mapcalc( "$bio = round(if($coldestq == 0, $precq0, \ if($coldestq == 1, $precq1, \ if($coldestq == 2, $precq2, \ if($coldestq == 3, $precq3, null())))))", bio=outpre + "bio19", coldestq=coldestq, precq0=precql[0], precq1=precql[1], precq2=precql[2], precq3=precql[3], ) else: # quartals == 12 grass.mapcalc( "$bio = round(if($coldestq == 0, $precq0, \ if($coldestq == 1, $precq1, \ if($coldestq == 2, $precq2, \ if($coldestq == 3, $precq3, \ if($coldestq == 4, $precq4, \ if($coldestq == 5, $precq5, \ if($coldestq == 6, $precq6, \ if($coldestq == 7, $precq7, \ if($coldestq == 8, $precq8, \ if($coldestq == 9, $precq9, \ if($coldestq == 10, $precq10, \ if($coldestq == 11, $precq11, null())))))))))))))", bio=outpre + "bio19", coldestq=coldestq, precq0=precql[0], precq1=precql[1], precq2=precql[2], precq3=precql[3], precq4=precql[4], precq5=precql[5], precq6=precql[6], precq7=precql[7], precq8=precql[8], precq9=precql[9], precq10=precql[10], precq11=precql[11], ) grass.run_command( "r.support", map=outpre + "bio19", description="BIOCLIM19: Generated by r.bioclim", ) grass.run_command("r.support", map=outpre + "bio19", history=os.environ["CMDLINE"]) grass.run_command( "g.remove", flags="f", type="raster", pattern=tmp_pattern, quiet=True )
def landscapeEvol(m, o, p, q, res, s, f): """ Now define "landscapeEvol", our main block of code, here defined because of the way g.parser needs to be called with python codes for grass (see below) m = last iteration number, o = iteration number, p = prefx, q = statsout, res = resolution of input elev map, s = master list of lists of climate data f = name of text file to write stats to """ # Get the process id to tag any temporary maps we make for easy clean up in the loop pid = os.getpid() # Get variables from user input elev = options["elev"] transp_eq = options["transp_eq"] initbdrk = options["initbdrk"] outdem = options["outdem"] outsoil = options["outsoil"] sdensity = options["sdensity"] K = options["k"] P = options["p"] C = options["c"] exp_m = options["exp_m"].split(",") exp_n = options["exp_n"].split(",") flowcontrib = options["flowcontrib"] convergence = options["convergence"] manningn = options["manningn"] p = options["prefx"] # Make some variables for temporary map names aspect = "%saspect%04d" % (p, o) flowacc = "%sflowacc%04d" % (p, o) flowdir = "%sflowdir%04d" % (p, o) flacclargenum = "%sflowacclargenum%04d" % (p, o) pc = "%spc%04d" % (p, o) tc = "%stc%04d" % (p, o) qsx = "%sQsx_%04d" % (p, o) qsy = "%sQsy_%04d" % (p, o) qsxdx = "%sDelta_Qsx_%04d" % (p, o) qsydy = "%sDelta_Qsy_%04d" % (p, o) rainexcess = "%s_rainfall_excess_map_%04d" % (p, o) tmpnetchange = "tmp%s_netchange%04d" % (pid, o) tmp90qle = "tmp%s_netchange_90qle%04d" % (pid, o) tmp10qle = "tmp%s_netchange_10qle%04d" % (pid, o) tmperosion = "tmp%s_erosion%04d" % (pid, o) tmpdep = "tmp%s_deposition%04d" % (pid, o) # List of temp maps to remove unless user wants to keep them all mapstoremove = [ aspect, flowacc, flowdir, flacclargenum, pc, tc, rainexcess, tmpnetchange, tmp10qle, tmp90qle, tmperosion, tmpdep, ] # Variables that come in as a list of lists and can update with each iteration # masterlist = [R2,rain2,stormlength2,storms2,stormi2] R = s[0][m] rain = s[1][m] stormtimet = float(s[2][m]) * 3600.00 # Convert storm length to seconds storms = s[3][m] stormi = (float(s[4][m]) * stormtimet ) # Calculate the length of time at peak flow depth # Maps that will update at each iteration to record state of landscape old_dem = "%s%s%04d" % (p, outdem, m) old_soil = "%s%s%04d" % (p, outsoil, m) slope = "%sslope%04d" % (p, o) netchange = "%sED_rate%04d" % (p, o) new_dem = "%s%s%04d" % (p, outdem, o) new_soil = "%s%s%04d" % (p, outsoil, o) # If first iteration, use input maps. Otherwise, use maps generated from # previous iterations if o == 1: grass.run_command("g.copy", raster=elev + "," + old_dem, quiet=True) # Grab the number of cells in the starting DEM numcells = grass.parse_command( "r.univar", flags="g", map=old_dem, )["n"] # Calculate soil as difference between surface and bedrock grass.mapcalc( "${old_soil}=${old_dem}-${initbdrk}", overwrite=True, quiet=True, old_soil=old_soil, old_dem=old_dem, initbdrk=initbdrk, ) grass.message("\n*************************\n" + "Iteration %s -- " % o + "step 1/6: calculating slope\n" + "*************************\n") grass.run_command("r.slope.aspect", quiet=True, elevation=old_dem, aspect=aspect, slope=slope) grass.message("\n*************************\n" + "Iteration %s -- " % o + "step 2/6: calculating accumulated flow depths\n" + "*************************\n") # Make map of rainfall excess (proportion each cell contributes to # downstrem flow) from flowcontrib. Note that if flowcontrib is a map, we # are just making a copy of it. This map is a percentage, but has to be # scaled from 0-100, because r.watershed will only allow values greater # than 1 as input in it's 'flow' variable. This creates a flow accumulation # map with large numbers, which will be divided by 100 after it is # made, bringing the values back down to what they should be. grass.mapcalc( "${rainexcess}=int(${flowcontrib})", quiet=True, rainexcess=rainexcess, flowcontrib=flowcontrib, ) grass.run_command( "r.watershed", quiet=True, flags="a", elevation=old_dem, threshold=numcells, flow=rainexcess, accumulation=flacclargenum, drainage=flowdir, convergence=convergence, ) grass.mapcalc( "${flowacc}=${flacclargenum}/100", quiet=True, flowacc=flowacc, flacclargenum=flacclargenum, ) # again, do something different if we are only making an evaluation of cutoffs if flags["p"] is True: samplePoints(old_dem, aspect, slope, pc, tc, flowacc, p) grass.message("\n*************************\n" + "Iteration %s -- " % o + "step 3/6: calculating sediment transport rates \n" + "*************************\n") # Figure out which transport equation to run. All equations estimate transport capacity as kg/m.s. Note that we integrate the step to calculate the Tc in the east and west directions, to simplify the divergence calculations in the next step (i.e., to reduce the overall number of mapcalc statements and intermediate maps) if transp_eq == "StreamPower": # Stream power equation: Tc=Kt*gw*1/N*h^m*B^n # where: h = depth of flow = (i*A)/(0.595*t) # and: B = change in slope # GIS Implementation: # Tc=K*C*P*gw*(1/N)*((i*A)/(0.595*t))^m*(tan(S)^n) # Variables: # Tc=Transport Capacity [kg/meters.second] # K*C*P=Kt=mitigating effects of soil type, vegetation cover, and landuse practices. [unitless] # gw=Hydrostatic pressure of water 9810 [kg/m2.second] # N=Manning's coefficient ~0.3-0.6 for different types of stream channesl [unitless] # i=rainfall intentsity [m/rainfall event] # A=uplsope accumulated area per contour (cell) width [m2/m] = [m] # 0.595 = constant for time-lagged peak flow (assumes symmetrical unit hydrograph) # t=length of rainfall event [seconds] # S=topographic slope [degrees] # m = transport coefficient for upslope area [unitless] # n transport coefficient for slope [unitless] # SLOPE VERSISON e1 = """${qsx}=${K}*${C}*${P} * exp(${manningn}, -1) * 9810. * \ exp((((${rain}/1000.)*${flowacc})/(0.595*${stormtimet})), \ graph(${flowacc}, ${exp_m1a},${exp_m1b}, ${exp_m2a},${exp_m2b}) ) * \ exp(tan(${slope}), graph(${slope}, ${exp_n1a},${exp_n1b}, ${exp_n2a},${exp_n2b}))\ * cos(${aspect})""" e2 = """${qsy}=${K}*${C}*${P} * exp(${manningn}, -1) * 9810. * \ exp((((${rain}/1000.)*${flowacc})/(0.595*${stormtimet})), \ graph(${flowacc}, ${exp_m1a},${exp_m1b}, ${exp_m2a},${exp_m2b})) * \ exp(tan(${slope}), graph(${slope}, ${exp_n1a},${exp_n1b}, ${exp_n2a},${exp_n2b}))\ * sin(${aspect})""" elif transp_eq == "ShearStress": # Shear stress equation: Tc=Kt*tau^m (critical shear stress assumed to be 0) # where: tau = shear stress = gw*h*B # and: S = change in slope # and: h = depth of flow = (i*A)/(0.595*t) # GIS Implmentation: # Tc=K*C*P*(gw*((i*A)/(0.595*t)*(tan(S))))^m # Variables: # Tc=Transport Capacity [kg/meters.second] # K*C*P=Kt=mitigating effects of soil type, vegetation cover, and landuse practices. [unitless] # gw=Hydrostatic pressure of water 9810 [kg/m2.second] # N=Manning's coefficient ~0.3-0.6 for different types of stream channesl [unitless] # i=rainfall intentsity [m/rainfall event] # A=uplsope accumulated area per contour (cell) width [m2/m] = [m] # 0.595 = constant for time-lagged peak flow (assumes symmetrical unit hydrograph) # t=length of rainfall event [seconds] # B=topographic slope [degrees] # m = transport coefficient (here assumed to be scaled to upslope area) [unitless] e1 = """${qsx}=(${K}*${C}*${P} * \ exp(9810.*(((${rain}/1000)*${flowacc})/(0.595*${stormtimet}))*tan(${slope}), \ graph(${flowacc}, ${exp_n1a},${exp_n1b}, ${exp_n2a},${exp_n2b}))) * \ cos(${aspect})""" e2 = """${qsy}=(${K}*${C}*${P} * \ exp(9810.*(((${rain}/1000)*${flowacc})/(0.595*${stormtimet}))*tan(${slope}), \ graph(${flowacc}, ${exp_n1a},${exp_n1b}, ${exp_n2a},${exp_n2b}) )) * \ sin(${aspect})""" elif transp_eq == "USPED": # USPED equation: Tc=R*K*C*P*A^m*B^n # where: B = change in slope # GIS Implementation: # Tc=R*K*C*P*A^m*tan(S)^n # Variables: # Tc=Transport Capacity [kg/meters.second] # R=Rainfall intensivity factor [MJ.mm/ha.h.yr] # A=uplsope accumulated area per contour (cell) width [m2/m] = [m] # S=topographic slope [degrees] # m = transport coefficient for upslope area [unitless] # n transport coefficient for slope [unitless] e1 = """${qsx}=((${R}*${K}*${C}*${P}*\ exp((${flowacc}*${res}),graph(${flowacc}, ${exp_m1a},${exp_m1b}, ${exp_m2a},${exp_m2b}))*\ exp(sin(${slope}), graph(${slope}, ${exp_n1a},${exp_n1b}, ${exp_n2a},${exp_n2b})))\ * cos(${aspect}))""" e2 = """${qsy}=((${R}*${K}*${C}*${P}*\ exp((${flowacc}*${res}),graph(${flowacc}, ${exp_m1a},${exp_m1b}, ${exp_m2a},${exp_m2b}))*\ exp(sin(${slope}), graph(${slope}, ${exp_n1a},${exp_n1b}, ${exp_n2a},${exp_n2b})))\ * sin(${aspect}))""" else: grass.fatal( 'You have entered a non-viable tranport equation name. Please ensure option "transp_eq" is one of "StreamPower," "ShearStress," or "USPED."' ) # Actually do the mapcalc statement for chosen transport equation x = grass.mapcalc_start( e1, quiet=True, qsx=qsx, slope=slope, aspect=aspect, R=R, K=K, C=C, P=P, res=res, flowacc=flowacc, rain=rain, stormtimet=stormtimet, stormi=stormi, exp_m1a=exp_m[0], exp_m1b=exp_m[1], exp_m2a=exp_m[2], exp_m2b=exp_m[3], exp_n1a=exp_n[0], exp_n1b=exp_n[1], exp_n2a=exp_n[2], exp_n2b=exp_n[3], manningn=manningn, ) y = grass.mapcalc_start( e2, quiet=True, qsy=qsy, slope=slope, aspect=aspect, R=R, K=K, C=C, P=P, res=res, flowacc=flowacc, rain=rain, stormtimet=stormtimet, stormi=stormi, exp_m1a=exp_m[0], exp_m1b=exp_m[1], exp_m2a=exp_m[2], exp_m2b=exp_m[3], exp_n1a=exp_n[0], exp_n1b=exp_n[1], exp_n2a=exp_n[2], exp_n2b=exp_n[3], manningn=manningn, ) x.wait() y.wait() grass.message( "\n*************************\n" + "Iteration %s -- " % o + "step 4/6: calculating divergence/difference of sediment transport and the actual amount of erosion or deposition in vertical meters/cell/year\n" + "*************************\n") # Taking divergence of transport capacity Tc converts kg/m.s to kg/m2.s sax = grass.start_command("r.slope.aspect", quiet=True, elevation=qsx, dx=qsxdx) say = grass.start_command("r.slope.aspect", quiet=True, elevation=qsy, dy=qsydy) sax.wait() say.wait() # Now convert output of divergence to calculated erosion and deposition in # vertical meters of elevation change. Add back the divergence in EW and NS # directions. Units are in kg/m2.s, so start by dividing by soil density # [kg/m3] to get m/s elevation change (for USPED that is m/year already, # but not for the shear stress or stream power). # For shear stress and stream power, also multiply by the number # of seconds at peak flow depth (stormi) and then by the number of erosive # storms per year to get m/year elevation change. if transp_eq == "USPED": ed = """${netchange}=((${qsxdx}+${qsydy})/${sdensity})""" grass.mapcalc( ed, quiet=True, netchange=tmpnetchange, qsxdx=qsxdx, qsydy=qsydy, sdensity=sdensity, ) else: ed = """${netchange}=((${qsxdx}+${qsydy})/${sdensity})*${stormi}*${storms}""" grass.mapcalc( ed, quiet=True, netchange=tmpnetchange, qsxdx=qsxdx, qsydy=qsydy, sdensity=sdensity, stormi=stormi, storms=storms, ) # Apply smoothing to the output to remove some spikes. Map will only be smoothed for values above the 90th quantile and below the 10th quantile (i.e., only extreme values will be smoothed) if flags["m"] is True: a = grass.start_command( "r.neighbors", quiet=True, input=tmpnetchange, output=tmp10qle, method="quantile", size=5, quantile=0.1, ) b = grass.start_command( "r.neighbors", quiet=True, input=tmpnetchange, output=tmp90qle, method="quantile", size=5, quantile=0.9, ) a.wait() b.wait() smoother = """${netchange}=if(${tmpnetchange}<${tmp10qle}, ${tmp10qle}, if(${tmpnetchange}>${tmp90qle}, ${tmp90qle}, ${tmpnetchange}))""" grass.mapcalc( smoother, quiet=True, netchange=netchange, tmpnetchange=tmpnetchange, tmp90qle=tmp90qle, tmp10qle=tmp10qle, ) else: grass.run_command("g.rename", quiet=True, raster=tmpnetchange + "," + netchange) grass.message( "\n*************************\n" + "Iteration %s -- " % o + "step 5/6: calculating terrain evolution and new soil depths\n" + " *************************\n") # Compute elevation changes: addition of ED change to old DEM. # This mapcalc statement first checks the amount of erodable soil in a given # cell against the amount of erosion calculated, and keeps the cell from # eroding past this amount (if there is soil, then if the amount of erosion # is more than the amount of soil, just remove all the soil and stop, else # remove the amount of caclulated erosion. It also runs an error catch that # checks to make sure that soil depth is not negative (could happen, I # suppose), and if it is, corrects it). Finally, do patch-job to catch the # shrinking edge problem (the edge cells have no upstream cell, so get # turned null in the calculations in step 4) e = """${new_dem} = eval(x=if(${old_soil} > 0.0 && (-1*${netchange}) <= ${old_soil}, ${netchange}, \ if((-1*${netchange}) > ${old_soil}, (-1*${old_soil}), 0)), \ y=(${old_dem} + x), if(isnull(y), ${old_dem}, y))""" grass.mapcalc( e, quiet=True, new_dem=new_dem, old_soil=old_soil, old_dem=old_dem, netchange=netchange, ) # Calculate new soil depths by subtracting initial bedrock elevations from # the new DEM. e = """${new_soil} = if((${new_dem} - ${initbdrk}) < 0, 0, (${new_dem} - ${initbdrk}))""" grass.mapcalc(e, quiet=True, new_soil=new_soil, new_dem=new_dem, initbdrk=initbdrk) # Set colors for elevation, soil, and ED maps grass.run_command("r.colors", quiet=True, map=new_dem, color="srtm") sdcolors = [ "100% 0:249:47", "20% 78:151:211", "6% 194:84:171", "0% 227:174:217" ] sdc = grass.feed_command("r.colors", quiet=True, map=new_soil, rules="-") sdc.stdin.write("\n".join(sdcolors)) sdc.stdin.close() nccolors = [ "100 127:0:255", "1 0:0:255", ".1 0:255:0", "0.001 152:251:152", "0 250:250:250", "-0.001 255:255:50", "-.1 255:127:0", "-1 255:0:0", "-100 127:0:255", ] ncc = grass.feed_command("r.colors", quiet=True, map=netchange, rules="-") ncc.stdin.write("\n".join(nccolors)) ncc.stdin.close() sdc.wait() ncc.wait() grass.message("\n*************************\n" + "Iteration %s -- " % o + "step 6/6: writing stats to output file\n" + "*************************\n") # Make some temp maps of just erosion rate and just deposition rates e = """${tmperosion}=if(${netchange} < -0, ${netchange}, null())""" ero1 = grass.mapcalc_start(e, quiet=True, tmperosion=tmperosion, netchange=netchange) e = """${tmpdep}=if(${netchange} > 0, ${netchange}, null())""" dep1 = grass.mapcalc_start(e, quiet=True, tmpdep=tmpdep, netchange=netchange) ero1.wait() dep1.wait() # Grab the stats from these temp files and save them to dictionaries erosstats = grass.parse_command("r.univar", flags="ge", percentile="1", map=tmperosion) depostats = grass.parse_command("r.univar", flags="ge", percentile="99", map=tmpdep) # Finish gathering stats (just need the soil depth stats now) soilstats = grass.parse_command("r.univar", flags="ge", map=new_soil, percentile="99") # Write stats to a new line in the stats file # HEADER of the file should be: ',,Mean Values,,,,Standard Deviations,,,,Totals,,,Additional Stats\nIteration,,Mean Erosion,Mean Deposition,Mean Soil Depth,,Standard Deviation Erosion,Standard Deviation Deposition,Standard Deviation Soil Depth,,Total Sediment Eroded,Total Sediment Deposited,,Minimum Erosion,First Quartile Erosion,Median Erosion,Third Quartile Erosion,Maximum Erosion,Original Un-smoothed Maximum Erosion,,Minimum Deposition,First Quartile Deposition,Median Deposition,Third Quartile Deposition,Maximum Deposition,Original Un-smoothed Maximum Deposition,,Minimum Soil Depth,First Quartile Soil Depth,Median Soil Depth,Third Quartile Soil Depth,Maximum Soil Depth' f.write("\n%s" % o + ",," + erosstats["mean"] + "," + depostats["mean"] + "," + soilstats["mean"] + ",," + erosstats["stddev"] + "," + depostats["stddev"] + "," + soilstats["stddev"] + ",," + erosstats["sum"] + "," + depostats["sum"] + ",," + erosstats["max"] + "," + erosstats["third_quartile"] + "," + erosstats["median"] + "," + erosstats["first_quartile"] + "," + erosstats["min"] + "," + depostats["min"] + "," + depostats["first_quartile"] + "," + depostats["median"] + "," + depostats["third_quartile"] + "," + depostats["max"] + "," + soilstats["min"] + "," + soilstats["first_quartile"] + "," + soilstats["median"] + "," + soilstats["third_quartile"] + "," + soilstats["max"]) # Cleanup temporary files if flags["k"] is True: grass.message("\nTemporary maps will NOT be deleted!!!!\n") else: grass.message("\nCleaning up temporary maps...\n\n") # Check all the flag options, and add to list of maps to delete if flags["s"] is True: grass.message("Keeping Slope map.") else: mapstoremove.append(slope) if flags["d"] is True: grass.message("Not keeping Soil Depth map.") mapstoremove.append(old_soil) # Check if this is the last year and remove the "new-soil" map too if o == int(options["number"]): mapstoremove.append(new_soil) else: # Check if this is the first year, and if so, remove the temporary initial soil depths map if o <= 1: grass.message(("%s%s%04d" % (p, outsoil, m))) mapstoremove.append("%s%s%04d" % (p, outsoil, m)) if flags["e"] is True: grass.message( "Keeping delta Transport Capacity (divergence) maps.") else: mapstoremove.extend([qsxdx, qsydy]) if flags["t"] is True: grass.message("Keeping Transport Capacity maps.") else: mapstoremove.extend([qsx, qsy]) if flags["r"] is True: grass.message("Not keeping an Erosion and Deposition rate map.") mapstoremove.append(netchange) if len(mapstoremove) == 0: pass else: grass.run_command( "g.remove", quiet=True, flags="f", type="rast", name=",".join(mapstoremove), ) grass.message("\n*************************\n" + "Done with Iteration %s " % o + "\n*************************\n") return 0
def pca(pan, ms1, ms2, ms3, out, pid, sproc): grass.verbose(_("Using PCA/inverse PCA algorithm")) grass.message(_("Creating PCA images and calculating eigenvectors...")) # initial PCA with RGB channels pca_out = grass.read_command( "i.pca", quiet=True, rescale="0,0", input="%s,%s,%s" % (ms1, ms2, ms3), output="tmp%s.pca" % pid, ) if len(pca_out) < 1: grass.fatal(_("Input has no data. Check region settings.")) b1evect = [] b2evect = [] b3evect = [] for line in pca_out.replace("(", ",").replace(")", ",").splitlines(): b1evect.append(float(line.split(",")[1])) b2evect.append(float(line.split(",")[2])) b3evect.append(float(line.split(",")[3])) # inverse PCA with hi res pan channel substituted for principal component 1 pca2 = "tmp%s.pca.2" % pid pca3 = "tmp%s.pca.3" % pid b1evect1 = b1evect[0] b1evect2 = b1evect[1] b1evect3 = b1evect[2] b2evect1 = b2evect[0] b2evect2 = b2evect[1] b2evect3 = b2evect[2] b3evect1 = b3evect[0] b3evect2 = b3evect[1] b3evect3 = b3evect[2] # Histogram matching outname = "tmp%s_pan1" % pid panmatch1 = matchhist(pan, ms1, outname) outname = "tmp%s_pan2" % pid panmatch2 = matchhist(pan, ms2, outname) outname = "tmp%s_pan3" % pid panmatch3 = matchhist(pan, ms3, outname) grass.message(_("Performing inverse PCA ...")) # Get mean value of each channel stats1 = grass.parse_command( "r.univar", map=ms1, flags="g", parse=(grass.parse_key_val, {"sep": "="}) ) stats2 = grass.parse_command( "r.univar", map=ms2, flags="g", parse=(grass.parse_key_val, {"sep": "="}) ) stats3 = grass.parse_command( "r.univar", map=ms3, flags="g", parse=(grass.parse_key_val, {"sep": "="}) ) b1mean = float(stats1["mean"]) b2mean = float(stats2["mean"]) b3mean = float(stats3["mean"]) if sproc: # serial processing outr = "%s_red" % out outg = "%s_green" % out outb = "%s_blue" % out cmd1 = "$outb = 1 * round(($panmatch1 * $b1evect1) + ($pca2 * $b1evect2) + ($pca3 * $b1evect3) + $b1mean)" cmd2 = "$outg = 1 * round(($panmatch2 * $b2evect1) + ($pca2 * $b2evect2) + ($pca3 * $b2evect3) + $b2mean)" cmd3 = "$outr = 1 * round(($panmatch3 * $b3evect1) + ($pca2 * $b3evect2) + ($pca3 * $b3evect3) + $b3mean)" cmd = "\n".join([cmd1, cmd2, cmd3]) grass.mapcalc( cmd, outb=outb, outg=outg, outr=outr, panmatch1=panmatch1, panmatch2=panmatch2, panmatch3=panmatch3, pca2=pca2, pca3=pca3, b1evect1=b1evect1, b2evect1=b2evect1, b3evect1=b3evect1, b1evect2=b1evect2, b2evect2=b2evect2, b3evect2=b3evect2, b1evect3=b1evect3, b2evect3=b2evect3, b3evect3=b3evect3, b1mean=b1mean, b2mean=b2mean, b3mean=b3mean, overwrite=True, ) else: # parallel processing pb = grass.mapcalc_start( "%s_blue = 1 * round((%s * %f) + (%s * %f) + (%s * %f) + %f)" % (out, panmatch1, b1evect1, pca2, b1evect2, pca3, b1evect3, b1mean), overwrite=True, ) pg = grass.mapcalc_start( "%s_green = 1 * round((%s * %f) + (%s * %f) + (%s * %f) + %f)" % (out, panmatch2, b2evect1, pca2, b2evect2, pca3, b2evect3, b2mean), overwrite=True, ) pr = grass.mapcalc_start( "%s_red = 1 * round((%s * %f) + (%s * %f) + (%s * %f) + %f)" % (out, panmatch3, b3evect1, pca2, b3evect2, pca3, b3evect3, b3mean), overwrite=True, ) pb.wait(), pg.wait(), pr.wait() try: pb.terminate(), pg.terminate(), pr.terminate() except: "" # Cleanup grass.run_command( "g.remove", flags="f", quiet=True, type="raster", name="%s,%s,%s" % (panmatch1, panmatch2, panmatch3), )
def main(): pts_input = options["input"] output = options["output"] cost_map = options["cost_map"] post_mask = options["post_mask"] column = options["column"] friction = float(options["friction"]) layer = options["layer"] where = options["where"] workers = int(options["workers"]) if workers == 1 and "WORKERS" in os.environ: workers = int(os.environ["WORKERS"]) if workers < 1: workers = 1 pid = str(os.getpid()) tmp_base = "tmp_icw_" + pid + "_" # do the maps exist? if not grass.find_file(pts_input, element="vector")["file"]: grass.fatal(_("Vector map <%s> not found") % pts_input) if post_mask: if grass.find_file("MASK")["file"]: grass.fatal( _("A MASK already exists; remove it before using the post_mask option.") ) if not grass.find_file(post_mask)["file"]: grass.fatal(_("Raster map <%s> not found") % post_mask) grass.verbose(_("v.surf.icw -- Inverse Cost Weighted Interpolation")) grass.verbose( _("Processing %s -> %s, column=%s, Cf=%g") % (pts_input, output, column, friction) ) if flags["r"]: grass.verbose(_("Using (d^n)*log(d) radial basis function.")) grass.verbose( "------------------------------------------------------------------------" ) # adjust so that tiny numbers don't hog all the FP precision space # if friction = 4: divisor ~ 10.0 # if friction = 5: divisor ~ 100.0 # if friction = 6: divisor ~ 500.0 if friction >= 4: divisor = 0.01 * pow(friction, 6) else: divisor = 1 # Check that we have the column and it is the correct type try: coltype = grass.vector_columns(pts_input, layer)[column] except KeyError: grass.fatal( _("Data column <%s> not found in vector points map <%s>") % (column, pts_input) ) if coltype["type"] not in ("INTEGER", "DOUBLE PRECISION"): grass.fatal(_("Data column must be numberic")) # cleanse cost area mask to a flat =1 for my porpoises area_mask = tmp_base + "area" grass.mapcalc( "$result = if($cost_map, 1, null())", result=area_mask, cost_map=cost_map, quiet=True, ) ## done with prep work, ######################################################################## ## Commence crunching .. # crop out only points in region addl_opts = {} if where: addl_opts["where"] = "%s" % where points_list = grass.read_command( "v.out.ascii", input=pts_input, output="-", flags="r", **addl_opts ).splitlines() # Needed to strip away empty entries from MS Windows newlines # list() is needed for Python 3 compatibility points_list = list([_f for _f in points_list if _f]) # convert into a 2D list, drop unneeded cat column # to drop cat col, add this to the end of the line [:-1] # fixme: how does this all react for 3D starting points? for i in range(len(points_list)): points_list[i] = points_list[i].split("|") # count number of starting points (n). This value will later be decremented # if points are found to be off the cost map or out of region. n = len(points_list) if n > 200: grass.warning( _( "Computation is expensive! Please consider " + "fewer points or get ready to wait a while ..." ) ) import time time.sleep(5) #### generate cost maps for each site in range grass.message(_("Generating cost maps ...")) # avoid do-it-yourself brain surgery points_list_orig = list(points_list) proc = {} num = 1 for i in range(n): position = points_list_orig[i] easting = position[0] northing = position[1] cat = int(position[-1]) # retrieve data value from vector's attribute table: data_value = grass.vector_db_select(pts_input, columns=column)["values"][cat][0] if not data_value: grass.message( _("Site %d of %d, e=%.4f n=%.4f cat=%d data=?") % (num, n, float(easting), float(northing), cat) ) grass.message(_(" -- Skipping, no data here.")) del points_list[num - 1] n -= 1 continue else: grass.message( _("Site %d of %d, e=%.4f n=%.4f cat=%d data=%.8g") % (num, n, float(easting), float(northing), cat, float(data_value)) ) # we know the point is in the region, but is it in a non-null area of the cost surface? rast_val = ( grass.read_command( "r.what", map=area_mask, coordinates="%s,%s" % (position[0], position[1]), ) .strip() .split("|")[-1] ) if rast_val == "*": grass.message(_(" -- Skipping, point lays outside of cost_map.")) del points_list[num - 1] n -= 1 continue # it's ok to proceed try: data_value = float(data_value) except: grass.fatal("Data value [%s] is non-numeric" % data_value) cost_site_name = tmp_base + "cost_site." + "%05d" % num proc[num - 1] = grass.start_command( "r.cost", flags="k", input=area_mask, output=cost_site_name, start_coordinates=easting + "," + northing, quiet=True, ) # stall to wait for the nth worker to complete, if num % workers == 0: proc[num - 1].wait() num += 1 # make sure everyone is finished for i in range(n): if proc[i].wait() != 0: grass.fatal(_("Problem running %s") % "r.cost") grass.message(_("Removing anomalies at site positions ...")) proc = {} for i in range(n): cost_site_name = tmp_base + "cost_site." + "%05d" % (i + 1) # max_cost="$GIS_OPT_MAX_COST" : commented out until r.null cleansing/continue code is sorted out # start_points=tmp_idw_cost_site_$$ # we do this so the divisor exists and the weighting is huge at the exact sample spots # more efficient to reclass to 1? proc[i] = grass.mapcalc_start( "$cost_n_cleansed = if($cost_n == 0, 0.1, $cost_n)", cost_n_cleansed=cost_site_name + ".cleansed", cost_n=cost_site_name, quiet=True, ) # stall to wait for the nth worker to complete, if (i + 1) % workers == 0: # print 'stalling ...' proc[i].wait() # make sure everyone is finished for i in range(n): if proc[i].wait() != 0: grass.fatal(_("Problem running %s") % "r.mapcalc") grass.message(_("Applying radial decay ...")) proc = {} for i in range(n): cost_site_name = tmp_base + "cost_site." + "%05d" % (i + 1) grass.run_command( "g.remove", flags="f", type="raster", name=cost_site_name, quiet=True ) grass.run_command( "g.rename", raster=cost_site_name + ".cleansed" + "," + cost_site_name, quiet=True, ) # r.to.vect then r.patch output # v.to.rast in=tmp_idw_cost_site_29978 out=tmp_idw_cost_val_$$ use=val val=10 if not flags["r"]: # exp(3,2) is 3^2 etc. as is pow(3,2) # r.mapcalc "1by_cost_site_sqrd.$NUM = 1.0 / exp(cost_site.$NUM , $FRICTION)" # EXPRESSION="1.0 / pow(cost_site.$NUM $DIVISOR, $FRICTION )" expr = "1.0 / pow($cost_n / " + str(divisor) + ", $friction)" else: # use log10() or ln() ? # EXPRESSION="1.0 / ( pow(cost_site.$NUM, $FRICTION) * log (cost_site.$NUM) )" expr = '1.0 / ( pow($cost_n, $friction) * log($cost_n) )"' grass.debug("r.mapcalc expression is: [%s]" % expr) one_by_cost_site_sq_n = tmp_base + "1by_cost_site_sq." + "%05d" % (i + 1) proc[i] = grass.mapcalc_start( "$result = " + expr, result=one_by_cost_site_sq_n, cost_n=cost_site_name, friction=friction, quiet=True, ) # stall to wait for the nth worker to complete, if (i + 1) % workers == 0: # print 'stalling ...' proc[i].wait() # r.patch in=1by_cost_site_sqrd.${NUM},tmp_idw_cost_val_$$ out=1by_cost_site_sqrd.${NUM} --o # g.remove type=rast name=cost_site.$NUM -f # make sure everyone is finished for i in range(n): if proc[i].wait() != 0: grass.fatal(_("Problem running %s") % "r.mapcalc") grass.run_command( "g.remove", flags="f", type="raster", pattern=tmp_base + "cost_site.*", quiet=True, ) # grass.run_command('g.list', type = 'raster', mapset = '.') ####################################################### #### Step 3) find sum(cost^2) grass.verbose("") grass.verbose(_("Finding sum of squares ...")) # todo: test if MASK exists already, fatal exit if it does? if post_mask: grass.message(_("Setting post_mask <%s>"), post_mask) grass.mapcalc("MASK = $maskmap", maskmap=post_mask, overwrite=True) grass.message(_("Summation of cost weights ...")) input_maps = tmp_base + "1by_cost_site_sq.%05d" % 1 global TMP_FILE TMP_FILE = grass.tempfile() with open(TMP_FILE, "w") as maplist: for i in range(2, n + 1): mapname = "%s1by_cost_site_sq.%05d" % (tmp_base, i) maplist.write(mapname + "\n") # grass.run_command('g.list', type = 'raster', mapset = '.') sum_of_1by_cost_sqs = tmp_base + "sum_of_1by_cost_sqs" try: grass.run_command( "r.series", method="sum", file=TMP_FILE, output=sum_of_1by_cost_sqs ) except CalledModuleError: grass.fatal(_("Problem running %s") % "r.series") if post_mask: grass.message(_("Removing post_mask <%s>"), post_mask) grass.run_command("g.remove", flags="f", name="MASK", quiet=True) ####################################################### #### Step 4) ( 1/di^2 / sum(1/d^2) ) * ai grass.verbose("") grass.message(_("Creating partial weights ...")) proc = {} num = 1 for position in points_list: easting = position[0] northing = position[1] cat = int(position[-1]) data_value = grass.vector_db_select(pts_input, columns=column)["values"][cat][0] data_value = float(data_value) # failsafe: at this point the data values should all be valid if not data_value: grass.message(_("Site %d of %d, cat = %d, data value = ?") % (num, n, cat)) grass.message(_(" -- Skipping, no data here. [Probably programmer error]")) n -= 1 continue else: grass.message( _("Site %d of %d, cat = %d, data value = %.8g") % (num, n, cat, data_value) ) # we know the point is in the region, but is it in a non-null area of the cost surface? rast_val = ( grass.read_command( "r.what", map=area_mask, coordinates="%s,%s" % (position[0], position[1]), ) .strip() .split("|")[-1] ) if rast_val == "*": grass.message( _( " -- Skipping, point lays outside of cost_map. [Probably programmer error]" ) ) n -= 1 continue partial_n = tmp_base + "partial." + "%05d" % num one_by_cost_site_sq = tmp_base + "1by_cost_site_sq." + "%05d" % num # "( $DATA_VALUE / $N ) * (1.0 - ( cost_sq_site.$NUM / sum_of_cost_sqs ))" # "( cost_sq_site.$NUM / sum_of_cost_sqs ) * ( $DATA_VALUE / $N )" proc[num - 1] = grass.mapcalc_start( "$partial_n = ($data * $one_by_cost_sq) / $sum_of_1by_cost_sqs", partial_n=partial_n, data=data_value, one_by_cost_sq=one_by_cost_site_sq, sum_of_1by_cost_sqs=sum_of_1by_cost_sqs, quiet=True, ) # stall to wait for the nth worker to complete, if num % workers == 0: proc[num - 1].wait() # free up disk space ASAP # grass.run_command('g.remove', flags = 'f', type = 'raster', name = one_by_cost_site_sq, quiet = True) num += 1 if num > n: break # make sure everyone is finished for i in range(n): proc[i].wait() # free up disk space ASAP grass.run_command( "g.remove", flags="f", type="raster", pattern=tmp_base + "1by_cost_site_sq.*", quiet=True, ) # grass.run_command('g.list', type = 'raster', mapset = '.') ####################################################### grass.message("") grass.message(_("Calculating final values ...")) input_maps = tmp_base + "partial.%05d" % 1 for i in range(2, n + 1): input_maps += ",%spartial.%05d" % (tmp_base, i) try: grass.run_command("r.series", method="sum", input=input_maps, output=output) except CalledModuleError: grass.fatal(_("Problem running %s") % "r.series") # TODO: r.patch in v.to.rast of values at exact seed site locations. currently set to null grass.run_command("r.colors", map=output, color="bcyr", quiet=True) grass.run_command( "r.support", map=output, history="", title="Inverse cost-weighted interpolation" ) grass.run_command("r.support", map=output, history="v.surf.icw interpolation:") grass.run_command( "r.support", map=output, history=" input map=" + pts_input + " attribute column=" + column, ) grass.run_command( "r.support", map=output, history=" cost map=" + cost_map + " coefficient of friction=" + str(friction), ) if flags["r"]: grass.run_command( "r.support", map=output, history=" (d^n)*log(d) as radial basis function" ) if post_mask: grass.run_command( "r.support", map=output, history=" post-processing mask=" + post_mask ) if where: grass.run_command( "r.support", map=output, history=" SQL query= WHERE " + where ) # save layer #? to metadata? command line hist? ####################################################### # Step 5) rm cost and cost_sq maps, tmp_icw_points, etc cleanup() ####################################################### # Step 6) done! grass.message(_("Done! Results written to <%s>." % output))
def main(): # User inputs friction = options['friction'] # Input friction raster inpoints = options['points'] # Input point layer rastout = options['rastout'] # Output least cost path raster radius = int(options['radius']) # Point search radius n_closepoints = int(options['nearpoints']) # Number of closest points vectout = options['vectout'] # Vector layer output knight = "k" if flags['k'] else "" # Knight's move flag costatt = "e" if flags['c'] else "" # Calculate total cost values for paths and add them to attribute table # Check no vector or raster output is chosen, raise an error if (not vectout) and (not rastout): grass.message("No output chosen!") sys.exit() # Check overwrite settings # If output raster file exists, but overwrite option isn't selected if not grass.overwrite(): if grass.find_file(rastout)['name']: grass.message(_("Output raster map <%s> already exists") % rastout) sys.exit() # If output vector file exists, but overwrite option isn't selected if not grass.overwrite(): if grass.find_file(vectout, element = 'vector')['name']: grass.message(_("Output vector map <%s> already exists") % vectout) sys.exit() # If overwrite is chosen, remove the previous layers before any action (to lessen the probability of some random errors) if grass.overwrite(): grass.run_command("g.remove", rast = rastout, vect = vectout, quiet = True) # Get a region resolution to be used in cost attribute calculation, because the default will be in map units if vectout and (costatt == "e"): # Get raster calculation region information regiondata = grass.read_command("g.region", flags = 'p') regvalues = grass.parse_key_val(regiondata, sep= ':') # Assign variables for necessary region info bits nsres = float(regvalues['nsres']) ewres = float(regvalues['ewres']) regionres = (nsres + ewres) / 2.0 rescoefficient = regionres # Get process id (pid) and create temporary layer names which are also added to tmp_rlayers list pid = os.getpid() # Process ID, used for getting unique temporary filenames costmap1 = "tmp_cost_%d" % pid # Cost surface for point 1 tmp_rlayers.append(costmap1) costmap2 = "tmp_cost_%d_%i" % (pid, 2) # Cost surface from point 2 (parallel process) tmp_rlayers.append(costmap2) costdir1 = "tmp_costdir_%d" % pid # Temporary cost direction raster 1 tmp_rlayers.append(costdir1) costdir2 = "tmp_costdir_%d_%i" % (pid, 2) # Temporary cost direction raster 2 tmp_rlayers.append(costdir2) lcpmap1 = "tmp_lcp_%d" % pid # Least cost path map from costmap1 tmp_rlayers.append(lcpmap1) lcpmap2 = "tmp_lcp_%d_%i" % (pid, 2) # Least cost path map from costmap2 (parallel process) tmp_rlayers.append(lcpmap2) lcptemp = "tmp_lcptemp_%d" % pid # Temporary file for mapcalc tmp_rlayers.append(lcptemp) region = "tmp_region_%d" % pid # Temporary vector layer of computational region tmp_vlayers.append(region) points = "tmp_points_%d" % pid # Temporary point layer which holds points only inside the region tmp_vlayers.append(points) if vectout: # if vector output is needed, create the temporary vectorlayers too vectdrain1 = "tmp_vectdrain_%d" % pid tmp_vlayers.append(vectdrain1) vectdrain2 = "tmp_vectdrain2_%d" % pid tmp_vlayers.append(vectdrain2) # Make sure input data points are inside raster computational region: create a region polygon and select points that are inside it grass.run_command('v.in.region', overwrite = True, output = region) grass.run_command('v.select', overwrite = True, flags = "tc", ainput = inpoints, atype = 'point', binput = region, btype = 'area', output = points , operator = 'within') # Create a new PointLayerInfo class instance using input point layer and get the categories list as well as total feature count of the layer pointlayer = PointLayerInfo(points) points_cats = pointlayer.featcats # A list() of layer feature categories points_featcount = pointlayer.featcount # integer of feature count in point layer points_coordsdict = pointlayer.coordsdict # dict() of point coordinates as tuple (x,y) # Create an empty dictionaries for storing cost distances between points costdict1 = dict() costdict2 = dict() # Create the first mapcalc process, so that it can be checked and stopped in the loop without using more complicated ways mapcalc = grass.Popen("") lcp1 = grass.Popen("") lcp2 = grass.Popen("") # The main loop for least cost path creation. For each point a cost surface is created, least cost paths created and then added to the general output file. Loop uses a range which has as many items as there are points in the input point layer. To make use of parallel processing, the step is 2, although the "item" is always the first of the selected pair. for item in range(0,points_featcount,2): # Get category number of the point from the point_cats list cat1 = points_cats[item] # Set p2 (i.e. using second or parallel process) to be False by default and make it True if there are enough points left to do so. In that case set it to true and also get the category number of the point from the point_cats list p2 = False if item+1 < points_featcount: p2 = True cat2 = points_cats[item+1] # Create a new PointLayerInfo object from input point layer with centerpoint (from which distances area measured in the class) feature as currently selected point cat point1 = PointLayerInfo(points, cat1) if p2: # The same for p2 if needed point2 = PointLayerInfo(points, cat2) # begin cost surface process with the start coordinate of currently selected point. Do the same for second process costsurf1 = grass.start_command('r.cost', flags=knight, overwrite=True, input=friction, output=costmap1, outdir=costdir1, start_coordinates=point1.centercoord()) if p2: costsurf2 = grass.start_command('r.cost', flags=knight, overwrite=True, input=friction, output=costmap2, outdir=costdir2, start_coordinates=point2.centercoord()) # Create the drainlist (list of feature coordinates where lcp from current point is made to) depending on whether radius and/or n_closepoints are used. Drainlist point coordinates will be used for r.drain. See PointLayerInfo class below for explanation of the process. if radius and n_closepoints: # If radius and n_closepoints are used drainlist1 = point1.near_points_in_radius(n_closepoints, radius) if p2: drainlist2 = point2.near_points_in_radius(n_closepoints, radius) elif radius: # If radius is used drainlist1 = point1.points_in_radius(radius) if p2: drainlist2 = point2.points_in_radius(radius) elif n_closepoints: # If n_closepoints is used drainlist1 = point1.near_points(n_closepoints) if p2: drainlist2 = point2.near_points(n_closepoints) else: # If neither radius or n_closepoints are used drainlist1 = point1.cats_without_centerpoint() if p2: drainlist2 = point2.cats_without_centerpoint() # Do the least cost path calculation procedures drain_coords1 = "" # An empty string that will be populated with point coordinates which in turn will be used for r.drain start coordinates for drainpoint in drainlist1: # Iterate through all points in drainlist drain_x, drain_y = point1.coordsdict[drainpoint] # variables are assigned coordinate values from the coordinate dictionary drain_coords1 = drain_coords1 + str(drain_x) + "," + str(drain_y) + "," # Add those coordinates to the string that is usable by r.drain if p2: # The same thing for second process, see previous section for comments drain_coords2 = "" for drainpoint in drainlist2: drain_x, drain_y = point2.coordsdict[drainpoint] drain_coords2 = drain_coords2 + str(drain_x) + "," + str(drain_y) + "," # Wait for the previous processes to finish their processing costsurf1.wait() costsurf2.wait() mapcalc.wait() # If vector output is needed, do the r.drain for each point in the drainlist separately to get the cost values if vectout: if costatt == "e": for drainpoint in drainlist1: # Each point cat in the drainlist is being iterated drain_x, drain_y = point1.coordsdict[drainpoint] # Currently selected point's coordinates drain_onecoord = str(str(drain_x) + "," + str(drain_y)) # The coordinate to be used in r.drain on the next line grass.run_command('r.drain', overwrite=True, flags="ad", input=costmap1, indir=costdir1, output = lcpmap1, start_coordinates = drain_onecoord) # Get raster max value (=total cost value for one path) and store it in dictionary with point cat being its key rastinfo = grass.raster_info(lcpmap1) costdict1[drainpoint] = rescoefficient * rastinfo['min'] if p2: # Same procedure as in the previous section for parallel process for drainpoint in drainlist2: drain_x, drain_y = point2.coordsdict[drainpoint] drain_onecoord = str(str(drain_x) + "," + str(drain_y)) grass.run_command('r.drain', overwrite=True, flags="ad", input=costmap2, indir=costdir2, output = lcpmap2, start_coordinates = drain_onecoord) rastinfo = grass.raster_info(lcpmap2) costdict2[drainpoint] = rescoefficient * rastinfo['min'] # Finally create the vector layer with all paths from the current point. It also (whether we want it or not) creates a raster output if len(drainlist1) > 0: lcp1 = grass.start_command('r.drain', overwrite=True, flags="d", input=costmap1, indir=costdir1, output = lcpmap1, vector_output = vectdrain1,start_coordinates=drain_coords1) if p2 and (len(drainlist2) > 0): lcp2 = grass.start_command('r.drain', overwrite=True, flags="d", input=costmap2, indir=costdir2, output = lcpmap2, vector_output = vectdrain2,start_coordinates=drain_coords2) # If raster output is needed, but path maps have not been made yet (i.e. vectout must be False) then make those if not vectout and (len(drainlist1) > 0): lcp1 = grass.start_command('r.drain', overwrite=True, flags="d", input=costmap1, indir=costdir1, output = lcpmap1, start_coordinates=drain_coords1) if p2 and (len(drainlist2) > 0): lcp2 = grass.start_command('r.drain', overwrite=True, flags="d", input=costmap2, indir=costdir2, output = lcpmap2, start_coordinates=drain_coords2) # Wait for the lcp processes to finish lcp1.wait() lcp2.wait() # If raster output is needed, do the mapcalc stuff: merge the path rasters if rastout: if len(drainlist1) == 0: lcpmap1 = 0 if len(drainlist2) == 0: lcpmap2 = 0 if cat1 == points_cats[0]: # If it's the very first iteration if p2: # Technically this should not be False in any situation, but let it be here for additional safety # Add lcpmap1 and lcpmap2 together mapcalc = grass.mapcalc_start("$outmap = if(isnull($tempmap1),0,1) + if(isnull($tempmap2),0,1)", outmap = rastout, tempmap1 = lcpmap1, tempmap2 = lcpmap2, overwrite=True) else: # Just in case mapcalc = grass.mapcalc_start("$outmap = if(isnull($tempmap1),0,1)", outmap = rastout, tempmap1 = lcpmap1, overwrite=True) else: # Rename the cumulative lcp map from previous iteration so that mapcalc can use it (x=x+y logic doesn't work with mapcalc) grass.run_command('g.rename', rast = rastout + ',' + lcptemp, overwrite=True) # rastout = Previous LCP + Current LCP if p2: mapcalc = grass.mapcalc_start("$outmap = $inmap + if(isnull($tempmap1),0,1) + if(isnull($tempmap2),0,1)", inmap = lcptemp, outmap = rastout, tempmap1 = lcpmap1, tempmap2 = lcpmap2) else: mapcalc = grass.mapcalc_start("$outmap = $inmap + if(isnull($tempmap1),0,1)", inmap = lcptemp, outmap = rastout, tempmap1 = lcpmap1) # If vector output is needed, do all necessary things like merging the vectors and getting values for attribute table (if specified) if vectout: if costatt == "e": # Only if cost attributes are needed if len(drainlist1) > 0: # Process 1 # Add attribute table to the vector path layer grass.run_command('v.db.addtable', map = vectdrain1) # Get path Euclidean distances and add them to the new column in attribute table. Also add the current point cat to the attribute "from_point" grass.run_command('v.db.addcolumn', map = vectdrain1, columns = "length double precision, from_point int, to_point int, cost double precision") grass.run_command('v.to.db', map = vectdrain1, type = "line", option = "length", columns = "length") grass.run_command('v.db.update', map = vectdrain1, column = "from_point", value = str(cat1)) # Same as previous section but for process 2 if p2 and (len(drainlist2) > 0): grass.run_command('v.db.addtable', map = vectdrain2) grass.run_command('v.db.addcolumn', map = vectdrain2, columns = "length double precision, from_point int, to_point int, cost double precision") grass.run_command('v.to.db', map = vectdrain2, type = "line", option = "length", columns = "length") grass.run_command('v.db.update', map = vectdrain2, column = "from_point", value = str(cat2)) # A loop to update the path attribute values to the attribute table if len(drainlist1) > 0: drainseq = 1 # This is just a helper counter because for newly created vector layer the cats start from 1 and just go successively, so no need to introduce any unnecessary catlist for drainpoint in drainlist1: # Update to_point column with values from drainlist grass.run_command('v.db.update', map = vectdrain1, column = "to_point", value = str(drainpoint), where = "cat = " + str(drainseq)) # Update the cost column using costdict created earlier grass.run_command('v.db.update', map = vectdrain1, column = "cost", value = costdict1[drainpoint], where = "cat = " + str(drainseq)) drainseq += 1 # The same for process 2 if p2 and (len(drainlist2) > 0): drainseq = 1 # Reset the counter for drainpoint in drainlist2: grass.run_command('v.db.update', map = vectdrain2, column = "to_point", value = str(drainpoint), where = "cat = " + str(drainseq)) grass.run_command('v.db.update', map = vectdrain2, column = "cost", value = costdict2[drainpoint], where = "cat = " + str(drainseq)) drainseq += 1 # Patch vector layers # For both processes, first make sure that drainlists for current iteration are not empty. If they are not (i.e. the drainlist for current iteration > 0), then drain vectors will be used in v.patch, otherwise empty strings will be used in patching. This is to make sure that vectors from previous iterations are not used. if len(drainlist1) > 0: vect1 = vectdrain1 else: vect1 = "" if len(drainlist2) > 0: vect2 = vectdrain2 else: vect2 = "" # If BOTH drain processes resulted in vectors, create a comma character to be used in v.patch (input parameter must be a string type and layers should be separated by comma) if (len(drainlist1) > 0) and (len(drainlist2) > 0): comma = "," else: comma = "" # Finally do the patching if cat1 == points_cats[0]: # If it's the very first iteration if p2: # If iteration has 2 points grass.run_command('v.patch', overwrite = True, flags=costatt, input = vect1 + comma + vect2, output = vectout) else: # Technically this should never be called (because not having 2 points per iteration can happen only for the very last iteration), but I'll leave it here just in case or for future reference grass.run_command('g.rename', overwrite = True, vect = vect1 + "," + vectout) else: if grass.find_file(vectout, element='vector')['name']: # Check whether vectout exists or not (this can happen when the first iteration did not produce any vectors, i.e. search radius was too small). If it does exist, add "a" (append) flag to v.patch, otherwise omit it. append = costatt + "a" else: append = costatt # Choose between two patching scenarios: 1 or 2 process versions. if p2: grass.run_command('v.patch', overwrite = True, flags=append, input = vect1 + comma + vect2, output = vectout) else: grass.run_command('v.patch', overwrite = True, flags=append, input = vect1, output = vectout) # Make 0 values of raster into NULLs if rastout: mapcalc.wait() nullproc = grass.run_command('r.null', map = rastout, setnull = "0") grass.message("All done!")
def main(): # User inputs friction = options['friction'] # Input friction raster inpoints = options['points'] # Input point layer rastout = options['rastout'] # Output least cost path raster radius = int(options['radius']) # Point search radius n_closepoints = int(options['nearpoints']) # Number of closest points vectout = options['vectout'] # Vector layer output knight = "k" if flags['k'] else "" # Knight's move flag costatt = "e" if flags[ 'c'] else "" # Calculate total cost values for paths and add them to attribute table # Check no vector or raster output is chosen, raise an error if (not vectout) and (not rastout): grass.message("No output chosen!") sys.exit() # Check overwrite settings # If output raster file exists, but overwrite option isn't selected if not grass.overwrite(): if grass.find_file(rastout)['name']: grass.message(_("Output raster map <%s> already exists") % rastout) sys.exit() # If output vector file exists, but overwrite option isn't selected if not grass.overwrite(): if grass.find_file(vectout, element='vector')['name']: grass.message(_("Output vector map <%s> already exists") % vectout) sys.exit() # If overwrite is chosen, remove the previous layers before any action (to lessen the probability of some random errors) if grass.overwrite(): grass.run_command("g.remove", rast=rastout, vect=vectout, quiet=True) # Get a region resolution to be used in cost attribute calculation, because the default will be in map units if vectout and (costatt == "e"): # Get raster calculation region information regiondata = grass.read_command("g.region", flags='p') regvalues = grass.parse_key_val(regiondata, sep=':') # Assign variables for necessary region info bits nsres = float(regvalues['nsres']) ewres = float(regvalues['ewres']) regionres = (nsres + ewres) / 2.0 rescoefficient = regionres # Get process id (pid) and create temporary layer names which are also added to tmp_rlayers list pid = os.getpid( ) # Process ID, used for getting unique temporary filenames costmap1 = "tmp_cost_%d" % pid # Cost surface for point 1 tmp_rlayers.append(costmap1) costmap2 = "tmp_cost_%d_%i" % ( pid, 2) # Cost surface from point 2 (parallel process) tmp_rlayers.append(costmap2) costdir1 = "tmp_costdir_%d" % pid # Temporary cost direction raster 1 tmp_rlayers.append(costdir1) costdir2 = "tmp_costdir_%d_%i" % (pid, 2 ) # Temporary cost direction raster 2 tmp_rlayers.append(costdir2) lcpmap1 = "tmp_lcp_%d" % pid # Least cost path map from costmap1 tmp_rlayers.append(lcpmap1) lcpmap2 = "tmp_lcp_%d_%i" % ( pid, 2) # Least cost path map from costmap2 (parallel process) tmp_rlayers.append(lcpmap2) lcptemp = "tmp_lcptemp_%d" % pid # Temporary file for mapcalc tmp_rlayers.append(lcptemp) region = "tmp_region_%d" % pid # Temporary vector layer of computational region tmp_vlayers.append(region) points = "tmp_points_%d" % pid # Temporary point layer which holds points only inside the region tmp_vlayers.append(points) if vectout: # if vector output is needed, create the temporary vectorlayers too vectdrain1 = "tmp_vectdrain_%d" % pid tmp_vlayers.append(vectdrain1) vectdrain2 = "tmp_vectdrain2_%d" % pid tmp_vlayers.append(vectdrain2) # Make sure input data points are inside raster computational region: create a region polygon and select points that are inside it grass.run_command('v.in.region', overwrite=True, output=region) grass.run_command('v.select', overwrite=True, flags="tc", ainput=inpoints, atype='point', binput=region, btype='area', output=points, operator='within') # Create a new PointLayerInfo class instance using input point layer and get the categories list as well as total feature count of the layer pointlayer = PointLayerInfo(points) points_cats = pointlayer.featcats # A list() of layer feature categories points_featcount = pointlayer.featcount # integer of feature count in point layer points_coordsdict = pointlayer.coordsdict # dict() of point coordinates as tuple (x,y) # Create an empty dictionaries for storing cost distances between points costdict1 = dict() costdict2 = dict() # Create the first mapcalc process, so that it can be checked and stopped in the loop without using more complicated ways mapcalc = grass.Popen("", shell=True) lcp1 = grass.Popen("", shell=True) lcp2 = grass.Popen("", shell=True) # The main loop for least cost path creation. For each point a cost surface is created, least cost paths created and then added to the general output file. Loop uses a range which has as many items as there are points in the input point layer. To make use of parallel processing, the step is 2, although the "item" is always the first of the selected pair. for item in range(0, points_featcount, 2): # Get category number of the point from the point_cats list cat1 = points_cats[item] # Set p2 (i.e. using second or parallel process) to be False by default and make it True if there are enough points left to do so. In that case set it to true and also get the category number of the point from the point_cats list p2 = False if item + 1 < points_featcount: p2 = True cat2 = points_cats[item + 1] # Create a new PointLayerInfo object from input point layer with centerpoint (from which distances area measured in the class) feature as currently selected point cat point1 = PointLayerInfo(points, cat1) if p2: # The same for p2 if needed point2 = PointLayerInfo(points, cat2) # begin cost surface process with the start coordinate of currently selected point. Do the same for second process costsurf1 = grass.start_command('r.cost', flags=knight, overwrite=True, input=friction, output=costmap1, outdir=costdir1, start_coordinates=point1.centercoord()) if p2: costsurf2 = grass.start_command( 'r.cost', flags=knight, overwrite=True, input=friction, output=costmap2, outdir=costdir2, start_coordinates=point2.centercoord()) # Create the drainlist (list of feature coordinates where lcp from current point is made to) depending on whether radius and/or n_closepoints are used. Drainlist point coordinates will be used for r.drain. See PointLayerInfo class below for explanation of the process. if radius and n_closepoints: # If radius and n_closepoints are used drainlist1 = point1.near_points_in_radius(n_closepoints, radius) if p2: drainlist2 = point2.near_points_in_radius( n_closepoints, radius) elif radius: # If radius is used drainlist1 = point1.points_in_radius(radius) if p2: drainlist2 = point2.points_in_radius(radius) elif n_closepoints: # If n_closepoints is used drainlist1 = point1.near_points(n_closepoints) if p2: drainlist2 = point2.near_points(n_closepoints) else: # If neither radius or n_closepoints are used drainlist1 = point1.cats_without_centerpoint() if p2: drainlist2 = point2.cats_without_centerpoint() # Do the least cost path calculation procedures drain_coords1 = "" # An empty string that will be populated with point coordinates which in turn will be used for r.drain start coordinates for drainpoint in drainlist1: # Iterate through all points in drainlist drain_x, drain_y = point1.coordsdict[ drainpoint] # variables are assigned coordinate values from the coordinate dictionary drain_coords1 = drain_coords1 + str(drain_x) + "," + str( drain_y ) + "," # Add those coordinates to the string that is usable by r.drain if p2: # The same thing for second process, see previous section for comments drain_coords2 = "" for drainpoint in drainlist2: drain_x, drain_y = point2.coordsdict[drainpoint] drain_coords2 = drain_coords2 + str(drain_x) + "," + str( drain_y) + "," # Wait for the previous processes to finish their processing costsurf1.wait() costsurf2.wait() mapcalc.wait() # If vector output is needed, do the r.drain for each point in the drainlist separately to get the cost values if vectout: if costatt == "e": for drainpoint in drainlist1: # Each point cat in the drainlist is being iterated drain_x, drain_y = point1.coordsdict[ drainpoint] # Currently selected point's coordinates drain_onecoord = str( str(drain_x) + "," + str(drain_y) ) # The coordinate to be used in r.drain on the next line grass.run_command('r.drain', overwrite=True, flags="ad", input=costmap1, indir=costdir1, output=lcpmap1, start_coordinates=drain_onecoord) # Get raster max value (=total cost value for one path) and store it in dictionary with point cat being its key rastinfo = grass.raster_info(lcpmap1) costdict1[drainpoint] = rescoefficient * rastinfo['min'] if p2: # Same procedure as in the previous section for parallel process for drainpoint in drainlist2: drain_x, drain_y = point2.coordsdict[drainpoint] drain_onecoord = str(str(drain_x) + "," + str(drain_y)) grass.run_command('r.drain', overwrite=True, flags="ad", input=costmap2, indir=costdir2, output=lcpmap2, start_coordinates=drain_onecoord) rastinfo = grass.raster_info(lcpmap2) costdict2[ drainpoint] = rescoefficient * rastinfo['min'] # Finally create the vector layer with all paths from the current point. It also (whether we want it or not) creates a raster output if len(drainlist1) > 0: lcp1 = grass.start_command('r.drain', overwrite=True, flags="d", input=costmap1, indir=costdir1, output=lcpmap1, vector_output=vectdrain1, start_coordinates=drain_coords1) if p2 and (len(drainlist2) > 0): lcp2 = grass.start_command('r.drain', overwrite=True, flags="d", input=costmap2, indir=costdir2, output=lcpmap2, vector_output=vectdrain2, start_coordinates=drain_coords2) # If raster output is needed, but path maps have not been made yet (i.e. vectout must be False) then make those if not vectout and (len(drainlist1) > 0): lcp1 = grass.start_command('r.drain', overwrite=True, flags="d", input=costmap1, indir=costdir1, output=lcpmap1, start_coordinates=drain_coords1) if p2 and (len(drainlist2) > 0): lcp2 = grass.start_command('r.drain', overwrite=True, flags="d", input=costmap2, indir=costdir2, output=lcpmap2, start_coordinates=drain_coords2) # Wait for the lcp processes to finish lcp1.wait() lcp2.wait() # If raster output is needed, do the mapcalc stuff: merge the path rasters if rastout: if len(drainlist1) == 0: lcpmap1 = 0 if len(drainlist2) == 0: lcpmap2 = 0 if cat1 == points_cats[0]: # If it's the very first iteration if p2: # Technically this should not be False in any situation, but let it be here for additional safety # Add lcpmap1 and lcpmap2 together mapcalc = grass.mapcalc_start( "$outmap = if(isnull($tempmap1),0,1) + if(isnull($tempmap2),0,1)", outmap=rastout, tempmap1=lcpmap1, tempmap2=lcpmap2, overwrite=True) else: # Just in case mapcalc = grass.mapcalc_start( "$outmap = if(isnull($tempmap1),0,1)", outmap=rastout, tempmap1=lcpmap1, overwrite=True) else: # Rename the cumulative lcp map from previous iteration so that mapcalc can use it (x=x+y logic doesn't work with mapcalc) grass.run_command('g.rename', rast=rastout + ',' + lcptemp, overwrite=True) # rastout = Previous LCP + Current LCP if p2: mapcalc = grass.mapcalc_start( "$outmap = $inmap + if(isnull($tempmap1),0,1) + if(isnull($tempmap2),0,1)", inmap=lcptemp, outmap=rastout, tempmap1=lcpmap1, tempmap2=lcpmap2) else: mapcalc = grass.mapcalc_start( "$outmap = $inmap + if(isnull($tempmap1),0,1)", inmap=lcptemp, outmap=rastout, tempmap1=lcpmap1) # If vector output is needed, do all necessary things like merging the vectors and getting values for attribute table (if specified) if vectout: if costatt == "e": # Only if cost attributes are needed if len(drainlist1) > 0: # Process 1 # Add attribute table to the vector path layer grass.run_command('v.db.addtable', map=vectdrain1) # Get path Euclidean distances and add them to the new column in attribute table. Also add the current point cat to the attribute "from_point" grass.run_command( 'v.db.addcolumn', map=vectdrain1, columns= "length double precision, from_point int, to_point int, cost double precision" ) grass.run_command('v.to.db', map=vectdrain1, type="line", option="length", columns="length") grass.run_command('v.db.update', map=vectdrain1, column="from_point", value=str(cat1)) # Same as previous section but for process 2 if p2 and (len(drainlist2) > 0): grass.run_command('v.db.addtable', map=vectdrain2) grass.run_command( 'v.db.addcolumn', map=vectdrain2, columns= "length double precision, from_point int, to_point int, cost double precision" ) grass.run_command('v.to.db', map=vectdrain2, type="line", option="length", columns="length") grass.run_command('v.db.update', map=vectdrain2, column="from_point", value=str(cat2)) # A loop to update the path attribute values to the attribute table if len(drainlist1) > 0: drainseq = 1 # This is just a helper counter because for newly created vector layer the cats start from 1 and just go successively, so no need to introduce any unnecessary catlist for drainpoint in drainlist1: # Update to_point column with values from drainlist grass.run_command('v.db.update', map=vectdrain1, column="to_point", value=str(drainpoint), where="cat = " + str(drainseq)) # Update the cost column using costdict created earlier grass.run_command('v.db.update', map=vectdrain1, column="cost", value=costdict1[drainpoint], where="cat = " + str(drainseq)) drainseq += 1 # The same for process 2 if p2 and (len(drainlist2) > 0): drainseq = 1 # Reset the counter for drainpoint in drainlist2: grass.run_command('v.db.update', map=vectdrain2, column="to_point", value=str(drainpoint), where="cat = " + str(drainseq)) grass.run_command('v.db.update', map=vectdrain2, column="cost", value=costdict2[drainpoint], where="cat = " + str(drainseq)) drainseq += 1 # Patch vector layers # For both processes, first make sure that drainlists for current iteration are not empty. If they are not (i.e. the drainlist for current iteration > 0), then drain vectors will be used in v.patch, otherwise empty strings will be used in patching. This is to make sure that vectors from previous iterations are not used. if len(drainlist1) > 0: vect1 = vectdrain1 else: vect1 = "" if len(drainlist2) > 0: vect2 = vectdrain2 else: vect2 = "" # If BOTH drain processes resulted in vectors, create a comma character to be used in v.patch (input parameter must be a string type and layers should be separated by comma) if (len(drainlist1) > 0) and (len(drainlist2) > 0): comma = "," else: comma = "" # Finally do the patching if cat1 == points_cats[0]: # If it's the very first iteration if p2: # If iteration has 2 points grass.run_command('v.patch', overwrite=True, flags=costatt, input=vect1 + comma + vect2, output=vectout) else: # Technically this should never be called (because not having 2 points per iteration can happen only for the very last iteration), but I'll leave it here just in case or for future reference grass.run_command('g.rename', overwrite=True, vect=vect1 + "," + vectout) else: if grass.find_file( vectout, element='vector' )['name']: # Check whether vectout exists or not (this can happen when the first iteration did not produce any vectors, i.e. search radius was too small). If it does exist, add "a" (append) flag to v.patch, otherwise omit it. append = costatt + "a" else: append = costatt # Choose between two patching scenarios: 1 or 2 process versions. if p2: grass.run_command('v.patch', overwrite=True, flags=append, input=vect1 + comma + vect2, output=vectout) else: grass.run_command('v.patch', overwrite=True, flags=append, input=vect1, output=vectout) # Make 0 values of raster into NULLs if rastout: mapcalc.wait() nullproc = grass.run_command('r.null', map=rastout, setnull="0") grass.message("All done!")