Saturday, June 9, 2012

BRT Cuesheets

How to parse cue-sheets from bikeroutetoaster (in order to prepare an aid-station summary)

Consider this, you are using  http://www.bikeroutetoaster.com/ (brt) to create a running course where you want to provide aid stations. In brt you do this by adding course-points (other points are track-points). When you add a new course-point or convert any existing track-point to a course-point you are asked to give it a name, type, and directions (or general description). For aid stations set type to Food.
brt then generates xml code which you can download on the cuesheet page. The xml looks something like this:

...
    <point>  
    <lat>48.614215</lat>  
    <lng>8.967376</lng>  
    <legDistance>5.57</legDistance>  
    <totalDistance>26.26</totalDistance>  
    <height>522</height>  
    <pointType>Food</pointType>  
    <name>VP1</name>  
    <directions>Parkplatz Kohltor (Hildrizhausen)</directions>  
   </point>  
   <point>  
    <lat>48.60916</lat>  
    <lng>8.96905</lng>  
    <legDistance>0.58</legDistance>  
    <totalDistance>26.83</totalDistance>  
    <height>517</height>  
    <pointType>Right</pointType>  
    <name />  
    <directions />  
   </point>  
...

So what I wanted from this was a listing that shows all points marked as Food (aid stations), at which mile/kilometer they are reached and the delta distance. In addition I output transit times for aid stations given estimated min/max finisher times. This gives some idea how long each aid station should be available.

The perl code to do this is printed here (download link below).
To run this script just provide the xml cuesheet filename as an argument on the commandline. $min_h and $max_h specify the expected minimum and maximum finisher times in hours.

1:  #!/usr/bin/perl  
2:  # Copyright Andreas Loeffler <al@exitzero.de>  
3:  # parse cuesheet xml files generated by http://www.bikeroutetoaster.com  
4:  # it will output information about coursepoints that are marked as type "Food"  
5:  # use this to provide information about your aid stations if you plan for  
6:  # some running event  
7:  # Example: $ parse-cuesheet.pl cuesheet-sut100q2.xml  
8:  use strict;  
9:  use warnings;  
10:  # use module  
11:  use XML::Simple;  
12:  use Data::Dumper;  
13:  use Date::Calc qw(:all);  
14:  my $xmlfile = $ARGV[0];  
15:  die "usage: parse-cuesheet.pl brt-cuesheet (xml)\n" unless (defined $xmlfile && -s $xmlfile);  
16:  # create object  
17:  my $xml = new XML::Simple (KeyAttr=>[]);  
18:  # read XML file  
19:  my $data = $xml->XMLin($ARGV[0]);  
20:  print "xml file: $ARGV[0]\n";  
21:  my $min_h = 16;  
22:  my $max_h = 30;  
23:  my @start = (2012, 10, 13, 8, 0, 0);  
24:  my @end = Add_Delta_DHMS(@start, 0, $max_h, 0, 0);  
25:  my @now = Today_and_Now();  
26:  my @d_dhms = Delta_DHMS(@now, @start);  
27:  my $days = Delta_Days($now[0], $now[1], $now[2],  
28:             $start[0], $start[1], $start[2]);  
29:  my $vpn = 1;  
30:  my $prev;  
31:  my @d;  
32:  my %vps;  
33:  my $points; # points hashref  
34:  my $units;  
35:  my $pt = 0;  
36:  my $have_point = 0;  
37:  my $total_distance = 0;  
38:  my $toMi = 0.6214;  
39:  my $toKm = 1;  
40:  print "start: @start\n";  
41:  print "end:  @end\n";  
42:  print "now: @now\n";  
43:  print "Noch $d_dhms[0] Tage, $d_dhms[1] Stunden und $d_dhms[2] Minuten bis zum Start\n";  
44:  print "Zielschluss: @end\n\n";  
45:  $units = $data->{distanceUnits};  
46:  print "distanceUnits: $units\n";  
47:  if ($units eq 'miles') {  
48:     $toMi = 1;  
49:     $toKm = 1.6092;  
50:  }  
51:  foreach my $p (@{$data->{points}->{point}})  
52:  {  
53:     next unless ($p->{pointType} eq 'Food');  
54:  #   print $p->{name}, "\n";  
55:  #   print "lat/lng: ", $p->{lat}, "/", $p->{lng}, "\n";  
56:  #   print "total dist: ", $p->{totalDistance}, "\n";  
57:  #   print "point type: ", $p->{pointType}, "\n";  
58:  #   print "\n";  
59:     if (!defined $prev){ $prev = $p->{totalDistance};}  
60:     my $delta = $p->{totalDistance} - $prev;  
61:     $prev = $p->{totalDistance};  
62:     printf "%s:\t%5.1f mi / %5.1f km ", $p->{name}, $p->{totalDistance} * $toMi, $p->{totalDistance} * $toKm;  
63:     printf "delta %5.2f mi / %5.2f km\n", $delta * $toMi, $delta * $toKm;  
64:     push @d, $delta;  
65:     $vps{$p->{name}} = $p->{totalDistance};  
66:     if ($p->{name} eq "ZIEL") { $total_distance = $p->{totalDistance}};  
67:     $vpn++;  
68:  }  
69:  my $avg = 0;  
70:  foreach (@d) {  
71:     $avg += $_;  
72:  }  
73:  printf "avg milage between aid stations: %.2f mi / %.2f km\n", ($avg / $#d) * $toMi, ($avg / $#d) * $toKm;  
74:  print "\n";  
75:  print "Durchgangszeiten:\n";  
76:  printf "total: %.2f km\n", $total_distance * $toKm;  
77:  my $min_pace = ($max_h * 60 * 60) / ($total_distance * $toKm);  
78:  my $max_pace = ($min_h * 60 * 60) / ($total_distance * $toKm);  
79:  printf "min pace (min/km) = %.2d:%.2d\n", $min_pace/60,$min_pace % 60;  
80:  printf "max pace (min/km) = %.2d:%.2d\n", $max_pace/60,$max_pace % 60;  
81:  foreach (sort keys %vps) {  
82:     next unless defined $vps{$_};  
83:     my @first_h_m = (($vps{$_} * $max_pace)/(60*60),  
84:             (($vps{$_} * $max_pace)/60) % 60);  
85:     my @last_h_m = (($vps{$_} * $min_pace)/(60*60),  
86:             (($vps{$_} * $min_pace)/60) % 60);  
87:     my @first_date = Add_Delta_DHMS(@start, 0, @first_h_m, 0);  
88:     my @last_date = Add_Delta_DHMS(@start, 0, @last_h_m, 0);  
89:     my @vp_delta = Delta_DHMS(@first_date, @last_date);  
90:     printf "%s\t%6.2f\t", $_, $vps{$_};  
91:     printf " von %s %.2d:%.2d (%.2d:%.2d)",  
92:       Day_of_Week_Abbreviation(Day_of_Week($first_date[0], $first_date[1], $first_date[2])),  
93:       $first_date[3], $first_date[4], @first_h_m;  
94:     printf " bis %s %.2d:%.2d (%.2d:%.2d)",  
95:       Day_of_Week_Abbreviation(Day_of_Week($last_date[0], $last_date[1], $last_date[2])),  
96:       $last_date[3], $last_date[4], @last_h_m;  
97:     printf " (VP offen fuer %.2d:%.2d Stunden)\n", $vp_delta[1], $vp_delta[2];  
98:     #printf "day %s\n", Day_of_Week_Abbreviation(Day_of_Week($first_date[0], $first_date[1], $first_date[2]));  
99:  }  

Download the perl script here.


Sample output:
xml file: sut100s-cuesheet.xml
start: 2012 10 13 8 0 0
end:   2012 10 14 14 0 0
now: 2012 6 9 21 1 7
Noch 125 Tage, 10 Stunden und 58 Minuten bis zum Start
Zielschluss: 2012 10 14 14 0 0

distanceUnits: kms
VP1:  16.3 mi /  26.3 km  delta  0.00 mi /  0.00 km
VP2:  30.1 mi /  48.5 km  delta 13.81 mi / 22.22 km
VP3:  44.5 mi /  71.6 km  delta 14.35 mi / 23.09 km
VP4:  56.2 mi /  90.4 km  delta 11.69 mi / 18.81 km
VP5:  67.3 mi / 108.3 km  delta 11.17 mi / 17.97 km
VP6:  80.1 mi / 128.8 km  delta 12.73 mi / 20.48 km
VP7:  93.0 mi / 149.7 km  delta 12.96 mi / 20.86 km
ZIEL: 101.0 mi / 162.6 km  delta  8.03 mi / 12.92 km
avg milage between aid stations: 12.10 mi / 19.48 km

Durchgangszeiten:
total: 162.61 km
min pace (min/km) = 11:04
max pace (min/km) = 05:54
VP1  26.26  von Sat 10:35 (02:35)  bis Sat 12:50 (04:50) (VP offen fuer 02:15 Stunden)
VP2  48.48  von Sat 12:46 (04:46)  bis Sat 16:56 (08:56) (VP offen fuer 04:10 Stunden)
VP3  71.57  von Sat 15:02 (07:02)  bis Sat 21:12 (13:12) (VP offen fuer 06:10 Stunden)
VP4  90.38  von Sat 16:53 (08:53)  bis Sun 00:40 (16:40) (VP offen fuer 07:47 Stunden)
VP5 108.35  von Sat 18:39 (10:39)  bis Sun 03:59 (19:59) (VP offen fuer 09:20 Stunden)
VP6 128.83  von Sat 20:40 (12:40)  bis Sun 07:46 (23:46) (VP offen fuer 11:06 Stunden)
VP7 149.69  von Sat 22:43 (14:43)  bis Sun 11:36 (27:36) (VP offen fuer 12:53 Stunden)
ZIEL 162.61  von Sun 00:00 (16:00)  bis Sun 14:00 (30:00) (VP offen fuer 14:00 Stunden)