aboutsummaryrefslogtreecommitdiffstats
path: root/metar
blob: 4953882a86428ced9fc86bca4c608db6b7d06a7d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/bin/sh

set -e

usage ()
{
    echo "usage: $0 (get | print | set STATION | list [PREFIX] | closest [LATITUDE LONGITUDE] [LIST])"
}

get_station ()
{
    icao=""
    if test -f ~/.config/metar; then
	icao="$(cat ~/.config/metar | head -n 1)"
    elif test -f /etc/metar; then
	icao="$(cat /etc/metar | head -n 1)"
    fi
    if test -z "$icao"; then
	echo 'No METAR station has been selected.' >&2
	exit 1
    fi
    echo "$icao"
}

get_location ()
{
    geo=""
    if test -f ~/.config/geolocation; then
	geo="$(cat ~/.config/geolocation | head -n 1)"
	geof=~/.config/geolocation
    elif test -f /etc/geolocation; then
	geo="$(cat /etc/geolocation | head -n 1)"
	geof=/etc/geolocation
    fi
    if test -z "$geo"; then
	echo 'No location has been specificed or configured.' >&2
	exit 1
    fi
    if ! test $(printf '%s\n' $geo | wc -l) = 2; then
	echo "Invalid geolocation configuration (${geof})." >&2
	exit 1
    fi
    echo "$geo"
}

list_stations ()
{
    file="$(mktemp)"
    if test -z "$file"; then
	exit 1
    fi
    if ! curl -s "http://weather.noaa.gov/pub/data/observations/metar/decoded/" > "$file"; then
	unlink "$file"
	exit 1
    fi
    list="$(sed -n 's/^.*<a href="\('"$1"'[A-Za-z0-9]*\)\.TXT">.*$/\1/p' "$file")"
    unlink "$file"
    for icao in $list; do
	curl -s "http://weather.noaa.gov/pub/data/observations/metar/decoded/${icao}.TXT" | head -n 1
    done
}

get_closest ()
{
    best=
    grep ')' | while read line; do
	icao="$(echo "$line" | cut -d ')' -f 1 | cut -d '(' -f 2)"
	location="$(echo "$line" | cut -d ')' -f 2 | cut -d ' ' -f 2,3)"
	latitude="$(echo "$location" | cut -d ' ' -f 1)"
	longitude="$(echo "$location" | cut -d ' ' -f 2)"
	north="$(set +e; echo "$latitude" | grep 'S' > /dev/null; echo $?)"
	east="$(set +e; echo "$longitude" | grep 'W' > /dev/null; echo $?)"
	latitude="$(echo "$latitude" | tr -d NSEW | sed 's/-/ /g')"
	longitude="$(echo "$longitude" | tr -d NSEW | sed 's/-/ /g')"
	best="$(python3 - $north "$latitude" $east "$longitude" "$1" "$2" "$icao" $best <<EOF
import sys, math
ploc = lambda ps : sum(float(p) / (60 ** i) for i, p in enumerate(ps))
haversin = lambda a : 0.5 - math.cos(a) / 2

lat = (+1 if sys.argv[1] == '1' else -1) * ploc(sys.argv[2].split(' '))
lon = (+1 if sys.argv[3] == '1' else -1) * ploc(sys.argv[4].split(' '))
rlat = float(sys.argv[5])
rlon = float(sys.argv[6])
icao = sys.argv[7]
best = None if len(sys.argv) == 8 else float(sys.argv[9])

ϕ1, λ1 = [c * math.pi / 180 for c in (lat, lon)]
ϕ2, λ2 = [c * math.pi / 180 for c in (rlat, rlon)]

h = haversin(ϕ2 - ϕ1) + math.cos(ϕ1) * math.cos(ϕ2) * haversin(λ2 - λ1)
d = 2 * 6367.5 * math.asin(h ** 0.5)

if best is None or d < best:
    print('%s %f' % (icao, d))
else:
    print(sys.argv[8] + ' ' + sys.argv[9])
EOF
        )"
	echo "$best"
    done | tail -n 1 | cut -d ' ' -f 1
}

if test $# = 1 && test "$1" =  "get"; then
    curl -s "http://weather.noaa.gov/pub/data/observations/metar/decoded/$(get_station).TXT" || exit 1
elif test $# = 1 && test "$1" =  "print"; then
    get_station
elif test $# = 2 && test "$1" =  "set"; then
    echo "$2" > ~/.config/metar
elif test $# = 1 && test "$1" =  "list"; then
    list_stations ""
elif test $# = 2 && test "$1" =  "list"; then
    list_stations "$(echo "$2" | tr [a-z] [A-Z])"
elif test $# = 1 && test "$1" =  "closest"; then
    location="$(get_location)"
    list_stations "" | get_closest $location
elif test $# = 2 && test "$1" =  "closest"; then
    location="$(get_location)"
    get_closest $location < "$2"
elif test $# = 3 && test "$1" =  "closest"; then
    list_stations "" | get_closest "$2" "$3"
elif test $# = 4 && test "$1" =  "closest"; then
    get_closest "$2" "$3" < "$4"
else
    usage
fi