Version 0 of Metar stations

Updated 2007-06-22 10:29:49 by jdc

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 "<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 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_countryName of state (USA) or country
stationName of METAR station (city or airport)
icaoMETAR 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