diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 0e575af96f..d20092d185 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -27,6 +27,7 @@ 26. AIRAC (2308) - Updated Bournemouth (EGHH) runway threshold coordinates - thanks to @rishab-alt 27. Enhancement - Added Missing Humberside (EGNJ) RNP Approach fixes - thanks to @rishab-alt 28. AIRAC (2308) - Added Leeds East (EGCM) RNP Fixes - thanks to @rishab-alt +29. Bug - Fixed incorrect VOR/NDB Coordinates - thanks to @TechieHelper (Alice Ford) # Changes from release 2023/06 to 2023/07 1. AIRAC (2307) - Add Farnborough (EGLF) Apron B - Thanks to @rishab-alt diff --git a/Navaids/VOR_UK.txt b/Navaids/VOR_UK.txt index ec377ed8a0..4161dd7a58 100644 --- a/Navaids/VOR_UK.txt +++ b/Navaids/VOR_UK.txt @@ -1,54 +1,54 @@ ADN 114.300 N057.18.37.620 W002.16.01.950 ; Aberdeen -BKY 116.250 N051.59.23.000 E000.03.43.000 ; Barkway -BEL 117.200 N054.39.40.120 W006.13.47.660 ; Belfast +BKY 116.250 N051.59.23.170 E000.03.42.870 ; Barkway (DME) +BEL 117.200 N054.39.40.270 W006.13.47.660 ; Belfast BEN 113.950 N057.28.40.570 W007.21.55.080 ; Benbecula (DME) BCL 108.100 N057.28.30.310 W007.22.13.160 ; Benbecula (DME) -BHD 112.050 N050.23.55.000 W003.29.37.000 ; Berry Head -BIG 115.100 N051.19.51.150 E000.02.05.320 ; Biggin -BNN 113.750 N051.43.34.000 W000.32.59.000 ; Bovingdon -BCN 117.450 N051.43.32.000 W003.15.47.000 ; Brecon (DME) -BPK 117.500 N051.44.59.000 W000.06.24.000 ; Brookmans Park -CLN 114.550 N051.50.55.000 E001.08.51.000 ; Clacton -CPT 114.350 N051.29.30.000 W001.13.11.000 ; Compton -DTY 116.400 N052.10.48.510 W001.06.49.640 ; Daventry -DCS 115.200 N054.43.19.000 W003.20.26.000 ; Dean Cross -DET 117.300 N051.18.14.000 E000.35.50.000 ; Detling +BHD 112.050 N050.23.54.980 W003.29.37.460 ; Berry Head +BIG 115.100 N051.19.51.150 E000.02.05.320 ; Biggin (DME) +BNN 113.750 N051.43.34.190 W000.32.59.100 ; Bovingdon (DME) +BCN 117.450 N051.43.31.890 W003.15.46.920 ; Brecon (DME) +BPK 117.500 N051.44.59.050 W000.06.24.250 ; Brookmans Park (DME) +CLN 114.550 N051.50.54.500 E001.08.51.320 ; Clacton +CPT 114.350 N051.29.29.660 W001.13.10.890 ; Compton +DTY 116.400 N052.10.48.510 W001.06.49.640 ; Daventry (DME) +DCS 115.200 N054.43.18.880 W003.20.26.300 ; Dean Cross (DME) +DET 117.300 N051.18.14.410 E000.35.50.190 ; Detling (DME) +DVR 114.950 N051.09.45.440 E001.21.32.720 ; Dover (DME) +GAM 112.800 N053.16.53.280 W000.56.49.790 ; Gamston (DME) +GOW 115.400 N055.52.13.810 W004.26.44.600 ; Glasgow (DME) +GWC 114.750 N050.51.18.790 W000.45.24.250 ; Goodwood (DME) DUF 115.250 N054.41.00.600 W002.27.03.660 ; Great Dun Fell (DME) -DVR 114.950 N051.09.45.000 E001.21.33.000 ; Dover -GAM 112.800 N053.16.53.000 W000.56.50.000 ; Gamston -GOW 115.400 N055.52.13.810 W004.26.44.610 ; Glasgow -GWC 114.750 N050.51.18.790 W000.45.24.250 ; Goodwood GUR 109.400 N049.26.14.000 W002.36.14.000 ; Guernsey -HON 113.650 N052.21.24.000 W001.39.49.000 ; Honiley +HON 113.650 N052.21.24.040 W001.39.49.410 ; Honiley INS 109.200 N057.32.33.490 W004.02.27.990 ; Inverness -IOM 112.200 N054.04.00.720 W004.45.48.510 ; Isle of Man +IOM 112.200 N054.04.00.720 W004.45.48.510 ; Isle Of Man IWL 110.150 N054.07.37.800 W003.15.52.620 ; Barrow (DME) -JSY 112.200 N049.13.16.000 W002.02.46.000 ; Jersey -LAM 115.600 N051.38.46.000 E000.09.06.000 ; Lambourne -LND 114.200 N050.08.11.000 W005.38.13.000 ; Land's End -LON 113.600 N051.29.14.000 W000.28.00.000 ; London -LYD 114.050 N050.59.59.000 E000.52.43.000 ; Lydd +JSY 112.200 N049.13.15.970 W002.02.46.150 ; Jersey +LAM 115.600 N051.38.45.690 E000.09.06.130 ; Lambourne (DME) +LND 114.200 N050.08.10.620 W005.38.12.960 ; Land's End +LON 113.600 N051.29.14.090 W000.27.59.540 ; London (DME) +LYD 114.050 N050.59.58.870 E000.52.43.180 ; Lydd (DME) MAC 116.000 N055.25.48.080 W005.39.01.490 ; Machrihanish (DME) -MCT 113.550 N053.21.25.290 W002.15.44.240 ; Manchester -MAY 117.900 N051.01.02.000 E000.06.58.000 ; Mayfield -MID 114.000 N051.03.14.000 W000.37.30.000 ; Midhurst -NEW 114.250 N055.02.18.410 W001.41.54.130 ; Newcastle (DME) -OCK 115.300 N051.18.18.000 W000.26.50.000 ; Ockham -OTR 113.900 N053.41.54.000 W000.06.13.000 ; Ottringham -PTH 110.400 N056.26.33.000 W003.22.07.000 ; Perth -POL 112.100 N053.44.38.000 W002.06.12.000 ; Pole Hill -SAB 112.500 N055.54.27.000 W002.12.23.000 ; Saint Abbs -SFD 117.000 N050.45.38.000 E000.07.19.000 ; Seaford -SAM 113.350 N050.57.18.900 W001.20.42.200 ; Southampton -STN 115.100 N058.12.25.010 W006.10.58.970 ; Stornoway -STU 113.100 N051.59.41.000 W005.02.25.000 ; Strumble +MCT 113.550 N053.21.25.290 W002.15.44.240 ; Manchester (DME) +MAY 117.900 N051.01.01.860 E000.06.58.040 ; Mayfield (DME) +MID 114.000 N051.03.14.230 W000.37.30.010 ; Midhurst (DME) +NEW 114.250 N055.02.18.410 W001.41.54.140 ; Newcastle (DME) +OCK 115.300 N051.18.18.170 W000.26.49.860 ; Ockham (DME) +OTR 113.900 N053.41.53.570 W000.06.13.450 ; Ottringham +PTH 110.400 N056.26.32.630 W003.22.06.960 ; Perth +POL 112.100 N053.44.37.560 W002.06.11.980 ; Pole Hill +SAB 112.500 N055.54.27.040 W002.12.22.810 ; Saint Abbs +SFD 117.000 N050.45.38.490 E000.07.18.890 ; Seaford +SAM 113.350 N050.57.18.900 W001.20.42.200 ; Southampton (DME) +STN 115.100 N058.12.25.020 W006.10.58.970 ; Stornoway +STU 113.100 N051.59.40.880 W005.02.24.690 ; Strumble SUM 117.350 N059.52.43.340 W001.17.11.490 ; Sumburgh SWB 116.800 N052.47.52.830 W002.39.44.350 ; Shawbury -TLA 113.800 N055.29.57.000 W003.21.10.000 ; Talla +TLA 113.800 N055.29.56.920 W003.21.10.200 ; Talla TIR 117.700 N056.29.35.570 W006.52.32.120 ; Tiree -TNT 115.700 N053.03.14.230 W001.40.11.890 ; Trent -TRN 117.500 N055.18.48.000 W004.47.02.000 ; Turnberry -WAL 114.100 N053.23.31.000 W003.08.04.000 ; Wallasey +TNT 115.700 N053.03.14.230 W001.40.11.890 ; Trent (DME) +TRN 117.500 N055.18.48.280 W004.47.01.890 ; Turnberry (DME) +WAL 114.100 N053.23.30.970 W003.08.04.060 ; Wallasey WIK 113.600 N058.27.31.740 W003.06.01.340 ; Wick WIN 115.750 N051.47.26.300 W002.03.00.770 ; Winstone (DME) @@ -70,4 +70,4 @@ WAD 117.100 N053.09.55.290 W000.31.36.160 ; Waddington WTN 113.200 N053.44.30.290 W002.53.06.560 ; Warton WTZ 109.300 N052.07.18.960 E000.56.25.700 ; Wattisham WIT 117.600 N052.36.28.460 W000.29.55.420 ; Wittering -VLN 111.000 N051.00.18.040 W002.38.19.460 ; Yeovilton +VLN 111.000 N051.00.18.040 W002.38.19.460 ; Yeovilton \ No newline at end of file diff --git a/_data/Tools/resources/airac-dates.pdf b/_data/Tools/resources/airac-dates.pdf new file mode 100644 index 0000000000..e0869ad3e6 Binary files /dev/null and b/_data/Tools/resources/airac-dates.pdf differ diff --git a/_data/Tools/src/api.py b/_data/Tools/src/api.py new file mode 100644 index 0000000000..4168ae1424 --- /dev/null +++ b/_data/Tools/src/api.py @@ -0,0 +1,65 @@ +from src.util import airac, util + +import requests +from bs4 import BeautifulSoup +from loguru import logger +import re + +class AipAPI: + def __init__(self): + self.airac = airac.Airac() + self.cycle = self.airac.cycle() + self.rootUrl = self.airac.url() + + def parseENR4_1(self) -> dict[str,list]: + """Parse the AIP ENR4.1 page + + Returns: + dict[str,list]: A dictionary containing the VOR identifier and some information about it (see example below) + { + "ADN": ["Aberdeen", "114.300", ("N057.18.37.620", "W002.16.01.950")], # name, frequency, coords + ... + } + """ + + url = self.rootUrl + "EG-ENR-4.1-en-GB.html" + text = requests.get(url).text + soup = BeautifulSoup(text, "html.parser") + + # get table rows from heading + + ad22 = soup.find("div", attrs={"id": "ENR-4.1"}) + rows = list(list(ad22.children)[1].children)[1].children + + outputs = {} + + for row in rows: + name = list(list(list(row.children)[0].children)[1].children)[1].string + name = util.capitalise(name) + + vorDmeNdb = str(list(list(row.children)[0].children)[3]) + if re.search(r"NDB", vorDmeNdb): + continue # skip NDBs + elif re.search(r"DME", vorDmeNdb) and not re.search(r"VOR", vorDmeNdb): + name += " (DME)" # add DME to the name if it's only a DME + + identifier = list(list(row.children)[1].children)[1].string + + freq = list(list(list(row.children)[2].children)[1].children)[1].string + try: + float(freq) + except ValueError: + if identifier == "LON": # LON's frequency isn't on the AIP + freq = "113.600" + else: + freq = list(list(list(row.children)[2].children)[3].children)[1].string + + coordA = list(list(list(row.children)[4].children)[0].children)[1].string + coordB = list(list(list(row.children)[4].children)[1].children)[1].string + coords = util.ukCoordsToSectorFile(coordA, coordB) + + # logger.debug(f"{identifier} {freq} {' '.join(coords)} ; {name}") + + outputs[identifier] = [name, freq, coords] + + return outputs diff --git a/_data/Tools/src/runner.py b/_data/Tools/src/runner.py new file mode 100644 index 0000000000..f88ba9a582 --- /dev/null +++ b/_data/Tools/src/runner.py @@ -0,0 +1,44 @@ +import api + +import argparse + +class Runner: + def __init__(self, args): + self.args = args + self.aipApi = api.AipAPI() + + def readCurrentData(self, page): + with open(f"./../../{page}", "r") as f: + return f.read().split("\n") + + def writeLines(self, page, data): + with open(f"./../../{page}", "w") as f: + f.write("\n".join(data)) + + def run(self): + if self.args["page"] == "ENR4.1" or self.args["page"] == "all": # ENR4.1 + # get current data + currentData = self.readCurrentData("Navaids/VOR_UK.txt") + + # get new data + newData = self.aipApi.parseENR4_1() + + # compare + for i, line in enumerate(currentData): + vorID = line.split(" ")[0] + if vorID in newData.keys(): # only rewrite if the VOR/DME is in both the old data and the new data, otherwise existing data is kept + # if the VOR/DME is in both the old data and the new data, write the new data onto the old data (if the data is the same we still write, just no change will be visible because the written data is the same as the stored data) + dataAboutVORDME = newData[vorID] + currentData[i] = f"{vorID} {dataAboutVORDME[1]} {' '.join(dataAboutVORDME[2])} ; {dataAboutVORDME[0]}" + + self.writeLines("Navaids/VOR_UK.txt", currentData) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Parse parts of the UK eAIP, using our AIP API") + parser.add_argument("page", help="The part of the AIP to parse", choices=["all", "ENR4.1"]) + + args = vars(parser.parse_args()) + + runner = Runner(args) + + runner.run() diff --git a/_data/Tools/src/util/airac.py b/_data/Tools/src/util/airac.py new file mode 100644 index 0000000000..4a0301b767 --- /dev/null +++ b/_data/Tools/src/util/airac.py @@ -0,0 +1,72 @@ +""" +eAIP Parser +Chris Parkinson (@chssn) +""" + +# Standard Libraries +import math +from datetime import date, timedelta + +# Third Party Libraries +from loguru import logger + +# Local Libraries + +class Airac: + """Class for general functions relating to AIRAC""" + + def __init__(self): + # First AIRAC date following the last cycle length modification + start_date = "2020-01-02" # 2001 + self.base_date = date.fromisoformat(str(start_date)) + # Length of one AIRAC cycle + self.cycle_days = 28 + + self.cycles = -1 + + def initialise(self, date_in=0) -> int: + """Calculate the number of AIRAC cycles between any given date and the start date""" + + if date_in: + input_date = date.fromisoformat(str(date_in)) + else: + input_date = date.today() + + # How many AIRAC cycles have occured since the start date + diff_cycles = (input_date - self.base_date) / timedelta(days=1) + # Round that number down to the nearest whole integer + self.cycles = math.floor(diff_cycles / self.cycle_days) + + return self.cycles + + def cycle(self, next_cycle:bool=False) -> str: + """Return the date of the current AIRAC cycle""" + + if self.cycles == -1: # only initialise if not already done + self.cycles = self.initialise() + + if next_cycle: + number_of_days = (self.cycles + 1) * self.cycle_days + else: + number_of_days = self.cycles * self.cycle_days + current_cycle = self.base_date + timedelta(days=number_of_days) + logger.debug("Current AIRAC Cycle is: {}", current_cycle) + + return current_cycle + + def url(self, next_cycle:bool=False) -> str: + """Return a generated URL based on the AIRAC cycle start date""" + + base_url = "https://www.aurora.nats.co.uk/htmlAIP/Publications/" + if next_cycle: + # if the 'next_cycle' variable is passed, generate a URL for the next AIRAC cycle + base_date = self.cycle(next_cycle=True) + else: + base_date = self.cycle() + + base_post_string = "-AIRAC/html/eAIP/" + + formatted_url = base_url + str(base_date) + base_post_string + logger.debug(formatted_url) + + return formatted_url diff --git a/_data/Tools/src/util/util.py b/_data/Tools/src/util/util.py new file mode 100644 index 0000000000..e6a7c7593b --- /dev/null +++ b/_data/Tools/src/util/util.py @@ -0,0 +1,17 @@ +import re + +def capitalise(inp): + # capitalise the first letter of each word in a string + out = [] + for word in inp.split(" "): + out.append(word[0].upper() + word[1:].lower()) + + return " ".join(out) + +def ukCoordsToSectorFile(inA, inB): + # convert from XXXXXX.XXN to NXXX.XX.XX.XXX etc + if re.match(r"[0-9]{6}(?:\.[0-9]{2}|)[N|S]", inA) is None or re.match(r"[0-9]{7}(?:\.[0-9]{2}|)[E|W]", inB) is None: + raise ValueError(f"Invalid coordinates provided: {inA}, {inB}") + outA = inA[-1] + "0" + inA[:2] + "." + inA[2:4] + "." + inA[4:6] + "." + inA[7:9].ljust(3, '0') + outB = inB[-1] + inB[:3] + "." + inB[3:5] + "." + inB[5:7] + "." + inB[8:10].ljust(3, '0') + return (outA, outB) # probably should be a tuple diff --git a/_data/Tools/tests/test_airac.py b/_data/Tools/tests/test_airac.py new file mode 100644 index 0000000000..0c6d4ab144 --- /dev/null +++ b/_data/Tools/tests/test_airac.py @@ -0,0 +1,62 @@ +import unittest +from datetime import date +from src.util import airac + +class TestAirac(unittest.TestCase): + def setUp(self): + self.airac = airac.Airac() + + def test_initialise(self): + self.assertEqual(self.airac.initialise("2020-01-02"), 0) # 0 date + self.assertEqual(self.airac.initialise("2023-07-12"), 45) # random date + self.assertEqual(self.airac.initialise("2023-07-19"), 46) # random date + self.assertEqual(self.airac.initialise("2023-08-09"), 46) # edge case + self.assertEqual(self.airac.initialise("2023-08-10"), 47) # edge case + self.assertEqual(self.airac.initialise("2028-12-20"), 116) # far edge case + self.assertEqual(self.airac.initialise("2028-12-21"), 117) # far edge case + + def test_cycle(self): + # next_cycle = False + self.airac.initialise("2020-01-02") + self.assertEqual(self.airac.cycle(), date(2020, 1, 2)) # 0 date + self.airac.initialise("2021-05-16") + self.assertEqual(self.airac.cycle(), date(2021, 4, 22)) # random date + self.airac.initialise("2023-12-27") + self.assertEqual(self.airac.cycle(), date(2023, 11, 30)) # edge case + self.airac.initialise("2023-12-28") + self.assertEqual(self.airac.cycle(), date(2023, 12, 28)) # edge case + + # next_cycle = True + self.airac.initialise("2020-01-02") + self.assertEqual(self.airac.cycle(next_cycle=True), date(2020, 1, 30)) # 0 date + self.airac.initialise("2021-05-16") + self.assertEqual(self.airac.cycle(next_cycle=True), date(2021, 5, 20)) # random date + self.airac.initialise("2023-12-27") + self.assertEqual(self.airac.cycle(next_cycle=True), date(2023, 12, 28)) # edge case + self.airac.initialise("2023-12-28") + self.assertEqual(self.airac.cycle(next_cycle=True), date(2024, 1, 25)) # edge case + + def test_url(self): + # next_cycle = False + self.airac.initialise("2020-01-02") + self.assertEqual(self.airac.url(next_cycle=False), "https://www.aurora.nats.co.uk/htmlAIP/Publications/2020-01-02-AIRAC/html/eAIP/") # 0 date + self.airac.initialise("2021-05-16") + self.assertEqual(self.airac.url(next_cycle=False), "https://www.aurora.nats.co.uk/htmlAIP/Publications/2021-04-22-AIRAC/html/eAIP/") # random date + self.airac.initialise("2023-12-27") + self.assertEqual(self.airac.url(next_cycle=False), "https://www.aurora.nats.co.uk/htmlAIP/Publications/2023-11-30-AIRAC/html/eAIP/") # edge case + self.airac.initialise("2023-12-28") + self.assertEqual(self.airac.url(next_cycle=False), "https://www.aurora.nats.co.uk/htmlAIP/Publications/2023-12-28-AIRAC/html/eAIP/") # edge case + + # next_cycle = True + self.airac.initialise("2020-01-02") + self.assertEqual(self.airac.url(next_cycle=True), "https://www.aurora.nats.co.uk/htmlAIP/Publications/2020-01-30-AIRAC/html/eAIP/") # 0 date + self.airac.initialise("2021-05-16") + self.assertEqual(self.airac.url(next_cycle=True), "https://www.aurora.nats.co.uk/htmlAIP/Publications/2021-05-20-AIRAC/html/eAIP/") # random date + self.airac.initialise("2023-12-27") + self.assertEqual(self.airac.url(next_cycle=True), "https://www.aurora.nats.co.uk/htmlAIP/Publications/2023-12-28-AIRAC/html/eAIP/") # edge case + self.airac.initialise("2023-12-28") + self.assertEqual(self.airac.url(next_cycle=True), "https://www.aurora.nats.co.uk/htmlAIP/Publications/2024-01-25-AIRAC/html/eAIP/") # edge case + + +if __name__ == '__main__': + unittest.main() diff --git a/_data/Tools/tests/test_api.py b/_data/Tools/tests/test_api.py new file mode 100644 index 0000000000..8aae7cac45 --- /dev/null +++ b/_data/Tools/tests/test_api.py @@ -0,0 +1,19 @@ +from src import api +from src.util import airac + +import unittest + +class TestApi(unittest.TestCase): + def setUp(self): + self.aip = api.AipAPI() + + def test_parseENR4_1(self): + self.aip.airac = airac.Airac() + self.aip.airac.initialise("2020-01-02") # test on AIRAC 2001 + self.aip.cycle = self.aip.airac.cycle() + self.aip.rootUrl = self.aip.airac.url() + self.assertEqual(self.aip.parseENR4_1(), {'ADN': ['Aberdeen', '114.300', ('N057.18.38.000', 'W002.16.02.000')], 'BKY': ['Barkway', '116.250', ('N051.59.23.000', 'E000.03.43.000')], 'BEL': ['Belfast', '117.200', ('N054.39.40.000', 'W006.13.48.000')], 'BEN': ['Benbecula (DME)', '113.950', ('N057.28.41.000', 'W007.21.55.000')], 'BHD': ['Berry Head', '112.050', ('N050.23.55.000', 'W003.29.37.000')], 'BIG': ['Biggin', '115.100', ('N051.19.51.000', 'E000.02.05.000')], 'BNN': ['Bovingdon', '113.750', ('N051.43.34.000', 'W000.32.59.000')], 'BCN': ['Brecon', '117.450', ('N051.43.32.000', 'W003.15.47.000')], 'BPK': ['Brookmans Park', '117.500', ('N051.44.59.000', 'W000.06.24.000')], 'CLN': ['Clacton', '114.550', ('N051.50.55.000', 'E001.08.51.000')], 'CPT': ['Compton', '114.350', ('N051.29.30.000', 'W001.13.11.000')], 'DTY': ['Daventry', '116.400', ('N052.10.49.000', 'W001.06.50.000')], 'DCS': ['Dean Cross (DME)', '115.200', ('N054.43.19.000', 'W003.20.26.000')], 'DET': ['Detling', '117.300', ('N051.18.14.000', 'E000.35.50.000')], 'DVR': ['Dover (DME)', '114.950', ('N051.09.45.000', 'E001.21.33.000')], 'DUD': ['Dundonald (DME)', '115.450', ('N055.33.32.000', 'W004.36.06.000')], 'GAM': ['Gamston (DME)', '112.800', ('N053.16.53.000', 'W000.56.50.000')], 'GOW': ['Glasgow (DME)', '115.400', ('N055.52.14.000', 'W004.26.45.000')], 'GWC': ['Goodwood (DME)', '114.750', ('N050.51.19.000', 'W000.45.24.000')], 'GLO': ['Green Lowther (DME)', '109.650', ('N055.23.24.000', 'W003.44.12.000')], 'HON': ['Honiley', '113.650', ('N052.21.24.000', 'W001.39.49.000')], 'IOM': ['Isle Of Man', '112.200', ('N054.04.01.000', 'W004.45.49.000')], 'JSY': ['Jersey', '112.200', ('N049.13.16.000', 'W002.02.46.000')], 'LAM': ['Lambourne', '115.600', ('N051.38.46.000', 'E000.09.06.000')], 'LND': ['Lands End', '114.200', ('N050.08.11.000', 'W005.38.13.000')], 'LON': ['London', '113.600', ('N051.29.14.000', 'W000.28.00.000')], 'LYD': ['Lydd (DME)', '114.050', ('N050.59.59.000', 'E000.52.43.000')], 'MAC': ['Machrihanish (DME)', '116.000', ('N055.25.48.000', 'W005.39.01.000')], 'MCT': ['Manchester', '113.550', ('N053.21.25.000', 'W002.15.44.000')], 'MAY': ['Mayfield', '117.900', ('N051.01.02.000', 'E000.06.58.000')], 'MID': ['Midhurst', '114.000', ('N051.03.14.000', 'W000.37.30.000')], 'NEW': ['Newcastle (DME)', '114.250', ('N055.02.18.000', 'W001.41.54.000')], 'OCK': ['Ockham (DME)', '115.300', ('N051.18.18.000', 'W000.26.50.000')], 'OTR': ['Ottringham', '113.900', ('N053.41.54.000', 'W000.06.13.000')], 'PTH': ['Perth', '110.400', ('N056.26.33.000', 'W003.22.07.000')], 'POL': ['Pole Hill', '112.100', ('N053.44.38.000', 'W002.06.12.000')], 'SAB': ['Saint Abbs', '112.500', ('N055.54.27.000', 'W002.12.23.000')], 'SFD': ['Seaford', '117.000', ('N050.45.38.000', 'E000.07.19.000')], 'SAM': ['Southampton (DME)', '113.350', ('N050.57.19.000', 'W001.20.42.000')], 'STN': ['Stornoway', '115.100', ('N058.12.25.000', 'W006.10.59.000')], 'STU': ['Strumble', '113.100', ('N051.59.41.000', 'W005.02.25.000')], 'SUM': ['Sumburgh', '117.350', ('N059.52.44.000', 'W001.17.12.000')], 'TLA': ['Talla', '113.800', ('N055.29.57.000', 'W003.21.10.000')], 'TIR': ['Tiree', '117.700', ('N056.29.36.000', 'W006.52.32.000')], 'TNT': ['Trent', '115.700', ('N053.03.14.000', 'W001.40.12.000')], 'TRN': ['Turnberry', '117.500', ('N055.18.48.000', 'W004.47.02.000')], 'WAL': ['Wallasey', '114.100', ('N053.23.31.000', 'W003.08.04.000')], 'WIK': ['Wick', '113.600', ('N058.27.32.000', 'W003.06.01.000')]} ) + + +if __name__ == '__main__': + unittest.main() diff --git a/_data/Tools/tests/test_util.py b/_data/Tools/tests/test_util.py new file mode 100644 index 0000000000..caf83fdcfe --- /dev/null +++ b/_data/Tools/tests/test_util.py @@ -0,0 +1,20 @@ +from src.util import util + +import unittest + +class TestUtil(unittest.TestCase): + def test_capitalise(self): + self.assertEqual(util.capitalise("hello world"), "Hello World") # normal + self.assertEqual(util.capitalise("hello world!"), "Hello World!") # punctuation + self.assertEqual(util.capitalise("HELLO WORLD"), "Hello World") # all caps + self.assertEqual(util.capitalise("hElLo"), "Hello") # random, 1 word + + def test_ukCoordsToSectorFile(self): + with self.assertRaises(ValueError): + util.ukCoordsToSectorFile("ABCDEF.GHN", "ABCDEFG.HIW") + self.assertEqual(util.ukCoordsToSectorFile("503011.88N", "0032833.64W"), ("N050.30.11.880", "W003.28.33.640")) # random + self.assertEqual(util.ukCoordsToSectorFile("503011N", "0032833W"), ("N050.30.11.000", "W003.28.33.000")) # no decimal + + +if __name__ == '__main__': + unittest.main()