#! /usr/local/perl/bin/perl -s

use POSIX;

($me = $0) =~ s%^.*/%% if !defined $me;
$conf = $ENV{HOME} . '/.' . $me . '.pl' if !defined $conf;

local *C;
my $expr;

if (open(C, "< $conf")) {
  {local $/; $expr = <C>;}
  close(C);
}

eval $expr if $expr ne '';

$show_version = 0 if !defined $show_version;
$debug = '' if !defined $debug;
$verbose = 1 if !defined $verbose;
$slopemax = 0.4 if !defined $slopemax;
$slopemin = 0.007 if !defined $slopemin;
$ascendmin = 9.75 if !defined $ascendmin; # in meters
$pi = POSIX::atan(1) * 4 if !defined $pi;
$rad_long = 6378137.0 if !defined $rad_long; # in meters
$rad_short = 6356752.314245 if !defined $rad_short; # in meters

my $version = '0.01';

if ($show_version) {
  print "$version\n";
  exit;
}

# using Hubeny's formula

my $E2 = 1 - ($rad_short / $rad_long) ** 2;

sub dist {
  my ($lat0, $lon0, $lat1, $lon1) = map {$_ * $pi / 180} @_;
  my $d_y = $lat1 - $lat0;
  my $d_x = $lon1 - $lon0;
  my $p = ($lat0 + $lat1) * 0.5;
  my $w = sqrt(1 - $E2 * POSIX::sin($p) ** 2);

  return sqrt(($d_y * $rad_long * (1 - $E2) / $w ** 3) ** 2 + ($d_x * $rad_long / $w * POSIX::cos($p)) ** 2);
}

if ($debug =~ /:dist:/) {
  while (@ARGV >= 4) {
    print &dist(splice(@ARGV, 0, 4)), "m\n";
  }

  exit;
}

sub ascend {
  my ($fn) = @_;
  local *F;

  unless (open(F, $fn)) {
    print STDERR "$0: open(F, \"$fn\"): $!\n" if $verbose > 1;
    return undef;
  }

  my ($txt, @trk, $a, $d, $i, $j);

  {local $/; $txt = <F>;}
  close(F);

  while ($txt =~ m|<trkpt\s+lat=\"([0-9.]+)\"\s+lon=\"([0-9.]+)\"\s*>(.*?)</trkpt\s*>|gs) {
    my $trkpt = $3;
    my %trk_n;

    @trk_n{qw(lat lon)} = ($1 + 0, $2 + 0);
    $trkpt =~ m|<ele\s*>\s*([0-9.]+)\s*</ele\s*>| and $trk_n{e} = $1 + 0;

    if (@trk) {
      my $trk_o = $trk[$#trk];

      $trk_n{d} = $trk_o->{d} + &dist(@$trk_o{qw(lat lon)}, @trk_n{qw(lat lon)});
    }
    else {
      $trk_n{d} = 0;
    }

    push @trk, +{%trk_n};
  }

  for ($a = 0, $i = 0, $j = 1 ; $j < $#trk ; ++$j) {
    my $d_delta;

    $d_delta = ($trk[$j]->{d} - $trk[$i]->{d});

    if ($d_delta > 0) {
      my ($a_delta, $slope);

      $a_delta = $trk[$j]->{e} - $trk[$i]->{e};
      $slope = $a_delta / $d_delta;

      if (($slopemax eq '' || ($slope >= -$slopemax && $slope <= $slopemax)) &&
	  !($slopemin ne '' && $slope > - $slopemin && $slope < $slopemin &&
	    $ascendmin ne '' && $a_delta > -$ascendmin && $a_delta < $ascendmin)) {
	$a += $a_delta if $a_delta > 0;
	$i = $j;
      }
    }
  }

  @trk and $d = $trk[$#trk]->{d};
  ($a, $d);
}

sub show {
  my ($a, $d, $f) = @_;

  defined $d and $d *= 0.001;

  if (defined $a) {
    $f ne '' and print "$f: ";
    print "distance ${d}km / elevation gain ${a}m\n";
  }
}

@ARGV or @ARGV = qw(-);

if (@ARGV > 1) {
  do {
    my $f = shift @ARGV;

    &show(&ascend($f), $f);
  } while (@ARGV);
}
else {
  &show(&ascend($ARGV[0]));
}

1;
__END__

=head1 NAME

Egpx2ascend - shows distances and elevation gains of GPX files with elevations.

=head1 SYNOPSIS

  egpx2ascend [<options>] [<GPX file> ...]

=head1 DESCRIPTION

B<Egpx2ascend> reads GPX files with elevations,
and shows distances and elvation gains of them.

When invoked with no I<<GPX file>>,
it reads its standarad input.

=for html The latest version is obtained via

=for html <blockquote>

=for html <!--#include virtual="/cgi-perl/showfile?/cycling/pub/egpx2ascend-[0-9]*.tar.gz"-->.

=for html </blockquote>

=head2 Options

=over 4

=item C<-show_version>

shows the version string of this program,
and exits.

=item C<-slopemax>C<=>I<<slope max>>

=item C<-slopemin>C<=>I<<slope min>>

=item C<-ascendmin>C<=>I<<ascend min>>

For two successive track points I<<p1>> and I<<p2>>,
let I<<d>> be the distance between I<<p1>> and I<<p2>>,
and I<<a>> be the absolute value of the difference between elevations of I<<p1>> and I<<p2>>,
both in meters.

When I<<p2>> is not the last track point,
if I<<d>> is 0,
or if I<<a>> / I<<d>> is larger than I<<slope max>>,
or if I<<a>> / I<<d>> is smaller than I<<slope min>> and I<<a>> is smaller than I<<ascend min>>,
then B<egpx2ascend> dismisses I<<p2>>.

=back

=head1 AUTHOR

Kiyokazu SUTO E<lt>suto@ks-and-ks.ne.jpE<gt>

=head1 DISCLAIMER etc.

This program is distributed with
ABSOLUTELY NO WARRANTY.

Anyone can use, modify, and re-distibute this program
without any restriction.

=cut
