
PDH 2007-02-15 Corrected tempeture to temperature on line 44, and (pedantically) Januayr to January on line 4. This is an impressive app that really needs a screenshot to show it off. That said, I don't understand why the forecast days scroll horizontally instead of vertically, but that's easily corrected. Replace lines 359-362 with these:
if {[incr row] > 1} {
incr col
set row 0
}This is the app I would have written had I the proper mojo.KPV 2009-08-27 -- vertical is better but you then have to figure out the correct row to start in because day 1 may have only one entry.S_M 2007-07-04 I also like and use this application, at first I did not understand the temperature ranges for the day and night. Replacing the line 350 (set txt "$WEATHER($key3,$id2,temp,minimum)\xB0 -...) with:
if {[regexp -nocase "night" $WEATHER($key,$id,name)]} {
set txt "Low $WEATHER($key3,$id2,temp,minimum)\xB0"
} else {
set txt "High $WEATHER($key2,$id2,temp,maximum)\xB0"
}will make it more similar to the forecast on the NOAA page.spacecowboy - 2009-08-21 00:20:47I have messed with this ndfdXML.htm file for HOURS... I finally had to drop a copy of nusoap.php in the same folder as the aforementioned file and the ndfdXMLclient.php file... the error "Parse error: syntax error, unexpected T_REQUIRE_ONCE in C:\Inetpub\vhosts\worldnewsvine.com\httpdocs\nws\ndfdSOAPclientByDay.php on line 55" finally disappeared by simply changing the runonce statement to 'nusoap.php'Now I am back to another error I was getting which is:Warning: Cannot modify header information - headers already sent by (output started at C:\Inetpub\vhosts\worldnewsvine.com\httpdocs\nws\ndfdXMLclient.php:1) in C:\Inetpub\vhosts\worldnewsvine.com\httpdocs\nws\ndfdXMLclient.php on line 111
(which is this line of code: header("Content-Type: text/xml");... what is it supposed to be? // Send the appropriate mime type for XML isn't text/xml correct?And to finish that all off, the error continues with:ERROR HTTP Error: Unsupported HTTP response status 404 Not Found (soapclient->response has contents of the response)Okay.. I am not professing to be a programmer but one would think that this would be easier to figure-out than this...Right now I am just using simplepie to fetch the rss feed however, I would love to get this mapping function/application running...Please any help, in plain ole english.... thanksKPV 2009-08-27 - huh? what are ndfdXML.htm, nusoap.php and ndfdXMLclient.php? Are you really running this app or some other php one?KPV 2009-08-27 - while trying to figure out the above error, I decided to replace everything with the more current code on my machine. Some of the changes include caching icon images (turned off for demoing); noon markers on temperature graph; the two suggestions from above; etc.
##+##########################################################################
#
# noaa.tcl -- Displays weather forecast from NOAA
# by Keith Vetter, January 2007
#
package require Tk
package require http
package require tdom
package require Img
package require Plotchart
package require tile
namespace import -force ::ttk::button
set S(noCache) 1
set S(iconDir) noaaIcons
set S(x,axisStep) 24
# see http://www.nws.noaa.gov/xml/
# http://www.weather.gov/forecasts/xml/SOAP_server/ndfdSOAPByDay.htm
set S(url,forecast) http://graphical.weather.gov/xml/SOAP_server/ndfdSOAPclientByDay.php
set S(url,temp) http://graphical.weather.gov/xml/SOAP_server/ndfdXMLclient.php
set S(url,temp,parameters) {?lat=${LAT}&lon=${LON}&product=time-series&begin=${BEGINDATE}T00%3A00%3A00&end=${ENDDATE}T00%3A00%3A00&temp=temp&Submit=Submit}
set S(format) 12+hourly
set S(days) 5
set COLORS {lightblue violet} ;# Temperature day's columns
set COLORS {\#82eeee \#ee82ee \#eeee82 \#8282ee \#82ee82 \#ee8282}
set COLORS {lightblue}
array set CITIES {
"Boston, MA" "42.35 -71.066666"
"Chicago, IL" "41.8675 -87.6243"
"Denver, CO" "39.75 -104.98"
"Granville, OH" "40.068088 -82.517967"
"Honolulu, HI" "21.31 -157.83"
"Los Angeles, CA" "34.054 -118.245"
"New York, NY" "40.7563 -73.9865"
"Providence, RI" "41.82355 -71.422132"
"San Francisco, CA" "37.77 -122.43"
"Washington, DC" "38.9136 -77.0132"
}
proc Submit {who} {
set ll [PrettyLat $::S(lat) $::S(lon)]
if {$who eq "temperature"} {
set ::S(msg) "Fetching NOAA temperature forecast"
set n [GetNOAATemp $::S(lat) $::S(lon)]
if {$n} {
set ::S(msg) "NOAA temperature forecast for $ll"
GetPlotData
PlotTemp
} else { set ::S(msg) "error fetching NOAA temperature forecast" }
} else {
set ::S(msg) "Fetching NOAA weather forecast"
set n [GetNOAA $::S(lat) $::S(lon)]
if {$n} {
set ::S(msg) "NOAA weather forecast for $ll"
DisplayWeather
} else { set ::S(msg) "error fetching NOAA weather forecast" }
}
}
proc GetNOAA {lat lon {XML ""}} {
global doc root xml
if {$XML ne ""} {
set xml $XML
} else {
set xml [GetForecastXML $lat $lon]
}
set n [catch {dom parse $xml doc}]
if {$n} {
tk_messageBox -icon error -message "Bad reply from NOAA"
return 0
}
set root [$doc documentElement]
ReadTimeLayouts $root
GetIcons $root
GetTemperatures $root
GetPrecipitation $root
GetWeather $root
unset doc
return 1
}
proc GetForecastXML {lat lon} {
set startdate [clock format [clock scan now] -format "%Y-%m-%d"]
set url $::S(url,forecast)
append url "?lat=$lat&lon=$lon&format=$::S(format)&startDate=$startdate"
append url "&numDays=$::S(days)&Submit=Submit"
set ::URL $url
set token [::http::geturl $url]
::http::wait $token
set xml [::http::data $token] ; list
::http::cleanup $token
return $xml
}
proc GetNOAATemp {lat lon {XML ""}} {
global doc root xml
if {$XML ne ""} {
set xml $XML
} else {
set xml [GetTempForecastXML $lat $lon]
}
set n [catch {dom parse $xml doc}]
if {$n} {
tk_messageBox -icon error -message "Bad reply from NOAA"
return 0
}
set root [$doc documentElement]
ReadTimeLayouts $root
GetTemperatures $root
unset doc
return 1
}
proc GetTempForecastXML {lat lon} {
global S url
if {! [string is double $lat] || ! [string is double $lon]} {
error "Bad latitude or longitude ($lat,$lon)"
return
}
set LAT $lat
set LON $lon
set BEGINDATE [clock format [clock scan now] -format "%Y-%m-%d"]
set ENDDATE [clock format [clock scan "now + $S(days) days"] -format "%Y-%m-%d"]
set params [subst -nobackslashes -nocommands $S(url,temp,parameters)]
set url "$S(url,temp)$params"
set token [::http::geturl $url]
::http::wait $token
set xml [::http::data $token] ; list
::http::cleanup $token
return $xml
}
proc ReadTimeLayouts {root} {
global WEATHER
unset -nocomplain WEATHER
set nodes [$root selectNodes /dwml/data/time-layout]
foreach node $nodes {
set key [[$node selectNodes layout-key/text()] data]
set WEATHER($key,summary) [$node getAttribute summarization "???"]
set starts [$node selectNodes start-valid-time]
set ends [$node selectNodes end-valid-time]
set cnt -1
foreach start $starts end $ends {
incr cnt
set name ""
set etime ""
if {[$start hasAttribute period-name]} {
set name [$start getAttribute period-name "???"]
}
set stime [[$start firstChild] data]
if {$end ne ""} {
set etime [[$end firstChild] data]
}
set WEATHER($key,$cnt,name) $name
set WEATHER($key,$cnt,start) $stime
set WEATHER($key,$cnt,end) $etime
}
}
}
proc GetIcons {root} {
set node [$root selectNodes /dwml/data/parameters/conditions-icon]
set key [$node getAttribute time-layout]
set ::WEATHER(icon,key) $key
set nodes [$node selectNodes icon-link]
for {set cnt 0} {$cnt < [llength $nodes]} {incr cnt} {
set url ""
set inode [lindex $nodes $cnt]
if {[$inode hasChildNodes]} {
set url [[$inode firstChild] data]
}
set ::WEATHER($key,$cnt,icon) $url
}
}
proc GetTemperatures {root} {
global WEATHER
array unset WEATHER *temp*
set nodes [$root selectNodes /dwml/data/parameters/temperature]
foreach node $nodes {
set type [$node getAttribute type]
set units [$node getAttribute units]
set key [$node getAttribute time-layout]
set WEATHER(temp,$type,key) $key
set WEATHER(temp,$type,units) $units
set vnodes [$node selectNodes value]
for {set cnt 0} {$cnt < [llength $vnodes]} {incr cnt} {
set vnode [lindex $vnodes $cnt]
set temp "?"
if {[$vnode hasChildNodes]} {
set temp [[$vnode firstChild] data]
}
set WEATHER($key,$cnt,temp,$type) $temp
}
}
}
proc GetPrecipitation {root} {
global WEATHER
array unset WEATHER *rain*
set node [$root selectNodes /dwml/data/parameters/probability-of-precipitation]
set units [$node getAttribute units]
set key [$node getAttribute time-layout]
set WEATHER(rain,key) $key
set WEATHER(rain,units) $units
set vnodes [$node selectNodes value]
for {set cnt 0} {$cnt < [llength $vnodes]} {incr cnt} {
set vnode [lindex $vnodes $cnt]
set rain "?"
if {[$vnode hasChildNodes]} {
set rain [[$vnode firstChild] data]
}
set WEATHER($key,$cnt,rain) $rain
}
}
proc GetWeather {root} {
global WEATHER
array unset WEATHER *weather*
set node [$root selectNodes /dwml/data/parameters/weather]
set key [$node getAttribute time-layout]
set WEATHER(weather,key) $key
set cnt -1
foreach value [$node selectNodes weather-conditions] {
incr cnt
set WEATHER($key,$cnt,weather,summary) [$value getAttribute weather-summary "?"]
}
}
proc DoDisplay {} {
wm title . "NOAA Weather Forecast"
bind all <F2> {console show}
frame .w -bd 2 -relief ridge
frame .ctrl -bd 2 -relief ridge -pady 5 -padx 30
label .msg -bd 2 -relief ridge -padx 30 -textvariable S(msg)
pack .msg -side bottom -fill x
pack .ctrl -side bottom -fill x
pack .w -side top -fill both -expand 1
set cities [lsort [array names ::CITIES]]
::ttk::combobox .ctrl.cb -values $cities -state readonly \
-textvariable ::S(city) -validatecommand {SetCity %P} -validate all
label .ctrl.llat -text "Latitude" -anchor w
entry .ctrl.elat -textvariable ::S(lat) -width 12 \
-validate key -vcmd {string is double %P}
label .ctrl.llon -text "Longitude" -anchor w
entry .ctrl.elon -textvariable ::S(lon) -width 12 \
-validate key -vcmd {string is double %P}
label .ctrl.ldays -text "Days" -anchor w
spinbox .ctrl.sbox -from 1 -to 7 -textvariable ::S(days) -width 7 \
-justify c -state readonly
.ctrl.sbox config -readonlybackground [.ctrl.sbox cget -bg]
frame .buttons
button .forecast -text "Forecast" -command {Submit forecast}
button .temp -text "Temperatures" -command {Submit temperature}
grid x .ctrl.cb - x .buttons -pady {0 5} -sticky news
grid x .ctrl.llat .ctrl.elat x ^ -sticky ew
grid x .ctrl.llon .ctrl.elon x ^ -sticky ew
grid x .ctrl.ldays .ctrl.sbox x ^ -sticky ew -pady {5 0}
grid columnconfigure .ctrl 3 -minsize 30
grid columnconfigure .ctrl 0 -weight 1
grid columnconfigure .ctrl 100 -weight 1
grid .forecast -in .buttons -sticky ew
grid .temp -in .buttons -sticky ew
grid rowconfigure .buttons {0 1} -weight 1
eval destroy [winfo child .w]
label .w.icon -image ::img::noaa
label .w.title1 -text "NOAA" -font {Times 32 bold}
label .w.title2 -text "Weather Forecast" -font {Times 28 bold}
#grid .w.icon .w.title1
#grid .w.title2 - -sticky ew -padx 10
#grid config .w.icon -padx {30 0}
#grid columnconfigure .w 1 -weight 1
pack .w.title2 -side bottom -padx 10
pack .w.icon -side left -padx {30 0}
pack .w.title1 -side left -expand 1
}
proc SetCity {where} {
global CITIES S
foreach {S(lat) S(lon)} $CITIES($where) break
return 1
}
image create photo ::img::noaa -data {
R0lGODlhNwA6ALMAACQybBSKtIzW9Eyy1DRGfHSCrJzy/ASe1FRilPwCBIyexPT+/Jy63CSaxMTa
9CxKlCH5BAEAAAkALAAAAAA3ADoAAwT/MMlJq7046827/2AojmRpnmiqrmzrvqXDMEpdKIwDazJC
/IBg8IfI7SgOxSP4IDyeUCAAodDtlAAndMtdAh6MlwPR7Jq3vwfCqmL4tOd4k1BsI75mePQ8DZ/G
eFAIZFmDRG9qZVBZbCRugXQODgtFC5QKlgUFCzKBAAVGJZYLBV9OCJQMBWMzqjg0VU5OOaOilkk/
kQQ0rFWTM5wOc5ijCyTFmUQLDw6rRTiv0A4EU8gyI8iZU6ygkqCaMwVrPwzIAw2NHdm3dz5kdO2n
dHSf1g0BAiHA2dPU/llPZD3JsquYgHMBBoAw0KBSNgWm5ATcVGxAgAMHGhj4wDBAQgPZ/+5IXEJg
kiUD5xqo1MixAcYGDEAWeyQnCDFLKV+y9GDg4gGPMZEx8Ndl3pqTCDFm3Nmhp8ufDQZkGwqQCzVi
KAM81bmRp1alGQXIHDWG6Jx6Cw76BKuya9OtSj9aU/BGCEUGHsEqbQtCpd6lAvipohJTwD2/f/l+
OPzXI8yx6wxIFvD3pcIQXysffuUAsoGDAypjDOCnL1y9HgNQMWmAQcrTbBuMsCga4wCxJw8ehg02
4QjDtQ+InZw0+N7SIWgHv5fZ+OjLJBCL5r0cpgngzrO/RB6duvbYKpR/r+x7hfjxe6GvWOlds1b1
LNSuNe7x9pHWFpvrZW79yITPr/G3kh19/l0g2WcCJChZgQw26OCDEEYo4YQUVmjhhSxEAAA7}
proc DisplayWeather {} {
global WEATHER
wm geom . {} ;# Reset main window geometry
set W .w
label $W.tmp
set font "[font actual [$W.tmp cget -font]] -weight bold"
eval destroy [winfo child $W]
pack [frame $W.f] -side left -fill both -expand 1
set W $W.f
set key $WEATHER(weather,key)
set row 0
set col 0
foreach arr [lsort -dictionary [array names WEATHER $key,*,weather,summary]] {
set id [lindex [split $arr ","] 1]
set id2 [expr {$id/2}]
set WF $W.col$id
frame $WF -bd 2 -relief ridge
label $WF.name -text $WEATHER($key,$id,name) -font $font
label $WF.icon -image [DownloadIcon $WEATHER($key,$id,icon)] -relief ridge
set key2 $WEATHER(temp,maximum,key)
set key3 $WEATHER(temp,minimum,key)
#set txt "$WEATHER($key3,$id2,temp,minimum)\xB0 - $WEATHER($key2,$id2,temp,maximum)\xB0"
if {[regexp -nocase "night" $WEATHER($key,$id,name)]} {
set txt "Low $WEATHER($key3,$id2,temp,minimum)\xB0"
if {$row == 0 && $col == 0} { incr row}
} else {
set txt "High $WEATHER($key2,$id2,temp,maximum)\xB0"
}
append txt "\n$WEATHER($key,$id,rain)%"
append txt "\n$WEATHER($key,$id,weather,summary)"
label $WF.txt -text $txt -wraplength 100
grid $WF -row $row -column $col -sticky news
grid columnconfigure $W $col -uniform a
eval pack [winfo child $WF] -side top
update
if {[incr row] > 1} {
incr col
set row 0
}
}
}
proc DisplayWeatherAsText {} {
global WEATHER
set result {}
set key $WEATHER(weather,key)
foreach arr [lsort -dictionary [array names WEATHER $key,*,weather,summary]] {
set id [lindex [split $arr ","] 1]
set id2 [expr {$id/2}]
set name $WEATHER($key,$id,name)
set icon [DownloadIcon $WEATHER($key,$id,icon)]
set ifile [file join [pwd] [$icon cget -file]]
set key2 $WEATHER(temp,maximum,key)
set key3 $WEATHER(temp,minimum,key)
if {[regexp -nocase "night" $WEATHER($key,$id,name)]} {
set txt [list "Low $WEATHER($key3,$id2,temp,minimum)\xB0"]
} else {
set txt [list "High $WEATHER($key2,$id2,temp,maximum)\xB0"]
}
lappend txt "$WEATHER($key,$id,rain)%"
lappend txt "$WEATHER($key,$id,weather,summary)"
lappend result [list $name $ifile $txt]
}
return $result
}
proc DownloadIcon {url} {
if {$url eq ""} {return ::img::noaa}
set cacheName [file join $::S(iconDir) [file tail $url]]
set sname [file rootname [file tail $url]]
set iname ::img::$sname
if {[lsearch [image names] $iname] == -1} {
image create photo $iname -width 55 -height 58
if {[file exists $cacheName]} {
$iname config -file $cacheName
} else {
$iname copy ::img::noaa
::http::geturl $url \
-command [list DownloadIcon_Callback $iname $cacheName]
}
}
return $iname
}
proc DownloadIcon_Callback {iname cacheName token} {
set ncode [::http::ncode $token]
if {[::http::ncode $token] != 200} {
error "bad http ncode for $iname"
} else {
set data [::http::data $token] ; list
$iname config -data [::http::data $token]
if {! $::S(noCache)} {
catch {
set fout [open $cacheName wb]
puts -nonewline $fout $data
close $fout
}
}
}
::http::cleanup $token
}
proc ScanTime {when} {
#2007-01-25T19:00:00-05:00
set ticks [clock scan "[string range $when 0 9] [string range $when 11 18]"]
return $ticks
}
proc PrettyLat {lat lon} {
set lat [int2lat $lat]
set lon [int2lat $lon]
foreach {lat1 lat2 lat3} $lat break
foreach {lon1 lon2 lon3} $lon break
set lat "$lat1\xB0 $lat2' $lat3\x22N"
set lon "$lon1\xB0 $lon2' $lon3\x22W"
return "$lat $lon"
}
proc int2lat {int} {
set int [expr {abs($int) * 3600}]
if {[string is integer -strict $int]} {
set sec [expr {$int % 60}]
} else {
#set fra [expr {$int - int($int)}]
#set fra [expr {round($fra * 10) / 10.0}]
#set int [expr {int($int)}]
#set sec [expr {$int % 60 + $fra}]
set v [expr {$int + .05}] ;# Round to 1 decimal place
foreach {int fra} [split $v "."] break ;# Use string representation
set fra [string range $fra 0 0] ;# 1 decimal place only
set sec [expr {$int % 60}]
if {$fra ne {0}} { append sec ".$fra"}
}
set int [expr {$int / 60}]
set min [expr {$int % 60}]
set deg [expr {$int / 60}]
return [list $deg $min $sec]
}
proc PlotTemp {} {
global PLOT s
wm geom . {} ;# Reset main window geometry
set W .w.c
set PLOT(W) $W
if {[winfo exists $W]} {
$W config -width [winfo width $W] -height [winfo height $W]
$W delete all
bind $W <Configure> {}
} else {
eval destroy [winfo child .w]
canvas $W -width 700
pack $W -fill both -expand 1
}
set s [::Plotchart::createXYPlot $W $PLOT(XS) $PLOT(YS)]
foreach x $PLOT(X) y $PLOT(Y) {
$s plot series1 $x $y
set xy [::Plotchart::coordsToPixel $W $x $y]
set xy [Box $xy 5]
$W create oval $xy -tag oval -fill red -outline red
}
$s grid $PLOT(Xgrid) $PLOT(Ygrid)
$s title "Temperature Forecast"
$s ytext $PLOT(YText)
XAxis
Freezing
Noons
Colorize
$W raise oval
update
if {[bind $W <Configure>] eq ""} { bind $W <Configure> PlotTemp }
}
proc XAxis {} {
global PLOT
set W $PLOT(W)
$W delete xaxis
set Xticks [lindex $PLOT(Xgrid) 0]
set Ymin [lindex $PLOT(YS) 0]
for {set i 0} {$i < [llength $Xticks]} {incr i} {
set x [lindex $Xticks $i]; ;# Hours from starting
set day [expr {$x / 24.}]
if {int($day) != $day} continue
set ticks [expr {$PLOT(basetime) + int($day)*60*60*24}]
set day [clock format $ticks -format "%a"]
set xy [::Plotchart::coordsToPixel $W $x $Ymin]
$W create text $xy -tag xaxis -anchor n -text $day
}
}
proc Freezing {} {
global PLOT
set W $PLOT(W)
foreach {xmin xmax} $PLOT(XS) break
foreach {ymin ymax} $PLOT(YS) break
foreach val {32 0} {
if {$ymin < $val && $ymax > $val} {
set xy0 [::Plotchart::coordsToPixel $W $xmin $val]
set xy1 [::Plotchart::coordsToPixel $W $xmax $val]
$W create line [concat $xy0 $xy1] -fill red -dash 1 -width 2
}
}
}
proc Noons {} {
global PLOT COLORS
set W $PLOT(W)
set xticks [lindex $PLOT(Xgrid) 0]
foreach {ymin ymax} $PLOT(YS) break
set x1 [lindex $xticks 0]
for {set i 1} {$i < [llength $xticks]} {incr i} {
set x0 $x1
set x1 [lindex $xticks $i]
set x [expr {($x0 + $x1)/2}]
set xy0 [::Plotchart::coordsToPixel $W $x $ymin]
set xy1 [::Plotchart::coordsToPixel $W $x $ymax]
$W create line [concat $xy0 $xy1] -fill black -dash 1 -width 1
}
}
proc Colorize {} {
global PLOT COLORS
set W $PLOT(W)
set xticks [lindex $PLOT(Xgrid) 0]
foreach {ymin ymax} $PLOT(YS) break
set x1 [lindex $xticks 0]
for {set i 1} {$i < [llength $xticks]} {incr i} {
set x0 $x1
set clr [lindex $COLORS [expr {$i % [llength $COLORS]}]]
set x1 [lindex $xticks $i]
set xy0 [::Plotchart::coordsToPixel $W $x0 $ymin]
set xy1 [::Plotchart::coordsToPixel $W $x1 $ymax]
$W create rect [concat $xy0 $xy1] -fill $clr -tag bg
}
$W lower bg
}
proc lat2int {lat1 lat2 lat3} {
scan "$lat1 $lat2 $lat3" "%g %g %g" lat1 lat2 lat3
set lat [expr {abs($lat1) + $lat2 / 60.0 + $lat3 / 3600.0}]
return $lat
}
proc GetPlotData {} {
global PLOT WEATHER
unset -nocomplain PLOT
set key $WEATHER(temp,hourly,key)
set basetime 0
set X {}
set Y {}
foreach arr [lsort -dictionary [array names WEATHER $key,*,temp,hourly]] {
set idx [lindex [split $arr ","] 1]
set ticks [ScanTime $WEATHER($key,$idx,start)]
if {$idx == 0} {
set basetime $ticks
set basetime [ScanTime [string range $WEATHER($key,$idx,start) 0 9]]
}
lappend X [expr {($ticks - $basetime)/60/60}]
lappend Y $WEATHER($key,$idx,temp,hourly)
}
;# Compute Y axis
set y_sort [lsort -real $Y]
set ys [::Plotchart::determineScale [lindex $y_sort 0] [lindex $y_sort end]]
set ys [MakeInt $ys]
set min 0
set max [lindex $X end]
set delta [expr {$max - $min}]
if {$delta/24 != $delta/24.0} { set max [expr {$min + 24*(1+$delta/24)}]}
set xs [list $min $max 24]
set xs [list $min $max $::S(x,axisStep)]
set Xticks {}
foreach {a b c} $xs break
while {$a <= $b} {
lappend Xticks $a
incr a $c
}
set Yticks {}
foreach {a b c} $ys break
while {$a <= $b} {
lappend Yticks $a
incr a $c
}
set Xgrid {}
foreach . $Yticks { lappend Xgrid $Xticks }
set Ygrid {}
set cnt [llength $Xticks]
foreach tick $Yticks { lappend Ygrid [string repeat "$tick " $cnt] }
set PLOT(X) $X
set PLOT(Y) $Y
set PLOT(XS) $xs
set PLOT(YS) $ys
set PLOT(Xgrid) $Xgrid
set PLOT(Ygrid) $Ygrid
set PLOT(YText) $WEATHER(temp,hourly,units)
set PLOT(basetime) $basetime
}
proc Box {xy r} {
foreach {x y} $xy break
return [list [expr {$x-$r}] [expr {$y-$r}] [expr {$x+$r}] [expr {$y+$r}]]
}
proc MakeInt {nlist} {
set ilist {}
foreach num $nlist { lappend ilist [expr {int($num)}]}
return $ilist
}
if {! $S(noCache)} {catch {file mkdir $S(iconDir)}}
DoDisplay
set S(city) "San Francisco, CA"
SetCity $S(city)
#Submit forecast
return