1const ZONE1970_TAB: &str = include_str!("zone1970.tab");
9const ISO3166_TAB: &str = include_str!("iso3166.tab");
10
11#[derive(Debug, Clone, Copy, PartialEq)]
13pub struct ZoneMeta<'a> {
14 pub lat: f64,
16 pub lon: f64,
18 pub commentary: &'a str,
20 codes: &'a str,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct Country<'a> {
27 pub code: &'a str,
29 pub name: &'a str,
31}
32
33impl ZoneMeta<'static> {
34 pub fn countries(&self) -> impl Iterator<Item = Country<'static>> {
36 self.codes.split(',').map(|code| Country {
37 code,
38 name: iso_name(code),
39 })
40 }
41}
42
43pub fn meta(name: &str) -> Option<ZoneMeta<'static>> {
45 for line in ZONE1970_TAB.split('\n') {
46 if line.is_empty() || line.starts_with('#') {
47 continue;
48 }
49 let mut fields = line.split('\t');
50 let codes = fields.next()?;
51 let coord = fields.next()?;
52 let zname = match fields.next() {
53 Some(z) => z,
54 None => continue,
55 };
56 if zname != name {
57 continue;
58 }
59 let commentary = fields.next().unwrap_or("");
60 let (lat, lon) = parse_iso6709(coord);
61 return Some(ZoneMeta {
62 lat,
63 lon,
64 commentary,
65 codes,
66 });
67 }
68 None
69}
70
71fn iso_name(code: &str) -> &'static str {
73 for line in ISO3166_TAB.split('\n') {
74 if line.is_empty() || line.starts_with('#') {
75 continue;
76 }
77 let mut parts = line.splitn(2, '\t');
78 let c = parts.next().unwrap_or("");
79 if c == code {
80 return parts.next().unwrap_or("");
81 }
82 }
83 ""
84}
85
86pub fn parse_iso6709(s: &str) -> (f64, f64) {
88 let b = s.as_bytes();
89 let mut lon_start = None;
91 for (i, &c) in b.iter().enumerate().skip(1) {
92 if c == b'+' || c == b'-' {
93 lon_start = Some(i);
94 break;
95 }
96 }
97 let Some(lon_start) = lon_start else {
98 return (0.0, 0.0);
99 };
100 let lat = parse_dms(&s[..lon_start], 2);
101 let lon = parse_dms(&s[lon_start..], 3);
102 (lat, lon)
103}
104
105fn parse_dms(s: &str, deg_digits: usize) -> f64 {
108 let b = s.as_bytes();
109 if b.len() < 1 + deg_digits + 2 {
110 return 0.0;
111 }
112 let neg = b[0] == b'-';
113 let mut i = 1; let deg = atoi(&b[i..i + deg_digits]);
116 i += deg_digits;
117 let min = atoi(&b[i..i + 2]);
118 i += 2;
119 let sec = if b.len() >= i + 2 {
120 atoi(&b[i..i + 2])
121 } else {
122 0
123 };
124
125 let total_seconds = deg * 3600 + min * 60 + sec;
127 let val_e4 = (total_seconds * 10000 + 1800) / 3600;
128 let v = val_e4 as f64 / 10000.0;
129 if neg {
130 -v
131 } else {
132 v
133 }
134}
135
136fn atoi(b: &[u8]) -> i64 {
137 let mut n = 0i64;
138 for &c in b {
139 if c.is_ascii_digit() {
140 n = n * 10 + (c - b'0') as i64;
141 }
142 }
143 n
144}