#!/bin/sh

#
#	geo-code: Convert a street address into a latitude/longitude
#
#	Requires: curl; gpsbabel; bash or ksh;
#		  mysql (if using the gpsdrive.sql output option)
#
#	Donated to the public domain by Rick Richardson <rickr@mn.rr.com>
#		http://home.mn.rr.com/richardsons/sw/geo-code
#
#	Use at your own risk.  Not suitable for any purpose.  Not legal tender.
#
#	$Id: geo-code,v 1.1 2003/01/26 20:27:46 ganter Exp $
#

PROGNAME="$0"

usage() {
	cat <<EOF
Usage:
	`basename $PROGNAME` [options] address citystate_or_zip [country]

		Convert (geocode) a street address into a latitude/longitude.

	`basename $PROGNAME` [options] tele-phone-number

		Convert (geocode) a phone number into a latitude/longitude.

	In either case, the output can be formatted to any of the output
	file types that gpsbabel supports, or directly imported into the
	GpsDrive SQL waypoint database.

Requires:
	curl		http://curl.haxx.se/
	gpsbabel	http://gpsbabel.sourceforge.net/

Options:
	-o format	Output format, -o? for possibilities [$OUTFMT]
			plus "gpsdrive.sql" for direct insertion into MySQL DB
	-n name		The waypoint name, e.g. Bob's House.  The default
			is the street address.
	-t type		The waypoint type, e.g. house, cache, bar [$CODETYPE]
	-q		Quiet. Do not output address confirmation on stderr.
	-S		Alias for -o gpsdrive.sql
	-D level	Debug level
	-U		Retrieve latest version of this script

Countries:
	us, ca, fr, de, it, es, uk

Examples:
	\$ geo-code "123 AnyStreet" 12345 
	123AnyStreet 42.81020 -73.95070 new

	\$ geo-code -t house "123 AnyStreet" 12345 
	123AnyStreet 42.81020 -73.95070 house

	\$ geo-code -n "Bob's House" -t house "123 AnyStreet" 12345 
	BobsHouse 42.81020 -73.95070 house

	\$ geo-code -S -n "Bob" -t house "123 AnyStreet" 12345 
	[waypoint is added to GpsDrive MySQL database]

	\$ geo-code 901-555-1212 
	123AnyStreet 42.81020 -73.95070 new

See Also:
	geo-nearest	http://home.mn.rr.com/richardsons/sw/geo-nearest
EOF

	exit 1
}

#
#       Report an error and exit
#
error() {
	echo "`basename $PROGNAME`: $1" >&2
	exit 1
}

#
#       Set default options, can be overriden on command line or in rc file
#
DEBUG=0
OUTFMT=gpsdrive
COUNTRY=us	# us, ca, fr, de, it,es, uk
SQLUSER=gast	# For -o gpsdrive.sql
SQLPASS=gast	# For -o gpsdrive.sql
SQLDB=geoinfo	# For -o gpsdrive.sql
CODETYPE=new
UPDATEcodeURL=http://home.mn.rr.com/richardsons/sw/geo-code
UPDATEcodeFILE=geo-code.new

#
#	Read RC file, if there is one
#
if [ -f $HOME/.georc ]; then
	. $HOME/.georc
fi

#
#       Process the options
#
SQL=0
NAME=
GURL=
QUIET=0
unset OPTIND
while getopts "n:o:qSs:t:D:Uh?" opt
do
	case $opt in
	n)	NAME="$OPTARG";;
	o)	OUTFMT="$OPTARG";;
	s)	BABELFLAGS="$BABELFLAGS -s";;
	S)	OUTFMT="gpsdrive.sql";;
	t)	CODETYPE="$OPTARG";;
	q)	QUIET=1;;
	D)	DEBUG="$OPTARG";;
	U)	echo "Getting latest version of this script..."
		curl -o$UPDATEcodeFILE "$UPDATEcodeURL"
		echo "Latest version is in $UPDATEcodeFILE"
		exit
		;;
	h|\?)	usage;;
	esac
done
shift `expr $OPTIND - 1`

case "$OUTFMT" in
gpsdrive)
	BABELFLAGS=-s
	;;
gpsdrive.sql)
	OUTFMT=gpsdrive
	SQL=1
	# DEBUG=1
	;;
\?)
	gpsbabel -? | sed '1,/Supported/d'
	echo "gpsdrive.sql          GpsDrive direct MySQL database insertion"
	exit
	;;
esac

case "$#" in
3)
	#
	#	street_address citystate_or_zip country
	#
	ADDRESS=$1
	address=`echo $ADDRESS | tr ' ' '+'`
	csz=`echo $2 | tr ' ' '+'`
	country=`echo $3 | tr '[A-Z]' '[a-z]'`
	case "$country" in
	us|usa)		country=us;;
	ca)		country=ca;;
	fr)		country=fr;;
	de)		country=de;;
	it)		country=it;;
	es)		country=es;;
	uk)		country=uk;;
	*)		error "Unknown country '$3'";;
	esac
	;;
2)
	#
	#	street-address citystate_or_zip
	#
	ADDRESS=$1
	address=`echo $ADDRESS | tr ' ' '+'`
	csz=`echo $2 | tr ' ' '+'`
	country=$COUNTRY
	;;
1)
	#
	# Google-able_phone_or_name
	#
	# first name (or first initial), last name, city (state is optional)
	# first name (or first initial), last name, state
	# first name (or first initial), last name, area code
	# first name (or first initial), last name, zip code
	# phone number, including area code
	# last name, city, state
	# last name, zip code
	#
	ADDRESS=$1
	GURL="http://www.google.com/search?q=$1"
	;;
*)
	usage
esac

if [ "$NAME" = "" ]; then
	NAME="$ADDRESS"
fi

#
#	procedure to make a gpsbabel style file
#
make_style() {
	cat <<EOF
FIELD_DELIMITER		TAB
RECORD_DELIMITER	NEWLINE
BADCHARS		TAB
IFIELD	LAT_DECIMAL, "", "%08.5f"
IFIELD	LON_DECIMAL, "", "%08.5f"
IFIELD	DESCRIPTION, "", "%s"
IFIELD	ICON_DESCR, "", "%s"
EOF
}

#
#	procedure to remove cruft files
#
remove_cruft() {
	for i in $STYLE $COORDS $OUTWAY
	do
		[ -f $i ] && rm -f $i
	done
}

#
#	Main Program
#
TMP=/tmp/geo$$
STYLE=${TMP}.style
COORDS=${TMP}.coords
OUTWAY=${TMP}.way
UA="Mozilla/5.0"

if [ "$GURL" != "" ]; then
	#
	# Treat a single command line argument as a phone number and use
	# Google to look up the Yahoo maps link
	#	
	if [ $DEBUG -gt 0 ]; then
		echo "curl $GURL"
	fi
	URL=`   curl -s -A "$UA" "$GURL" \
		| tee $COORDS \
		| sed -n 's#.*href=http://maps.yahoo.com/\([^>]*\)>.*#\1#p' \
		| head -n1 \
		`
	if [ "$URL" = "" ]; then
		cp $COORDS /tmp/geo.google
		error "Unable to lookup telephone number or name with Google"
	else
		URL="http://maps.yahoo.com/$URL"
	fi
else
	#
	#	Fetch a web page which geocode's the lat/lon
	#
	#	<input type=hidden name=slt value="44.496478">
	#	<input type=hidden name=sln value="-93.941013">
	#
	URL="http://maps.yahoo.com/py/maps.py"
	URL="$URL?BFCat="
	URL="$URL&Pyt=Tmap"
	URL="$URL&addr=$address"
	URL="$URL&csz=$csz"
	URL="$URL&country=$country"	# us, ca, fr, de, it,es, uk
	URL="$URL&Get&nbsp;Map=Get+Map"
fi

if [ $DEBUG -gt 0 ]; then
	echo "curl $URL"
fi

curl -s -A "$UA" "$URL" \
| sed -n \
	-e 's/<title>Yahoo! Maps - \([^<]*\)<.*/\1/p' \
	-e 's/.*name=slt value="\([^"]*\)".*/\1/p' \
	-e 's/.*name=sln value="\([^"]*\)".*/\1/p' \
> $COORDS

if [ $DEBUG -gt 0 ]; then
	cp $COORDS /tmp/geo.coords
fi

#
#	Convert the coords, address, and type to the desired
#	output format.
#
get_latlon() {
	read title
	read lat
	read lon
}

get_latlon < $COORDS;

if [ "$title" != ""  -a "$QUIET" != 1 ]; then
	echo "$title" >&2
fi
if [ "$lat" = "" -o "$lon" = "" ]; then
	error "Cannot determine coordinates of that address"
	remove_cruft
	exit
fi

make_style > $STYLE

echo "$lat	$lon	$NAME	$CODETYPE" \
| gpsbabel $BABELFLAGS -i xcsv,style=$STYLE -f /dev/fd/0 -o $OUTFMT -F $OUTWAY

#
#	Output the data or add it to the MySQL database
#
gpsdrive_add() {
	delcmd="delete from waypoints"
	addcmd="insert into waypoints (name,lat,lon,type)"
	read address extra
	echo "use $SQLDB;"
	echo "$delcmd where name='$NAME' and type='$CODETYPE';"
	echo "$addcmd values ('$NAME','$lat','$lon','$CODETYPE');"
}

if [ -f $OUTWAY ]; then
	if [ $SQL = 1 ]; then
		#
		# add it via mysql
		#
		if [ $QUIET != 1 ]; then
			echo "$NAME $lat $lon $CODETYPE" >&2
		fi
		if [ $DEBUG -gt 0 ]; then
			gpsdrive_add <$OUTWAY
		else
			gpsdrive_add <$OUTWAY | mysql -u$SQLUSER -p$SQLPASS
		fi
	else
		#
		# output to stdout
		#
		cat $OUTWAY
	fi
fi

remove_cruft
