#!/bin/sh

############################################################################
#
# MODULE:       r.in.wms for GRASS 6
# AUTHOR(S):    Cedric Shock (cedricgrass AT shockfamily.net)
# PURPOSE:      To import data from web mapping servers
# COPYRIGHT:    (C) 2006 by Cedric Shock
#
#               This program is free software under the GNU General Public
#               License (>=v2). Read the file COPYING that comes with GRASS
#               for details.
#
#############################################################################

#%Module
#%  description: Downloads and imports data from WMS servers.
#%  keywords: wms
#%End
#%flag
#% key: l
#% description: List available layers and exit
#% guisection: Request
#%end
#%flag
#% key: d
#% description: Skip to downloading (to resume downloads faster)
#% guisection: Download
#%end
#%flag
#% key: o
#% description: Don't request transparent data
#% guisection: Request
#%end
#%flag
#% key: c
#% description: Clean existing data out of download directory
#% guisection: Download
#%end
#%flag
#% key: k
#% description: Keep band numbers instead of using band color names
#% guisection: Import
#%end
#%flag
#% key: p
#% description: Don't reproject the data, just patch it
#% guisection: Import
#%end
#%flag
#% key: g
#% label: Use GET method instead of POST data method
#% description: This may be needed to connect to servers which lack POST capability
#% guisection: Request
#%end
#%option
#% key: output
#% type: string
#% description: Name for output raster map
#% gisprompt: new,cell,raster
#% required : no
#% guisection: Import
#%end
#%option
#% key: mapserver
#% type: string
#% description: Mapserver to request data from
#% required: yes
#% guisection: Request
#%end
#%option
#% key: layers
#% type: string
#% description: Layers to request from map server
#% multiple: yes
#% required: no
#% guisection: Request
#%end
#%option
#% key: styles
#% type: string
#% description: Styles to request from map server
#% multiple: yes
#% required: no
#% guisection: Request
#%end
#%option
#% key: srs
#% type: string
#% description: Source projection to request from server
#% answer:EPSG:4326
#% guisection: Request
#%end
#%option
#% key: format
#% type: string
#% description: Image format requested from the server
#% options: geotiff,tiff,jpeg,gif,png
#% answer: geotiff
#% required: yes
#% guisection: Request
#%end
#%option
#% key: wmsquery
#% type:string
#% description: Addition query options for server
#% answer: version=1.1.1
#% guisection: Request
#%end
#%option
#% key: maxcols
#% type: integer
#% description: Maximum columns to request at a time
#% answer: 1024
#% required : yes
#% guisection: Request
#%end
#%option
#% key: maxrows
#% type: integer
#% description: Maximum rows to request at a time
#% answer: 1024
#% required : yes
#% guisection: Request
#%end
#%option
#% key: tileoptions
#% type: string
#% description: Additional options for r.tileset
#% required : no
#%end
#%option
#% key: region
#% type: string
#% description: Named region to request data for. Current region used if omitted
#% required : no
#% guisection: Request
#%end
#%option
#% key: folder
#% type: string
#% description: Folder to save downloaded data to
#% required : no
#% guisection: Download
#%end
#%option
#% key: wgetoptions
#% type: string
#% description: Additional options for wget
#% answer: -c -t 5 -nv
#% required : no
#% guisection: Download
#%end
#%option
#% key: curloptions
#% type: string
#% description: Additional options for curl
#% answer: -C - --retry 5 -s -S
#% required : no
#% guisection: Download
#%end
#%option
#% key: method
#% type: string
#% description: Reprojection method to use
#% options:nearest,bilinear,cubic,cubicspline
#% answer:nearest
#% required: yes
#% guisection: Import
#%end
#%option
#% key: cap_file
#% type: string
#% label: Filename to save capabilities XML file to
#% description: Requires list available layers flag
#% required: no
#% guisection: Request
#%end
#%option
#% key: v
#% type: integer
#% description: Verbosity level
#% answer: 1
#%end

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

# save command line
if [ "$1" != "@ARGS_PARSED@" ] ; then
    CMDLINE=`basename "$0"`
    for arg in "$@" ; do
        CMDLINE="$CMDLINE \"$arg\""
    done
    export CMDLINE
    exec g.parser "$0" "$@"
fi

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

# check if we have wget or curl
if [ ! -x "`which wget`" ] ; then
    if [ ! -x "`which curl`" ] ; then
        g.message -e "Either 'wget' or 'curl' is required, please install one first" 
        exit 1
    else
        g.message -v "Using CURL for downloading data."
        USE_CURL=1
    fi
else
    g.message -v "Using WGET for downloading data."
    USE_WGET=1
fi

# check if we have bc -> used in wms.request and wms.download
if [ ! -x "`which bc`" ] ; then
    g.message -e "'bc' is required, please install it first"
    exit 1
fi

# check if we have sed
if [ ! -x "`which sed`" ] ; then
    g.message -e "'sed' is required, please install it first" 
    exit 1
fi

# check if we have gdalwarp -> used in r.in.gdalwarp
if [ $GIS_FLAG_P -ne 1 ] ; then
    if [ ! -x "`which gdalwarp`" ] ; then
        g.message -e "'gdalwarp' is required, please install it first" 
        exit 1
    fi
fi

# turn verbose mode on if requested either way
if [ "$GIS_OPT_V" -lt 2 ] ; then
   if [ -n "$GRASS_VERBOSE" ] && [ "$GRASS_VERBOSE" -ge 2 ] ; then
      GIS_OPT_V=2
   fi
else
   g.message -w "The verbosity option is superseded. Use --verbose instead"
   if [ -n "$GRASS_VERBOSE" ] && [ "$GRASS_VERBOSE" -lt 2 ] ; then
      GRASS_VERBOSE=3
      export GRASS_VERBOSE
   fi
fi


# Remember the intial field seperator
defaultIFS="$IFS"


#####################
# name:     message
# purpose:  displays messages to the user
# usage: message level text

message () {
    if [ "$1" -lt "$GIS_OPT_V" ] ; then
	shift
	echo "$@" 1>&2
    fi
}

#######################################################################
# name:     exitprocedure
# purpose:  removes all temporary files
#
exitprocedure()
{
    g.message -e 'User break!'
    rm -f "$TEMPFILE"*
    exit 1
}
trap "exitprocedure" 2 3 15

BC="bc"
BCARGS="-l"

#######################################################################
# name:     addargument
# purpose:  make arguments for a sub command
#
addargument() {
    if [ -z "$3" ] ; then
	message 1 "Skipping argument for $2"
    else
	eval "$1=\"\$$1 '$2=$3'\""
    fi
}

#######################################################################
# name:     addflag
# purpose:  make arguments for a sub command
#
addflag() {
    if [ $3 -eq 1 ] ; then
	eval "$1=\"\$$1 -$2\""
    fi
}

######################
# name: calculate
# purpose: perform calculations
# usage: varname "expr"

calculate() {
    message 3 "$2"
    c_tmp=`echo "$2" | "$BC" "$BCARGS"`
    eval $1=$c_tmp
}


######################
# name: listLayers
# purpose: list layers available from this server

listLayers() {

	CAPABILITIES_FILE="${TEMPFILE}capabilities.xml"

	STRING="service=WMS&request=GetCapabilities&${GIS_OPT_WMSQUERY}";
	g.message -d message="POST-data: [$STRING]"

	### wget or curl the XML and grep the lines with layer names
	g.message message="List of layers for server <$GIS_OPT_MAPSERVER>:"
	g.message -v message=""

	if [ "$GIS_FLAG_G" -eq 0 ] ; then
	    if [ "$USE_WGET" ] ; then
		wget -nv $GIS_OPT_WGETOPTIONS --post-data="$STRING" \
		  "$GIS_OPT_MAPSERVER" -O "$CAPABILITIES_FILE"
		message 1 "wget $GIS_OPT_WGETOPTIONS --post-data=\"${STRING}\" \"$GIS_OPT_MAPSERVER\" -O \"$CAPABILITIES_FILE\";"
	    else
		curl $GIS_OPT_CURLOPTIONS -o "$CAPABILITIES_FILE" \
		  -d "${STRING}" "$GIS_OPT_MAPSERVER"
		message 1 "curl $GIS_OPT_CURLOPTIONS -o \"$CAPABILITIES_FILE\" -d \"${STRING}\" \"$GIS_OPT_MAPSERVER\""
	    fi
	    g.message -v message=""

	    if [ ! -e "$CAPABILITIES_FILE" ] ; then
		g.message -e "Downloading XML file"
		rm -f "$TEMPFILE"
		exit 1
	    fi

	    # work-around for brain-dead WMSs which want POST-data as part of the GET URL
	    #   (allowed by OGC WMS def S6.3.4)
	    if [ `wc -c < "$CAPABILITIES_FILE"` -eq 0 ] ; then
		g.message -w "Downloaded XML file was empty -- trying another method"
		GIS_FLAG_G=1
	    fi
	fi

	if  [ "$GIS_FLAG_G" -eq 1 ] ; then
	    g.message -v message=""
	    if [ "$USE_WGET" ] ; then
		wget $GIS_OPT_WGETOPTIONS "${GIS_OPT_MAPSERVER}?${STRING}" \
		  -O "$CAPABILITIES_FILE";
		message 1 "wget $GIS_OPT_WGETOPTIONS \"${GIS_OPT_MAPSERVER}?${STRING}\" -O \"$CAPABILITIES_FILE\";"
	    else
		curl $GIS_OPT_CURLOPTIONS -o "$CAPABILITIES_FILE" \
		  "${GIS_OPT_MAPSERVER}?${STRING}"
		message 1 "curl $GIS_OPT_CURLOPTIONS -o \"$CAPABILITIES_FILE\" \"${GIS_OPT_MAPSERVER}?${STRING}\""
	    fi
	    g.message -v message=""
	fi

	if [ ! -e "$CAPABILITIES_FILE" ] ; then
	    g.message -e "Downloading XML file"
	    rm -f "$TEMPFILE"
	    exit 1
	fi

	if [ `wc -c < "$CAPABILITIES_FILE"` -eq 0 ] ; then
	     g.message -e "Downloaded XML file was empty"
	     rm -f "$CAPABILITIES_FILE" "$TEMPFILE"
	     exit 1
	fi

	# use xml2 if we have it
	if [ -x "`which xml2`" ] ; then
		cat "$CAPABILITIES_FILE" | xml2 | \
		  grep "Layer/SRS=\|Layer/Name=\|Layer/Title=\|Style/Name=\|Style/Title=\|Layer/Abstract=" | \
		  sed -e 's|.*Layer/Name=|\
\
 LAYER: |' \
		      -e "s/.*Layer\\/SRS=/  SRS: /" \
		      -e "s/.*Layer\\/Title=/  Title: /" \
		      -e "s/.*Layer\\/Abstract=/  |/" \
		      -e "s/.*Layer\\/Style\\/Name=/  STYLE: /" \
		      -e "s/.*Layer\\/Style\\/Title=/    Style title: /" \
		  > "${TEMPFILE}capabilities.txt"

	else : # xml2 is not available
		g.message -w "The 'xml2' program was not found. Proceeding with internal XML parsing which may not be as reliable."
		sed -e 's/<Layer/\
<Layer/g' \
		    -e 's/<Name>/\
<Name>/g' \
		    -e 's/<Style>/\
<Style>/g' \
		    -e 's/<Title>/\
<Title>/g' \
		    "$CAPABILITIES_FILE" | \
		    grep "Name\|Title\|Style\|Layer" | \
		    sed -e 's/<Layer .*>/-=-=-=-=-\
\
LAYER:/' \
		      -e "s/<\/Layer.*>//" \
		      -e "s/<Name>\s*\([^<]*\)/~\1~/g" \
		      -e "s/<\/Name>\n\?//g" \
		      -e "s/<Style>\n*\s*\(\w*\)/Style: \1/" \
		      -e "s/<\/Style>//" \
		      -e "s/<Title>\(.*\)<\/Title>/\t --\1 /" \
		      -e "s/<\/Title>//" \
		      -e "s/<\/*.*\/*\/*>//" \
		    > "${TEMPFILE}capabilities.txt"
	fi

	if [ `wc -l < "${TEMPFILE}capabilities.txt"` -eq 0 ] ; then
	    g.message message=""
	    g.message -e "Parsing XML file"
	    g.message message="------------------------"
	    cat "$CAPABILITIES_FILE"
	else
	    cat "${TEMPFILE}capabilities.txt"
	fi
	g.message message=""

	if [ -e "$CAPABILITIES_FILE" ] ; then
	    \rm -f "${TEMPFILE}capabilities.txt" "$TEMPFILE"

	    if [ -n "$GIS_OPT_CAP_FILE" ] ; then
		mv "$CAPABILITIES_FILE" "$GIS_OPT_CAP_FILE"
	    else
		\rm -f "$CAPABILITIES_FILE"
	    fi
    
        fi
        exit 0
}


#### Test for listing layers

if [ $GIS_FLAG_L -eq 1 ] ; then
	listLayers
fi

#### Exit if there is no map
# It would be a good idea to test if the map already exists here too

if [ -z "$GIS_OPT_OUTPUT" ] ; then
    g.message -e "No output map specified"
    exit 1
fi

REQUEST=""
DOWNLOAD=""
GDALWARP=""

#Job number one: pick a folder
if [ -z "$GIS_OPT_FOLDER" ] ; then
    GISDBASE=`g.gisenv get=GISDBASE`
    GIS_OPT_FOLDER="$GISDBASE/wms_download"
fi


PREFIX="$GIS_OPT_OUTPUT"
if [ -x "`which wget`" ] ; then
    REQUESTFILE="$GIS_OPT_FOLDER/${PREFIX}_$GIS_OPT_REGION.wget"
else
    REQUESTFILE="$GIS_OPT_FOLDER/${PREFIX}_$GIS_OPT_REGION.curl"
fi

if [ ! -z "$GIS_OPT_REGION" ] ; then
   eval `g.findfile element=windows file="$GIS_OPT_REGION"`
   if [ ! "$file" ] ; then
      g.message -e "Region file '$GIS_OPT_REGION' not found."
      exit 1
   fi
fi

# URL-proof spaces in layer names. (convert other chars to hex too?)
#    HTTP reserved are: ";", "/", "?", ":", "@", "=", "#" and "&"
LAYERS=`echo ${GIS_OPT_LAYERS} | sed -e 's/ /%20/g'`


addflag REQUEST o "$GIS_FLAG_O"
addflag REQUEST c "$GIS_FLAG_C"
addflag REQUEST p "$GIS_FLAG_P"
addargument REQUEST folder "$GIS_OPT_FOLDER"
addargument REQUEST prefix "$PREFIX"
addargument REQUEST region "$GIS_OPT_REGION"
addargument REQUEST mapserver "$GIS_OPT_MAPSERVER"
addargument REQUEST layers "$LAYERS"
addargument REQUEST styles "$GIS_OPT_STYLES"
addargument REQUEST srs "$GIS_OPT_SRS"
addargument REQUEST format "$GIS_OPT_FORMAT"
addargument REQUEST wmsquery "$GIS_OPT_WMSQUERY"
addargument REQUEST maxcols "$GIS_OPT_MAXCOLS"
addargument REQUEST maxrows "$GIS_OPT_MAXROWS"
addargument REQUEST tileoptions "$GIS_OPT_TILEOPTIONS"

if [ $GIS_FLAG_D -eq 0 ] ; then
	message 1 "wms.request $REQUEST"
	eval "\"$GISBASE/etc/r.in.wms/wms.request\" $REQUEST"
	if [ $? -ne 0 ] ; then
	   g.message -e "wms.request failure" 
	   exit 1
	fi
fi

addflag DOWNLOAD g "$GIS_FLAG_G"
addargument DOWNLOAD requestfile "$REQUESTFILE"
addargument DOWNLOAD wgetoptions "$GIS_OPT_WGETOPTIONS"
addargument DOWNLOAD curloptions "$GIS_OPT_CURLOPTIONS"

message 1 "wms.download $DOWNLOAD"
eval "\"$GISBASE/etc/r.in.wms/wms.download\" $DOWNLOAD"


# Job 2: make list of files

FILES=""
COUNT=0

while read line ; do
    if [ -z "$line" ] ; then
	continue
    fi
    eval "$line"
    if [ "$COUNT" -eq 0 ] ; then
	FILES="$OUTPUT_FILE"
    else
	FILES="$FILES,$OUTPUT_FILE"
    fi
    COUNT=`expr "$COUNT" + 1`
done < "$REQUESTFILE"


addflag GDALWARP c 1
addflag GDALWARP k "$GIS_FLAG_K"
addflag GDALWARP p "$GIS_FLAG_P"
addargument GDALWARP input "$FILES"
addargument GDALWARP output "$GIS_OPT_OUTPUT"
addargument GDALWARP method "$GIS_OPT_METHOD"
addargument GDALWARP s_srs "$GIS_OPT_SRS"

# check for error like 'Service Exception Report'
if [ `file -b "$FILES" | grep -c "^HTML\|^XML"` -eq 1 ] ; then
  g.message "#################################"
  cat "$FILES"
  g.message message=""
  rm -f "$TEMPFILE"*
  exit 1
fi

message 1 "r.in.gdalwarp $GDALWARP"
eval "\"$GISBASE/etc/r.in.wms/r.in.gdalwarp\" $GDALWARP"
if [ $? -ne 0 ] ; then
   g.message -e message="r.in.gdalwarp failed"
   exit 1
fi

r.support map="$GIS_OPT_OUTPUT" title="WMS Download" history="" \
  source1=`echo "$GIS_OPT_MAPSERVER" | cut -c-70` \
  source2=`echo "$GIS_OPT_MAPSERVER" | cut -c71-140`
if [ $? -ne 0 ] ; then
   g.message -e message="r.support failed"
   exit 1
fi

LAY_LEN="echo "$GIS_OPT_LAYERS" | wc -c"

r.support map="$GIS_OPT_OUTPUT" history="layers: $GIS_OPT_LAYERS"
if [ -n "$GIS_OPT_STYLES" ] ; then
    r.support map="$GIS_OPT_OUTPUT" history="styles: $GIS_OPT_STYLES"
fi
r.support map="$GIS_OPT_OUTPUT" history=""
r.support map="$GIS_OPT_OUTPUT" history="Source projection: $GIS_OPT_SRS"
if [ "$GIS_FLAG_P" -eq 1 ] ; then
    r.support map="$GIS_OPT_OUTPUT" history="  (imported without reprojection)"
fi
r.support map="$GIS_OPT_OUTPUT" description="generated by r.in.wms" history=""
r.support map="$GIS_OPT_OUTPUT" history="$CMDLINE"


g.message -v message="Map <$GIS_OPT_OUTPUT> written"

# Clean up:
rm -f "$TEMPFILE"*

exit
