Version 1 of A graphical timetable

Updated 2007-09-16 13:35:40 by suchenwi

if 0 {Richard Suchenwirth 2007-09-16 - Graphical timetables visualize operations on a transit line, typically a railway. The X axis represents time of day, the Y axis the sequence of stations in distance scale.

WikiDbImage gratt.jpg

The public API is just an overloaded canvas named gratt, which accepts some extra configuration parameters, and has the extra add method. A global variable named like the widget is created, and upvar-ed to the "anonymous array" in all functions. }

 proc gratt {w args} {
    upvar #0 $w ""
    set (from)   0
    set (to)     24
    set (offset) 80
    canvas $w
    foreach {key value} $args {
        switch -- $key {
            -line   {set line $value}
            -from   {set (from)   $value}
            -to     {set (to)     $value}
            -offset {set (offset) $value}
            default {$w configure $key $value}
        }
    }
    gratt'draw $w $line
    rename $w _$w
    proc $w {cmd args} {
        set self [lindex [info level 0] 0]
        switch -- $cmd {
            add     {eval gratt'add $self $args}
            default {eval _$self $cmd $args}
        }
    }
    return $w
 }

#-- Internal routines: draw grid, label stations and hours

 proc gratt'draw {w line} {
    upvar #0 $w ""
    set x0 $(offset)
    set width [$w cget -width]
    set (dx) [expr {($width - $x0) / ($(to) - $(from))}]
    set height [$w cget -height]
    for {set h $(from); set x $x0} {$h <= $(to)} {incr h; incr x $(dx)} {
        $w create text $x 8 -text $h
        $w create line $x 16 $x [expr {$height-8}] -fill gray
    }
    incr x -$(dx)
    set yfac [expr {double($height-24)/[lindex $line end]}]
    foreach {name distance} $line {
        set ($name) [expr {$distance*$yfac+16}]
        $w create text 5 $($name) -text "$name $distance" -anchor w
        $w create line $x0 $($name) $x $($name)
    }
 }

#-- Add a train

 proc gratt'add {w number schedule args} {
    upvar #0 $w ""
    set coords {}
    foreach {name times} $schedule {
        foreach time $times {
            lappend coords [gratt'x $w $time] $($name)
        }
    }
    eval $w create line $coords $args
 }

#-- convert a "military" time (HHMM) to x coordinate

 proc gratt'x {w time} {
    upvar #0 $w ""
    scan $time %d time ;# strip off leading zero
    set mins [expr {($time/100-$(from))*60+$time%100}]
    expr {double($(offset)) + $mins * $(dx)/60}
 }

if 0 {Here's a testing example: the fictional "Mason - Dixon line". Assume it is single-tracked, and trains can only cross in Mason-East and Springfield. We have regular trains (blue) and expresses (red). First, to declare the line with mileposts:}

 set md {Mason 0 Mason-East 4 Springfield 12 Dixon-West 18 Dixon 20}

 pack [gratt .g -line $md -from 5 -to 19 -bg white -width 720 -height 240]

#-- And now for the timetable. Westbound trains:

 .g add 1201 {
    Mason 0550 Mason-East {0556 0558} Springfield {0612 0618} Dixon 0639
 } -fill blue
 .g add 1203 {
    Mason 0655 Mason-East {0701 0703} Springfield {0714 0720}
    Dixon-West {0728 0730} Dixon 0741
 } -fill blue
 .g add X303 {Mason 0730 Springfield {0748 0750} Dixon 0811} -fill red
 .g add 1205 {
    Mason 0800 Mason-East {0806 0810} Springfield {0824 0830} Dixon 0853
 } -fill blue
 .g add 1207 {
    Mason 1000 Mason-East {1006 1008} Springfield {1022 1026} Dixon 1051
 } -fill blue
 .g add 1401 {Springfield 1200 Dixon-West {1210 1212} Dixon 1221} -fill blue
 .g add 1209 {
    Mason 1200 Mason-East {1206 1208} Springfield 1222 Dixon 1251
 } -fill blue
 .g add X305 {Mason 1330 Springfield {1348 1350} Dixon 1411} -fill red
 .g add 1211 {
    Mason 1500 Mason-East {1506 1508} Springfield {1522 1524} Dixon 1553
 } -fill blue
 .g add 1213 {
    Mason 1700 Mason-East {1706 1708} Springfield {1722 1724} Dixon 1753
 } -fill blue

#---------------------- Eastbound trains

 .g add 1200 {
    Dixon 0550 Dixon-West {0556 0558} Springfield {0612 0619}
    Mason-East {0628 0630} Mason 0643
 } -fill blue
 .g add 1202 {
    Dixon 0650 Dixon-West {0656 0658} Springfield {0712 0719}
    Mason-East {0728 0740} Mason 0753
 } -fill blue
 .g add 1204 {
    Dixon 0900 Dixon-West {0906 0908} Springfield {0922 0929}
    Mason-East {0936 0938} Mason 0951
 } -fill blue
 .g add 1206 {
    Dixon 1000 Dixon-West {1006 1008} Springfield {1022 1029}
    Mason-East {1036 1038} Mason 1051
 } -fill blue
 .g add 1408 {Dixon 1130 Dixon-West {1136 1138} Springfield 1146} -fill blue
 .g add X306 {Dixon 1100 Springfield {1122 1125} Mason 1143} -fill red
 .g add 1208 {
    Dixon 1330 Dixon-West {1336 1338} Springfield {1342 1353}
    Mason-East {1408 1410} Mason 1423
 } -fill blue
 .g add 1210 {
    Dixon 1600 Dixon-West {1606 1608} Springfield {1622 1629}
    Mason-East {1636 1638} Mason 1651
 } -fill blue
 .g add X308 {Dixon 1730 Springfield {1752 1755} Mason 1813} -fill red
 .g add 1212 {
    Dixon 1800 Dixon-West {1806 1808} Springfield {1822 1829}
    Mason-East {1836 1838} Mason 1851
 } -fill blue

if 0 {Looking at the diagram, one can deduce that at least three trainsets are needed: a regular one that stays in Dixon overnight, and a regular and an express (if they differ in type) stationed in Mason.


Category Example