#!/bin/bash # # DO NOT EDIT! This file is generated from geo-code.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 # # Use at your own risk. Not suitable for any purpose. Not legal tender. # # $Id: geo-code.sh,v 1.15 2007/03/12 14:04:38 rick Exp $ # PROGNAME="$0" usage() { cat <Long.fraq. plus "mindec" for just DD MM.MMMDD MM.MMM. plus "map[,geo-map-opts]" to display a geo-map. -n name The waypoint name, e.g. Bob's House. The default is the street address. Percent escapes can be used: %d/%D for DegDec lat/lon, %m/%M for MinDec lat/lon, %a for address, %c for citystate_or_zip, %p for phone -s Output shortened names (a gpsbabel option) -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 -a For SQL, delete existing record only if it matches all fields. Otherwise, delete it if it matches just the name and the type. -D level Debug level -U Retrieve latest version of this script COUNTRIES at, be, ca, dk, fr, de, it, lu, nl, es, ch, uk, us, fi, no, pt, se EXAMPLES Geocode... \$ 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 \$ geo-code "Schlossplatz 10" "76131 Karlsruhe" de Schlossplatz10 49.01294 08.40584 new \$ geo-code "" "Mankato, MN" MankatoMN 44.16562 -94.00130 new SEE ALSO geo-nearest, geo-waypoint, geo-pg, $WEBHOME EOF exit 1 } ############################################################################## # begin #include "geo-common" ############################################################################## # I doubt this stuff will work in other than english LANG=en_US # # Common global constants # UA="Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)" WEBHOME="http://geo.rkkda.com/" # # Common global variables # DEBUG=0 CRUFT= # # Report an error and exit # error() { echo "`basename $PROGNAME`: $1" >&2 exit 1 } debug() { if [ $DEBUG -ge $1 ]; then echo "`basename $PROGNAME`: $2" >&2 fi } verbose() { if [ $VERBOSE -ge $1 ]; then echo "$2" >&2 fi } dbgcmd() { if [ $DEBUG -ge $DBGCMD_LVL ]; then echo "$@" >&2 fi "$@" } DBGCMD_LVL=2 # # procedure to remove cruft files # remove_cruft() { if [ $DEBUG = 0 -a "$CRUFT" != "" ]; then for i in $CRUFT do [ -f $i ] && rm -f $i done fi } trap remove_cruft EXIT # # Convert DegDec, MinDec, or DMS lat/lon to DegDec # latlon() { # Handle NSEW prefixes arg1=`echo "$1" | sed -e 's/^[nNeE]//' -e 's/^[wW]/-/' -e 's/^[sS]/-/'` # If negative, print the sign then take the absolute value case "$arg1" in -*) echo -n "-"; arg1=`echo "$arg1" | sed 's/^-//'`;; esac # Now handle the 3 different formats case $# in 1) case "$arg1" in *.*.*) echo "$arg1" \ | sed -e 's/,//' -e 's#\([^.]*\)\.#\1 #' -e 's#$# 6k 60/+p#' \ | dc ;; lat=*) echo "$arg1" | sed 's/^lat=//' ;; lon=*) echo "$arg1" | sed 's/^lon=//' ;; *) echo $arg1 ;; esac ;; 2) echo "6k $arg1 $2 60/+p" | dc;; 3) echo "6k $arg1 $2 60/ $3 3600/++p" | dc;; esac } # # Convert DegDec to MinDec # degdec2mindec() { awk -v v=$1 \ 'BEGIN{ i=int(v); f=(v-i)*60; if(f<0)f=-f; printf "%d.%06.3f\n", i, f}' } # # Read RC file, if there is one # read_rc_file() { if [ -f $HOME/.georc ]; then . $HOME/.georc # Allow LAT/LON in rc file to be in any of the formats that we grok if [ "" != "$LAT" ]; then LAT=`latlon $LAT` fi if [ "" != "$LON" ]; then LON=`latlon $LON` fi else cat <<-EOF > $HOME/.georc # # These are the default values for the geo-* series of programs # Please edit this file as needed. Setting values for # USERNAME, PASSWORD, LAT/LON, and STATE are required. # ################################# # Login and paid membership status for www.geocaching.com... #USERNAME=name #PASSWORD=pasword #MOC=0 ################################# # Your HOME lat/lon and state... #LAT=N44.55.666 #LON=W93.11.222 #STATE=MN ################################# # Default map scale, font, and source... #MAPSCALE=10K #MAPFONT=helvetica #MAPSRC=2 ################################# # Login for terraserver.com... #TSCOM_EMAIL=xxx@yyy.com #TSCOM_PW=password ################################# # Miscellaneous... #OUTFMT=gpsdrive EOF error "First time user: please review and edit $HOME/.georc" fi } if [ `uname` = 'Darwin' ]; then sed=gsed date=gdate touch=gtouch PATH=$PATH:/usr/local/bin:/opt/local/bin export PATH else sed=sed date=date touch=touch fi # # Get the value from a name= value= pair in a file # get_value() { # = 97 && val <= 122) #0x61-0x7A encoded = encoded c else if (val >= 65 && val <= 90) #0x41-0x5A encoded = encoded c else if (val >= 48 && val <= 57) #0x30-0x39 encoded = encoded c else if (val >= 45 && val <= 46) #0x2D-0x2E encoded = encoded c else if (c == " ") encoded = encoded "+" else if (val < 128) { lo = val % 16 hi = int(val / 16); encoded = encoded "%" hextab[hi] hextab[lo] } else { byte = 192 + val/64 lo = byte % 16 hi = int(byte / 16); encoded = encoded "%" hextab[hi] hextab[lo] byte = 128 + val%64 lo = byte % 16 hi = int(byte / 16); encoded = encoded "%" hextab[hi] hextab[lo] } } print encoded } ' } # # return true if current arguments appear to be a lat/lon # is_latlon() { if [ "$#" -lt 2 ]; then return 1 fi case "$1" in lat=*) ;; # cut/paste from GPX file [NS]) return 0;; # cut/paste from gc.com [NSns][0-9]*) ;; [-][0-9]*) ;; [0-9]*) ;; *) return 1;; esac case "$2" in lon=*) return 0;; [EWew][0-9]*) return 0;; [-][0-9]*) return 0;; [0-9]*) return 0;; *) return 1;; esac } # # split lines between two strings # # $1 - string 1 # $2 - string 2 # $3 - null or 'g' # split_lines_between() { sed "s@$1$2@$1\\ $2@$3" } ############################################################################## # end #include "geo-common" ############################################################################## geocode_us_parse_db() { read lat lon title title=`echo "$title" | sed -e 's/%2c/,/g' -e 's/[+]/ /g' -e 's/.*TI=//'` } geocode_us(){ URL="http://geocoder.us/service/csv" URL="$URL?address=$address,$city,$state,$zip" debug 1 "curl -s $URL" curl -A "$UA" -s "$URL" | tail -1 >$COORDS # 44.978564,-93.309726,200 Queen Ave N,Minneapolis,MN,55405 OIFS="$IFS"; IFS=","; geocode_us_parse_db < $COORDS; IFS="$OIFS" } mappoint_parse_db() { read latlon unk1 title unk2 lat=`echo "$latlon" | sed 's/%2c.*//'` lon=`echo "$latlon" | sed 's/.*%2c//'` title=`echo "$title" | sed -e 's/%2c/,/g' -e 's/[+]/ /g' -e 's/.*TI=//'` } mappoint(){ # # Get the header of the main page to find out where we are redirected # # Header redirect is: # Location: /(bhvcud2gm4bjo155pjdyi1rq)/home.aspx # MPURL=http://mappoint.msn.com URL="$MPURL" debug 1 "curl --head $URL" NEXTURL=` curl -A "$UA" -s --head "$URL" | tr -d '\015' | sed -n 's/^Location: \(.*\)/\1/p' ` # # Get the main page so we can extract the VIEWSTATE # URL="$MPURL/$NEXTURL" debug 1 "curl $URL" curl -A "$UA" -s "$URL" > $HTMLPAGE get_value __VIEWSTATE $HTMLPAGE __VIEWSTATE=`urlencode2 "$__VIEWSTATE"` # # Post the request # if [ "$ADDRESS" = "" ]; then # Placename csz=`urlencode2 "$csz"` curl -A "$UA" -s \ -d __VIEWSTATE="$__VIEWSTATE" \ -d "FndControl:SearchType=Place" \ -d "FndControl:ARegionSelect=$region" \ -d "FndControl:StreetText=$csz" \ -d "FndControl:CityText=" \ -d "FndControl:StateText=" \ -d "FndControl:ZipText=" \ -d "FndControl:isRegionChange=0" \ -d "FndControl:resultOffSet=0" \ -d "FndControl:BkARegion=$region" \ -d "FndControl:BkPRegion=$region" \ -d "FndControl:hiddenSearchType" \ "$URL" | tr -d '\015' > $HTMLPAGE # # Extract the normalized address and coordinates # # &C=44.16562%2c-94.00130&A=43.00000 \ # &P=|4E9E|&TI=Mankato%2c+Minnesota%2c+United+States'> # #grep "C=" $HTMLPAGE # sed -n -e "s/^.*P=|\([^']*\).*/\1/p" < $HTMLPAGE > $COORDS sed -n -e "s/^.*;C=\([^']*\).*/\1/p" < $HTMLPAGE > $COORDS else curl -A "$UA" -s \ -d __VIEWSTATE="$__VIEWSTATE" \ -d "FndControl:SearchType=Address" \ -d "FndControl:ARegionSelect=$region" \ -d "FndControl:StreetText=$address" \ -d "FndControl:CityText=$city" \ -d "FndControl:StateText=$state" \ -d "FndControl:ZipText=$zip" \ -d "FndControl:isRegionChange=0" \ -d "FndControl:resultOffSet=0" \ -d "FndControl:BkARegion=$region" \ -d "FndControl:BkPRegion=$region" \ -d "FndControl:hiddenSearchType" \ "$URL" | tr -d '\015' > $HTMLPAGE # # Extract the normalized address and coordinates # # Multiple possibilities... # # # # Exact match... # #

Object moved to here.

# # Output to coords... # 45.10382%2c-93.45307|1|13603+Grove+Dr%2c+Osseo%2c+MN+55311|L1| # sed 's/map.aspx/\ &/g' < $HTMLPAGE \ | grep map.aspx \ | sed -n -e "s/^.*P=|\([^'"'"'"]*\).*/\1/p" \ | head -1 > $COORDS # ^----- For now, we just keep the most likely match fi OIFS="$IFS"; IFS="|"; mappoint_parse_db < $COORDS; IFS="$OIFS" } # # 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 UPDATE_URL=$WEBHOME/geo-code UPDATE_FILE=geo-code.new read_rc_file # # Process the options # ADDRESS= CSZ= MODE=babel SQL=0 NAME= GURL= QUIET=0 SQLMATCH=type_name while getopts "an: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;; a) SQLMATCH=all;; D) DEBUG="$OPTARG";; U) echo "Getting latest version of this script..." curl -o$UPDATE_FILE "$UPDATE_URL" chmod +x "$UPDATE_FILE" echo "Latest version is in $UPDATE_FILE" exit ;; h|\?) usage;; esac done shift `expr $OPTIND - 1` case "$OUTFMT" in map) MODE=map MAPOPTS= ;; map,*) MODE=map MAPOPTS=`echo "$OUTFMT" | sed -n 's/map,\(.*\)$/\1/p'` ;; degdec) MODE=degdec ;; mindec) MODE=mindec ;; gpsdrive) BABELFLAGS=-s ;; gpsdrive.sql) BABELFLAGS=-s OUTFMT=gpsdrive MODE=sql # DEBUG=1 ;; \?) gpsbabel -? | sed -e '1,/File Types/d' -e '/Supported data filters/,$d' echo " gpsdrive.sql " \ "GpsDrive direct MySQL database insertion" echo " degdec " \ "Just latitude and longitude, thank you" echo " mindec " \ "Just latitude and longitude, thank you" echo " map[,geo-map-opts] " \ "Display map of waypoints using geo-map" exit ;; esac case "$#" in 3) # # street_address citystate_or_zip country # ADDRESS=$1 CSZ=$2 COUNTRY=$3 address=`urlencode2 "$ADDRESS"` csz="$CSZ" myname="$1, $2 $3" ;; 2) # # street-address citystate_or_zip # ADDRESS=$1 CSZ=$2 address=`urlencode2 "$ADDRESS"` csz="$CSZ" myname="$1, $2" ;; 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 # PHONE=$1 ADDRESS=$1 GURL="http://www.google.com/search?q=phonebook:$1" myname="$1" ;; *) usage esac country=`echo $COUNTRY | tr '[A-Z]' '[a-z]'` # # 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 } # # Main Program # if [ $DEBUG -gt 0 ]; then TMP=/tmp/geo else TMP=/tmp/geo$$ fi HTMLPAGE=${TMP}.html STYLE=${TMP}.style COORDS=${TMP}.coords OUTWAY=${TMP}.way CRUFT="$CRUFT $HTMLPAGE" CRUFT="$CRUFT $STYLE" CRUFT="$CRUFT $COORDS" CRUFT="$CRUFT $OUTWAY" case "$country" in at|austri*) region=0;; be|bel*) region=1;; ca|can*) region=2;; dk|den*) region=3;; fr|fra*) region=4;; de|ger*) region=5;; it|ita*) region=6;; lu|lux*) region=7;; nl|net*) region=8;; es|spa*) region=9;; ch|swi*) region=10;; uk|bri*) region=11;; us|usa) region=12;; fi|fin*) region=16;; no|nor*) region=17;; pt|por*) region=18;; se|swe*) region=19;; *) error "Unknown country '$country'";; esac if [ "$GURL" != "" ]; then # # Treat a single command line argument as a phone number and use # Google to look up the address # debug 1 "curl $GURL" curl -s -A "$UA" "$GURL" > $HTMLPAGE address=` sed -n -e 's/[+]/ /' -e 's/^.*&address=\([^&]*\).*/\1/p' <$HTMLPAGE ` city=` sed -n -e 's/[+]/ /' -e 's/^.*&city=\([^&]*\).*/\1/p' <$HTMLPAGE ` state=` sed -n -e 's/[+]/ /' -e 's/^.*&state=\([^&]*\).*/\1/p' <$HTMLPAGE ` zip=` sed -n -e 's/[+]/ /' -e 's/^.*&zipcode=\([^>&]*\).*/\1/p' <$HTMLPAGE ` if [ "$address" = "" ]; then error "Unable to lookup telephone number or name with Google" fi else city= state= zip= case "$region" in 2) # Canada case "$csz" in [A-Z][0-9][A-Z]\ [0-9][A-Z][0-9]) zip="$csz" ;; *,*\ [A-Z][0-9][A-Z]\ [0-9][A-Z][0-9]) city=`echo "$csz" | sed 's/,.*//'` state=`echo "$csz" | sed -e 's/.*, *//' -e 's/ \+... \+...$//' ` zip=`echo "$csz" | sed -e 's/^.* \(... \+...\)$/\1/' ` ;; *,*) city=`echo "$csz" | sed 's/,.*//'` state=`echo "$csz" | sed 's/.*, *//'` ;; *) error "Unable to parse '$csz' into city, state zip" ;; esac ;; 12) # US # # Parse CSZ into C, S, Z for MapPoint # case "$csz" in [0-9][0-9][0-9][0-9][0-9]) zip="$csz" ;; [0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]) zip="$csz" ;; *,*\ [0-9][0-9][0-9][0-9][0-9]) city=`echo "$csz" | sed 's/,.*//'` state=`echo "$csz" | sed -e 's/.*, *//' -e 's/ \+[0-9-]\+//' ` zip=`echo "$csz" | sed -e 's/^.* \+//' ` ;; *,*\ [0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]) city=`echo "$csz" | sed 's/,.*//'` state=`echo "$csz" | sed -e 's/.*, *//' -e 's/ \+[0-9-]\+//' ` zip=`echo "$csz" | sed -e 's/^.* \+//' ` ;; *,*) city=`echo "$csz" | sed 's/,.*//'` state=`echo "$csz" | sed 's/.*, *//'` ;; *) error "Unable to parse '$csz' into city, state zip" ;; esac ;; *) case "$csz" in *\ *) city=`echo "$csz" | sed 's/^[^ ]\+ \+//'` zip=`echo "$csz" | sed 's/ \+.*//'` ;; *) zip="$csv" ;; esac ;; esac fi debug 1 "address=<$address> city=<$city> state=<$state> zip=<$zip>" debug 1 "myname=<$myname>" debug 1 "country=<$country> region=<$region>" # Determine lat, lon, and title case "$region" in 12) geocode_us;; *) mappoint;; esac if [ "$title" != "" -a "$QUIET" != 1 ]; then echo "$title" >&2 fi if [ "$lat" = "" -o "$lon" = "" ]; then error "Cannot determine coordinates of that address" fi latM=`degdec2mindec $lat` lonM=`degdec2mindec $lon` case "$MODE" in map) echo "$lat $lon" geo-map -D$DEBUG $MAPOPTS $lat $lon "$myname" exit ;; degdec) echo "$lat $lon" exit ;; mindec) echo "$latM $lonM" exit ;; esac if [ "$NAME" = "" ]; then if [ "$ADDRESS" = "" ]; then NAME="$CSZ" else NAME="$ADDRESS" fi else # # Expand % escapes in name to lat/lon, address, etc. # case "$NAME" in *%m*) NAME=`echo "$NAME" | sed "s/%m/$latM/g" `;; esac case "$NAME" in *%M*) NAME=`echo "$NAME" | sed "s/%M/$lonM/g" `;; esac case "$NAME" in *%d*) NAME=`echo "$NAME" | sed "s/%d/$lat/g" `;; esac case "$NAME" in *%D*) NAME=`echo "$NAME" | sed "s/%D/$lon/g" `;; esac case "$NAME" in *%a*) NAME=`echo "$NAME" | sed "s/%a/$ADDRESS/g" `;; esac case "$NAME" in *%c*) NAME=`echo "$NAME" | sed "s/%a/$CSZ/g" `;; esac case "$NAME" in *%p*) NAME=`echo "$NAME" | sed "s/%p/$PHONE/g" `;; esac case "$NAME" in *%%*) NAME=`echo "$NAME" | sed "s/%%/%/g" `;; esac fi make_style > $STYLE echo "$lat $lon $NAME $CODETYPE" > $COORDS gpsbabel $BABELFLAGS -i xcsv,style=$STYLE -f $COORDS -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,comment)" read _name _lat _lon _type COMMENT="$ADDRESS" if [ "$CSZ" != "" ]; then COMMENT="$COMMENT, $CSZ" fi echo "use $SQLDB;" case "$SQLMATCH" in all) # Must be a complete match to delete existing record echo "$delcmd where name='$_name' and type='$_type'" echo "and lat='$_lat' and lon='$_lon';" ;; *) # Must match only name and type echo "$delcmd where name='$_name' and type='$_type';" ;; esac echo "$addcmd values ('$_name','$_lat','$_lon','$_type'," echo "'$COMMENT');" } if [ -f $OUTWAY ]; then case "$MODE" in sql) # # 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 ;; *) # # output to stdout # cat $OUTWAY ;; esac fi exit 0