[Keith Vetter] 2007-01-23 : The National Oceanic and Atmospheric Administration (NOAA) has a some nice web services providing current weather conditions and forecasts. For details on some of those services, check out [http://www.weather.gov/forecasts/xml/] and [http://www.weather.gov/forecasts/xml/SOAP_server/ndfdSOAPByDay.htm]. Here's a little program that gets the weather forecast for a given latitude and longitude. It parses the SOAP reply and displays the result. [KPV] 2007-02-03 : Added some more features including graphing predicted temperatures (using [tklib]'s [PlotChart]), a few built-in cities and more robust XML handling. ---- [http://wiki.tcl.tk/_repo/wiki_images/noaa_screenshot.png] ---- PDH 2007-02-15 Corrected ''tempeture'' to ''temperature'' on line 44, and (pedanticly) ''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:47 I 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.... thanks [KPV] 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://www.weather.gov/forecasts/xml/SOAP_server/ndfdSOAPclientByDay.php set S(url,temp) http://www.weather.gov/forecasts/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 {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 {} } 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 ] eq ""} { bind $W 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 ====== ---- !!!!!! %|[Category Application] | [Category Science]|% !!!!!!