[jdc] 22-jun-2007 **Metar stations package** A list of [METAR] stations can be found here: http://www.rap.ucar.edu/weather/surface/stations.txt The following code uses this file to get information about locations based on name or coordinates: ====== package provide metarstations 0.1 namespace eval ::metarstations { variable stations_file [file join $::env(METARSTATIONSDIR) data stations.txt] variable station_keys {state_or_country station icao latitude longitude elevation metar} variable station_pos { state_or_country 0 1 station 3 18 icao 20 23 latitude_degrees 38 40 latitude_minutes 42 43 latitude_orien 44 44 longitude_degrees 47 49 longitude_minutes 51 52 longitude_orien 53 53 elevation 55 58 metar 62 62 } } proc ::metarstations::make_num { n } { set n [string trimleft [string trim $n] "0"] if { [string length $n] == 0 } { set n 0 } return $n } proc ::metarstations::parse_stations { arnm {metar_only 1} } { upvar $arnm ar variable station_keys variable station_pos variable stations_file set f [open $stations_file] set ll [split [read $f] "\n"] close $f set state "" foreach l $ll { if { [string length [string trim $l]] == 0 } { continue } if { [string index $l 0] eq "!" } { continue } if { [string range $l 0 1] eq "CD" } { continue } if { [llength $l] < 10 } { set state [string trim [string range $l 0 18]] } else { set station [string trim [string range $l 3 18]] if { [info exists stationar($station)] } { incr stationar($station) append station "-#$stationar($station)" } else { set stationar($station) 1 } foreach {key from to} $station_pos { set ar($station,$key) [string trim [string range $l $from $to]] } set ar($station,state_or_country) $state } } foreach stationnm [array names stationar] { for { set i 0 } { $i < $stationar($stationnm) } { incr i } { set station $stationnm if { $i } { append station "-#[expr {$i+1}]" } set latd [make_num $ar($station,latitude_degrees)] set latm [make_num $ar($station,latitude_minutes)] set lond [make_num $ar($station,longitude_degrees)] set lonm [make_num $ar($station,longitude_minutes)] set lat [expr {$latd + $latm/60.0}] if { $ar($station,longitude_orien) eq "S" } { set lat [expr {-$lat}] } set lon [expr {$lond + $lonm/60.0}] if { $ar($station,longitude_orien) eq "E" } { set lon [expr {-$lon}] } set ar($station,latitude) $lat set ar($station,longitude) $lon unset ar($station,latitude_degrees) unset ar($station,latitude_minutes) unset ar($station,latitude_orien) unset ar($station,longitude_degrees) unset ar($station,longitude_minutes) unset ar($station,longitude_orien) if { $ar($station,metar) eq "X" } { set ar($station,metar) 1 } else { set ar($station,metar) 0 } set ar($station,elevation) [make_num $ar($station,elevation)] } } } proc ::metarstations::get_station_by_key_match { key match {n 1} } { variable statar variable station_keys if { ![info exists statar] } { ::metarstations::parse_stations statar } set rl {} foreach k [array names statar "*,$key"] { if { [string match -nocase $match $statar($k)] } { set tl {} set station [lindex [split $k ","] 0] foreach sk $station_keys { lappend tl $sk $statar($station,$sk) } lappend rl $tl if { [llength $rl] >= $n } break } } return $rl } proc ::metarstations::get_station_by_coordinates { lat lon {n 1} } { variable statar variable station_keys if { ![info exists statar] } { ::metarstations::parse_stations statar } set dl {} foreach k [array names statar "*,latitude"] { foreach {station nm} [split $k ","] break set d [expr {abs($lat - $statar($station,latitude)) + abs($lon - $statar($station,longitude))}] lappend dl [list $d $station] } set dl [lsort -real -index 0 $dl] set rl {} foreach sl $dl { set station [lindex $sl 1] set tl {} foreach sk $station_keys { lappend tl $sk $statar($station,$sk) } lappend rl $tl if { [llength $rl] >= $n } break } return $rl } ====== **Initialisation** To use this package, first download the stations file from the location given above. Now do a ====== package require metarstations ====== and set the path to the stations file (e.g. when downloaded to `/tmp/stations.txt`): ====== set ::metarstations::stations_file /tmp/stations.txt ====== Now you can use the access functions. **::metarstations::get_station_by_key_match** Possible keys are: +++ state_or_country Name of state (USA) or country station Name of METAR station (city or airport) icao METAR code of the station +++ ====== # Metar stations matching a given string puts "" puts "Latitude Longitude Country/state, station" puts "========= ========= ===============================" foreach r [::metarstations::get_station_by_key_match station *brussel* 100] { array set mLoc $r puts [format "%9.3f %9.3f %s, %s" $mLoc(latitude) $mLoc(longitude) $mLoc(state_or_country) $mLoc(station)] unset mLoc } ====== The result of this script is: ====== Latitude Longitude Country/state, station ========= ========= =============================== 50.883 -4.517 BELGIUM, BRUSSELS (MIL) 50.883 -4.517 BELGIUM, BRUSSELS NATIONA ====== **::metarstations::get_station_by_coordinates** Coordinates are given as decimal latitude and longitude. Latitude must be in degrees '''north''', longitude in degrees '''west'''. ====== # 20 Metar stations close to given coordinates # latitude: degrees north # longitude: degrees west puts "Latitude Longitude Country/state, station" puts "========= ========= ===============================" foreach r [::metarstations::get_station_by_coordinates 51 -5 20] { array set mLoc $r puts [format "%9.3f %9.3f %s, %s" $mLoc(latitude) $mLoc(longitude) $mLoc(state_or_country) $mLoc(station)] unset mLoc } ====== The result of this script is: ====== Latitude Longitude Country/state, station ========= ========= =============================== 51.000 -5.067 BELGIUM, SCHAFFEN 50.767 -4.950 BELGIUM, GOETSENHOVEN (MI 51.183 -5.217 BELGIUM, BALEN-KEIHEUVEL 51.417 -5.000 BELGIUM, WEELDE (MIL) 50.783 -5.200 BELGIUM, ST. TRUIDEN (BAF 50.750 -4.767 BELGIUM, BEAUVECHAIN (BAF 50.917 -5.500 BELGIUM, GENK/ZWARTBERG 50.883 -4.517 BELGIUM, BRUSSELS (MIL) 50.883 -4.517 BELGIUM, BRUSSELS NATIONA 50.883 -4.500 BELGIUM, MELSBROEK (BEL-A 51.167 -5.467 BELGIUM, KLEINE-BROGEL(BA 50.950 -4.400 BELGIUM, GRIMBERGEN 51.567 -4.917 NETHERLANDS, GILZE-RIJEN RNLA 51.200 -4.467 BELGIUM, ANTWERP/DEURNE 50.633 -5.450 BELGIUM, BIERSET/LIEGE (M 50.633 -5.450 BELGIUM, BIERSET/LIEGE (C 51.317 -4.500 BELGIUM, BRASSCHAAT (MIL) 50.917 -5.767 NETHERLANDS, ZUID-LIMBURG/BEE 50.800 -4.350 BELGIUM, UCCLE/UKKLE 51.450 -5.417 NETHERLANDS, EINDHOVEN RNLAFB ======