#!/bin/sh
############################################################################
#
# MODULE:       v.colors
# AUTHOR:       M. Hamish Bowman, Dept. Marine Science, Otago Univeristy,
#                 New Zealand
# PURPOSE:      Populate a GRASSRGB column with a color map and data column
#		Helper script for thematic mapping tasks
#
# COPYRIGHT:    (c) 2008 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: Set color rules for features in a vector using a numeric attribute column.
#% keywords: vector, color table
#%End
#% option
#% key: map
#% type: string
#% gisprompt: old,vector,vector
#% key_desc: name
#% description: Name of input vector map 
#% required: yes
#%end
#%option
#% key: column
#% type: string
#% description: Name of column containing numeric data
#% gisprompt: old_dbcolumn,dbcolumn,dbcolumn
#% required : yes
#%end
#%option
#% key: layer
#% type: integer
#% description: Layer number of data column
#% gisprompt: old_layer,layer,layer
#% answer: 1
#% required: no
#%end
#%Option
#% key: rgb_column
#% type: string
#% required: no
#% description: Name of color column to populate
#% gisprompt: old_dbcolumn,dbcolumn,dbcolumn
#% answer: GRASSRGB
#% guisection: Colors
#%End
#% option
#% key: range
#% type: double
#% required: no
#% multiple: no
#% key_desc: min,max
#% description: Manually set range (min,max)
#%End
#% option
#% key: color
#% type: string
#% key_desc: style
#% options: aspect,aspectcolr,bcyr,bgyr,byg,byr,celsius,corine,curvature,differences,elevation,etopo2,evi,gdd,grey,grey1.0,grey255,grey.eq,grey.log,gyr,ndvi,population,precipitation,rainbow,ramp,random,ryb,ryg,sepia,slope,srtm,terrain,wave
#% description: Type of color table
#% required: no
#% guisection: Colors
#%end
#%Option
#% key: raster
#% type: string
#% required: no
#% description: Raster map name from which to copy color table
#% gisprompt: old,cell,raster
#% guisection: Colors
#%End
#%Option
#% key: rules
#% type: string
#% required: no
#% description: Path to rules file
#% gisprompt: old_file,file,input
#% guisection: Colors
#%End
#%Flag
#% key: s
#% description: Save placeholder raster map for use with d.legend
#%End
#%Flag
#% key: n
#% description: Invert colors
#% guisection: Colors
#%End


## TODO: implement -e (equalized) and -g (logarithmic) methods in r.colors
##   'v.db.select column= | wc -l' to set region size (1xLength)
##   then create r.in.ascii 1xLength matrix with data (WITHOUT uniq)
##   and run r.colors on that raster map.
##      or
##   v.to.rast, r.colors -g, then parse colr/ file. But that's resolution dependent


if  [ -z "$GISBASE" ] ; then
    echo "You must be in GRASS GIS to run this program." 1>&2
    exit 1
fi

if [ "$1" != "@ARGS_PARSED@" ] ; then
    exec g.parser "$0" "$@"
fi


cleanup()
{
   if [ -n "$TEMPFILE" ] ; then
      \rm "$TEMPFILE" "${TEMPFILE}_vcol.sql"
   fi
   eval `g.findfile elem=windows file="tmp_vcolors.$$" | grep '^name='`
   if [ -n "$name" ] ; then
      unset WIND_OVERRIDE
      g.remove region="tmp_vcolors.$$" --quiet
   fi
   eval `g.findfile elem=cell file="tmp_colr_$$" | grep '^name='`
   if [ -n "$name" ] ; then
      g.remove rast="tmp_colr_$$" --quiet
   fi
}
trap "cleanup" 2 3 15


### setup enviro vars ###
eval `g.gisenv`
: ${GISBASE?} ${GISDBASE?} ${LOCATION_NAME?} ${MAPSET?}

#### does map exist in CURRENT mapset?
eval `g.findfile element=vector file="$GIS_OPT_MAP" mapset="$MAPSET"`
VECT_MAPSET=`echo "$GIS_OPT_MAP" | grep '@' | cut -f2 -d'@'`
if [ -z "$VECT_MAPSET" ] ; then
   VECT_MAPSET="$MAPSET"
fi
if [ ! "$file" ] || [ "$VECT_MAPSET" != "$MAPSET" ] ; then
   g.message -e "Vector map <$GIS_OPT_MAP> not found in current mapset"
   exit 1
else
   VECTOR=`echo "$GIS_OPT_MAP" | cut -f1 -d'@'`
fi



# sanity check mutually exclusive color options
CTEST=0
for OPT in COLOR RASTER RULES ; do
   eval "OPTNAME=\$GIS_OPT_${OPT}"
   if [ -n "$OPTNAME" ] ; then
      CTEST=`expr $CTEST + 1`
   fi
done
if [ "$CTEST" -ne 1 ] ; then
    g.message -e "Pick one of color, rules, or raster options"
    exit 1
fi

if [ -n "$GIS_OPT_COLOR" ] ; then
   #### check the color rule is valid
   COLOR_OPTS=`ls "$GISBASE/etc/colors/"`
   COLOR_OPTS="`echo "$COLOR_OPTS" | tr '\n' ' '` random grey.eq grey.log rules"

   HAVE_COLR=0
   for COLR in $COLOR_OPTS ; do
       if [ "$COLR" = "$GIS_OPT_COLOR" ] ; then
          HAVE_COLR=1
          break
       fi
   done
   if [ "$HAVE_COLR" -eq 0 ] ; then
       g.message -e "Invalid color rule <$GIS_OPT_COLOR>"
       echo "       Valid options are: $COLOR_OPTS"
       exit 1
   fi
elif [ -n "$GIS_OPT_RASTER" ] ; then
   r.info -r "$GIS_OPT_RASTER" > /dev/null
   if [ $? -ne 0 ] ; then
       g.message -e "Unable to open raster map <$GIS_OPT_RASTER>"
       exit 1
   fi
elif [ -n "$GIS_OPT_RULES" ] ; then
   if [ ! -r "$GIS_OPT_RULES" ] ; then
       g.message -e "Unable to read color rules file <$GIS_OPT_RULES>"
       exit 1
   fi
fi


#### column checks
# check input data column 
NCOLUMN_TYPE=`v.info -c map="$GIS_OPT_MAP" layer="$GIS_OPT_LAYER" --quiet | grep -i "|$GIS_OPT_COLUMN$" | cut -f1 -d'|'`
if [ -z "$NCOLUMN_TYPE" ] ; then
    g.message -e "Column <$GIS_OPT_COLUMN> not found"
    exit 1
elif [ "$NCOLUMN_TYPE" != "INTEGER" ] && [ "$NCOLUMN_TYPE" != "DOUBLE PRECISION" ] ; then
    g.message -e "Column <$GIS_OPT_COLUMN> is not numeric"
    exit 1
fi
#g.message "column <$GIS_OPT_COLUMN> is type [$NCOLUMN_TYPE]"

# check if GRASSRGB column exists, make it if it doesn't
TABLE=`v.db.connect -gl map="$GIS_OPT_MAP" layer="$GIS_OPT_LAYER" fs="|" | cut -f2 -d'|'`
COLUMN_TYPE=`v.info -c map="$GIS_OPT_MAP" layer="$GIS_OPT_LAYER" --quiet | grep -i "|$GIS_OPT_RGB_COLUMN$" | cut -f1 -d'|'`
if [ -z "$COLUMN_TYPE" ] ; then
    # RGB Column not found, create it
    g.message "Creating column <$GIS_OPT_RGB_COLUMN> ..."
    v.db.addcol map="$GIS_OPT_MAP" layer="$GIS_OPT_LAYER" column="$GIS_OPT_RGB_COLUMN varchar(11)"
    if [ $? -ne 0 ] ; then
       g.message -e "Creating color column"
       exit 1
    fi
elif [ "$COLUMN_TYPE" != "CHARACTER" -a "$COLUMN_TYPE" != "TEXT" ] ; then
    g.message -e "Column <$GIS_OPT_RGB_COLUMN> is not of compatible type (found $COLUMN_TYPE)"
    exit 1
else
    NUM_CHARS=`db.describe -c "$TABLE" | grep -i " $GIS_OPT_RGB_COLUMN:" | cut -f4 -d':'`
    if [ "$NUM_CHARS" -lt 11 ] ; then
       g.message -e "Color column <$GIS_OPT_RGB_COLUMN> is not wide enough (needs 11 characters)"
       exit 1
    fi
fi


# find data range
if [ -n "$GIS_OPT_RANGE" ] ; then
   #order doesn't matter
   MINVAL=`echo "$GIS_OPT_RANGE" | grep '[[:digit:]]' | grep ',' | cut -f1 -d','`
   MAXVAL=`echo "$GIS_OPT_RANGE" | grep '[[:digit:]]' | grep ',' | cut -f2 -d','`
else
   g.message "Scanning values ..."
   MINVAL=`v.db.select map="$GIS_OPT_MAP" column="$GIS_OPT_COLUMN" layer="$GIS_OPT_LAYER" | sort -n | grep '^[-0-9]' | head -n 1`
   MAXVAL=`v.db.select map="$GIS_OPT_MAP" column="$GIS_OPT_COLUMN" layer="$GIS_OPT_LAYER" | sort -n | grep '^[-0-9]' | tail -n 1`
fi
g.message -v message=" min=[$MINVAL]  max=[$MAXVAL]"
if [ -z "$MINVAL" ] || [ -z "$MAXVAL" ] ; then
   g.message -e "Scanning data range"
   exit 1
fi


# setup internal region
g.region save="tmp_vcolors.$$"
WIND_OVERRIDE="tmp_vcolors.$$"
export WIND_OVERRIDE
g.region rows=2 cols=2

# create dummy raster map
if [ "$NCOLUMN_TYPE" = "INTEGER" ] ; then
   r.mapcalc "tmp_colr_$$ = if( row() == 1, $MINVAL, $MAXVAL)"
else
   r.mapcalc "tmp_colr_$$ = double( if( row() == 1, $MINVAL, $MAXVAL) )"
fi


if [ -n "$GIS_OPT_COLOR" ] ; then
   COLOR_CMD="color=$GIS_OPT_COLOR"
elif [ -n "$GIS_OPT_RASTER" ] ; then
   COLOR_CMD="raster=$GIS_OPT_RASTER"
elif [ -n "$GIS_OPT_RULES" ] ; then
   COLOR_CMD="rules=$GIS_OPT_RULES"
fi
if [ $GIS_FLAG_N -eq 1 ] ; then
   FLIP_FLAG="-n"
else
   FLIP_FLAG=""
fi
r.colors map="tmp_colr_$$" "$COLOR_CMD" $FLIP_FLAG --quiet



TEMPFILE="`g.tempfile pid=$$`"
if [ $? -ne 0 ] || [ -z "$TEMPFILE" ] ; then
    g.message -e "Unable to create temporary file"
    cleanup
    exit 1
fi


# calculate colors and write SQL command file
g.message "Looking up colors ..."
v.db.select map="$GIS_OPT_MAP" layer="$GIS_OPT_LAYER" column="$GIS_OPT_COLUMN" | \
  sort -n | grep '^[-0-9]' | uniq | \
  r.what.color -i in="tmp_colr_$$" | sed -e 's/: /|/' | grep -v '|\*$' | \
  ( while read LINE ; do
      #g.message message="LINE=[$LINE]"
      VALUE=`echo $LINE | cut -f1 -d'|'`
      COLR=`echo $LINE | cut -f2 -d'|'`
      echo "UPDATE $TABLE SET $GIS_OPT_RGB_COLUMN = '$COLR' WHERE $GIS_OPT_COLUMN = $VALUE;" >> "${TEMPFILE}_vcol.sql"
  done )


if [ ! -e "${TEMPFILE}_vcol.sql" ] ; then
    g.message -e "No values found in color range"
    cleanup
    exit 1
fi

# apply SQL commands to update the table with values
NUM_RULES=`wc -l < "${TEMPFILE}_vcol.sql"`
g.message "Writing $NUM_RULES colors ..."
#less "$TEMPFILE"
db.execute input="${TEMPFILE}_vcol.sql"
if [ $? -ne 0 ] ; then
   g.message -e "Processing SQL transaction"
   cleanup
   exit 1
fi


if [ $GIS_FLAG_S -eq 1 ] ; then
   g.rename rast="tmp_colr_$$","vcolors_$$" --quiet
   g.message "Raster map containing color rules saved to <vcolors_$$>"
   # TODO save full v.colors command line history
   r.support map="vcolors_$$" history="" \
	source1="vector map=$GIS_OPT_MAP" \
	source2="column=$GIS_OPT_COLUMN" \
	title="Dummy raster to use as thematic vector legend" \
	description="generated by v.colors using r.mapcalc"
   r.support map="vcolors_$$" \
	history="RGB saved into <$GIS_OPT_RGB_COLUMN> using <$GIS_OPT_COLOR$GIS_OPT_RASTER$GIS_OPT_RULES>"
else
   g.remove rast="tmp_colr_$$" --quiet
fi
cleanup

exit 0
#v.db.dropcol map=vcol_test col=GRASSRGB
#d.vect -a vcol_test icon=basic/circle color=none size=8

