#!/bin/sh
#
############################################################################
#
# MODULE: v.in.gpsbabel
#
# PURPOSE: Import GPS data from a GPS receiver or file into a GRASS
# vector map using gpsbabel
#
# COPYRIGHT: (c) 2000-2007 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.
#
# AUTHORS: Claudio Porta & Lucio Davide Spano, students of Computer
# Science, University of Pisa (Italy).
# Commission from Faunalia Pontedera (PI)
#
# With updates by Markus Neteler and Hamish Bowman
#
# Based on v.in.garmin for GRASS 6 by Hamish Bowman
# and v.in.garmin.sh for GRASS 5 by Andreas Lange
#
#############################################################################
#
# REQUIREMENTS:
# - gpsbabel from:
# http://gpsbabel.sourceforge.net
# - unix tools: grep, cat, cut, paste, awk/nawk/gawk, sed
# - cs2cs from PROJ.4 http://proj.maptools.org/
#
# - report supported formats:
# gpsbabel -^2 | tr '\t' ';' | sort -t';' -k3
#
#############################################################################
#%Module
#% description: Import waypoints, routes, and tracks from a GPS receiver or GPS download file into a vector map.
#% keywords: vector, import, GPS
#%End
#%flag
#% key: v
#% description: Verbose mode
#%end
#%flag
#% key: w
#% description: Import waypoints
#%end
#%flag
#% key: r
#% description: Import routes
#%end
#%flag
#% key: t
#% description: Import track
#%end
#%flag
#% key: p
#% description: Force vertices of track or route data as points
#%end
#%flag
#% key: k
#% description: Do not attempt projection transform from WGS84
#%end
#%option
#% key: input
#% type: string
#% description: Device or file used to import data
#% gisprompt: old_file,file,file
#% answer: /dev/gps
#%end
#%option
#% key: output
#% type: string
#% gisprompt: new,vector,vector
#% description: Name for output vector map (omit for display to stdout)
#% required : no
#%end
#%option
#% key: format
#% type: string
#% description: Format of GPS input data (use gpsbabel supported formats)
#% answer: garmin
#%end
#%option
#% key: proj
#% type: string
#% description: Projection of input data (PROJ.4 style), if not set Lat/Lon WGS84 is assumed
#% required: no
#%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
# save command line
CMDLINE=`basename "$0"`
for arg in "$@" ; do
CMDLINE="$CMDLINE \"$arg\""
done
export CMDLINE
exec g.parser "$0" "$@"
fi
# set environment so that awk works properly in all languages
unset LC_ALL
LC_NUMERIC=C
export LC_NUMERIC
eval `g.gisenv`
: ${GISBASE?} ${GISDBASE?} ${LOCATION_NAME?} ${MAPSET?}
LOCATION="$GISDBASE"/"$LOCATION_NAME"/"$MAPSET"
PROG=`basename "$0"`
#### check for gpsbabel
if [ ! -x "`which gpsbabel`" ] ; then
g.message -e "The gpsbabel program was not found, please install it first.
http://gpsbabel.sourceforge.net"
exit 1
fi
#### check for cs2cs
if [ ! -x "`which cs2cs`" ] ; then
g.message -e "The cs2cs program was not found, please install it first.
http://proj.maptools.org"
exit 1
fi
#### check if we have awk
if [ ! -x "`which awk`" ] ; then
g.message -e "awk is required, please install awk or gawk first."
exit 1
fi
if [ `ogr2ogr --formats | grep -cw GPX` -eq 1 ] ; then
g.message -v "OGR is GPX capable."
OGR_GPX=1
else
OGR_GPX=0
fi
#### set temporary files
TEMPFILE="`g.tempfile pid=$$`"
if [ $? -ne 0 ] || [ -z "$TEMPFILE" ] ; then
g.message -e "Unable to create temporary files"
exit 1
fi
#### trap ctrl-c so that we can clean up tmp
trap 'rm -f "${TEMPFILE}."*' 2 3 15
#### process command line arguments
WPT=1 ; RTE=0 ; TRK=0 ; KEEP_WGS84=0; VERBOSE=0
if [ $GIS_FLAG_V -eq 1 ] ; then
VERBOSE=1
PROGVERBOSE="-D 1"
fi
if [ -n "$GIS_OPT_OUTPUT" ] ; then
NAME="$GIS_OPT_OUTPUT"
if [ $VERBOSE -eq 1 ] ; then
g.message message="output=$NAME"
fi
fi
if [ -n "$GIS_OPT_INPUT" ] ; then
GPSdevice="$GIS_OPT_INPUT"
if [ $VERBOSE -eq 1 ] ; then
g.message message="file=$GIS_OPT_INPUT"
fi
fi
if [ $GIS_FLAG_W -eq 1 ] && ([ $GIS_FLAG_R -eq 1 ] || [ $GIS_FLAG_T -eq 1 ]) ; then
g.message -e "One feature at a time please. Use v.patch if you need to combine them."
rm -f "${TEMPFILE}"
exit 1
fi
# logic eludes me at pressent.. [combine with above]
if [ $GIS_FLAG_R -eq 1 ] && [ $GIS_FLAG_T -eq 1 ] ; then
g.message -e "One feature at a time please. Use v.patch if you need to combine them."
rm -f "${TEMPFILE}"
exit 1
fi
CREATE_POINTS=0
if [ $GIS_FLAG_W -eq 1 ] ; then
WPT=1
CREATE_POINTS=1
elif [ $GIS_FLAG_R -eq 1 ] ; then
WPT=0
RTE=1
CREATE_POINTS=0
elif [ $GIS_FLAG_T -eq 1 ] ; then
WPT=0
TRK=1
CREATE_POINTS=0
fi
if [ $GIS_FLAG_P -eq 1 ] ; then
CREATE_POINTS=1
fi
if [ $GIS_FLAG_K -eq 1 ] ; then
KEEP_WGS84=1
fi
if [ $GIS_FLAG_W -eq 0 ] && [ $GIS_FLAG_R -eq 0 ] && [ $GIS_FLAG_T -eq 0 ] ; then
g.message -w "No features requested for download. Assuming waypoints."
WPT=1
CREATE_POINTS=1
fi
#### set up projection info
if [ -n "$GIS_OPT_PROJ" ] ; then
IN_PROJ="$GIS_OPT_PROJ"
else
IN_PROJ="+proj=longlat +datum=WGS84"
fi
OUT_PROJ=`g.proj -j | (
OUT_PROJ=
while read line ; do
OUT_PROJ="$OUT_PROJ '$line'"
done
echo "$OUT_PROJ"
)`
PROJ_TYPE=`g.region -p | grep 'projection' | cut -f2 -d" "`
if [ $PROJ_TYPE -eq 0 ] && [ $KEEP_WGS84 -ne 1 ] ; then
g.message -e "Cannot project to a XY location."
rm -f "${TEMPFILE}"
exit 1
fi
STYLE="$GISBASE/etc/grass_write_ascii.style"
# How to do it
#
# gpsbabel [options] -i INTYPE -f INFILE -o OUTTYPE -F OUTFILE
# gpsbabel [options] -i INTYPE -o OUTTYPE INFILE [OUTFILE]
#
#(GPX file example):
# gpsbabel -w -i gpx -o xcsv,style=grass_write_ascii.style outfile.csv
#### receive data...
if [ $CREATE_POINTS -eq 1 ] ; then
if [ $WPT -eq 1 ] ; then
g.message "Loading Waypoints from <$GIS_OPT_INPUT>..."
TYPE="-w"
elif [ $RTE -eq 1 ] ; then
g.message "Loading Routes as points from <$GIS_OPT_INPUT>..."
TYPE="-r"
elif [ $TRK -eq 1 ] ; then
g.message "Loading Tracks as points from <$GIS_OPT_INPUT>..."
TYPE="-t"
fi
gpsbabel $TYPE -i $GIS_OPT_FORMAT -f "$GPSdevice" $PROGVERBOSE \
-o xcsv,style="$STYLE" -F "$TEMPFILE".xcsv
EXITCODE=$?
if [ "`wc -l < "${TEMPFILE}.xcsv"`" -eq 0 ] ; then
g.message -w 'No data! Exiting.'
rm -f "${TEMPFILE}."*
exit 0
fi
else
if [ $RTE -eq 1 ] ; then
g.message "Loading Routes from <$GIS_OPT_INPUT>..."
TYPE="-r"
elif [ $TRK -eq 1 ] ; then
g.message "Loading Tracks from <$GIS_OPT_INPUT>..."
TYPE="-t"
fi
gpsbabel $TYPE -i $GIS_OPT_FORMAT -f "$GPSdevice" $PROGVERBOSE \
-o gpx -F "$TEMPFILE".gpx
EXITCODE=$?
if [ $EXITCODE -eq 0 ] && \
[ "`grep -c '' "${TEMPFILE}.gpx"`" -eq 0 ] && \
[ "`grep -c '' "${TEMPFILE}.gpx"`" -eq 0 ] ; then
g.message -e 'No data! Exiting.'
rm -f "${TEMPFILE}."*
exit 0
fi
fi
#### check success/failure
if [ $EXITCODE -ne 0 ] ; then
g.message -e "Error loading data from gpsbabel"
rm -f "${TEMPFILE}."*
exit 1
fi
###################################
if [ $CREATE_POINTS -eq 1 ] ; then
###################################
cat "${TEMPFILE}.xcsv" | cut -f 1,2 -d '|'| tr '|' ' ' > "$TEMPFILE".base
# FIXME: if last field (comments) is empty it causes a not-enough fields error in v.in.ascii
# FIXME: if altitude column is empty (1st attr) v.in.ascii complains as the column type is defined as 'double'
cat "${TEMPFILE}.xcsv" | tr '+' '|' | cut -f3,4,5,6 -d '|' | \
sed -e 's/-99999999.000000//' -e 's/|$/|_/' \
-e 's/01\/01\/1970|00:00:00 AM/|/' \
-e 's/^|/-9999|/'> "$TEMPFILE".attributes
#### reproject if needed
if [ "$IN_PROJ" = "$OUT_PROJ" ] || [ $KEEP_WGS84 -eq 1 ] ; then
g.message "No projection transformation performed"
cp "${TEMPFILE}.base" "${TEMPFILE}.P_base"
else
g.message "Attempting waypoint projection transform with cs2cs"
eval cs2cs -f %.9f `echo $IN_PROJ` +to `echo $OUT_PROJ` \
< "${TEMPFILE}.base" > "${TEMPFILE}.P_base"
EXITCODE=$?
# check if transform REALLY worked (e.g. when the grid file is not found)
BAD_PTS="`grep -c "^\*" "${TEMPFILE}.P_base"`"
if [ "$BAD_PTS" -gt 0 ] ; then
g.message message=""
g.message -w "$BAD_PTS point(s) failed reprojection."
EXITCODE=1
fi
if [ $EXITCODE -ne 0 ] ; then
g.message -w "Projection transform failed, retaining WGS84"
g.message message=""
cp -f "${TEMPFILE}.base" "${TEMPFILE}.P_base"
fi
fi
cat "${TEMPFILE}.P_base" | awk '{print $1 "|" $2}' > "${TEMPFILE}.vertices"
#### and put back together
# wpt list: x|y||...|
paste -d"|" "$TEMPFILE".vertices "$TEMPFILE".attributes > "$TEMPFILE".asc
#### output or import
if [ -z "$NAME" ] ; then
g.message "ASCII file redirected to stdout"
cat "${TEMPFILE}.asc" 2> /dev/null
else
#### import into new points file
if [ $WPT -eq 1 ] ; then
g.message "Importing Waypoints..."
elif [ $RTE -eq 1 ] ; then
g.message "Importing Routes as points..."
elif [ $TRK -eq 1 ] ; then
g.message "Importing Tracks as points..."
fi
v.in.ascii in="${TEMPFILE}.asc" output="$NAME" cat=0 \
columns='x double precision, y double precision, altitude double precision, gmt_date varchar(10), gmt_time varchar(11), comments varchar(40)'
EXITCODE=$?
# EXITCODE also used at pgm termination below!
if [ $EXITCODE -ne 0 ] ; then
g.message -e "While Importing data with v.in.ascii"
rm -f "${TEMPFILE}"*
exit 1
fi
fi
#####################
else # CREATE_LINES
#####################
#### prepare line components
if [ $RTE -eq 1 ] ; then
if [ $ORG_GPX -eq 1 ] ; then
# http://www.gdal.org/ogr/drv_gpx.html
# make it really easy ; but need to reproject from WGS84/LL.
# use '-c location=', then v.proj it in, then rm -rf the temp locn?
#? GPX_USE_EXTENSIONS=YES
#? export GPX_USE_EXTENSIONS
#v.in.ogr dsn="$TEMPFILE.gpx" out=$map_name
echo
fi
# this part is quite difficult using gpx... I'll explain all I've done
# if someone has any suggest please mail!!!!
# list of bytes where routes finish
cat "$TEMPFILE.gpx" | grep -n "" > "$TEMPFILE.bytes"
# number of routes
cat "$TEMPFILE.bytes" | grep -c "" > "$TEMPFILE.var"
ROUTE_NUMBER=0
read ROUTE_NUMBER < "$TEMPFILE.var" # route number to process
READ_BYTES=0 # offset of bytes already read
cp "$TEMPFILE.gpx" "$TEMPFILE.gpx2" # file to be "eaten" by head commands in while
ROUTE_ID=0 # route identifier
while [ "$ROUTE_NUMBER" -gt 0 ] ; do
head -n 1 "$TEMPFILE.bytes" | cut -f 1 -d ':'> "$TEMPFILE.var"
END_BYTE=0
read END_BYTE < "$TEMPFILE.var" # this route ends at END_BYTE in $TEMPFILE.gpx file
TO_READ=0
TO_READ="`expr $END_BYTE - $READ_BYTES`" # bytes to read from $TEMPFILE.gpx2 file
READ_BYTES="`expr $READ_BYTES + $TO_READ`" # update readed bytes
# list of points in route
head -n $TO_READ "$TEMPFILE.gpx2" | grep " "$TEMPFILE.points"
POINTS=0 # number of points in route
cat "$TEMPFILE.points" | grep -c " "$TEMPFILE.var"
read POINTS < "$TEMPFILE.var"
echo "L $POINTS 1" >> "$TEMPFILE.base"
# read lat lon data
cat "$TEMPFILE.points" | cut -f2 -d'<' | cut -f2,3 -d ' ' | cut -f2,4 -d '"' | tr '"' '\t' > "$TEMPFILE.latlon"
cat "$TEMPFILE.latlon" | tr ',' '.' | awk '{printf(" %s %s\n", $2, $1) }' >> "$TEMPFILE.base"
# create attribute line
head -n $TO_READ "$TEMPFILE.gpx2" | grep -n " "$TEMPFILE.var"
OFFSET=0
read OFFSET < "$TEMPFILE.var"
head -n $OFFSET "$TEMPFILE.gpx2" > "$TEMPFILE.rte_attr"
# read needed attributes
ROUTE_ID="`expr $ROUTE_ID + 1`"
cat "$TEMPFILE.rte_attr"| grep "' > "$TEMPFILE.var"
NUMBER=0
read NUMBER < "$TEMPFILE.var" # read the route number
cat "$TEMPFILE.rte_attr"| grep "' > "$TEMPFILE.var"
R_NAME=""
read R_NAME < "$TEMPFILE.var" # read the route name
OFFSET="`expr $TO_READ - $OFFSET`"
head -n $TO_READ "$TEMPFILE.gpx2" | tail -n $OFFSET | grep "' > "$TEMPFILE.var"
START_PNT=""
read START_PNT < "$TEMPFILE.var" # read the name of start point
# check that numberic values don't try and pass an empty string
### variable names don't line up with column names ?!
if [ -z "$ROUTE_ID" ] ; then
ROUTE_ID="NULL"
g.message -w "Route $ROUTE_NUMBER: category number was empty. Bug?"
fi
if [ -z "$NUMBER" ] ; then
NUMBER="NULL"
g.message -w "Route $ROUTE_NUMBER: route ID was empty. Bug?"
fi
echo "$ROUTE_ID|$NUMBER|$R_NAME|$START_PNT" >> "$TEMPFILE.route_atts"
ROUTE_NUMBER="`expr $ROUTE_NUMBER - 1`"
# eat files
tail -n $ROUTE_NUMBER "$TEMPFILE.bytes" > "$TEMPFILE.bytes2"
mv "$TEMPFILE.bytes2" "$TEMPFILE.bytes"
head -n $TO_READ "$TEMPFILE.gpx2" > "$TEMPFILE.points"
B_GPX=0
wc -c < "$TEMPFILE.gpx2" > "$TEMPFILE.var"
read B_GPX < "$TEMPFILE.var"
B_PNT=0
wc -c < "$TEMPFILE.points" > "$TEMPFILE.var"
read B_PNT < "$TEMPFILE.var"
TO_READ="`expr $B_GPX - $B_PNT`"
tail -c $TO_READ "$TEMPFILE.gpx2" > "$TEMPFILE.points"
mv "$TEMPFILE.points" "$TEMPFILE.gpx2"
done
# create attr table: cat(int), id number(int 0-19), name varchar(16+), starting_wpt(varchar 10)
ATTR_FILE="${TEMPFILE}.route_atts"
ATTR_COLS='cat int, route_id int, name varchar(40), start_wpt varchar(40)'
fi
if [ $TRK -eq 1 ] ; then
# list of bytes where tracks finish
cat "$TEMPFILE.gpx" | grep -n "" > "$TEMPFILE.bytes"
# number of tracks
cat "$TEMPFILE.bytes" | grep -c "" > "$TEMPFILE.var"
TRACK_NUMBER=0
read TRACK_NUMBER < "$TEMPFILE.var" # route number to process
READ_BYTES=0 # offset of bytes already read
cp "$TEMPFILE.gpx" "$TEMPFILE.gpx2" # file to be "eaten" by head commands in while
TRACK_ID=0 # track identifier
while [ "$TRACK_NUMBER" -gt 0 ] ; do
head -n 1 "$TEMPFILE.bytes" | cut -f 1 -d ':'> "$TEMPFILE.var"
END_BYTE=0
read END_BYTE < "$TEMPFILE.var" # this route ends at END_BYTE in $TEMPFILE.gpx file
TO_READ=0
TO_READ="`expr $END_BYTE - $READ_BYTES`" # bytes to read from $TEMPFILE.gpx2 file
READ_BYTES="`expr $READ_BYTES + $TO_READ`" # update readed bytes
# list of points in route
head -n $TO_READ "$TEMPFILE.gpx2" | grep " "$TEMPFILE.points"
POINTS=0 # number of points in track
cat "$TEMPFILE.points" | grep -c " "$TEMPFILE.var"
read POINTS < "$TEMPFILE.var"
echo "L $POINTS 1" >> "$TEMPFILE.base"
# read lat lon data
cat "$TEMPFILE.points" | cut -f2 -d'<' | cut -f2,3 -d ' ' | cut -f2,4 -d '"' | tr '"' '\t' > "$TEMPFILE.latlon"
cat "$TEMPFILE.latlon" | tr ',' '.' | awk '{printf(" %s %s\n", $2, $1) }' >> "$TEMPFILE.base"
# create attribute line
head -n $TO_READ "$TEMPFILE.gpx2" | grep -n " "$TEMPFILE.var"
OFFSET=0
read OFFSET < "$TEMPFILE.var"
S_LAT=""
head -n 1 "$TEMPFILE.latlon" | cut -f1 > "$TEMPFILE.var"
read S_LAT < "$TEMPFILE.var"
S_LON=""
head -n 1 "$TEMPFILE.latlon" | cut -f2 > "$TEMPFILE.var"
read S_LON < "$TEMPFILE.var"
E_LAT=""
tail -n 1 "$TEMPFILE.latlon" | cut -f1 > "$TEMPFILE.var"
read E_LAT < "$TEMPFILE.var"
E_LON=""
tail -n 1 "$TEMPFILE.latlon" | cut -f2 > "$TEMPFILE.var"
read E_LON < "$TEMPFILE.var"
OFFSET="`expr $TO_READ - $OFFSET`"
head -n $TO_READ "$TEMPFILE.gpx2" | tail -n $OFFSET | grep "