#!/usr/bin/env python ############################################################################ # # 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 # i18N import gettext gettext.install('grassmods', os.path.join(os.getenv("GISBASE"), 'locale')) 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())