Version 4 of World locations

Updated 2007-06-27 14:51:28 by jdc

jdc 27-jun-2007

World locations package

A list of 13000 waypoints can be found as a spreadsheet here: http://www.tapr.org/~kh2z/Waypoint/WaypointFiles.htm . To use this file, export the locations tab as a CSV file with ; used as field separator and " as string delimiter. (TODO: use csv package)

The following code uses this file to get information about locations based on name or coordinates:

package provide worldlocations 0.1

namespace eval ::worldlocations {
    variable locations_file WorldLocations.csv
    variable location_keys     {major_location name abbrev state country country_code info airport_id icoa latitude longitude}
    variable location_csv_keys {major_location name abbrev state country country_code info airport_id icoa latitude ns longitude ew}
}

proc ::worldlocations::make_num { n } {
    set n [string trimleft [string trim $n] "0"]
    if { [string length $n] == 0 } {
	set n 0
    }
    return $n
}

proc ::worldlocations::make_num { n } {
    set n [string trimleft [string trim $n] "0"]
    if { [string length $n] == 0 } {
	set n 0
    }
    return $n
}

proc ::worldlocations::parse_locations { arnm {metar_only 1} } {
    upvar $arnm ar
    variable location_keys
    variable location_csv_keys
    variable locations_file

    set f [open $locations_file]
    set ll [split [read $f] "\n"]
    close $f

    foreach l $ll {
	foreach $location_csv_keys [split $l ";"] break
	set kname [string trim $name " \""]
	if { [info exists ar($kname,name)] } {
	    incr locid($kname)
	    append kname "-#$locid($kname)"
	} else {
	    set locid($kname) 0
	}
	set ar($kname,name) [string trim $name " \""]
	set ar($kname,major_location) [make_num [string trim $major_location " \""]]
	set ar($kname,abbrev) [string trim $abbrev " \""]
	set ar($kname,state) [string trim $state " \""]
	set ar($kname,country) [string trim $country " \""]
	set ar($kname,country_code) [string trim $country_code " \""]
	set ar($kname,info) [string trim $info " \""]
	set ar($kname,airport_id) [string trim $airport_id " \""]
	set ar($kname,icoa) [string trim $icoa " \""]
	set ar($kname,latitude) [make_num [string trim $latitude " \""]]
	set ar($kname,longitude) [make_num [string trim $longitude " \""]]
	if { [string trim $ns " \""] eq "South" } { 
	    set ar($kname,latitude) -$ar($kname,latitude)
	}
	if { [string trim $ew " \""] eq "East" } { 
	    set ar($kname,longitude) -$ar($kname,longitude)
	}
    }
}

proc ::worldlocations::get_location_by_key_match { key match {n 1} } {
    variable statar
    variable location_keys
    if { ![info exists statar] } {
	::worldlocations::parse_locations statar
    }
    set rl {}
    foreach k [array names statar "*,$key"] {
	if { [string match -nocase $match $statar($k)] } {
	    set tl {}
	    set location [lindex [split $k ","] 0]
	    foreach sk $location_keys {
		lappend tl $sk $statar($location,$sk)
	    }
	    lappend rl $tl
	    if { [llength $rl] >= $n } break
	}
    }
    return $rl
}

proc ::worldlocations::get_location_by_coordinates { lat lon {n 1} } {
    variable statar
    variable location_keys
    if { ![info exists statar] } {
	::worldlocations::parse_locations statar
    }
    set dl {}
    foreach k [array names statar "*,latitude"] {
	foreach {location nm} [split $k ","] break
	set d [expr {pow($lat - $statar($location,latitude), 2) + pow($lon - $statar($location,longitude), 2)}]
	lappend dl [list $d $location]
    }
    set dl [lsort -real -index 0 $dl]
    set rl {}
    foreach sl $dl {
	set location [lindex $sl 1]
	set tl {}
	foreach sk $location_keys {
	    lappend tl $sk $statar($location,$sk)
	}
	lappend rl $tl
	if { [llength $rl] >= $n } break
    }
    return $rl
}

Save the code in a file with name worldlocations.tcl and add a pkgIndex.tcl file with this contents:

package ifneeded worldlocations 0.1 [list source [file join $dir worldlocations.tcl]]

Initialisation

After downloading the locations file and converting it to CSV format, do a:

package require worldlocations

and set the path of the locations file:

set ::worldlocations::locations_file /tmp/WorldLocations10.csv

Available data

nameLocation name
major_locationIndication if location is a major location (capital, large city) or not
abbrevAbbreviation of the location, unique in country of for USA unique in state
stateState or province of the location
countryCountry of the location
country_codeCountry abbreviation for the location's country
infoAdditional info for the location
airport_idAirport identifier
icoaMETAR code of the location
latitudeLocation latitude in degrees north
longitudeLocation longitude in degrees west

API

::worldlocations::get_location_by_key_match

Command:

::worldlocations::get_location_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.

::worldlocations::get_location_by_coordinates

::worldlocations::get_location_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)