Exemplo n.º 1
0
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:
        ""
Exemplo n.º 2
0
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)
Exemplo n.º 3
0
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")
Exemplo n.º 4
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 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))
Exemplo n.º 5
0
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
    )
Exemplo n.º 6
0
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
Exemplo n.º 7
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),
    )
Exemplo n.º 8
0
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))
Exemplo n.º 9
0
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!")
Exemplo n.º 10
0
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!")