jdc 22-jun-2007
Metar stations package
A list of
METAR stations can be found here:
http://www.rap.ucar.edu/weather/surface/stations.txtThe 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 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 "<unknown>"
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 kstation [string trim [string range $l 3 18]]
if { [info exists stationar($kstation)] } {
incr stationar($kstation)
set kstation "$kstation-#$stationar($kstation)"
} else {
set stationar($kstation) 1
}
foreach {key from to} $station_pos {
set ar($kstation,$key) [string trim [string range $l $from $to]]
}
set ar($kstation,state_or_country) $state
set latd [make_num $ar($kstation,latitude_degrees)]
set latm [make_num $ar($kstation,latitude_minutes)]
set lond [make_num $ar($kstation,longitude_degrees)]
set lonm [make_num $ar($kstation,longitude_minutes)]
set lat [expr {$latd + $latm/60.0}]
if { $ar($kstation,latitude_orien) eq "S" } {
set lat [expr {-$lat}]
}
set lon [expr {$lond + $lonm/60.0}]
if { $ar($kstation,longitude_orien) eq "E" } {
set lon [expr {-$lon}]
}
set ar($kstation,latitude) $lat
set ar($kstation,longitude) $lon
unset ar($kstation,latitude_degrees)
unset ar($kstation,latitude_minutes)
unset ar($kstation,latitude_orien)
unset ar($kstation,longitude_degrees)
unset ar($kstation,longitude_minutes)
unset ar($kstation,longitude_orien)
if { $ar($kstation,metar) eq "X" } {
set ar($kstation,metar) 1
} else {
set ar($kstation,metar) 0
}
set ar($kstation,elevation) [make_num $ar($kstation,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 {pow($lat - $statar($station,latitude), 2) + pow($lon - $statar($station,longitude), 2)}]
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
}Save the code in a file with name
metarstations.tcl and add a
pkgIndex.tcl file with this contents:
package ifneeded metarstations 0.1 [list source [file join $dir metarstations.tcl]]
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.
Available data
state_or_country | Name of state (USA) or country |
station | Name of METAR station (city or airport) |
icao | METAR code of the station |
latitude | Latitude of METAR station, in degrees north |
longitude | Longitude of METAR station, in degrees west |
elevation | Elevation of METAR station, in meters |
metar | Indication if METAR data is available or not for |
API
::metarstations::get_station_by_key_match
Command:
::metarstations::get_station_by_key_match key match ?n?
All the available data can be queried using the
key names listed above. A
match string is specified in the same format as for the
string match command. Optionally the maximum number of matches
n can be specified. A list-of-lists is returned. Each sub-list is a key/value list suitable for use with
array set.
# Maximum 100 Metar stations matching a given string
puts ""
puts "Latitude Longitude Country/state, station"
puts "========= ========= ==============================="
foreach r [::metarstations::get_station_by_key_match station *paris* 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
========= ========= ===============================
39.700 87.667 ILLINOIS, PARIS
48.817 -2.317 FRANCE, PARIS MET CENTER
33.617 95.450 TEXAS, PARIS/COX FIELD
48.717 -2.383 FRANCE, PARIS/ORLY
30.383 103.683 TEXAS, ALPINE-CASPARIS
48.967 -2.450 FRANCE, PARIS/LE BOURGET
36.333 88.383 TENNESSEE, PARIS HENRY CTY
::metarstations::get_station_by_coordinates
Command:
::metarstations::get_station_by_coordinates lat lon ?n?
Coordinates are given as decimal
latitude and
longitude. The latitude must be in degrees
north, the longitude in degrees
west. Optionally the maximum number of matches
n can be specified. A list-of-lists is returned. Each sub-list is a key/value list suitable for use with
array set.
Closest to is calculated as the minimum of:
pow(longitude_station - longitude_query, 2) + pow(latitude_station - latitude_query, 2)
# 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
50.783 -5.200 BELGIUM, ST. TRUIDEN (BAF
50.750 -4.767 BELGIUM, BEAUVECHAIN (BAF
51.417 -5.000 BELGIUM, WEELDE (MIL)
51.167 -5.467 BELGIUM, KLEINE-BROGEL(BA
50.883 -4.517 BELGIUM, BRUSSELS (MIL)
50.883 -4.517 BELGIUM, BRUSSELS NATIONA
50.917 -5.500 BELGIUM, GENK/ZWARTBERG
50.883 -4.500 BELGIUM, MELSBROEK (BEL-A
51.200 -4.467 BELGIUM, ANTWERP/DEURNE
51.567 -4.917 NETHERLANDS, GILZE-RIJEN RNLA
50.633 -5.450 BELGIUM, BIERSET/LIEGE (M
50.633 -5.450 BELGIUM, BIERSET/LIEGE (C
51.317 -4.500 BELGIUM, BRASSCHAAT (MIL)
50.950 -4.400 BELGIUM, GRIMBERGEN
51.450 -5.417 NETHERLANDS, EINDHOVEN RNLAFB
50.800 -4.350 BELGIUM, UCCLE/UKKLE
50.467 -4.450 BELGIUM, CHARLEROI/GOSSEL
Combined example
# Metar stations close to stations matching a given string
foreach r [::metarstations::get_station_by_key_match station *paris* 100] {
array set mLoc $r
foreach m [::metarstations::get_station_by_coordinates $mLoc(latitude) $mLoc(longitude) 5] {
array set tLoc $m
lappend ml [list $tLoc(latitude) $tLoc(longitude) $tLoc(state_or_country) $tLoc(station)]
}
unset mLoc
}
set ml [lsort -unique $ml]
puts ""
puts "Latitude Longitude Country/state, station"
puts "========= ========= ==============================="
foreach r $ml {
foreach {latitude longitude state_or_country station} $r break
puts [format "%9.3f %9.3f %s, %s" $latitude $longitude $state_or_country $station]
}The result for this scripts is:
Latitude Longitude Country/state, station
========= ========= ===============================
30.167 102.417 TEXAS, SANDERSON (RAMOS
30.367 104.017 TEXAS, MARFA
30.383 103.683 TEXAS, ALPINE-CASPARIS
30.917 102.917 TEXAS, FORT STOCKTON
31.383 103.517 TEXAS, PECOS CITY
33.167 95.617 TEXAS, SULPHUR SPRINGS
33.600 95.067 TEXAS, CLARKSVILLE
33.617 95.450 TEXAS, PARIS/COX FIELD
33.917 94.867 OKLAHOMA, IDABEL
35.650 88.383 TENNESSEE, FRANKLIN WILKINS
36.083 88.467 TENNESSEE, HUNTINGDON
36.333 88.383 TENNESSEE, PARIS HENRY CTY
36.383 88.983 TENNESSEE, UNION CITY
37.067 88.767 KENTUCKY, PADUCAH
39.450 87.333 INDIANA, TERRE HAUTE
39.467 88.267 ILLINOIS, MATTOON/CHARLEST
39.483 87.250 INDIANA, TERRE HAUTE VOR
39.700 87.667 ILLINOIS, PARIS
40.200 87.600 ILLINOIS, DANVILLE
48.600 -2.317 FRANCE, BRETIGNY-SUR-ORG
48.717 -2.383 FRANCE, PARIS/ORLY
48.750 -2.117 FRANCE, TOUSSUS LE NOBLE
48.767 -2.200 FRANCE, VILLACOUBLAY/VEL
48.817 -2.317 FRANCE, PARIS MET CENTER
48.967 -2.450 FRANCE, PARIS/LE BOURGET
49.017 -2.517 FRANCE, CHARLES DE GAULL
49.250 -2.517 FRANCE, CREIL (FAFB)