Drawing a wind rose

Arjen Markus (29 september 2009) on the comp.lang.tcl newsgroup Stuart asked for a script to plot wind roses. As I had some time on my hands, I did a first implementation of such a diagram using Plotchart. The result is below.

AM (22 october 2009) I have added this to Plotchart, along with some new plot methods.


# windrose.tcl --
#     Add a wind rose diagram to Plotchart
#

# package --
#     Register the new plot type
#
package require Plotchart

namespace eval ::Plotchart {
    set methodProc(windrose,plot)     DrawWindRoseData
    set methodProc(windrose,saveplot) SavePlot
    set methodProc(windrose,title)    DrawTitle
}

# createWindRose --
#     Create a new command for plotting a windrose
#
# Arguments:
#    w             Name of the canvas
#    radius_data   Maximum radius and step
#    sectors       Number of sectors (default: 16)
# Result:
#    Name of a new command
# Note:
#    The entire canvas will be dedicated to the windrose
#    Possible additional arguments (optional): nautical/mathematical
#    step in phi
#
proc ::Plotchart::createWindRose { w radius_data {sectors 16}} {
    variable data_series

    foreach s [array names data_series "$w,*"] {
        unset data_series($s)
    }

    set newchart "windrose_$w"
    interp alias {} $newchart {} ::Plotchart::PlotHandler windrose $w

    set rad_max   [lindex $radius_data 0]
    set rad_step  [lindex $radius_data 1]

    if { $rad_step <= 0.0 } {
        return -code error "Step size can not be zero or negative"
    }
    if { $rad_max <= 0.0 } {
        return -code error "Maximum radius can not be zero or negative"
    }

    foreach {pxmin pymin pxmax pymax} [MarginsCircle $w] {break}

    viewPort         $w $pxmin     $pymin     $pxmax   $pymax
    polarCoordinates $w $rad_max
    DrawRoseAxes     $w $rad_max   $rad_step


    set data_series($w,radius) {}
    for { set i 0 } { $i < $sectors } { incr i } {
        lappend data_series($w,cumulative_radius) 0.0
    }

    set data_series($w,start_angle)     [expr {90.0 - 360.0/(4.0*$sectors)}]
    set data_series($w,d_angle)         [expr {360.0/(2.0*$sectors)}]
    set data_series($w,increment_angle) [expr {360.0/$sectors}]
    set data_series($w,count_data)      0


    return $newchart
}

# DrawRoseAxes --
#    Draw the axes to support a wind rose
# Arguments:
#    w           Name of the canvas
#    rad_max     Maximum radius
#    rad_step    Step in radius
# Result:
#    None
# Side effects:
#    Axes drawn in canvas
#
proc ::Plotchart::DrawRoseAxes { w rad_max rad_step } {

    #
    # Draw the spikes
    #
    set angle 0.0

    foreach {xcentre ycentre} [polarToPixel $w 0.0 0.0] {break}

    foreach {angle text dir} {
         90  North s
        180  West  e
        270  South n
          0  East  w } {
        foreach {xcrd ycrd} [polarToPixel $w $rad_max $angle] {break}
        foreach {xtxt ytxt} [polarToPixel $w [expr {1.05*$rad_max}] $angle] {break}
        $w create line $xcentre $ycentre $xcrd $ycrd
        $w create text $xtxt    $ytxt    -text $text -anchor $dir
    }

    #
    # Draw the concentric circles
    #
    set rad $rad_step

    while { $rad < $rad_max+0.5*$rad_step } {
        foreach {xtxt   ytxt}    [polarToPixel $w $rad   45.0] {break}
        foreach {xright ycrd}    [polarToPixel $w $rad    0.0] {break}
        foreach {xleft  ycrd}    [polarToPixel $w $rad  180.0] {break}
        foreach {xcrd   ytop}    [polarToPixel $w $rad   90.0] {break}
        foreach {xcrd   ybottom} [polarToPixel $w $rad  270.0] {break}

        $w create oval $xleft $ytop $xright $ybottom

        $w create text $xtxt [expr {$ytxt+3}] -text $rad -anchor s

        set rad [expr {$rad+$rad_step}]
    }
}

# DrawWindRoseData --
#    Draw the data for each sector
# Arguments:
#    w           Name of the canvas
#    data        List of "sectors" data
#    colour      Colour to use
# Result:
#    None
# Side effects:
#    Data added to the wind rose
#
proc ::Plotchart::DrawWindRoseData { w data colour } {

    variable data_series

    set start_angle  $data_series($w,start_angle)
    set increment    $data_series($w,increment_angle)
    set width_sector $data_series($w,d_angle)

    set new_cumulative {}

    foreach value $data cumulative_radius $data_series($w,cumulative_radius) {
        console show ; puts $start_angle
        set radius [expr {$value + $cumulative_radius}]

        foreach {xright ytop}    [polarToPixel $w [expr {$radius*sqrt(2.0)}]  45.0] {break}
        foreach {xleft  ybottom} [polarToPixel $w [expr {$radius*sqrt(2.0)}] 225.0] {break}

        $w create arc $xleft $ytop $xright $ybottom -style pie -fill $colour \
            -tag data_$data_series($w,count_data) -start $start_angle -extent $width_sector

        lappend new_cumulative $radius

        set start_angle [expr {$start_angle - $increment}]
    }

    $w lower data_$data_series($w,count_data)

    set data_series($w,cumulative_radius) $new_cumulative
    incr data_series($w,count_data)
}

# main --
#     Testing the wind rose diagram
#
pack [canvas .c -bg white]
set p [::Plotchart::createWindRose .c {30 6} 4]

$p plot { 5 10  0 3} red
$p plot {10 10 10 3} blue

$p title "Simple wind rose - margins need to be corrected ..."