#!/bin/sh
############################################################################
#
# MODULE:       d.frontline
# AUTHOR(S):    Alexander Muriy
#               (Institute of Environmental Geoscience, Moscow, Russia)  
#               e-mail: amuriy AT gmail DOT com 
#
# PURPOSE:      Displays frontlines on the graphic monitor using d.graph
#               (optionally save graph file and ps.map file)
#
# COPYRIGHT:    (C) 05-06/2011 Alexander Muriy / GRASS Development Team
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
############################################################################
#%Module 
#%  description: Display different types of frontlines on the graphic monitor using d.graph (optionally save graph file and ps.map file)
#%  keywords: display, graphics, vector, symbology
#%End
#%Option
#%  key: map
#%  type: string
#%  required: yes
#%  multiple: no
#%  key_desc: name
#%  description: Name of input vector map
#%  gisprompt: old,vector,vector
#%  guisection: Select
#%End
#%Option
#%  key: where
#%  type: string
#%  description: The WHERE statement for data filtering
#%  label:  WHERE conditions of SQL query statement without 'where' keyword
#%  required : no
#%  guisection: Select
#%End
#%Option
#%  key: hcolor
#%  type: string
#%  description: Standard color name or R:G:B 
#%  label: Highlight color for vector interactive selecting
#%  required : no
#%  answer: yellow
#%  gisprompt: old_color,color,color
#%  guisection: Select
#%End
#%Option
#%  key: list
#%  type: string
#%  description: Example: 1,3,7-9,13
#%  label: Category number(s)
#%  required : no
#%  guisection: Select
#%End
#%Option
#%  key: layer
#%  type: integer
#%  label: Layer number. If -1, all layers are extracted.
#%  description: A single vector map can be connected to multiple database tables. This number determines which table to use.
#%  required : no
#%  answer: 1
#%  guisection: Select
#%End
#%Option
#%  key: side
#%  type: string
#%  description: Side of the frontline
#%  options: left,right,both  
#%  answer: left
#%End
#%Option
#%  key: interval
#%  type: double
#%  required: yes
#%  description: Interval between symbols (in mapset units)   
#%End
#%Option
#%  key: symbol
#%  type: string
#%  required: yes
#%  multiple: no
#%  description: Type of symbol (<front/...> symbols will be produced by script at first use: front/arrow_int, front/arrow_ext, front/box, front/circle, front/half_box, front/line, front/triangle). May be any symbol from $GISBASE/etc/symbol/ or $MAPSET/symbol/ directories.
#%  guisection: Symbol
#%End
#%Option
#%  key: size
#%  type: integer
#%  required: no
#%  description: Symbol size 
#%  answer: 20
#%  guisection: Symbol
#%End
#%Option
#%  key: line_color
#%  type: string
#%  required: no
#%  description: Color for line (standard color name or R:G:B)
#%  answer: black
#%  gisprompt: old_color,color,color
#%  guisection: Symbol
#%End
#%Option
#%  key: fill_color
#%  type: string
#%  required: no
#%  description: Color for fill (standard color name or R:G:B) 
#%  answer: grey
#%  gisprompt: old_color,color,color
#%  guisection: Symbol
#%End
#%Option
#%  key: width
#%  type: integer
#%  required: no
#%  description: Width of symbol's line  
#%  answer: 0
#%  guisection: Symbol
#%End
#%Option
#%  key: graph
#%  type: string
#%  required: no
#%  multiple: no
#%  key_desc: name
#%  description: Output file to save the d.graph commands 
#%  gisprompt: new_file,file,output
#%End
#%Option
#%  key: psmap
#%  type: string
#%  required: no
#%  multiple: no
#%  key_desc: name
#%  description: Output file to save the ps.map commands 
#%  gisprompt: new_file,file,output
#%End
#%Flag
#%  key: m
#%  description: Select vector features interactively (with a mouse) 
#%  guisection: Select
#%End
#%Flag
#%  key: s 
#%  description: Save graphics commands in file(s) (for use with d.graph and/or ps.map)
#%End


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


## set environment so that awk works properly in all languages ##
unset LC_ALL
export LC_NUMERIC=C


############################################################
cleanup()
{ 
    g.mremove -f vect="TMP*" --quiet
    \rm -f $TMP1* $TMP2* $TMP3* $TMP4* $TMP5*
    
}
############################################################

## what to do in case of user break:
exitprocedure()
{
    echo "User break!"
    cleanup
    exit 1
}

## shell check for user break (signal list: trap -l)
trap "exitprocedure" 2 3 15

############################################################


## check for awk

if [ ! -x "$(which awk)" ] ; then
    g.message -e "awk required, please install awk or gawk first"
    exit 1
fi


## does map exist?
eval "$(g.gisenv)"
eval "$(g.findfile mapset="$MAPSET" element=vector file="$GIS_OPT_MAP")"
if [ ! "$file" ]; then
    g.message -e "Vector map <"$GIS_OPT_MAP"> not found"
    exit 1
fi


## check for lines and boundaries in input map

eval "$(v.info -t "$GIS_OPT_MAP")"

if [ "$lines" -eq 0 ] && [ "$boundaries" -eq 0 ]; then
    g.message -e "Input vector map not contain lines or boundaries"
    exit 1
fi

# for vars in "$(v.info -t "$GIS_OPT_MAP" | cut -d"=" -f1)"; do
#     unset -v "$vars"
# done

## check if monitor started

if [ "$(d.mon -p | cut -f1 -d' ')" != "Currently" ] ; then
    g.message -e "No active monitors. Please start <d.mon ...> first"
    exit 1
fi


## check for "-m" flag and "where" option

if [ "$GIS_FLAG_M" -eq 1 ] && [ -n "$GIS_OPT_WHERE" -o -n "$GIS_OPT_LIST" ]; then
    g.message -e "Please choose only one of selection methods:
interactive (flag <"-m">) OR non-interactive -- (<"where"> statement or <"list"> of category values )"
    exit 1
fi 

## check for "list" and "where" options

if [ -n "$GIS_OPT_WHERE" ] && [ -n "$GIS_OPT_LIST" ]; then
    g.message -e "Please choose only one of non-interactive selection methods: <"where"> statement or <"list"> of category values"
    exit 1
fi


## is input vector displayed now ?

d.save -oc | grep -e "d.vect map=" | cut -d'"' -f2 | grep -e "$GIS_OPT_MAP" --quiet

if [ "$?" -eq 1 ]; then
    g.message -e "Input vector map <""$GIS_OPT_MAP""> is not on the monitor. Please use <d.vect "$GIS_OPT_MAP" ..> to display it"
    exit 1
fi

############################################################


## setup temporary files ##

# create temporary file for segments azimuth
TMP1="$(g.tempfile pid=$$)"
if [ "$?" -ne 0 ] || [ -z "$TMP1" ] ; then
    g.message -e "ERROR: Unable to create temporary files."
    exit 1
fi

# create temporary file for symbols' points coordinates 
TMP2="$(g.tempfile pid=$$)"
if [ "$?" -ne 0 ] || [ -z "$TMP2" ] ; then
    g.message -e "ERROR: Unable to create temporary files."
    exit 1
fi

# create temporary file for points to lines query 
TMP3="$(g.tempfile pid=$$)"
if [ "$?" -ne 0 ] || [ -z "$TMP3" ] ; then
    g.message -e "ERROR: Unable to create temporary files."
    exit 1
fi

# create temporary file for d.graph commands file
TMP4="$(g.tempfile pid=$$)"
if [ "$?" -ne 0 ] || [ -z "$TMP4" ] ; then
    g.message -e "ERROR: Unable to create temporary files."
    exit 1
fi

# create temporary file for d.graph commands file
TMP5="$(g.tempfile pid=$$)"
if [ "$?" -ne 0 ] || [ -z "$TMP4" ] ; then
    g.message -e "ERROR: Unable to create temporary files."
    exit 1
fi

# create temporary file for psmap file
TMP6="$(g.tempfile pid=$$)"
if [ "$?" -ne 0 ] || [ -z "$TMP5" ] ; then
    g.message -e "ERROR: Unable to create temporary files."
    exit 1
fi

############################################################


## vector selecting 

if [ "$GIS_FLAG_M" -eq 1 ]; then
    echo "Select vector(s) with mouse
- L: draw box with left mouse button to select
- M: draw box with middle mouse button to remove from display
- R: quit and save selected vectors to new map"
    
    d.extract input="$GIS_OPT_MAP" output=TMP_select type=line,boundary hcolor="$GIS_OPT_HCOLOR" --quiet
elif  [ "$GIS_FLAG_M" -eq 0 -a -z "$GIS_OPT_WHERE" -a -z "$GIS_OPT_LIST" ]; then
    g.copy vect="$GIS_OPT_MAP",TMP_select --quiet
fi


if [ -n "$GIS_OPT_WHERE" ]; then
    v.extract -t input="$GIS_OPT_MAP" output=TMP_select type=line,boundary where="$GIS_OPT_WHERE" layer="$GIS_OPT_LAYER" --quiet
elif [ -n "$GIS_OPT_LIST" ]; then
    v.extract -t input="$GIS_OPT_MAP" output=TMP_select type=line,boundary list="$GIS_OPT_LIST" layer="$GIS_OPT_LAYER" --quiet
fi


## TODO: maybe use v.segment for more precise segments splitting ??? 
## In that case --> generate split-file with points for v.segment with v.to.db 
## (line length) and AWK 


## bounds to lines (if needed)

eval "$(v.info -t TMP_select)"
if [ "$boundaries" -ne 0 ]; then
    v.type input=TMP_select output=TMP_select_lines type=boundary,line --quiet
else
    g.rename vect=TMP_select,TMP_select_lines --quiet
fi

# for vars in "$(v.info -t TMP_select_lines | cut -d"=" -f1)"; do
#     unset -v "$vars"
# done


## split line into segments; delete old cats, add new cats

v.split input=TMP_select_lines output=TMP_select_lines_split vertices=2 --quiet > /dev/null

v.category input=TMP_select_lines_split output=TMP_select_lines_split_catdel option=del --quiet 

v.category input=TMP_select_lines_split_catdel output=TMP_select_lines_split_catdel_cats option=add step=1 --quiet 

SEGM=TMP_select_lines_split_catdel_cats


## find azimuth of each segment in a line or boundary

v.to.db -p map="$SEGM" option=azimuth --quiet >> "$TMP1"


## take points for future symbols 

v.to.points -t -i input=TMP_select_lines type=line dmax="$GIS_OPT_INTERVAL" output=TMP_pts --quiet 


## take coordinates of points 

v.to.db -p map=TMP_pts layer=2 option=coor --quiet >> "$TMP2"


## points to segments' categories query

v.distance -p from=TMP_pts to="$SEGM" from_layer=2 to_type=line,boundary upload=cat column=dummy_nearest_cats --quiet >> "$TMP3"

awk 'NR>1 {print $0}' "$TMP3" | tr '|' ' '  > "$TMP3.clean"


## 

awk -F"|" 'BEGIN { OFMT="%.6f" } {print $1,$2,$3}' "$TMP2"  >> "$TMP2.pts.coor"

join -j 1 "$TMP2.pts.coor" "$TMP3.clean" > "$TMP2.pts.segm"


## find angles of symbols

left_front()
{
    awk -F"|" 'BEGIN { OFMT="%.0f" } {print $1,450-$2}' "$TMP1" > "$TMP1.left.angle"
    
    awk 'NR==FNR{a[$1]=$2;next}$4 in a{$4=a[$4]}1' "$TMP1.left.angle" "$TMP2.pts.segm" > "$TMP1.left.table"
    
    awk '{print "'"rotation"'", $4, "'"symbol $GIS_OPT_SYMBOL"'", "'"$GIS_OPT_SIZE"'", $2, $3, "'"$GIS_OPT_LINE_COLOR"'", "'"$GIS_OPT_FILL_COLOR"'", "'"width"'", "'"$GIS_OPT_WIDTH"'"}' "$TMP1.left.table" >> "$TMP4"
    if [ -n "$GIS_OPT_PSMAP" ]; then
	awk '{print "'"point"'", $2, $3, "'"color $GIS_OPT_LINE_COLOR"'", "'"fcolor $GIS_OPT_FILL_COLOR"'", "'"symbol $GIS_OPT_SYMBOL"'", "'"size $GIS_OPT_SIZE"'", "'"rotate"'", $4, "'"end"'"}' "$TMP1.left.table" >> "$TMP6" 
    fi
}


right_front()
{
    awk -F"|" 'BEGIN { OFMT="%.0f" } {print $1,270-$2}' "$TMP1" > "$TMP1.right.angle"
    
    awk 'NR==FNR{a[$1]=$2;next}$4 in a{$4=a[$4]}1' "$TMP1.right.angle" "$TMP2.pts.segm" > "$TMP1.right.table"
    
    awk '{print "'"rotation"'", $4, "'"symbol $GIS_OPT_SYMBOL"'", "'"$GIS_OPT_SIZE"'", $2, $3, "'"$GIS_OPT_LINE_COLOR"'", "'"$GIS_OPT_FILL_COLOR"'", "'"width"'", "'"$GIS_OPT_WIDTH"'"}' "$TMP1.right.table" >> "$TMP4"
    if [ -n "$GIS_OPT_PSMAP" ]; then
	awk '{print "'"point"'", $2, $3, "'"color $GIS_OPT_LINE_COLOR"'", "'"fcolor $GIS_OPT_FILL_COLOR"'", "'"symbol $GIS_OPT_SYMBOL"'", "'"size $GIS_OPT_SIZE"'", "'"rotate"'", $4, "'"end"'"}' "$TMP1.right.table" >> "$TMP6" 
    fi
}


case "$GIS_OPT_SIDE" in
    left)
	left_front
	;;

    right)
	right_front
	;;

    both)
	left_front
	right_front
	;;
    *)
esac

## make custom front symbols in the user's mapset ($MAPSET/symbol/type/name) ??? 

echo "VERSION 1.0
BOX -0.5 -0.5 1.5 1.5
STRING
  LINE
    0 0
    0 1.5
  END
END
STRING
  LINE
    0.4 0.75
    0 0
  END
END
STRING
  LINE
    -0.4 0.75
    0 0
  END
END" > "$TMP5.arrow.int"

echo "VERSION 1.0
BOX -0.5 -0.5 1.5 1.5
STRING
  LINE
    0 0
    0 1.5
  END
END
STRING
  LINE
    0.4 0.75
    0 1.5
  END
END
STRING
  LINE
    -0.4 0.75
    0 1.5
  END
END" > "$TMP5.arrow.ext"

echo "VERSION 1.0
BOX -0.5 -0.5 0.5 0.5
POLYGON
  RING
    LINE
      -0.2 0
      -0.2 0.4
      0.2 0.4
      0.2 0
    END
  END
END" > "$TMP5.box"

echo "VERSION 1.0
BOX -0.5 -0.5 0.5 0.5
POLYGON
  RING
    ARC 0 0 0.333 0 180
  END
END" > "$TMP5.circle"

echo "VERSION 1.0
BOX -0.5 -0.5 0.5 0.5
POLYGON
  RING
    LINE
      -0.2 0
      -0.2 0.2
      0.2 0.2
      0.2 0
    END
  END
END" > "$TMP5.half_box"

echo "VERSION 1.0
BOX -0.5 -0.5 0.5 0.5
STRING
  LINE
    0 0
    0 0.5
  END
END" > "$TMP5.line"

echo "VERSION 1.0
BOX -0.5 -0.5 0.5 0.5
POLYGON
  RING
    LINE
      -0.25 0
      0 0.5
      0.25 0
    END
  END
END" > "$TMP5.triangle"


eval "$(g.gisenv)"

symbol_path="$GISDBASE"/"$LOCATION_NAME"/"$MAPSET"/symbol/front

if [ ! -d "$symbol_path" ]; then
    mkdir -p "$symbol_path"
    cp -f "$TMP5.arrow.int" "$symbol_path"/arrow_int
    cp -f "$TMP5.arrow.ext" "$symbol_path"/arrow_ext
    cp -f "$TMP5.box" "$symbol_path"/box
    cp -f "$TMP5.circle" "$symbol_path"/circle
    cp -f "$TMP5.half_box" "$symbol_path"/half_box
    cp -f "$TMP5.line" "$symbol_path"/line
    cp -f "$TMP5.triangle" "$symbol_path"/triangle
fi


## format d.graph input file and ps.map points file

awk '{print $1,$2,"'"\n"'",$10,$11,"'"\n"'",$3,$4,$5,$6,$7,$8,$9}' "$TMP4" >> "$TMP4.fmt"

awk '{print $1,$2,$3,"'"\n"'",$4,$5,"'"\n"'",$6,$7,"'"\n"'",$8,$9,"'"\n"'",$10,$11,"'"\n"'",$12,$13,"'"\n"'","'"end"'"}' "$TMP6" >> "$TMP6.fmt"


## draw frontlines and (optionally) save graph file and psmap file   

if [ "$GIS_FLAG_S" -eq 0 ] && [ -n "$GIS_OPT_GRAPH" ]; then
    g.message -e "Please use flag <"-s"> to save line graphics in file <"$GIS_OPT_GRAPH">"
    cleanup
    exit 1
fi   
    

if [ "$GIS_FLAG_S" -eq 1 ]; then
    if [ -n "$GIS_OPT_GRAPH" ]; then
	cp -f "$TMP4.fmt" "$GIS_OPT_GRAPH"
	d.graph -m input="$GIS_OPT_GRAPH" color=none
    else
	SYMBOL="$(echo $GIS_OPT_SYMBOL | tr '/' '_')"
	GRAPH_FILE=""$GISDBASE"/"$LOCATION_NAME"/"$GIS_OPT_MAP"__"$SYMBOL"_"$GIS_OPT_INTERVAL"_"$GIS_OPT_SIDE"_"$GIS_OPT_LINE_COLOR"".graph
#	cp -f "$TMP4.fmt" "$GRAPH_FILE"
	cat "$TMP4.fmt" >> "$GRAPH_FILE"
	d.graph -m input="$GRAPH_FILE" color=none
    fi
else 
    d.graph -m input="$TMP4.fmt" color=none
fi


if [ "$GIS_FLAG_S" -eq 0 ] && [ -n "$GIS_OPT_PSMAP" ]; then
    g.message -e "Please use flag <"-s"> to save line graphics in file <"$GIS_OPT_PSMAP"> for use with ps.map" 
    cleanup
    exit 1
fi

if [ -n "$GIS_OPT_PSMAP" ]; then
#    cp -f "$TMP6.fmt" "$GIS_OPT_PSMAP"
    cat "$TMP6.fmt" >> "$GIS_OPT_PSMAP" 
fi


cleanup

exit 0