#!/usr/bin/env python
############################################################################
#
# MODULE:       r.colors.stddev
# AUTHOR:       M. Hamish Bowman, Dept. Marine Science, Otago Univeristy,
#                 New Zealand
#               Converted to Python by Glynn Clements
# PURPOSE:      Set color rules based on stddev from a map's mean value.
#
# COPYRIGHT:    (c) 2007,2009-2010 Hamish Bowman, and 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.
#
#############################################################################

#%module
#% description: Sets color rules based on stddev from a raster map's mean value.
#% keyword: raster
#% keyword: color table
#%end
#% option G_OPT_R_MAP
#%end
#%flag
#% key: b
#% description: Color using standard deviation bands
#%end
#%flag
#% key: z
#% description: Force center at zero
#%end

import sys
import os
import atexit
import grass.script as grass

def z(n):
    return mean + n * stddev

def cleanup():
    if tmpmap:
	grass.run_command('g.remove', flags = 'f', type = 'raster',
                      name = tmpmap, quiet = True)

def main():
    global tmpmap
    tmpmap = None

    map = options['map']
    zero = flags['z']
    bands = flags['b']

    if not zero:
	s = grass.read_command('r.univar', flags = 'g', map = map)
	kv = grass.parse_key_val(s)
	global mean, stddev
	mean = float(kv['mean'])
	stddev = float(kv['stddev'])

	if not bands:
	    # smooth free floating blue/white/red
	    rules = '\n'.join([
		"0% blue",
		"%f blue"  % z(-2),
		"%f white" % mean,
		"%f red"   % z(+2),
		"100% red"])
	else:
	    # banded free floating  black/red/yellow/green/yellow/red/black

	    # reclass with labels only works for category (integer) based maps
            #r.reclass input="$GIS_OPT_MAP" output="${GIS_OPT_MAP}.stdevs" << EOF

	    # >3 S.D. outliers colored black so they show up in d.histogram w/ white background
	    rules = '\n'.join([
		"0% black",
		"%f black"  % z(-3),
		"%f red"    % z(-3),
		"%f red"    % z(-2),
		"%f yellow" % z(-2),
		"%f yellow" % z(-1),
		"%f green"  % z(-1),
		"%f green"  % z(+1),
		"%f yellow" % z(+1),
		"%f yellow" % z(+2),
		"%f red"    % z(+2),
		"%f red"    % z(+3),
		"%f black"  % z(+3),
		"100% black"])
    else:
	tmpmap = "r_col_stdev_abs_%d" % os.getpid()
	grass.mapcalc("$tmp = abs($map)", tmp = tmpmap, map = map)

	# data centered on 0  (e.g. map of deviations)
	info = grass.raster_info(tmpmap)
	maxv = info['max']

	# current r.univar truncates percentage to the base integer
	s = grass.read_command('r.univar', flags = 'eg', map = map, percentile = [95.45,68.2689,99.7300])
	kv = grass.parse_key_val(s)

	stddev1 = float(kv['percentile_68_2689'])
	stddev2 = float(kv['percentile_95_45'])
	stddev3 = float(kv['percentile_99_73'])

	if not bands:
	    # zero centered smooth blue/white/red
	    rules = '\n'.join([
		"%f blue" % -maxv,
		"%f blue" % -stddev2,
		"0 white",
		"%f red"  % stddev2,
		"%f red"  % maxv])
	else:
	    # zero centered banded  black/red/yellow/green/yellow/red/black

	    # >3 S.D. outliers colored black so they show up in d.histogram w/ white background
	    rules = '\n'.join([
		"%f black" % -maxv,
		"%f black" % -stddev3,
		"%f red" % -stddev3,
		"%f red" % -stddev2,
		"%f yellow" % -stddev2,
		"%f yellow" % -stddev1,
		"%f green" % -stddev1,
		"%f green" % stddev1,
		"%f yellow" % stddev1,
		"%f yellow" % stddev2,
		"%f red" % stddev2,
		"%f red" % stddev3,
		"%f black" % stddev3,
		"%f black" % maxv,
		])

    grass.write_command('r.colors', map = map, rules = '-', stdin = rules)

if __name__ == "__main__":
    options, flags = grass.parser()
    atexit.register(cleanup)
    main()
