#!/usr/bin/perl

use Time::Local qw(timegm);
use Data::Dumper;
use Math::Trig;
use Storable qw(dclone);

# Garmin Text Format (surely not a standardised extention, but WTH) file
# This file contains the raw data from the GPS for the trip

#     FIELD DESCRIPTION:      WIDTH:  NOTES:
#     ----------------------- ------- ------------------------
#     Sentence start          1       Always '@'
#     ----------------------- ------- ------------------------
#    /Year                    2       Last two digits of UTC year
#   | ----------------------- ------- ------------------------
#   | Month                   2       UTC month, "01".."12"
# T | ----------------------- ------- ------------------------
# i | Day                     2       UTC day of month, "01".."31"
# m | ----------------------- ------- ------------------------
# e | Hour                    2       UTC hour, "00".."23"
#   | ----------------------- ------- ------------------------
#   | Minute                  2       UTC minute, "00".."59"
#   | ----------------------- ------- ------------------------
#    \Second                  2       UTC second, "00".."59"
#     ----------------------- ------- ------------------------
#    /Latitude hemisphere     1       'N' or 'S'
#   | ----------------------- ------- ------------------------
#   | Latitude position       7       WGS84 ddmmmmm, with an implied
#   |                                 decimal after the 4th digit
#   | ----------------------- ------- ------------------------
#   | Longitude hemishpere    1       'E' or 'W'
#   | ----------------------- ------- ------------------------
#   | Longitude position      8       WGS84 dddmmmmm with an implied
# P |                                 decimal after the 5th digit
# o | ----------------------- ------- ------------------------
# s | Position status         1       'd' if current 2D differential GPS position
# i |                                 'D' if current 3D differential GPS position
# t |                                 'g' if current 2D GPS position
# i |                                 'G' if current 3D GPS position
# o |                                 'S' if simulated position
# n |                                 '_' if invalid position
#   | ----------------------- ------- ------------------------
#   | Horizontal posn error   3       EPH in meters
#   | ----------------------- ------- ------------------------
#   | Altitude sign           1       '+' or '-'
#   | ----------------------- ------- ------------------------
#   | Altitude                5       Height above or below mean
#    \                                sea level in meters
#     ----------------------- ------- ------------------------
#    /East/West velocity      1       'E' or 'W'
#   |     direction
#   | ----------------------- ------- ------------------------
#   | East/West velocity      4       Meters per second in tenths,
#   |     magnitude                   ("1234" = 123.4 m/s)
# V | ----------------------- ------- ------------------------
# e | North/South velocity    1       'N' or 'S'
# l |     direction
# o | ----------------------- ------- ------------------------
# c | North/South velocity    4       Meters per second in tenths,
# i |     magnitude                   ("1234" = 123.4 m/s)
# t | ----------------------- ------- ------------------------
# y | Vertical velocity       1       'U' (up) or 'D' (down)
#   |     direction
#   | ----------------------- ------- ------------------------
#   | Vertical velocity       4       Meters per second in hundredths,
#    \    magnitude                   ("1234" = 12.34 m/s)
#     ----------------------- ------- ------------------------
#     Sentence end            2       Carriage return, '0x0D', and
#                                     line feed, '0x0A'
#     ----------------------- ------- ------------------------
# 
# If a numeric value does not fill its entire field width, the field is padded
# with leading '0's (eg. an altitude of 50 meters above MSL will be output as
# "+00050").
# 
# Any or all of the data in the text sentence (except for the sentence start
# and sentence end fields) may be replaced with underscores to indicate
# invalid data.
$gtfFile = shift @ARGV;

# For granularity control, speed (in km/h)
#
# Granularity (for lack of a better term) controls how many datapoints end up
# on the map.  Smaller numbers mean more data points, larger ones mean less.
# It's an exponential scale (see resolution.png).
#
# The logic where this is used goes something like this: we're going to plot a
# bunch of lines to connect the dots between each data point.  For any given
# line segment, we know what the first, starting point for our line is (either
# it's the very first point in the file, or it's the point at the end of the
# last line we drew).  
#
# Now, look at the next point in the file.  Do we use it or not?  To find out,
# we're going to look at some numbers our GPS gives us: the latitudinal and
# longitudinal speed (think east-west speed and north-south speed) at each of
# the points (someday we can rewrite this to calculate it from basic elements
# of timestamps and positions).
#
# We will use the next point as the endpoint of the current line IF the
# difference in each dimension's speed is big enough.  We'll take the
# difference in its east-west speed and the difference in its nort-south speed
# and sum them.  (We'll use absolute differences because we don't care what
# direction the change of speed is in.)  If the sum is greater than the number
# below, we use the point to end our line, and as the starting point for our
# next line segment.
#
# If it isn't, we drop that point.  We keep the point we already have as our
# starting point, and examine the point AFTER the one we just dropped.  We keep
# looking until we find one whose component-speed-change-sum is great enough.  
$min_vSpeed = shift @ARGV;

# Takes two timestamps and returns a string which is the elapsed HR:MIN:SEC
sub elapsed_time {
	my $this_timestamp = shift @_;
	my $last_timestamp = shift @_;

	my $hr = int(($this_timestamp - $last_timestamp) / 3600);
	my $min = int(int(($this_timestamp - $last_timestamp) % 3600) / 60);
	my $sec = int(int(($this_timestamp - $last_timestamp) % 3600) % 60);

	return sprintf("%.2d:%.2d:%.2d",$hr,$min,$sec);
}

sub delta_distance_bearing {
	$this_lat = shift @_;
	$this_lon = shift @_;
	$last_lat = shift @_;
	$last_lon = shift @_;


	$this_lat = deg2rad($this_lat);
	$this_lon = deg2rad($this_lon);
	$last_lat = deg2rad($last_lat);
	$last_lon = deg2rad($last_lon);
	$R = 6371;

	# /*
	#  * Calculate distance (in km) between two points specified by latitude/longitude with Haversine formula
	#  *
	#  * from: Haversine formula - R. W. Sinnott, "Virtues of the Haversine",
	#  *       Sky and Telescope, vol 68, no 2, 1984
	#  *       http://www.census.gov/cgi-bin/geo/gisfaq?Q5.1
	#  */
	# LatLong.distHaversine = function(p1, p2) {
	#   var R = 6371; // earth's mean radius in km
	#   var dLat  = p2.lat - p1.lat;
	#   var dLong = p2.lon - p1.lon;
	# 
	#   var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
	#           Math.cos(p1.lat) * Math.cos(p2.lat) * Math.sin(dLong/2) * Math.sin(dLong/2);
	#   var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
	#   var d = R * c;
	# 
	#   return d;
	# }
	$dLat = $last_lat - $this_lat;
	$dLon = $last_lon - $this_lon;

	$a = sin($dLat/2) * sin($dLat/2) + cos($this_lat) * cos($last_lat) * sin($dLon/2) * sin($dLon/2);
	$c = 2 * atan2(sqrt($a), sqrt(1-$a));
	%d->{'distance'} = $R * $c;


	# /*
	#  * calculate (initial) bearing (in radians clockwise) between two points
	#  *
	#  * from: Ed Williams' Aviation Formulary, http://williams.best.vwh.net/avform.htm#Crs
	#  */
	# LatLong.bearing = function(p1, p2) {
	#   var y = Math.sin(p2.lon-p1.lon) * Math.cos(p2.lat);
	#   var x = Math.cos(p1.lat)*Math.sin(p2.lat) -
	#           Math.sin(p1.lat)*Math.cos(p2.lat)*Math.cos(p2.lon-p1.lon);
	#   return Math.atan2(y, x);
	# }
	$y = sin($dLon) * cos($last_lat);
	$x = cos($this_lat) * sin($last_lat) - sin($this_lat) * cos($last_lat) * cos($dLon);

	$bearing = rad2deg(atan2($y,$x));
	if ($bearing < 0) { $bearing = 360 + $bearing; }

	%d->{'bearing'} = $bearing;

	return %d;
}

require "create-googlemap-waypoints.inc.pl";

# $i is our iterator, incremented once for every line in the file
#
# $u is incremented whenever we use a point in a line segment
#
#
# $moving is a flag we'll use.  0 = not moving, 1 = moving, $moving_thresh is
# the speed at which the flag flips (km/h)
$i=0;
$u=0;
$stopcount=0;
$stopsecs=0;
$trip_dist=0;
$moving = 0;
$moving_thresh = 0.25;
$waypoint_distThresh = 0.025;

open (GTF_FH, $gtfFile);
while (<GTF_FH>) {
	chomp $_;

	# Store the raw data for this line from the file
	%this->{'raw'}->{'timestamp'} = substr($_,1,12);
	%this->{'raw'}->{'lat'} = substr($_,13,8);
	%this->{'raw'}->{'lon'} = substr($_,21,9);
	%this->{'raw'}->{'accuracy'} = substr($_,30,4);
	%this->{'raw'}->{'elev'} = substr($_,34,6);
	%this->{'raw'}->{'ewspeed'} = substr($_,40,5);
	%this->{'raw'}->{'nsspeed'} = substr($_,45,5);
	%this->{'raw'}->{'udspeed'} = substr($_,50,5);

	# Calculate a unix timestamp from what we were given
	# This is totally inefficient, but it reads well
	$yr = "20" . substr(%this->{'raw'}->{'timestamp'},0,2);
	$mo = substr(%this->{'raw'}->{'timestamp'},2,2) - 1;
	$dy = substr(%this->{'raw'}->{'timestamp'},4,2);
	$hr = substr(%this->{'raw'}->{'timestamp'},6,2);
	$mn = substr(%this->{'raw'}->{'timestamp'},8,2);
	$sc = substr(%this->{'raw'}->{'timestamp'},10,2);
	
	%this->{'timestamp'} = timegm($sc,$mn,$hr,$dy,$mo,$yr);

	# Get signed decimal-degree lat/longs
	# Our lat/longs come in DD(D)MMmmm format
	# We want DD(D).dddddddd format
	$lat_deg = substr(%this->{'raw'}->{'lat'}, 1, 2);
	$lat_MMmmm = substr(%this->{'raw'}->{'lat'}, 3, 2) . "." . substr(%this->{'raw'}->{'lat'}, 5, 3);
	$lat_dec = $lat_MMmmm / 60;
	%this->{'lat'} = $lat_deg + $lat_dec;
	if (substr(%this->{'raw'}->{'lat'},0,1) eq "S") { %this->{'lat'} = 0 - %this->{'lat'}; }
	
	$lon_deg = substr(%this->{'raw'}->{'lon'}, 1, 3);
	$lon_MMmmm = substr(%this->{'raw'}->{'lon'}, 4, 2) . "." . substr(%this->{'raw'}->{'lon'}, 6, 3);
	$lon_dec = $lon_MMmmm / 60;
	%this->{'lon'} = $lon_deg + $lon_dec;
	if (substr(%this->{'raw'}->{'lon'},0,1) eq 'W') { %this->{'lon'} = 0 - %this->{'lon'}; }

	# If we didn't care about speed colour, we'd just use this as part of
	# the code to create an array of points to hand to the GPolyLine.  It
	# turns out that GPolyLine will crash the Gmap if given more than
	# something like five or six hundred points, though.
	##	print "points[$i] = new GPoint(-$this_long, $this_lat);\n";

	# Now to get speed, which should be the square root of the sum of the
	# squares of each 3D vector.  We also need to convert to km/h (*3.6) or
	# mi/h (*2.2369363)
	%this->{'ewspeed'} = substr(%this->{'raw'}->{'ewspeed'}, 1, length(%this->{'raw'}->{'ewspeed'}) -2) . "." . substr(%this->{'raw'}->{'ewspeed'}, -1);
	%this->{'nsspeed'} = substr(%this->{'raw'}->{'nsspeed'}, 1, length(%this->{'raw'}->{'nsspeed'}) -2) . "." . substr(%this->{'raw'}->{'nsspeed'}, -1);
	%this->{'udspeed'} = substr(%this->{'raw'}->{'udspeed'}, 1, length(%this->{'raw'}->{'udspeed'}) -3) . "." . substr(%this->{'raw'}->{'udspeed'}, -2);
	%this->{'speed'} = 3.6 * sqrt(%this->{'ewspeed'}**2 + %this->{'nsspeed'}**2 + %this->{'udspeed'}**2);
	%this->{'ewspeed'} = %this->{'ewspeed'} * 3.6;
	%this->{'nsspeed'} = %this->{'nsspeed'} * 3.6;
	%this->{'udspeed'} = %this->{'udspeed'} * 3.6;


	# Colours will be assigned using the following fading scales:
	# 0km/h = full red, 50km/h = full green, 100km/h = full blue, 150km/h = full yellow
	# 12.5km/h = 75%R/25%G, 25km/h = 50%R/50%G, 75km/h=50%G/50%B, etc.
	#
	# if speed is 0 < x < 50 then red=(50-speed)*0.02*255
	# if speed is 0 < x < 50 then green=(0+speed)*0.02*255
	#
	# if speed is 50 < x < 100 then green=(100-speed)*0.02*255
	# if speed is 50 < x < 100 then blue=(-50+speed)*0.02*255
	#
	# if speed is 100 < x < 150 then blue=(150-speed)*0.02*255
	# if speed is 100 < x < 150 then red=(-100+speed)*0.02*255
	# if speed is 100 < x < 150 then green=(-100+speed)*0.02*255

	# There's probably a much better way to do this
	if (%this->{'speed'} >= 0 && %this->{'speed'} <= 50) {
		$red = sprintf ("%02X", (50-%this->{'speed'})*0.02*255);
		$green = sprintf ("%02X", (-0+%this->{'speed'})*0.02*255);
		$blue = sprintf ("%02X", (-0+0)*0.02*255);
		%this->{'rgbstr'} = "#$red$green$blue";
	} elsif (%this->{'speed'} > 50 && %this->{'speed'} <= 100) {
		$red = sprintf ("%02X", (0-0)*0.02*255);
		$green = sprintf ("%02X", (100-%this->{'speed'})*0.02*255);
		$blue = sprintf ("%02X", (-50+%this->{'speed'})*0.02*255);
		%this->{'rgbstr'} = "#$red$green$blue";
	} elsif (%this->{'speed'} > 100 && %this->{'speed'} <= 150) {
		$red = sprintf ("%02X", (-100+%this->{'speed'})*0.02*255);
		$green = sprintf ("%02X", (-100+%this->{'speed'})*0.02*255);
		$blue = sprintf ("%02X", (150-%this->{'speed'})*0.02*255);
		%this->{'rgbstr'} = "#$red$green$blue";
	}

	if (%last) {

		# Here's where we see if the next point is different enough to
		# use in our plot or not (used later)
		$diff_EWSpeed = abs(%this->{'ewspeed'} - %last_res->{'ewspeed'});
		$diff_NSSpeed = abs(%this->{'nsspeed'} - %last_res->{'nsspeed'});
		%this->{'comb_vSpeed'} = $diff_EWSpeed + $diff_NSSpeed;
		
		# Calculate the distance beteen this point and the last one (used
		# later)
		%d = delta_distance_bearing(%this->{'lat'},%this->{'lon'}, %last->{'lat'},%last->{'lon'});
		$trip_dist += %d->{'distance'};
		%this->{'total_dist'} = $trip_dist;
		%this->{'final_bearing'} = %d->{'bearing'};

		# NOTE: moved here because we deep-copy the hash and want all the stuff
		# in it first
		#
		# OK, we know the decimal lat/long position of this point.  Now we compare
		# it to our collection of known waypoints.  We'll use the classic,
		# inefficient method of comparing the distance of each point to every
		# waypoint and storing it if it's closer than any other point we've
		# compared so far.
		#
		# There has got to be a better way to do this.  For starters, a route that
		# crosses over the same waypoint multiple times will result in clobbered
		# data.  This is good enough for a Q&D v1, but needs fixin'.
		foreach $trip (keys %{%waypoints}) {
			# print STDERR "$trip\n";
			foreach $waypoint (keys %{%waypoints->{$trip}}) {
				%d = delta_distance_bearing(%waypoints->{$trip}->{$waypoint}->{'lat'},%waypoints->{$trip}->{$waypoint}->{'lon'}, %this->{'lat'},%this->{'lon'});
				# print STDERR "Distance to $waypoint: " . %d->{'distance'} . ", eh\n";
				if (!%waypoints->{$trip}->{$waypoint}->{'closest_dist'}) {
					%waypoints->{$trip}->{$waypoint}->{'closest_dist'} = %d->{'distance'};
					%waypoints->{$trip}->{$waypoint}->{'closest_point'} = dclone(\%this);
				} elsif (%waypoints->{$trip}->{$waypoint}->{'closest_dist'} > %d->{'distance'}) {
					%waypoints->{$trip}->{$waypoint}->{'closest_dist'} = %d->{'distance'};
					%waypoints->{$trip}->{$waypoint}->{'closest_point'} = dclone(\%this);
				}
			}
		}

		# Useful?  Not yet....
		#%last->{initial_bearing = %d->{bearing};

		# Make a marker and plot a datapoint if this is a place where we change
		# from not moving to moving
		# TODO: actually plot a datapoint
		if ($moving == 0 && %this->{'speed'} >= $moving_thresh) {

			if (%begin) {

				$movestart_timestamp = %this->{'timestamp'};

				# Sanity check
				if (!$movestop_timestamp) { die "No movestop_timestamp!\n"; }

				# These are used to put a marker where 
				$time_str = localtime($movestop_timestamp);
				$elapsed_stoptimestr = elapsed_time($movestart_timestamp,$movestop_timestamp);
				$elapsed_timestr = elapsed_time($movestart_timestamp,$start_timestamp);

				# TODO: replace with a subroutine
				print "var stop$i = new GMarker (new GPoint(". %this->{'lon'} . ", " . %this->{'lat'} . "),stopsign_icon);\n";
				print "var stophtml$i = \"Stopsign time: $time_str<BR>Elapsed stop time: $elapsed_stoptimestr<BR>Elapsed trip time: $elapsed_timestr\"; \n";
				print "GEvent.addListener(stop$i, \"click\", function() { stop$i.openInfoWindowHtml(stophtml$i); });\n";
				print "map.addOverlay(stop$i);\n";

				$stopsecs += $movestart_timestamp - $movestop_timestamp;
				$stopcount++;
			}
			else {
				# If we haven't started moving ever before, then this is our first
				# movement and we'll put some special data in the marker info
				# window
				#
				# %begin is our first moving datapoint -- we can't just assume
				# the first datapoint is where we begin 'cause there's always a
				# few seconds, as much as a few minutes maybe, where we just
				# sit still, letting the engine warm up or waiting for the GPS
				# to get into good accuracy, futzing with other random stuff,
				# etc.

				$time_str = localtime(%this->{'timestamp'});
				$start_timestamp = %this->{'timestamp'};
				$elapsed_timestr = elapsed_time(%this->{'timestamp'}, %this->{'timestamp'});

				# TODO: replace with subroutine
				print "var startpoint = new GMarker (new GPoint(" . %this->{'lon'} . ", " . %this->{'lat'} . "),rtstart_icon);\n";
				print "var startpointhtml = \"Trip start time: $time_str<BR>Elapsed trip time: $elapsed_timestr\"; \n";
				print "GEvent.addListener(startpoint, \"click\", function() { startpoint.openInfoWindowHtml(startpointhtml); });\n";
				print "map.addOverlay(startpoint);\n";

				%begin = %this;
			}
			
			$moving = 1;
		}

		# Change from moving to not moving
		if ($moving == 1 && %this->{'speed'} < $moving_thresh) {

			$movestop_timestamp = %this->{'timestamp'};
			$moving = 0;
		}

		# Plot the datapoint if there's enough of a change in speed (this is
		# the resolution bit)
		if (%this->{'comb_vSpeed'} > $min_vSpeed) {

			print "var polyline = new GPolyline([new GPoint(" . %last_res->{'lon'} . ", " . %last_res->{'lat'} . "), new GPoint(" . %this->{'lon'} . ", " . %this->{'lat'} . ")], \"" . %this->{'rgbstr'} . "\", 7);\n";
			print "map.addOverlay(polyline);\n";
			%last_res = %this;
			$u++;
		}
	}

	%last = %this;

	# Always save the first data point for res
	if ($i == 0) { %last_res = %this; }
	
	$i++;
#	if ($i > 100) {exit;}
}

# Technically, it is possible for the vehicle to drift around for hours at
# speed under the threshold we've set, and thus we'd be putting this next
# marker in the wrong place from where we thought the vehicle actually stopped
# at.  However, a) that's not terribly likely, and b) we're more concerned with
# WHEN the vehicle stopped last 
$time_str = localtime($movestop_timestamp);
print "var stoppoint = new GMarker (new GPoint(" . %this->{'lon'} . ", " . %this->{'lat'} . "),rtstop_icon);\n";
print "var stoppointhtml = \"Trip stop time: $time_str<BR>Elapsed trip time: $elapsed_timestr\"; \n";
print "GEvent.addListener(stoppoint, \"click\", function() { stoppoint.openInfoWindowHtml(stoppointhtml); });\n";
print "map.addOverlay(stoppoint);\n";

$resolution = sprintf("%.1f", $u/$i*100);
print STDERR "$i points given, $u points used ($resolution%) at resolution $min_vSpeed\n";
print STDERR "Trip started " . localtime($start_timestamp) . ", elapsed trip time: " . elapsed_time($movestop_timestamp, $start_timestamp) . "\n";

print STDERR "Stopped $stopcount time(s), total stoppage " . elapsed_time($stopsecs,0) . "\n";

%d = delta_distance_bearing(%this->{'lat'},%this->{'lon'}, %begin->{'lat'},%begin->{'lon'});

printf STDERR "Trip distance: %.2f, crow flies: %.2f, bearing: %.2f\n", $trip_dist, %d->{'distance'}, %d->{'bearing'};
	
# This is a hack ... we figure out which route by the total bearing.  
#
# TODO: Replace this with something like an array of known routes where we try
# to match a route
if (%d->{'bearing'} > 210 && %d->{'bearing'} < 215) {
	$trip = 'home-work';
} elsif (%d->{'bearing'} > 30 && %d->{'bearing'} < 35) {
	$trip = 'work-home';
} else { print STDERR "I dunno WTF is goign on\n"; }

# We build a hash of waypoints to unixtimestamps, used later to determine the
# chronological order in which each waypoint was visited...
#
# ... again, there's probably a way to do this without doing two foreach()es, I
# just can't think of it right now
foreach $waypoint (keys %{%waypoints->{$trip}}) {
	# print STDERR "Point closest to $waypoint (" . %waypoints->{$trip}->{$waypoint}->{'closest_dist'} . ": " . %waypoints->{$trip}->{$waypoint}->{'closest_point'}->{'timestamp'} . "\n";
	if (%waypoints->{$trip}->{$waypoint}->{'closest_dist'} < $waypoint_distThresh) {
		%waypointorder->{$waypoint} = %waypoints->{$trip}->{$waypoint}->{'closest_point'}->{'timestamp'};
	}
}

# Now we build a sorted array of waypoints.  Note that we have waypoints of
# type 'wpt', which means it's a waypoint whose elapsed distance/time should be
# counted, and of type 'dec' which means it's just a waypoint to help us tell
# which leg was chosen (dec=decision).
#
# We'll skip the first waypoint, and then for every waypoint thereafter we'll
# compare distances and times between the last one and the current one.
$i=0;
print STDERR "\n";
foreach $waypoint (sort {$waypointorder{$a} cmp $waypointorder{$b}} keys %{%waypointorder}) {
	if ($i > 0) {
	    if (%waypoints->{$trip}->{$waypoint}->{'type'} eq "wpt") {
#			print STDERR "($i) $waypoint: " . %waypoints->{$trip}->{$waypoint}->{'closest_point'}->{'timestamp'} . "\n";
#			print STDERR "($i) " . %last->{'name'} . ": " .  %last->{'point'}->{'timestamp'} . "\n";
			if (%last->{'intermediate'}) {
				print STDERR %last->{'name'} . " via " . %last->{'intermediate'} . " to $waypoint: ";
			} else {
				print STDERR %last->{'name'} . " to $waypoint: ";
			}
			$elapsedTime = elapsed_time(%waypoints->{$trip}->{$waypoint}->{'closest_point'}->{'timestamp'}, %last->{'point'}->{'timestamp'});
			$elapsedDist = sprintf "%.2f",%waypoints->{$trip}->{$waypoint}->{'closest_point'}->{'total_dist'} - %last->{'point'}->{'total_dist'};
			print STDERR "$elapsedTime, $elapsedDist" . "km\n";
			
			
			undef(%last);
			%last->{'name'} = $waypoint;
			%last->{'point'} = %waypoints->{$trip}->{$waypoint}->{'closest_point'};
		} else {
			%last->{'intermediate'} = $waypoint . " (" . %waypoints->{$trip}->{$waypoint}->{'desc'} . ")";
		}
	} else {
		%last->{'name'} = $waypoint;
		%last->{'point'} = %waypoints->{$trip}->{$waypoint}->{'closest_point'};
	}
	$i++;
}

# vi:set autoindent:
# vi:set ts=4:
# vi:set sw=4:
