123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- #!/usr/bin/env python3
- ############################################################################
- #
- # MODULE: r.reclass.area
- # AUTHOR(S): NRCS
- # Converted to Python by Glynn Clements
- # Added rmarea method by Luca Delucchi
- # PURPOSE: Reclasses a raster map greater or less than user specified area size (in hectares)
- # COPYRIGHT: (C) 1999,2008,2014 by the GRASS Development Team
- #
- # This program is free software under the GNU General Public
- # License (>=v2). Read the file COPYING that comes with GRASS
- # for details.
- #
- #############################################################################
- # 10/2013: added option to use a pre-clumped input map (Eric Goddard)
- # 8/2012: added fp maps support, cleanup, removed tabs AK
- # 3/2007: added label support MN
- # 3/2004: added parser support MN
- # 11/2001 added mapset support markus
- # 2/2001 fixes markus
- # 2000: updated to GRASS 5
- # 1998 from NRCS, slightly modified for GRASS 4.2.1
- # %module
- # % description: Reclasses a raster map greater or less than user specified area size (in hectares).
- # % keyword: raster
- # % keyword: statistics
- # % keyword: aggregation
- # %end
- # %option G_OPT_R_INPUT
- # %end
- # %option G_OPT_R_OUTPUT
- # %end
- # %option
- # % key: value
- # % type: double
- # % description: Value option that sets the area size limit (in hectares)
- # % required: yes
- # % guisection: Area
- # %end
- # %option
- # % key: mode
- # % type: string
- # % description: Lesser or greater than specified value
- # % options: lesser,greater
- # % required: yes
- # % guisection: Area
- # %end
- # %option
- # % key: method
- # % type: string
- # % description: Method used for reclassification
- # % options: reclass,rmarea
- # % answer: reclass
- # % guisection: Area
- # %end
- # %flag
- # % key: c
- # % description: Input map is clumped
- # %end
- # %flag
- # % key: d
- # % description: Clumps including diagonal neighbors
- # %end
- import sys
- import os
- import atexit
- import grass.script as grass
- from grass.script.utils import decode, encode
- TMPRAST = []
- def reclass(inf, outf, lim, clump, diag, les):
- infile = inf
- outfile = outf
- lesser = les
- limit = lim
- clumped = clump
- diagonal = diag
- s = grass.read_command("g.region", flags="p")
- s = decode(s)
- kv = grass.parse_key_val(s, sep=":")
- s = kv["projection"].strip().split()
- if s == "0":
- grass.fatal(_("xy-locations are not supported"))
- grass.fatal(_("Need projected data with grids in meters"))
- if not grass.find_file(infile)["name"]:
- grass.fatal(_("Raster map <%s> not found") % infile)
- if clumped and diagonal:
- grass.fatal(_("flags c and d are mutually exclusive"))
- if clumped:
- clumpfile = infile
- else:
- clumpfile = "%s.clump.%s" % (infile.split("@")[0], outfile)
- TMPRAST.append(clumpfile)
- if not grass.overwrite():
- if grass.find_file(clumpfile)["name"]:
- grass.fatal(_("Temporary raster map <%s> exists") % clumpfile)
- if diagonal:
- grass.message(
- _("Generating a clumped raster file including " "diagonal neighbors...")
- )
- grass.run_command("r.clump", flags="d", input=infile, output=clumpfile)
- else:
- grass.message(_("Generating a clumped raster file ..."))
- grass.run_command("r.clump", input=infile, output=clumpfile)
- if lesser:
- grass.message(
- _(
- "Generating a reclass map with area size less than "
- "or equal to %f hectares..."
- )
- % limit
- )
- else:
- grass.message(
- _(
- "Generating a reclass map with area size greater "
- "than or equal to %f hectares..."
- )
- % limit
- )
- recfile = outfile + ".recl"
- TMPRAST.append(recfile)
- sflags = "aln"
- if grass.raster_info(infile)["datatype"] in ("FCELL", "DCELL"):
- sflags += "i"
- p1 = grass.pipe_command("r.stats", flags=sflags, input=(clumpfile, infile), sep=";")
- p2 = grass.feed_command("r.reclass", input=clumpfile, output=recfile, rules="-")
- rules = ""
- for line in p1.stdout:
- f = decode(line).rstrip(os.linesep).split(";")
- if len(f) < 5:
- continue
- hectares = float(f[4]) * 0.0001
- if lesser:
- test = hectares <= limit
- else:
- test = hectares >= limit
- if test:
- rules += "%s = %s %s\n" % (f[0], f[2], f[3])
- if rules:
- p2.stdin.write(encode(rules))
- p1.wait()
- p2.stdin.close()
- p2.wait()
- if p2.returncode != 0:
- if lesser:
- grass.fatal(
- _("No areas of size less than or equal to %f " "hectares found.")
- % limit
- )
- else:
- grass.fatal(
- _("No areas of size greater than or equal to %f " "hectares found.")
- % limit
- )
- grass.mapcalc("$outfile = $recfile", outfile=outfile, recfile=recfile)
- def rmarea(infile, outfile, thresh, coef):
- # transform user input from hectares to map units (kept this for future)
- # thresh = thresh * 10000.0 / (float(coef)**2)
- # grass.debug("Threshold: %d, coeff linear: %s, coef squared: %d" % (thresh, coef, (float(coef)**2)), 0)
- # transform user input from hectares to meters because currently v.clean
- # rmarea accept only meters as threshold
- thresh = thresh * 10000.0
- vectfile = "%s_vect_%s" % (infile.split("@")[0], outfile)
- TMPRAST.append(vectfile)
- grass.run_command("r.to.vect", input=infile, output=vectfile, type="area")
- cleanfile = "%s_clean_%s" % (infile.split("@")[0], outfile)
- TMPRAST.append(cleanfile)
- grass.run_command(
- "v.clean", input=vectfile, output=cleanfile, tool="rmarea", threshold=thresh
- )
- grass.run_command(
- "v.to.rast", input=cleanfile, output=outfile, use="attr", attrcolumn="value"
- )
- def main():
- infile = options["input"]
- value = options["value"]
- mode = options["mode"]
- outfile = options["output"]
- global method
- method = options["method"]
- clumped = flags["c"]
- diagonal = flags["d"]
- # check for unsupported locations
- in_proj = grass.parse_command("g.proj", flags="g")
- if in_proj["unit"].lower() == "degree":
- grass.fatal(_("Latitude-longitude locations are not supported"))
- if in_proj["name"].lower() == "xy_location_unprojected":
- grass.fatal(_("xy-locations are not supported"))
- # check lesser and greater parameters
- limit = float(value)
- if mode == "greater" and method == "rmarea":
- grass.fatal(_("You have to specify mode='lesser' with method='rmarea'"))
- if not grass.find_file(infile)["name"]:
- grass.fatal(_("Raster map <%s> not found") % infile)
- if method == "reclass":
- reclass(infile, outfile, limit, clumped, diagonal, mode == "lesser")
- elif method == "rmarea":
- rmarea(infile, outfile, limit, in_proj["meters"])
- grass.message(_("Generating output raster map <%s>...") % outfile)
- def cleanup():
- """!Delete temporary maps"""
- TMPRAST.reverse() # reclassed map first
- for mapp in TMPRAST:
- if method == "rmarea":
- grass.run_command(
- "g.remove", flags="f", type="vector", name=mapp, quiet=True
- )
- else:
- grass.run_command(
- "g.remove", flags="f", type="raster", name=mapp, quiet=True
- )
- if __name__ == "__main__":
- options, flags = grass.parser()
- atexit.register(cleanup)
- sys.exit(main())
|